]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/player.c
(#639) Introduce rigid_bodies_collide_with_itself
[nothing.git] / src / game / level / player.c
index d45a578adeee77f212000a8037b795eae86b2792..c9e06d3ddb3040836003080bf0195e6a10cf2e49 100644 (file)
@@ -1,65 +1,92 @@
 #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);
@@ -68,7 +95,6 @@ player_t *create_player(float x, float y, color_t color)
     player->dying_body = PUSH_LT(
         lt,
         create_dying_rect(
-            rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
             color,
             PLAYER_DEATH_DURATION),
         destroy_dying_rect);
@@ -76,38 +102,35 @@ player_t *create_player(float x, float y, color_t color)
         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:
@@ -122,22 +145,16 @@ int player_render(const player_t * player,
     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);
@@ -148,14 +165,12 @@ void player_update(player_t *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;
@@ -164,59 +179,76 @@ void player_update(player_t *player,
     }
 }
 
-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,
@@ -225,37 +257,78 @@ void player_focus_camera(player_t *player,
             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));
+}