]> git.lizzy.rs Git - nothing.git/blobdiff - src/player.c
Introduce squishing force into platform collision detection
[nothing.git] / src / player.c
index 5945dd053b5bc5ac5511b9ff3fcfc4712cfe023f..7e1a1a7717f4b1e6d4d4d39f733f4d0aa86b03b8 100644 (file)
@@ -1,37 +1,68 @@
+#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 {
     vec_t position;
     vec_t velocity;
     vec_t movement;
+    float height;
+    float width;
 };
 
-/* static const vec_t impact_vecs[RECT_SIDE_N] = { */
-/*     /\* left *\/ */
-/*     { .x = 1.0f, .y = 0.0f }, */
-/*     /\* right *\/ */
-/*     { .x = -1.0f, .y = 0.0f }, */
-/*     /\* top *\/ */
-/*     { .x = 0.0f, .y = 1.0f }, */
-/*     /\* bottom *\/ */
-/*     { .x = 0.0f, .y = -1.0f } */
-/* }; */
+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])
+{
+    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;
     }
 
@@ -41,6 +72,8 @@ player_t *create_player(float x, float y)
     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;
 }
@@ -50,92 +83,142 @@ 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;
+}
+
 int render_player(const player_t * player,
                   SDL_Renderer *renderer,
                   const camera_t *camera)
 {
+    assert(player);
+    assert(renderer);
+    assert(camera);
+
     if (SDL_SetRenderDrawColor(renderer, 96, 255, 96, 255) < 0) {
+        throw_error(ERROR_TYPE_SDL2);
         return -1;
     }
-    rect_t player_object = {
-        .x = player->position.x,
-        .y = player->position.y,
-        .w = PLAYER_WIDTH,
-        .h = PLAYER_HEIGHT
-    };
+    rect_t player_object = player_hitbox(player);
 
 
     return camera_fill_rect(camera, renderer, &player_object);
 }
 
-void update_player(player_t * player,
+void update_player(player_t *player,
                    const platforms_t *platforms,
                    Uint32 delta_time)
 {
+    assert(player);
+    assert(platforms);
+
     float d = (float) delta_time / 1000.0f;
 
-    float x = player->position.x;
-    float y = player->position.y;
+    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);
 
-    rect_t player_object = {
-        .x = x + (player->velocity.x + player->movement.x) * d,
-        .y = y + (player->velocity.y + player->movement.y) * d,
-        .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[4] = {0, 0, 0, 0};
 
-    platforms_rect_object_collide(platforms, &player_object, sides);
+    int sides[RECT_SIDE_N] = { 0, 0, 0, 0 };
 
-    if (sides[RECT_SIDE_LEFT] || sides[RECT_SIDE_RIGHT]) {
-        player->velocity.x = 0.0f;
-        player->movement.x = 0.0f;
-    }
+    platforms_rect_object_collide(platforms, player_hitbox(player), sides);
+    vec_t opposing_force = opposing_force_by_sides(sides);
 
-    int hit_floor_ceiling = 0;
-
-    if (sides[RECT_SIDE_TOP] || sides[RECT_SIDE_BOTTOM]) {
-        player->velocity.y = 0.0f;
-        player->movement.y = 0.0f;
-        hit_floor_ceiling = 0;
+    if (opposing_force.y < 0.0f && (player->velocity.y + player->movement.y) > 800.0f) {
+        player->height = PLAYER_HEIGHT / 2;
     }
 
-    player->position.x += (player->velocity.x + player->movement.x) * d;
-    player->position.y += (player->velocity.y + player->movement.y) * d;
-
-    if (!hit_floor_ceiling) {
-        player->velocity.y += PLAYER_GRAVITY * d;
+    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(player_t *player)
 {
+    assert(player);
+
     player->movement.x = -PLAYER_SPEED;
     player->movement.y = 0.0f;
 }
 
 void player_move_right(player_t *player)
 {
+    assert(player);
+
     player->movement.x = PLAYER_SPEED;
     player->movement.y = 0.0f;
 }
 
 void player_stop(player_t *player)
 {
+    assert(player);
+
     player->movement.x = 0.0f;
     player->movement.y = 0.0f;
 }
 
 void player_jump(player_t *player)
 {
-    player->velocity.y = -1000.0f;
+    assert(player);
+
+    player->velocity.y = -500.0f;
 }
 
 void player_focus_camera(player_t *player,
                          camera_t *camera)
 {
-    camera_translate(camera,
-                     player->position.x - 800.0f * 0.5f + PLAYER_WIDTH * 0.5f,
-                     player->position.y - 600.0f * 0.5f + PLAYER_HEIGHT * 0.5f);
+    assert(player);
+    assert(camera);
+
+    camera_center_at(camera, player->position);
 }