2 #include "system/stacktrace.h"
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"
18 #define PLAYER_WIDTH 25.0f
19 #define PLAYER_HEIGHT 25.0f
20 #define PLAYER_SPEED 500.0f
21 #define PLAYER_JUMP 32000.0f
22 #define PLAYER_DEATH_DURATION 0.75f
23 #define PLAYER_MAX_JUMP_THRESHOLD 2
25 typedef enum Player_state {
26 PLAYER_STATE_ALIVE = 0,
34 RigidBodies *rigid_bodies;
36 RigidBodyId alive_body_id;
37 Explosion *dying_body;
48 Player *create_player_from_line_stream(LineStream *line_stream, RigidBodies *rigid_bodies, Broadcast *broadcast)
50 trace_assert(line_stream);
55 Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
61 player->rigid_bodies = rigid_bodies;
63 float x = 0.0f, y = 0.0f;
67 line_stream_next(line_stream),
69 &x, &y, colorstr) == EOF) {
70 log_fail("Could not read player\n");
74 player->script = PUSH_LT(
76 create_script_from_line_stream(line_stream, broadcast),
78 if (player->script == NULL) {
82 const Color color = hexstr(colorstr);
84 player->alive_body_id = rigid_bodies_add(
86 rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT));
88 player->dying_body = PUSH_LT(
92 PLAYER_DEATH_DURATION),
94 if (player->dying_body == NULL) {
98 player->jump_threshold = 0;
99 player->color = color;
100 player->checkpoint = vec(x, y);
101 player->play_die_cue = 0;
102 player->state = PLAYER_STATE_ALIVE;
107 void destroy_player(Player * player)
109 RETURN_LT0(player->lt);
112 int player_render(const Player * player,
115 trace_assert(player);
116 trace_assert(camera);
118 char debug_text[256];
120 switch (player->state) {
121 case PLAYER_STATE_ALIVE: {
122 snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
123 Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
125 if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
129 return rigid_bodies_render(
130 player->rigid_bodies,
131 player->alive_body_id,
136 case PLAYER_STATE_DYING:
137 return explosion_render(player->dying_body, camera);
145 void player_update(Player *player,
148 trace_assert(player);
150 switch (player->state) {
151 case PLAYER_STATE_ALIVE: {
152 rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
154 const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
157 if (hitbox.y > 1000.0f) {
162 case PLAYER_STATE_DYING: {
163 explosion_update(player->dying_body, delta_time);
165 if (explosion_is_done(player->dying_body)) {
166 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
167 rigid_bodies_transform_velocity(
168 player->rigid_bodies,
169 player->alive_body_id,
170 make_mat3x3(0.0f, 0.0f, 0.0f,
173 rigid_bodies_teleport_to(
174 player->rigid_bodies,
175 player->alive_body_id,
177 player->state = PLAYER_STATE_ALIVE;
185 void player_move_left(Player *player)
187 trace_assert(player);
188 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
191 void player_move_right(Player *player)
193 trace_assert(player);
195 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
198 void player_stop(Player *player)
200 trace_assert(player);
202 rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
205 void player_jump(Player *player)
207 trace_assert(player);
209 if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
210 player->jump_threshold = 0;
213 if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
214 rigid_bodies_transform_velocity(
215 player->rigid_bodies,
216 player->alive_body_id,
217 make_mat3x3(1.0f, 0.0f, 0.0f,
220 rigid_bodies_apply_force(
221 player->rigid_bodies,
222 player->alive_body_id,
223 vec(0.0f, -PLAYER_JUMP));
224 player->jump_threshold++;
226 if (script_has_scope_value(player->script, "on-jump")) {
227 script_eval(player->script, "(on-jump)");
232 void player_die(Player *player)
234 trace_assert(player);
236 if (player->state == PLAYER_STATE_ALIVE) {
239 player->rigid_bodies,
240 player->alive_body_id);
242 player->play_die_cue = 1;
243 explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
244 player->state = PLAYER_STATE_DYING;
245 rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
249 void player_focus_camera(Player *player,
252 trace_assert(player);
253 trace_assert(camera);
255 const Rect player_hitbox = rigid_bodies_hitbox(
256 player->rigid_bodies,
257 player->alive_body_id);
262 vec(player_hitbox.x, player_hitbox.y),
263 vec(0.0f, -player_hitbox.h * 0.5f)));
266 void player_hide_goals(const Player *player,
269 trace_assert(player);
271 goals_hide_from_player(
274 player->rigid_bodies,
275 player->alive_body_id));
278 void player_die_from_lava(Player *player,
281 if (lava_overlaps_rect(
284 player->rigid_bodies,
285 player->alive_body_id))) {
290 void player_checkpoint(Player *player, Vec checkpoint)
292 player->checkpoint = checkpoint;
295 int player_sound(Player *player,
296 Sound_samples *sound_samples)
298 if (player->play_die_cue) {
299 player->play_die_cue = 0;
301 if (sound_samples_play_sound(sound_samples, 0) < 0) {
309 bool player_overlaps_rect(const Player *player,
312 trace_assert(player);
314 return player->state == PLAYER_STATE_ALIVE
316 rect, rigid_bodies_hitbox(
317 player->rigid_bodies,
318 player->alive_body_id));
321 Rect player_hitbox(const Player *player)
323 return rigid_bodies_hitbox(
324 player->rigid_bodies,
325 player->alive_body_id);