#include <stdlib.h>
#include <SDL2/SDL.h>
+#include "./lt.h"
#include "./player.h"
#include "./platforms.h"
#include "./point.h"
#include "./error.h"
+#include "./rigid_rect.h"
+#include "./dying_rect.h"
-#define PLAYER_WIDTH 50.0f
-#define PLAYER_HEIGHT 50.0f
+#define PLAYER_WIDTH 25.0f
+#define PLAYER_HEIGHT 25.0f
#define PLAYER_SPEED 500.0f
#define PLAYER_JUMP 550.0f
-#define PLAYER_GRAVITY 1500.0f
+#define PLAYER_DEATH_DURATION 500
+
+typedef enum player_state_t {
+ PLAYER_STATE_ALIVE = 0,
+ PLAYER_STATE_DYING
+} player_state_t;
struct player_t {
- vec_t position;
- vec_t velocity;
- vec_t movement;
- float height;
- float width;
+ lt_t *lt;
+ player_state_t state;
+
+ rigid_rect_t *alive_body;
+ dying_rect_t *dying_body;
+
int jump_count;
-};
+ color_t color;
-static const vec_t opposing_rect_side_forces[RECT_SIDE_N] = {
- { .x = 1.0f, .y = 0.0f }, /* RECT_SIDE_LEFT = 0, */
- { .x = -1.0f, .y = 0.0f }, /* RECT_SIDE_RIGHT, */
- { .x = 0.0f, .y = 1.0f, }, /* RECT_SIDE_TOP, */
- { .x = 0.0f, .y = -1.0f, } /* RECT_SIDE_BOTTOM, */
+ /* TODO(#110): introduce checkpoints */
+ vec_t checkpoint;
};
-static vec_t opposing_force_by_sides(int sides[RECT_SIDE_N])
+player_t *create_player(float x, float y, color_t color)
{
- vec_t opposing_force = {
- .x = 0.0f,
- .y = 0.0f
- };
-
- for (rect_side_t side = 0; side < RECT_SIDE_N; ++side) {
- if (sides[side]) {
- vec_add(
- &opposing_force,
- opposing_rect_side_forces[side]);
- }
- }
-
- return opposing_force;
-}
+ lt_t *lt = create_lt();
-player_t *create_player(float x, float y)
-{
- player_t *player = malloc(sizeof(player_t));
+ if (lt == NULL) {
+ return NULL;
+ }
+ player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
if (player == NULL) {
throw_error(ERROR_TYPE_LIBC);
- return NULL;
+ RETURN_LT(lt, NULL);
+ }
+
+ player->state = PLAYER_STATE_ALIVE;
+
+ 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->position.x = x;
- player->position.y = y;
- player->velocity.x = 0.0f;
- player->velocity.y = 0.0f;
- player->movement.x = 0.0f;
- player->movement.y = 0.0f;
- player->height = PLAYER_HEIGHT;
- player->width = PLAYER_WIDTH;
- player->jump_count = 2;
+ player->dying_body = PUSH_LT(
+ lt,
+ create_dying_rect(
+ rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
+ color,
+ PLAYER_DEATH_DURATION),
+ destroy_dying_rect);
+ if (player->dying_body == NULL) {
+ RETURN_LT(lt, NULL);
+ }
+
+ player->lt = lt;
+ player->jump_count = 0;
+ player->color = color;
+ player->checkpoint = vec(x, y);
return player;
}
{
float x = 0.0f, y = 0.0f;
- if (fscanf(stream, "%f%f", &x, &y) == EOF) {
+ 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);
+ return create_player(x, y, color_from_hexstr(color));
}
void destroy_player(player_t * player)
{
- free(player);
-}
-
-rect_t player_hitbox(const player_t *player)
-{
- rect_t hitbox = {
- .x = player->position.x - player->width / 2,
- .y = player->position.y - player->height,
- .w = player->width,
- .h = player->height
- };
-
- return hitbox;
+ RETURN_LT0(player->lt);
}
-int render_player(const player_t * player,
+int player_render(const player_t * player,
SDL_Renderer *renderer,
const camera_t *camera)
{
assert(renderer);
assert(camera);
- if (SDL_SetRenderDrawColor(renderer, 96, 255, 96, 255) < 0) {
- throw_error(ERROR_TYPE_SDL2);
- return -1;
- }
- rect_t player_object = player_hitbox(player);
+ switch (player->state) {
+ case PLAYER_STATE_ALIVE:
+ return rigid_rect_render(player->alive_body, renderer, camera);
+
+ case PLAYER_STATE_DYING:
+ return dying_rect_render(player->dying_body, renderer, camera);
+ default: {}
+ }
- return camera_fill_rect(camera, renderer, &player_object);
+ return 0;
}
-void update_player(player_t *player,
+void player_update(player_t *player,
const platforms_t *platforms,
Uint32 delta_time)
{
assert(player);
assert(platforms);
- float d = (float) delta_time / 1000.0f;
-
- player->velocity.y += PLAYER_GRAVITY * d;
- player->position = vec_sum(
- player->position,
- vec_scala_mult(
- vec_sum(
- player->velocity,
- player->movement),
- d));
- player->position.y = fmodf(player->position.y, 800.0f);
-
- int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
+ switch (player->state) {
+ case PLAYER_STATE_ALIVE: {
+ rigid_rect_update(player->alive_body, platforms, delta_time);
- platforms_rect_object_collide(platforms, player_hitbox(player), sides);
- vec_t opposing_force = opposing_force_by_sides(sides);
-
- if (sides[RECT_SIDE_BOTTOM]) {
- player->jump_count = 2;
- }
+ if (rigid_rect_touches_ground(player->alive_body)) {
+ player->jump_count = 0;
+ }
- for (int i = 0; i < 1000 && vec_length(opposing_force) > 1e-6; ++i) {
- player->position = vec_sum(
- player->position,
- vec_scala_mult(
- opposing_force,
- 1e-2f));
+ const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
- if (fabs(opposing_force.x) > 1e-6 && (opposing_force.x < 0.0f) != ((player->velocity.x + player->movement.x) < 0.0f)) {
- player->velocity.x = 0.0f;
- player->movement.x = 0.0f;
+ if (hitbox.y > 1000.0f) {
+ player_die(player);
}
-
- if (fabs(opposing_force.y) > 1e-6 && (opposing_force.y < 0.0f) != ((player->velocity.y + player->movement.y) < 0.0f)) {
- player->velocity.y = 0.0f;
- player->movement.y = 0.0f;
+ } 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));
+ player->state = PLAYER_STATE_ALIVE;
}
+ } break;
- platforms_rect_object_collide(
- platforms,
- player_hitbox(player),
- sides);
- opposing_force = opposing_force_by_sides(sides);
+ default: {}
}
}
void player_move_left(player_t *player)
{
assert(player);
-
- player->movement.x = -PLAYER_SPEED;
- player->movement.y = 0.0f;
+ rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
}
void player_move_right(player_t *player)
{
assert(player);
- player->movement.x = PLAYER_SPEED;
- player->movement.y = 0.0f;
+ rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
}
void player_stop(player_t *player)
{
assert(player);
- player->movement.x = 0.0f;
- player->movement.y = 0.0f;
+ rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
}
void player_jump(player_t *player)
+{
+ assert(player);
+ if (player->jump_count < 2) {
+ rigid_rect_jump(player->alive_body, PLAYER_JUMP);
+ player->jump_count++;
+ }
+}
+
+void player_die(player_t *player)
{
assert(player);
- if (player->jump_count > 0) {
- player->velocity.y = -PLAYER_JUMP;
- --player->jump_count;
+ if (player->state == PLAYER_STATE_ALIVE) {
+ player->dying_body = RESET_LT(
+ player->lt,
+ player->dying_body,
+ create_dying_rect(
+ rigid_rect_hitbox(player->alive_body),
+ player->color,
+ PLAYER_DEATH_DURATION));
+ player->state = PLAYER_STATE_DYING;
}
}
assert(player);
assert(camera);
- camera_center_at(camera, vec_sum(player->position, vec(0.0f, -player->height * 0.5f)));
+ const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
+
+ camera_center_at(
+ camera,
+ vec_sum(
+ vec(player_hitbox.x, player_hitbox.y),
+ vec(0.0f, -player_hitbox.h * 0.5f)));
}
void player_hide_goals(const player_t *player,
{
assert(player);
assert(goals);
- goals_hide(goals, player->position);
+ goals_hide(goals, rigid_rect_hitbox(player->alive_body));
+}
+
+void player_die_from_lava(player_t *player,
+ const lava_t *lava)
+{
+ if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
+ player_die(player);
+ }
}