]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
7f23fc4e56bb66ee5b78a9c7eb6b17de613680dd
[nothing.git] / src / game / level / player.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include <SDL.h>
5
6 #include "game/level/explosion.h"
7 #include "game/level/rigid_bodies.h"
8 #include "goals.h"
9 #include "math/vec.h"
10 #include "platforms.h"
11 #include "player.h"
12 #include "system/line_stream.h"
13 #include "system/log.h"
14 #include "system/lt.h"
15 #include "system/nth_alloc.h"
16 #include "system/stacktrace.h"
17 #include "config.h"
18
19 #define PLAYER_WIDTH 25.0f
20 #define PLAYER_HEIGHT 25.0f
21 #define PLAYER_SPEED 500.0f
22 #define PLAYER_JUMP 32000.0f
23 #define PLAYER_DEATH_DURATION 0.75f
24 #define PLAYER_MAX_JUMP_THRESHOLD 2
25
26 typedef enum Player_state {
27     PLAYER_STATE_ALIVE = 0,
28     PLAYER_STATE_DYING
29 } Player_state;
30
31 struct Player {
32     Lt *lt;
33     Player_state state;
34
35     RigidBodies *rigid_bodies;
36
37     RigidBodyId alive_body_id;
38     Explosion *dying_body;
39
40     int jump_threshold;
41     Color color;
42
43     Vec2f checkpoint;
44
45     int play_die_cue;
46 };
47
48 Player *create_player_from_player_layer(const PlayerLayer *player_layer,
49                                         RigidBodies *rigid_bodies)
50 {
51     trace_assert(player_layer);
52     trace_assert(rigid_bodies);
53
54     Lt *lt = create_lt();
55
56     Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
57     if (player == NULL) {
58         RETURN_LT(lt, NULL);
59     }
60     player->lt = lt;
61
62     player->rigid_bodies = rigid_bodies;
63
64     player->alive_body_id = rigid_bodies_add(
65         rigid_bodies,
66         rect(
67             player_layer->position.x,
68             player_layer->position.y,
69             PLAYER_WIDTH,
70             PLAYER_HEIGHT));
71
72     player->dying_body = PUSH_LT(
73         lt,
74         create_explosion(
75             color_picker_rgba(&player_layer->color_picker),
76             PLAYER_DEATH_DURATION),
77         destroy_explosion);
78     if (player->dying_body == NULL) {
79         RETURN_LT(lt, NULL);
80     }
81
82     player->jump_threshold = 0;
83     player->color = color_picker_rgba(&player_layer->color_picker);
84     player->checkpoint = player_layer->position;
85     player->play_die_cue = 0;
86     player->state = PLAYER_STATE_ALIVE;
87
88     return player;
89 }
90
91 void destroy_player(Player * player)
92 {
93     rigid_bodies_remove(player->rigid_bodies, player->alive_body_id);
94     RETURN_LT0(player->lt);
95 }
96
97 int player_render(const Player * player,
98                   const Camera *camera)
99 {
100     trace_assert(player);
101     trace_assert(camera);
102
103     char debug_text[256];
104
105     switch (player->state) {
106     case PLAYER_STATE_ALIVE: {
107         snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
108         Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
109
110         if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
111             return -1;
112         }
113
114         return rigid_bodies_render(
115             player->rigid_bodies,
116             player->alive_body_id,
117             player->color,
118             camera);
119     }
120
121     case PLAYER_STATE_DYING:
122         return explosion_render(player->dying_body, camera);
123
124     default: {}
125     }
126
127     return 0;
128 }
129
130 void player_update(Player *player,
131                    float delta_time)
132 {
133     trace_assert(player);
134
135     switch (player->state) {
136     case PLAYER_STATE_ALIVE: {
137         rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
138
139         const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
140
141
142         if (hitbox.y > PLAYER_DEATH_LEVEL) {
143             player_die(player);
144         }
145     } break;
146
147     case PLAYER_STATE_DYING: {
148         explosion_update(player->dying_body, delta_time);
149
150         if (explosion_is_done(player->dying_body)) {
151             rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
152             rigid_bodies_transform_velocity(
153                 player->rigid_bodies,
154                 player->alive_body_id,
155                 make_mat3x3(0.0f, 0.0f, 0.0f,
156                             0.0f, 0.0f, 0.0f,
157                             0.0f, 0.0f, 1.0f));
158             rigid_bodies_teleport_to(
159                 player->rigid_bodies,
160                 player->alive_body_id,
161                 player->checkpoint);
162             player->state = PLAYER_STATE_ALIVE;
163         }
164     } break;
165
166     default: {}
167     }
168 }
169
170 void player_move_left(Player *player)
171 {
172     trace_assert(player);
173     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
174 }
175
176 void player_move_right(Player *player)
177 {
178     trace_assert(player);
179
180     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
181 }
182
183 void player_stop(Player *player)
184 {
185     trace_assert(player);
186
187     rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
188 }
189
190 void player_jump(Player *player)
191 {
192     trace_assert(player);
193
194     if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
195         player->jump_threshold = 0;
196     }
197
198     if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
199         rigid_bodies_transform_velocity(
200             player->rigid_bodies,
201             player->alive_body_id,
202             make_mat3x3(1.0f, 0.0f, 0.0f,
203                         0.0f, 0.0f, 0.0f,
204                         0.0f, 0.0f, 1.0f));
205         rigid_bodies_apply_force(
206             player->rigid_bodies,
207             player->alive_body_id,
208             vec(0.0f, -PLAYER_JUMP));
209         player->jump_threshold++;
210     }
211 }
212
213 void player_die(Player *player)
214 {
215     trace_assert(player);
216
217     if (player->state == PLAYER_STATE_ALIVE) {
218         const Rect hitbox =
219             rigid_bodies_hitbox(
220                 player->rigid_bodies,
221                 player->alive_body_id);
222
223         player->play_die_cue = 1;
224         player->jump_threshold = 0;
225         explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
226         player->state = PLAYER_STATE_DYING;
227         rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
228     }
229 }
230
231 void player_focus_camera(Player *player,
232                          Camera *camera)
233 {
234     trace_assert(player);
235     trace_assert(camera);
236
237     const Rect player_hitbox = rigid_bodies_hitbox(
238         player->rigid_bodies,
239         player->alive_body_id);
240
241     camera_center_at(
242         camera,
243         vec_sum(
244             vec(player_hitbox.x, player_hitbox.y),
245             vec(0.0f, -player_hitbox.h * 0.5f)));
246 }
247
248 void player_die_from_lava(Player *player,
249                           const Lava *lava)
250 {
251     if (lava_overlaps_rect(
252             lava,
253             rigid_bodies_hitbox(
254                 player->rigid_bodies,
255                 player->alive_body_id))) {
256         player_die(player);
257     }
258 }
259
260 void player_checkpoint(Player *player, Vec2f checkpoint)
261 {
262     player->checkpoint = checkpoint;
263 }
264
265 int player_sound(Player *player,
266                  Sound_samples *sound_samples)
267 {
268     if (player->play_die_cue) {
269         player->play_die_cue = 0;
270
271         if (sound_samples_play_sound(sound_samples, 0) < 0) {
272             return -1;
273         }
274     }
275
276     return 0;
277 }
278
279 bool player_overlaps_rect(const Player *player,
280                           Rect rect)
281 {
282     trace_assert(player);
283
284     return player->state == PLAYER_STATE_ALIVE
285         && rects_overlap(
286             rect, rigid_bodies_hitbox(
287                 player->rigid_bodies,
288                 player->alive_body_id));
289 }
290
291 Rect player_hitbox(const Player *player)
292 {
293     return rigid_bodies_hitbox(
294         player->rigid_bodies,
295         player->alive_body_id);
296 }