#include <SDL2/SDL.h>
-#include <assert.h>
+#include "system/stacktrace.h"
#include <stdio.h>
#include <stdlib.h>
#include "game/level/player/dying_rect.h"
#include "game/level/player/rigid_rect.h"
+#include "game/level/script.h"
#include "goals.h"
#include "math/point.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"
#define PLAYER_WIDTH 25.0f
#define PLAYER_HEIGHT 25.0f
#define PLAYER_SPEED 500.0f
-#define PLAYER_JUMP 550.0f
+#define PLAYER_JUMP 32000.0f
#define PLAYER_DEATH_DURATION 0.75f
+#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_t {
- lt_t *lt;
- player_state_t state;
+/* TODO(#633): Player's alive_body should be a body from RigidBodies */
+struct Player {
+ Lt *lt;
+ Player_state state;
- rigid_rect_t *alive_body;
- dying_rect_t *dying_body;
+ Rigid_rect *alive_body;
+ Dying_rect *dying_body;
+ Script *script;
- int jump_count;
- color_t color;
+ int jump_threshold;
+ Color color;
- /* TODO(#110): introduce checkpoints */
- vec_t checkpoint;
+ Vec checkpoint;
int play_die_cue;
};
-player_t *create_player(float x, float y, color_t color)
+Player *create_player_from_line_stream(LineStream *line_stream, Broadcast *broadcast)
{
- lt_t *lt = create_lt();
+ trace_assert(line_stream);
+
+ Lt *lt = create_lt();
if (lt == NULL) {
return NULL;
}
- player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
+ Player *player = PUSH_LT(lt, nth_alloc(sizeof(Player)), free);
if (player == NULL) {
- throw_error(ERROR_TYPE_LIBC);
RETURN_LT(lt, NULL);
}
+ player->lt = lt;
- player->state = PLAYER_STATE_ALIVE;
+ float x = 0.0f, y = 0.0f;
+ char colorstr[7];
+
+ if (sscanf(
+ line_stream_next(line_stream),
+ "%f%f%6s",
+ &x, &y, colorstr) == EOF) {
+ log_fail("Could not read player\n");
+ RETURN_LT(lt, NULL);
+ }
+
+ player->script = PUSH_LT(
+ lt,
+ create_script_from_line_stream(line_stream, broadcast),
+ destroy_script);
+ if (player->script == NULL) {
+ RETURN_LT(lt, NULL);
+ }
+
+ const Color color = hexstr(colorstr);
player->alive_body = PUSH_LT(
lt,
create_rigid_rect(
rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
- color),
+ color,
+ "player"),
destroy_rigid_rect);
if (player->alive_body == NULL) {
RETURN_LT(lt, NULL);
player->dying_body = PUSH_LT(
lt,
create_dying_rect(
- rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
color,
PLAYER_DEATH_DURATION),
destroy_dying_rect);
RETURN_LT(lt, NULL);
}
- player->lt = lt;
- player->jump_count = 0;
+ player->jump_threshold = 0;
player->color = color;
player->checkpoint = vec(x, y);
player->play_die_cue = 0;
+ player->state = PLAYER_STATE_ALIVE;
return player;
}
-player_t *create_player_from_stream(FILE *stream)
+void destroy_player(Player * player)
{
- 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));
+ RETURN_LT0(player->lt);
}
-void destroy_player(player_t * player)
+Solid_ref player_as_solid(Player *player)
{
- RETURN_LT0(player->lt);
+ Solid_ref ref = {
+ .tag = SOLID_PLAYER,
+ .ptr = (void*) player
+ };
+
+ return ref;
}
-int player_render(const player_t * player,
- const camera_t *camera)
+int player_render(const Player * player,
+ Camera *camera)
{
- assert(player);
- assert(camera);
+ trace_assert(player);
+ trace_assert(camera);
switch (player->state) {
case PLAYER_STATE_ALIVE:
return 0;
}
-void player_update(player_t *player,
- const platforms_t *platforms,
+void player_update(Player *player,
float delta_time)
{
- assert(player);
- assert(platforms);
+ trace_assert(player);
switch (player->state) {
case PLAYER_STATE_ALIVE: {
- rigid_rect_update(player->alive_body, platforms, delta_time);
-
- if (rigid_rect_touches_ground(player->alive_body)) {
- player->jump_count = 0;
- }
+ rigid_rect_update(player->alive_body, delta_time);
- const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
+ const Rect hitbox = rigid_rect_hitbox(player->alive_body);
if (hitbox.y > 1000.0f) {
player_die(player);
dying_rect_update(player->dying_body, delta_time);
if (dying_rect_is_dead(player->dying_body)) {
- player->alive_body = RESET_LT(
- player->lt,
+ rigid_rect_transform_velocity(
player->alive_body,
- create_rigid_rect(
- rect_from_vecs(
- player->checkpoint,
- vec(PLAYER_WIDTH, PLAYER_HEIGHT)),
- player->color));
+ make_mat3x3(0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f));
+ rigid_rect_teleport_to(player->alive_body, player->checkpoint);
player->state = PLAYER_STATE_ALIVE;
}
} break;
}
}
-void player_move_left(player_t *player)
+void player_collide_with_solid(Player *player, Solid_ref solid)
{
- assert(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_threshold = 0;
+ }
+ }
+}
+
+void player_move_left(Player *player)
+{
+ trace_assert(player);
rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
}
-void player_move_right(player_t *player)
+void player_move_right(Player *player)
{
- assert(player);
+ trace_assert(player);
rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
}
-void player_stop(player_t *player)
+void player_stop(Player *player)
{
- assert(player);
+ trace_assert(player);
rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
}
-void player_jump(player_t *player)
+void player_jump(Player *player)
{
- assert(player);
- if (player->jump_count < 2) {
- rigid_rect_jump(player->alive_body, PLAYER_JUMP);
- player->jump_count++;
+ trace_assert(player);
+ if (player->jump_threshold < PLAYER_MAX_JUMP_THRESHOLD) {
+ rigid_rect_transform_velocity(player->alive_body,
+ make_mat3x3(1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f));
+ rigid_rect_apply_force(player->alive_body,
+ vec(0.0f, -PLAYER_JUMP));
+ player->jump_threshold++;
+
+ if (script_has_scope_value(player->script, "on-jump")) {
+ script_eval(player->script, "(on-jump)");
+ }
}
}
-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_rect_hitbox(player->alive_body);
+
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));
+ dying_rect_start_dying(player->dying_body, vec(hitbox.x, hitbox.y));
player->state = PLAYER_STATE_DYING;
}
}
-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_rect_hitbox(player->alive_body);
camera_center_at(
camera,
vec(0.0f, -player_hitbox.h * 0.5f)));
}
-void player_hide_goals(const player_t *player,
- goals_t *goals)
+void player_hide_goals(const Player *player,
+ Goals *goals)
{
- assert(player);
- assert(goals);
- goals_hide(goals, rigid_rect_hitbox(player->alive_body));
+ trace_assert(player);
+ trace_assert(goals);
+ goals_hide_from_player(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))) {
player_die(player);
}
}
-void player_checkpoint(player_t *player, vec_t checkpoint)
+void player_checkpoint(Player *player, Vec checkpoint)
{
player->checkpoint = checkpoint;
}
-int player_sound(player_t *player,
- sound_medium_t *sound_medium)
+int player_sound(Player *player,
+ Sound_samples *sound_samples)
{
if (player->play_die_cue) {
player->play_die_cue = 0;
- if (sound_medium_play_sound(sound_medium, 0, 0) < 0) {
+ if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
return -1;
}
}
return 0;
}
+
+void player_touches_rect_sides(Player *player,
+ Rect object,
+ int sides[RECT_SIDE_N])
+{
+ if (player->state == PLAYER_STATE_ALIVE) {
+ rigid_rect_touches_rect_sides(player->alive_body, object, sides);
+ }
+}
+
+void player_apply_force(Player *player, Vec force)
+{
+ if (player->state == PLAYER_STATE_ALIVE) {
+ rigid_rect_apply_force(player->alive_body, force);
+ }
+}
+
+Rigid_rect *player_rigid_rect(Player *player, const char *id)
+{
+ trace_assert(player);
+ trace_assert(id);
+
+ if (player->state == PLAYER_STATE_ALIVE) {
+ if (rigid_rect_has_id(player->alive_body, id)) {
+ return player->alive_body;
+ }
+ }
+
+ return NULL;
+}
+
+bool player_overlaps_rect(const Player *player,
+ Rect rect)
+{
+ trace_assert(player);
+
+ return player->state == PLAYER_STATE_ALIVE
+ && rects_overlap(
+ rect, rigid_rect_hitbox(
+ player->alive_body));
+}