6 #include "game/level/explosion.h"
7 #include "game/level/rigid_bodies.h"
10 #include "platforms.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"
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
26 typedef enum Player_state {
27 PLAYER_STATE_ALIVE = 0,
35 RigidBodies *rigid_bodies;
37 RigidBodyId alive_body_id;
38 Explosion *dying_body;
48 Player *create_player_from_player_layer(const PlayerLayer *player_layer,
49 RigidBodies *rigid_bodies)
51 trace_assert(player_layer);
52 trace_assert(rigid_bodies);
56 Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
62 player->rigid_bodies = rigid_bodies;
64 player->alive_body_id = rigid_bodies_add(
67 player_layer->position.x,
68 player_layer->position.y,
72 player->dying_body = PUSH_LT(
75 color_picker_rgba(&player_layer->color_picker),
76 PLAYER_DEATH_DURATION),
78 if (player->dying_body == NULL) {
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;
91 void destroy_player(Player * player)
93 rigid_bodies_remove(player->rigid_bodies, player->alive_body_id);
94 RETURN_LT0(player->lt);
97 int player_render(const Player * player,
100 trace_assert(player);
101 trace_assert(camera);
103 char debug_text[256];
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);
110 if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
114 return rigid_bodies_render(
115 player->rigid_bodies,
116 player->alive_body_id,
121 case PLAYER_STATE_DYING:
122 return explosion_render(player->dying_body, camera);
130 void player_update(Player *player,
133 trace_assert(player);
135 switch (player->state) {
136 case PLAYER_STATE_ALIVE: {
137 rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
139 const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
142 if (hitbox.y > PLAYER_DEATH_LEVEL) {
147 case PLAYER_STATE_DYING: {
148 explosion_update(player->dying_body, delta_time);
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,
158 rigid_bodies_teleport_to(
159 player->rigid_bodies,
160 player->alive_body_id,
162 player->state = PLAYER_STATE_ALIVE;
170 void player_move_left(Player *player)
172 trace_assert(player);
173 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
176 void player_move_right(Player *player)
178 trace_assert(player);
180 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
183 void player_stop(Player *player)
185 trace_assert(player);
187 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
190 void player_jump(Player *player)
192 trace_assert(player);
194 if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
195 player->jump_threshold = 0;
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,
205 rigid_bodies_apply_force(
206 player->rigid_bodies,
207 player->alive_body_id,
208 vec(0.0f, -PLAYER_JUMP));
209 player->jump_threshold++;
213 void player_die(Player *player)
215 trace_assert(player);
217 if (player->state == PLAYER_STATE_ALIVE) {
220 player->rigid_bodies,
221 player->alive_body_id);
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);
231 void player_focus_camera(Player *player,
234 trace_assert(player);
235 trace_assert(camera);
237 const Rect player_hitbox = rigid_bodies_hitbox(
238 player->rigid_bodies,
239 player->alive_body_id);
244 vec(player_hitbox.x, player_hitbox.y),
245 vec(0.0f, -player_hitbox.h * 0.5f)));
248 void player_die_from_lava(Player *player,
251 if (lava_overlaps_rect(
254 player->rigid_bodies,
255 player->alive_body_id))) {
260 void player_checkpoint(Player *player, Vec2f checkpoint)
262 player->checkpoint = checkpoint;
265 int player_sound(Player *player,
266 Sound_samples *sound_samples)
268 if (player->play_die_cue) {
269 player->play_die_cue = 0;
271 if (sound_samples_play_sound(sound_samples, 0) < 0) {
279 bool player_overlaps_rect(const Player *player,
282 trace_assert(player);
284 return player->state == PLAYER_STATE_ALIVE
286 rect, rigid_bodies_hitbox(
287 player->rigid_bodies,
288 player->alive_body_id));
291 Rect player_hitbox(const Player *player)
293 return rigid_bodies_hitbox(
294 player->rigid_bodies,
295 player->alive_body_id);