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"
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;
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);
60 Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
66 player->rigid_bodies = rigid_bodies;
68 player->script = PUSH_LT(
70 create_script_from_string(broadcast, player_layer->source_code),
72 if (player->script == NULL) {
76 player->alive_body_id = rigid_bodies_add(
79 player_layer->position.x,
80 player_layer->position.y,
84 player->dying_body = PUSH_LT(
87 player_layer->color_picker.color,
88 PLAYER_DEATH_DURATION),
90 if (player->dying_body == NULL) {
94 player->jump_threshold = 0;
95 player->color = player_layer->color_picker.color;
96 player->checkpoint = player_layer->position;
97 player->play_die_cue = 0;
98 player->state = PLAYER_STATE_ALIVE;
103 void destroy_player(Player * player)
105 RETURN_LT0(player->lt);
108 int player_render(const Player * player,
111 trace_assert(player);
112 trace_assert(camera);
114 char debug_text[256];
116 switch (player->state) {
117 case PLAYER_STATE_ALIVE: {
118 snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
119 Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
121 if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
125 return rigid_bodies_render(
126 player->rigid_bodies,
127 player->alive_body_id,
132 case PLAYER_STATE_DYING:
133 return explosion_render(player->dying_body, camera);
141 void player_update(Player *player,
144 trace_assert(player);
146 switch (player->state) {
147 case PLAYER_STATE_ALIVE: {
148 rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
150 const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
153 if (hitbox.y > 1000.0f) {
158 case PLAYER_STATE_DYING: {
159 explosion_update(player->dying_body, delta_time);
161 if (explosion_is_done(player->dying_body)) {
162 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
163 rigid_bodies_transform_velocity(
164 player->rigid_bodies,
165 player->alive_body_id,
166 make_mat3x3(0.0f, 0.0f, 0.0f,
169 rigid_bodies_teleport_to(
170 player->rigid_bodies,
171 player->alive_body_id,
173 player->state = PLAYER_STATE_ALIVE;
181 void player_move_left(Player *player)
183 trace_assert(player);
184 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
187 void player_move_right(Player *player)
189 trace_assert(player);
191 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
194 void player_stop(Player *player)
196 trace_assert(player);
198 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
201 void player_jump(Player *player)
203 trace_assert(player);
205 if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
206 player->jump_threshold = 0;
209 if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
210 rigid_bodies_transform_velocity(
211 player->rigid_bodies,
212 player->alive_body_id,
213 make_mat3x3(1.0f, 0.0f, 0.0f,
216 rigid_bodies_apply_force(
217 player->rigid_bodies,
218 player->alive_body_id,
219 vec(0.0f, -PLAYER_JUMP));
220 player->jump_threshold++;
222 if (script_has_scope_value(player->script, "on-jump")) {
223 script_eval(player->script, "(on-jump)");
228 void player_die(Player *player)
230 trace_assert(player);
232 if (player->state == PLAYER_STATE_ALIVE) {
235 player->rigid_bodies,
236 player->alive_body_id);
238 player->play_die_cue = 1;
239 explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
240 player->state = PLAYER_STATE_DYING;
241 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
245 void player_focus_camera(Player *player,
248 trace_assert(player);
249 trace_assert(camera);
251 const Rect player_hitbox = rigid_bodies_hitbox(
252 player->rigid_bodies,
253 player->alive_body_id);
258 vec(player_hitbox.x, player_hitbox.y),
259 vec(0.0f, -player_hitbox.h * 0.5f)));
262 void player_hide_goals(const Player *player,
265 trace_assert(player);
267 goals_hide_from_player(
270 player->rigid_bodies,
271 player->alive_body_id));
274 void player_die_from_lava(Player *player,
277 if (lava_overlaps_rect(
280 player->rigid_bodies,
281 player->alive_body_id))) {
286 void player_checkpoint(Player *player, Vec checkpoint)
288 player->checkpoint = checkpoint;
291 int player_sound(Player *player,
292 Sound_samples *sound_samples)
294 if (player->play_die_cue) {
295 player->play_die_cue = 0;
297 if (sound_samples_play_sound(sound_samples, 0) < 0) {
305 bool player_overlaps_rect(const Player *player,
308 trace_assert(player);
310 return player->state == PLAYER_STATE_ALIVE
312 rect, rigid_bodies_hitbox(
313 player->rigid_bodies,
314 player->alive_body_id));
317 Rect player_hitbox(const Player *player)
319 return rigid_bodies_hitbox(
320 player->rigid_bodies,
321 player->alive_body_id);