-#include <SDL2/SDL.h>
-#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
-#include "game/level/player/dying_rect.h"
-#include "game/level/player/rigid_rect.h"
+#include <SDL.h>
+
+#include "game/level/explosion.h"
+#include "game/level/rigid_bodies.h"
#include "goals.h"
-#include "math/point.h"
+#include "math/vec.h"
#include "platforms.h"
#include "player.h"
-#include "system/error.h"
+#include "system/line_stream.h"
+#include "system/log.h"
#include "system/lt.h"
+#include "system/nth_alloc.h"
+#include "system/stacktrace.h"
+#include "config.h"
#define PLAYER_WIDTH 25.0f
#define PLAYER_HEIGHT 25.0f
#define PLAYER_SPEED 500.0f
-#define PLAYER_JUMP 38000.0f
+#define PLAYER_JUMP 32000.0f
#define PLAYER_DEATH_DURATION 0.75f
-/* TODO(#206): double-jump is broken */
-#define PLAYER_MAX_JUMP_COUNT 1
+#define PLAYER_MAX_JUMP_THRESHOLD 2
-typedef enum player_state_t {
+typedef enum Player_state {
PLAYER_STATE_ALIVE = 0,
PLAYER_STATE_DYING
-} player_state_t;
+} Player_state;
+
+struct Player {
+ Lt *lt;
+ Player_state state;
-struct player_t {
- lt_t *lt;
- player_state_t state;
+ RigidBodies *rigid_bodies;
- rigid_rect_t *alive_body;
- dying_rect_t *dying_body;
+ RigidBodyId alive_body_id;
+ Explosion *dying_body;
- int jump_count;
- color_t color;
+ int jump_threshold;
+ Color color;
- vec_t checkpoint;
+ Vec2f checkpoint;
int play_die_cue;
};
-player_t *create_player(float x, float y, color_t color)
+Player *create_player_from_player_layer(const PlayerLayer *player_layer,
+ RigidBodies *rigid_bodies)
{
- lt_t *lt = create_lt();
+ trace_assert(player_layer);
+ trace_assert(rigid_bodies);
- if (lt == NULL) {
- return NULL;
- }
+ Lt *lt = create_lt();
- player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
+ Player *player = PUSH_LT(lt, nth_calloc(1, sizeof(Player)), free);
if (player == NULL) {
- throw_error(ERROR_TYPE_LIBC);
RETURN_LT(lt, NULL);
}
+ player->lt = lt;
- player->state = PLAYER_STATE_ALIVE;
+ player->rigid_bodies = rigid_bodies;
- player->alive_body = PUSH_LT(
- lt,
- create_rigid_rect(
- rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
- color),
- destroy_rigid_rect);
- if (player->alive_body == NULL) {
- RETURN_LT(lt, NULL);
- }
+ player->alive_body_id = rigid_bodies_add(
+ rigid_bodies,
+ rect(
+ player_layer->position.x,
+ player_layer->position.y,
+ PLAYER_WIDTH,
+ PLAYER_HEIGHT));
player->dying_body = PUSH_LT(
lt,
- create_dying_rect(
- rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
- color,
+ create_explosion(
+ color_picker_rgba(&player_layer->color_picker),
PLAYER_DEATH_DURATION),
- destroy_dying_rect);
+ destroy_explosion);
if (player->dying_body == NULL) {
RETURN_LT(lt, NULL);
}
- player->lt = lt;
- player->jump_count = 0;
- player->color = color;
- player->checkpoint = vec(x, y);
+ player->jump_threshold = 0;
+ player->color = color_picker_rgba(&player_layer->color_picker);
+ player->checkpoint = player_layer->position;
player->play_die_cue = 0;
+ player->state = PLAYER_STATE_ALIVE;
return player;
}
-player_t *create_player_from_stream(FILE *stream)
-{
- float x = 0.0f, y = 0.0f;
-
- char color[7];
- if (fscanf(stream, "%f%f%6s", &x, &y, color) == EOF) {
- throw_error(ERROR_TYPE_LIBC);
- return NULL;
- }
-
- return create_player(x, y, color_from_hexstr(color));
-}
-
-void destroy_player(player_t * player)
+void destroy_player(Player * player)
{
+ rigid_bodies_remove(player->rigid_bodies, player->alive_body_id);
RETURN_LT0(player->lt);
}
-solid_ref_t player_as_solid(player_t *player)
+int player_render(const Player * player,
+ const Camera *camera)
{
- solid_ref_t ref = {
- .tag = SOLID_PLAYER,
- .ptr = (void*) player
- };
+ trace_assert(player);
+ trace_assert(camera);
- return ref;
-}
-
-int player_render(const player_t * player,
- const camera_t *camera)
-{
- assert(player);
- assert(camera);
+ char debug_text[256];
switch (player->state) {
- case PLAYER_STATE_ALIVE:
- return rigid_rect_render(player->alive_body, camera);
+ case PLAYER_STATE_ALIVE: {
+ snprintf(debug_text, 256, "Jump: %d", player->jump_threshold);
+ Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
+
+ if (camera_render_debug_text(camera, debug_text, vec(hitbox.x, hitbox.y - 20.0f)) < 0) {
+ return -1;
+ }
+
+ return rigid_bodies_render(
+ player->rigid_bodies,
+ player->alive_body_id,
+ player->color,
+ camera);
+ }
case PLAYER_STATE_DYING:
- return dying_rect_render(player->dying_body, camera);
+ return explosion_render(player->dying_body, camera);
default: {}
}
return 0;
}
-void player_update(player_t *player,
+void player_update(Player *player,
float delta_time)
{
- assert(player);
+ trace_assert(player);
switch (player->state) {
case PLAYER_STATE_ALIVE: {
- rigid_rect_update(player->alive_body, delta_time);
+ rigid_bodies_update(player->rigid_bodies, player->alive_body_id, delta_time);
- const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
+ const Rect hitbox = rigid_bodies_hitbox(player->rigid_bodies, player->alive_body_id);
- if (hitbox.y > 1000.0f) {
+
+ if (hitbox.y > PLAYER_DEATH_LEVEL) {
player_die(player);
}
} break;
case PLAYER_STATE_DYING: {
- dying_rect_update(player->dying_body, delta_time);
-
- if (dying_rect_is_dead(player->dying_body)) {
- player->alive_body = RESET_LT(
- player->lt,
- player->alive_body,
- create_rigid_rect(
- rect_from_vecs(
- player->checkpoint,
- vec(PLAYER_WIDTH, PLAYER_HEIGHT)),
- player->color));
+ explosion_update(player->dying_body, delta_time);
+
+ if (explosion_is_done(player->dying_body)) {
+ rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, false);
+ rigid_bodies_transform_velocity(
+ player->rigid_bodies,
+ player->alive_body_id,
+ make_mat3x3(0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f));
+ rigid_bodies_teleport_to(
+ player->rigid_bodies,
+ player->alive_body_id,
+ player->checkpoint);
player->state = PLAYER_STATE_ALIVE;
}
} break;
}
}
-void player_collide_with_solid(player_t *player, solid_ref_t solid)
+void player_move_left(Player *player)
{
- if (player->state == PLAYER_STATE_ALIVE) {
- rigid_rect_collide_with_solid(player->alive_body, solid);
-
- if (rigid_rect_touches_ground(player->alive_body)) {
- player->jump_count = 0;
- }
- }
+ trace_assert(player);
+ rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(-PLAYER_SPEED, 0.0f));
}
-void player_move_left(player_t *player)
+void player_move_right(Player *player)
{
- assert(player);
- rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
+ trace_assert(player);
+
+ rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(PLAYER_SPEED, 0.0f));
}
-void player_move_right(player_t *player)
+void player_stop(Player *player)
{
- assert(player);
+ trace_assert(player);
- rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
+ rigid_bodies_move(player->rigid_bodies, player->alive_body_id, vec(0.0f, 0.0f));
}
-void player_stop(player_t *player)
+void player_jump(Player *player)
{
- assert(player);
+ trace_assert(player);
- rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
-}
+ if (rigid_bodies_touches_ground(player->rigid_bodies, player->alive_body_id)) {
+ player->jump_threshold = 0;
+ }
-void player_jump(player_t *player)
-{
- assert(player);
- if (player->jump_count < PLAYER_MAX_JUMP_COUNT) {
- rigid_rect_apply_force(player->alive_body,
- vec(0.0f, -PLAYER_JUMP));
- player->jump_count++;
+ if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
+ rigid_bodies_transform_velocity(
+ player->rigid_bodies,
+ player->alive_body_id,
+ make_mat3x3(1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f));
+ rigid_bodies_apply_force(
+ player->rigid_bodies,
+ player->alive_body_id,
+ vec(0.0f, -PLAYER_JUMP));
+ player->jump_threshold++;
}
}
-void player_die(player_t *player)
+void player_die(Player *player)
{
- assert(player);
+ trace_assert(player);
if (player->state == PLAYER_STATE_ALIVE) {
+ const Rect hitbox =
+ rigid_bodies_hitbox(
+ player->rigid_bodies,
+ player->alive_body_id);
+
player->play_die_cue = 1;
- player->dying_body = RESET_LT(
- player->lt,
- player->dying_body,
- create_dying_rect(
- rigid_rect_hitbox(player->alive_body),
- player->color,
- PLAYER_DEATH_DURATION));
+ explosion_start(player->dying_body, vec(hitbox.x, hitbox.y));
player->state = PLAYER_STATE_DYING;
+ rigid_bodies_disable(player->rigid_bodies, player->alive_body_id, true);
}
}
-void player_focus_camera(player_t *player,
- camera_t *camera)
+void player_focus_camera(Player *player,
+ Camera *camera)
{
- assert(player);
- assert(camera);
+ trace_assert(player);
+ trace_assert(camera);
- const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
+ const Rect player_hitbox = rigid_bodies_hitbox(
+ player->rigid_bodies,
+ player->alive_body_id);
camera_center_at(
camera,
vec(0.0f, -player_hitbox.h * 0.5f)));
}
-void player_hide_goals(const player_t *player,
- goals_t *goals)
-{
- assert(player);
- assert(goals);
- goals_hide(goals, rigid_rect_hitbox(player->alive_body));
-}
-
-void player_die_from_lava(player_t *player,
- const lava_t *lava)
+void player_die_from_lava(Player *player,
+ const Lava *lava)
{
- if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
+ if (lava_overlaps_rect(
+ lava,
+ rigid_bodies_hitbox(
+ player->rigid_bodies,
+ player->alive_body_id))) {
player_die(player);
}
}
-void player_checkpoint(player_t *player, vec_t checkpoint)
+void player_checkpoint(Player *player, Vec2f checkpoint)
{
player->checkpoint = checkpoint;
}
-int player_sound(player_t *player,
- sound_samples_t *sound_samples)
+int player_sound(Player *player,
+ Sound_samples *sound_samples)
{
if (player->play_die_cue) {
player->play_die_cue = 0;
- if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
+ if (sound_samples_play_sound(sound_samples, 0) < 0) {
return -1;
}
}
return 0;
}
-void player_touches_rect_sides(player_t *player,
- rect_t object,
- int sides[RECT_SIDE_N])
+bool player_overlaps_rect(const Player *player,
+ Rect rect)
{
- if (player->state == PLAYER_STATE_ALIVE) {
- rigid_rect_touches_rect_sides(player->alive_body, object, sides);
- }
+ trace_assert(player);
+
+ return player->state == PLAYER_STATE_ALIVE
+ && rects_overlap(
+ rect, rigid_bodies_hitbox(
+ player->rigid_bodies,
+ player->alive_body_id));
}
-void player_apply_force(player_t *player, vec_t force)
+Rect player_hitbox(const Player *player)
{
- if (player->state == PLAYER_STATE_ALIVE) {
- rigid_rect_apply_force(player->alive_body, force);
- }
+ return rigid_bodies_hitbox(
+ player->rigid_bodies,
+ player->alive_body_id);
}