6 #include "game/level/explosion.h"
7 #include "game/level/script.h"
8 #include "game/level/rigid_bodies.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"
21 #define PLAYER_WIDTH 25.0f
22 #define PLAYER_HEIGHT 25.0f
23 #define PLAYER_SPEED 500.0f
24 #define PLAYER_JUMP 32000.0f
25 #define PLAYER_DEATH_DURATION 0.75f
26 #define PLAYER_MAX_JUMP_THRESHOLD 2
28 typedef enum Player_state {
29 PLAYER_STATE_ALIVE = 0,
37 RigidBodies *rigid_bodies;
39 RigidBodyId alive_body_id;
40 Explosion *dying_body;
50 Player *create_player_from_player_layer(const PlayerLayer *player_layer,
51 RigidBodies *rigid_bodies,
54 trace_assert(player_layer);
55 trace_assert(rigid_bodies);
56 trace_assert(broadcast);
60 Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
66 player->rigid_bodies = rigid_bodies;
68 player->alive_body_id = rigid_bodies_add(
71 player_layer->position.x,
72 player_layer->position.y,
76 player->dying_body = PUSH_LT(
79 color_picker_rgba(&player_layer->color_picker),
80 PLAYER_DEATH_DURATION),
82 if (player->dying_body == NULL) {
86 player->jump_threshold = 0;
87 player->color = color_picker_rgba(&player_layer->color_picker);
88 player->checkpoint = player_layer->position;
89 player->play_die_cue = 0;
90 player->state = PLAYER_STATE_ALIVE;
95 void destroy_player(Player * player)
97 rigid_bodies_remove(player->rigid_bodies, player->alive_body_id);
98 RETURN_LT0(player->lt);
101 int player_render(const Player * player,
102 const Camera *camera)
104 trace_assert(player);
105 trace_assert(camera);
107 char debug_text[256];
109 switch (player->state) {
110 case PLAYER_STATE_ALIVE: {
111 snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
112 Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
114 if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
118 return rigid_bodies_render(
119 player->rigid_bodies,
120 player->alive_body_id,
125 case PLAYER_STATE_DYING:
126 return explosion_render(player->dying_body, camera);
134 void player_update(Player *player,
137 trace_assert(player);
139 switch (player->state) {
140 case PLAYER_STATE_ALIVE: {
141 rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
143 const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
146 if (hitbox.y > PLAYER_DEATH_LEVEL) {
151 case PLAYER_STATE_DYING: {
152 explosion_update(player->dying_body, delta_time);
154 if (explosion_is_done(player->dying_body)) {
155 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
156 rigid_bodies_transform_velocity(
157 player->rigid_bodies,
158 player->alive_body_id,
159 make_mat3x3(0.0f, 0.0f, 0.0f,
162 rigid_bodies_teleport_to(
163 player->rigid_bodies,
164 player->alive_body_id,
166 player->state = PLAYER_STATE_ALIVE;
174 void player_move_left(Player *player)
176 trace_assert(player);
177 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
180 void player_move_right(Player *player)
182 trace_assert(player);
184 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
187 void player_stop(Player *player)
189 trace_assert(player);
191 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
194 void player_jump(Player *player, Script *supa_script)
196 trace_assert(player);
197 trace_assert(supa_script);
199 if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
200 player->jump_threshold = 0;
203 if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
204 rigid_bodies_transform_velocity(
205 player->rigid_bodies,
206 player->alive_body_id,
207 make_mat3x3(1.0f, 0.0f, 0.0f,
210 rigid_bodies_apply_force(
211 player->rigid_bodies,
212 player->alive_body_id,
213 vec(0.0f, -PLAYER_JUMP));
214 player->jump_threshold++;
216 if (script_has_scope_value(supa_script, "on-player-jump")) {
217 Gc *gc = script_gc(supa_script);
218 script_eval(supa_script, list(gc, "q", "on-player-jump"));
223 void player_die(Player *player)
225 trace_assert(player);
227 if (player->state == PLAYER_STATE_ALIVE) {
230 player->rigid_bodies,
231 player->alive_body_id);
233 player->play_die_cue = 1;
234 explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
235 player->state = PLAYER_STATE_DYING;
236 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
240 void player_focus_camera(Player *player,
243 trace_assert(player);
244 trace_assert(camera);
246 const Rect player_hitbox = rigid_bodies_hitbox(
247 player->rigid_bodies,
248 player->alive_body_id);
253 vec(player_hitbox.x, player_hitbox.y),
254 vec(0.0f, -player_hitbox.h * 0.5f)));
257 void player_hide_goals(const Player *player,
260 trace_assert(player);
262 goals_hide_from_player(
265 player->rigid_bodies,
266 player->alive_body_id));
269 void player_die_from_lava(Player *player,
272 if (lava_overlaps_rect(
275 player->rigid_bodies,
276 player->alive_body_id))) {
281 void player_checkpoint(Player *player, Vec checkpoint)
283 player->checkpoint = checkpoint;
286 int player_sound(Player *player,
287 Sound_samples *sound_samples)
289 if (player->play_die_cue) {
290 player->play_die_cue = 0;
292 if (sound_samples_play_sound(sound_samples, 0) < 0) {
300 bool player_overlaps_rect(const Player *player,
303 trace_assert(player);
305 return player->state == PLAYER_STATE_ALIVE
307 rect, rigid_bodies_hitbox(
308 player->rigid_bodies,
309 player->alive_body_id));
312 Rect player_hitbox(const Player *player)
314 return rigid_bodies_hitbox(
315 player->rigid_bodies,
316 player->alive_body_id);