6 #include "game/level/explosion.h"
7 #include "game/level/script.h"
8 #include "game/level/rigid_bodies.h"
10 #include "math/point.h"
11 #include "platforms.h"
13 #include "system/line_stream.h"
14 #include "system/log.h"
15 #include "system/lt.h"
16 #include "system/nth_alloc.h"
17 #include "system/stacktrace.h"
18 #include "ebisp/builtins.h"
20 #define PLAYER_WIDTH 25.0f
21 #define PLAYER_HEIGHT 25.0f
22 #define PLAYER_SPEED 500.0f
23 #define PLAYER_JUMP 32000.0f
24 #define PLAYER_DEATH_DURATION 0.75f
25 #define PLAYER_MAX_JUMP_THRESHOLD 2
27 typedef enum Player_state {
28 PLAYER_STATE_ALIVE = 0,
36 RigidBodies *rigid_bodies;
38 RigidBodyId alive_body_id;
39 Explosion *dying_body;
49 Player *create_player_from_player_layer(const PlayerLayer *player_layer,
50 RigidBodies *rigid_bodies,
53 trace_assert(player_layer);
54 trace_assert(rigid_bodies);
55 trace_assert(broadcast);
59 Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
65 player->rigid_bodies = rigid_bodies;
67 player->alive_body_id = rigid_bodies_add(
70 player_layer->position.x,
71 player_layer->position.y,
75 player->dying_body = PUSH_LT(
78 player_layer->color_picker.color,
79 PLAYER_DEATH_DURATION),
81 if (player->dying_body == NULL) {
85 player->jump_threshold = 0;
86 player->color = player_layer->color_picker.color;
87 player->checkpoint = player_layer->position;
88 player->play_die_cue = 0;
89 player->state = PLAYER_STATE_ALIVE;
94 void destroy_player(Player * player)
96 RETURN_LT0(player->lt);
99 int player_render(const Player * player,
102 trace_assert(player);
103 trace_assert(camera);
105 char debug_text[256];
107 switch (player->state) {
108 case PLAYER_STATE_ALIVE: {
109 snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
110 Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
112 if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
116 return rigid_bodies_render(
117 player->rigid_bodies,
118 player->alive_body_id,
123 case PLAYER_STATE_DYING:
124 return explosion_render(player->dying_body, camera);
132 void player_update(Player *player,
135 trace_assert(player);
137 switch (player->state) {
138 case PLAYER_STATE_ALIVE: {
139 rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
141 const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
144 if (hitbox.y > 1000.0f) {
149 case PLAYER_STATE_DYING: {
150 explosion_update(player->dying_body, delta_time);
152 if (explosion_is_done(player->dying_body)) {
153 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
154 rigid_bodies_transform_velocity(
155 player->rigid_bodies,
156 player->alive_body_id,
157 make_mat3x3(0.0f, 0.0f, 0.0f,
160 rigid_bodies_teleport_to(
161 player->rigid_bodies,
162 player->alive_body_id,
164 player->state = PLAYER_STATE_ALIVE;
172 void player_move_left(Player *player)
174 trace_assert(player);
175 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
178 void player_move_right(Player *player)
180 trace_assert(player);
182 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
185 void player_stop(Player *player)
187 trace_assert(player);
189 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
192 void player_jump(Player *player, Script *supa_script)
194 trace_assert(player);
195 trace_assert(supa_script);
197 if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
198 player->jump_threshold = 0;
201 if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
202 rigid_bodies_transform_velocity(
203 player->rigid_bodies,
204 player->alive_body_id,
205 make_mat3x3(1.0f, 0.0f, 0.0f,
208 rigid_bodies_apply_force(
209 player->rigid_bodies,
210 player->alive_body_id,
211 vec(0.0f, -PLAYER_JUMP));
212 player->jump_threshold++;
214 if (script_has_scope_value(supa_script, "on-player-jump")) {
215 Gc *gc = script_gc(supa_script);
216 script_eval(supa_script, list(gc, "q", "on-player-jump"));
221 void player_die(Player *player)
223 trace_assert(player);
225 if (player->state == PLAYER_STATE_ALIVE) {
228 player->rigid_bodies,
229 player->alive_body_id);
231 player->play_die_cue = 1;
232 explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
233 player->state = PLAYER_STATE_DYING;
234 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
238 void player_focus_camera(Player *player,
241 trace_assert(player);
242 trace_assert(camera);
244 const Rect player_hitbox = rigid_bodies_hitbox(
245 player->rigid_bodies,
246 player->alive_body_id);
251 vec(player_hitbox.x, player_hitbox.y),
252 vec(0.0f, -player_hitbox.h * 0.5f)));
255 void player_hide_goals(const Player *player,
258 trace_assert(player);
260 goals_hide_from_player(
263 player->rigid_bodies,
264 player->alive_body_id));
267 void player_die_from_lava(Player *player,
270 if (lava_overlaps_rect(
273 player->rigid_bodies,
274 player->alive_body_id))) {
279 void player_checkpoint(Player *player, Vec checkpoint)
281 player->checkpoint = checkpoint;
284 int player_sound(Player *player,
285 Sound_samples *sound_samples)
287 if (player->play_die_cue) {
288 player->play_die_cue = 0;
290 if (sound_samples_play_sound(sound_samples, 0) < 0) {
298 bool player_overlaps_rect(const Player *player,
301 trace_assert(player);
303 return player->state == PLAYER_STATE_ALIVE
305 rect, rigid_bodies_hitbox(
306 player->rigid_bodies,
307 player->alive_body_id));
310 Rect player_hitbox(const Player *player)
312 return rigid_bodies_hitbox(
313 player->rigid_bodies,
314 player->alive_body_id);