]> git.lizzy.rs Git - nothing.git/blobdiff - src/player.c
Introduce squishing force into platform collision detection
[nothing.git] / src / player.c
index 5a1adcbb3dcfdf0cb9b86ecc2ea75b9a7f5e6022..7e1a1a7717f4b1e6d4d4d39f733f4d0aa86b03b8 100644 (file)
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <SDL2/SDL.h>
 
 #include "./player.h"
 #include "./platforms.h"
+#include "./point.h"
+#include "./error.h"
 
 #define PLAYER_WIDTH 50.0f
 #define PLAYER_HEIGHT 50.0f
 #define PLAYER_SPEED 500.0f
 #define PLAYER_GRAVITY 1500.0f
+#define PLAYER_INFLATION 100.0f
 
 struct player_t {
-    float x, y;
-    float dx, dy;
+    vec_t position;
+    vec_t velocity;
+    vec_t movement;
+    float height;
+    float width;
 };
 
-struct player_t *create_player(float x, float y)
+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, */
+};
+
+static vec_t opposing_force_by_sides(int sides[RECT_SIDE_N])
+{
+    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;
+}
+
+static int squishing_horizontal_force(int sides[RECT_SIDE_N])
 {
-    struct player_t *player = malloc(sizeof(struct player_t));
+    return sides[RECT_SIDE_LEFT] && sides[RECT_SIDE_RIGHT];
+}
+
+static int squishing_vertical_force(int sides[RECT_SIDE_N])
+{
+    return sides[RECT_SIDE_TOP] && sides[RECT_SIDE_BOTTOM];
+}
+
+player_t *create_player(float x, float y)
+{
+    player_t *player = malloc(sizeof(player_t));
 
     if (player == NULL) {
+        throw_error(ERROR_TYPE_LIBC);
         return NULL;
     }
 
-    player->x = x;
-    player->y = y;
-    player->dx = 0.0f;
-    player->dy = 0.0f;
+    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;
 
     return player;
 }
 
-void destroy_player(struct player_t * player)
+void destroy_player(player_t * player)
 {
     free(player);
 }
 
-int render_player(const struct player_t * player,
-                  SDL_Renderer *renderer)
+rect_t player_hitbox(const player_t *player)
 {
-    if (SDL_SetRenderDrawColor(renderer, 96, 255, 96, 255) < 0) {
-        return -1;
-    }
+    rect_t hitbox = {
+        .x = player->position.x - player->width / 2,
+        .y = player->position.y - player->height,
+        .w = player->width,
+        .h = player->height
+    };
 
-    SDL_Rect rect;
-    rect.x = roundf(player->x);
-    rect.y = roundf(player->y);
-    rect.w = roundf(PLAYER_WIDTH);
-    rect.h = roundf(PLAYER_HEIGHT);
+    return hitbox;
+}
+
+int render_player(const player_t * player,
+                  SDL_Renderer *renderer,
+                  const camera_t *camera)
+{
+    assert(player);
+    assert(renderer);
+    assert(camera);
 
-    if (SDL_RenderFillRect(renderer, &rect) < 0) {
+    if (SDL_SetRenderDrawColor(renderer, 96, 255, 96, 255) < 0) {
+        throw_error(ERROR_TYPE_SDL2);
         return -1;
     }
+    rect_t player_object = player_hitbox(player);
+
 
-    return 0;
+    return camera_fill_rect(camera, renderer, &player_object);
 }
 
-void update_player(struct player_t * player,
-                   const struct platforms_t *platforms,
-                   int delta_time)
+void update_player(player_t *player,
+                   const platforms_t *platforms,
+                   Uint32 delta_time)
 {
-    float d = delta_time / 1000.0;
+    assert(player);
+    assert(platforms);
 
-    float dx = player->dx;
-    float dy = player->dy + PLAYER_GRAVITY * d;
+    float d = (float) delta_time / 1000.0f;
 
-    float x = player->x + dx * d;
-    float y = fmod(player->y + dy * d, 600.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);
 
-    struct rect_t player_object = {
-        .x = x,
-        .y = y,
-        .w = PLAYER_WIDTH,
-        .h = PLAYER_HEIGHT
-    };
+    player->height = fminf(player->height + PLAYER_INFLATION * d, PLAYER_HEIGHT);
+    player->width = (PLAYER_WIDTH * PLAYER_HEIGHT) / player->height;
+
+
+    int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
+
+    platforms_rect_object_collide(platforms, player_hitbox(player), sides);
+    vec_t opposing_force = opposing_force_by_sides(sides);
 
-    /* TODO(#6): Implement collision for the left/right sides */
-    if (platforms_rect_object_collide(platforms, &player_object)) {
-        dy = -player->dy * 0.75;
-        x = player->x + dx * d;
-        y = fmod(player->y + dy * d, 600.0f);
+    if (opposing_force.y < 0.0f && (player->velocity.y + player->movement.y) > 800.0f) {
+        player->height = PLAYER_HEIGHT / 2;
     }
 
-    player->dx = dx;
-    player->dy = dy;
-    player->x = x;
-    player->y = y;
+    for (int i = 0; i < 1000
+                    && (vec_length(opposing_force) > 1e-6
+                        || squishing_vertical_force(sides)
+                        || squishing_horizontal_force(sides)); ++i) {
+        player->position = vec_sum(
+            player->position,
+            vec_scala_mult(
+                opposing_force,
+                1e-2f));
+
+        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 (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;
+        }
+
+        if (squishing_vertical_force(sides)) {
+            player->height -= 1e-2f;
+            /* player->width = (PLAYER_WIDTH * PLAYER_HEIGHT) / player->height; */
+        }
+
+        if (squishing_horizontal_force(sides)) {
+           player->width -= 1e-2f;
+           /* player->height = (PLAYER_WIDTH * PLAYER_HEIGHT) / player->width; */
+        }
+
+        platforms_rect_object_collide(
+            platforms,
+            player_hitbox(player),
+            sides);
+        opposing_force = opposing_force_by_sides(sides);
+    }
 }
 
-void player_move_left(struct player_t *player)
+void player_move_left(player_t *player)
 {
-    player->dx = -PLAYER_SPEED;
+    assert(player);
+
+    player->movement.x = -PLAYER_SPEED;
+    player->movement.y = 0.0f;
 }
 
-void player_move_right(struct player_t *player)
+void player_move_right(player_t *player)
 {
-    player->dx = PLAYER_SPEED;
+    assert(player);
+
+    player->movement.x = PLAYER_SPEED;
+    player->movement.y = 0.0f;
 }
 
-void player_stop(struct player_t *player)
+void player_stop(player_t *player)
 {
-    player->dx = 0.0f;
+    assert(player);
+
+    player->movement.x = 0.0f;
+    player->movement.y = 0.0f;
 }
 
-void player_jump(struct player_t *player)
+void player_jump(player_t *player)
 {
-    player->dy = -500.0f;
+    assert(player);
+
+    player->velocity.y = -500.0f;
+}
+
+void player_focus_camera(player_t *player,
+                         camera_t *camera)
+{
+    assert(player);
+    assert(camera);
+
+    camera_center_at(camera, player->position);
 }