2 #include "system/stacktrace.h"
6 #include "game/level/player/dying_rect.h"
7 #include "game/level/player/rigid_rect.h"
8 #include "game/level/script.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,
30 /* TODO(#633): Player's alive_body should be a body from RigidBodies */
35 Rigid_rect *alive_body;
36 Dying_rect *dying_body;
47 Player *create_player_from_line_stream(LineStream *line_stream, Broadcast *broadcast)
49 trace_assert(line_stream);
57 Player *player = PUSH_LT(lt, nth_alloc(sizeof(Player)), free);
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 = PUSH_LT(
87 rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
91 if (player->alive_body == NULL) {
95 player->dying_body = PUSH_LT(
99 PLAYER_DEATH_DURATION),
101 if (player->dying_body == NULL) {
105 player->jump_threshold = 0;
106 player->color = color;
107 player->checkpoint = vec(x, y);
108 player->play_die_cue = 0;
109 player->state = PLAYER_STATE_ALIVE;
114 void destroy_player(Player * player)
116 RETURN_LT0(player->lt);
119 Solid_ref player_as_solid(Player *player)
123 .ptr = (void*) player
129 int player_render(const Player * player,
132 trace_assert(player);
133 trace_assert(camera);
135 switch (player->state) {
136 case PLAYER_STATE_ALIVE:
137 return rigid_rect_render(player->alive_body, camera);
139 case PLAYER_STATE_DYING:
140 return dying_rect_render(player->dying_body, camera);
148 void player_update(Player *player,
151 trace_assert(player);
153 switch (player->state) {
154 case PLAYER_STATE_ALIVE: {
155 rigid_rect_update(player->alive_body, delta_time);
157 const Rect hitbox = rigid_rect_hitbox(player->alive_body);
159 if (hitbox.y > 1000.0f) {
164 case PLAYER_STATE_DYING: {
165 dying_rect_update(player->dying_body, delta_time);
167 if (dying_rect_is_dead(player->dying_body)) {
168 rigid_rect_transform_velocity(
170 make_mat3x3(0.0f, 0.0f, 0.0f,
173 rigid_rect_teleport_to(player->alive_body, player->checkpoint);
174 player->state = PLAYER_STATE_ALIVE;
182 void player_collide_with_solid(Player *player, Solid_ref solid)
184 if (player->state == PLAYER_STATE_ALIVE) {
185 rigid_rect_collide_with_solid(player->alive_body, solid);
187 if (rigid_rect_touches_ground(player->alive_body)) {
188 player->jump_threshold = 0;
193 void player_move_left(Player *player)
195 trace_assert(player);
196 rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
199 void player_move_right(Player *player)
201 trace_assert(player);
203 rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
206 void player_stop(Player *player)
208 trace_assert(player);
210 rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
213 void player_jump(Player *player)
215 trace_assert(player);
216 if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
217 rigid_rect_transform_velocity(player->alive_body,
218 make_mat3x3(1.0f, 0.0f, 0.0f,
221 rigid_rect_apply_force(player->alive_body,
222 vec(0.0f, -PLAYER_JUMP));
223 player->jump_threshold++;
225 if (script_has_scope_value(player->script, "on-jump")) {
226 script_eval(player->script, "(on-jump)");
231 void player_die(Player *player)
233 trace_assert(player);
235 if (player->state == PLAYER_STATE_ALIVE) {
237 rigid_rect_hitbox(player->alive_body);
239 player->play_die_cue = 1;
240 dying_rect_start_dying(player->dying_body, vec(hitbox.x, hitbox.y));
241 player->state = PLAYER_STATE_DYING;
245 void player_focus_camera(Player *player,
248 trace_assert(player);
249 trace_assert(camera);
251 const Rect player_hitbox = rigid_rect_hitbox(player->alive_body);
256 vec(player_hitbox.x, player_hitbox.y),
257 vec(0.0f, -player_hitbox.h * 0.5f)));
260 void player_hide_goals(const Player *player,
263 trace_assert(player);
265 goals_hide_from_player(goals, rigid_rect_hitbox(player->alive_body));
268 void player_die_from_lava(Player *player,
271 if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
276 void player_checkpoint(Player *player, Vec checkpoint)
278 player->checkpoint = checkpoint;
281 int player_sound(Player *player,
282 Sound_samples *sound_samples)
284 if (player->play_die_cue) {
285 player->play_die_cue = 0;
287 if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
295 void player_touches_rect_sides(Player *player,
297 int sides[RECT_SIDE_N])
299 if (player->state == PLAYER_STATE_ALIVE) {
300 rigid_rect_touches_rect_sides(player->alive_body, object, sides);
304 void player_apply_force(Player *player, Vec force)
306 if (player->state == PLAYER_STATE_ALIVE) {
307 rigid_rect_apply_force(player->alive_body, force);
311 Rigid_rect *player_rigid_rect(Player *player, const char *id)
313 trace_assert(player);
316 if (player->state == PLAYER_STATE_ALIVE) {
317 if (rigid_rect_has_id(player->alive_body, id)) {
318 return player->alive_body;
325 bool player_overlaps_rect(const Player *player,
328 trace_assert(player);
330 return player->state == PLAYER_STATE_ALIVE
332 rect, rigid_rect_hitbox(
333 player->alive_body));