Nimbin[12]?Games / Entropie / entropie/src/game/player.c

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (18 hours ago) · synced 10 hours ago

C 88.5% Markdown 9.1% GLSL 2.3%
git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gz
entropie/src/game/player.c 4.8 KB · 90 lines raw
#include "game/player.h"

#define WALK_SPEED       2.2f  /* brisk walk */
#define JOG_SPEED        3.6f  /* Shift held */
#define TAP_TIME         0.6f  /* Shift pressed again within this: faster */
#define TAP_BOOST        0.45f /* ... by this much of the way to a sprint */
#define BOOST_DECAY      0.8f  /* per second with Shift up */
#define ACCELERATION     3.5f  /* m/s per second speeding up */
#define DECELERATION     7.0f
#define RUN_JUMP_BONUS   0.25f /* a jump at full speed goes this much higher (take-off speed) */
#define CAREFUL_SPEED    4.4f  /* carrying a chainsaw: no faster than this */
#define SIDEWAYS_FACTOR  0.65f  /* side steps are slower than walking forward */
#define DRUNK_VEER       0.55f  /* radians the walk direction wanders when very drunk */
#define DRUNK_SLOWDOWN   0.25f

/* Drunk people don't walk quite where they want to. */
static Vec3 veer(const Object *player, Vec3 move) {
    float d = player->drunk;
    if (d <= 0.0f) return move;
    float t = player->body.clock;
    float angle = (sinf(t * 0.7f) + 0.6f * sinf(t * 1.9f + 1.3f)) * DRUNK_VEER * d;
    float c = cosf(angle), s = sinf(angle);
    return vec3(move.x * c - move.z * s, move.y, move.x * s + move.z * c);
}

/* Speed the player wants: walking, jogging (Shift held) or sprinting (Shift
   tapped again and again); eased so it never jumps from walk to sprint. */
static float runSpeed(Object *player, const Input *input, bool can_run, bool moving, float dt) {
    player->tap_timer += dt;
    if (input->sprint_tap) {
        if (player->tap_timer < TAP_TIME) player->sprint_boost = fminf(1.0f, player->sprint_boost + TAP_BOOST);
        player->tap_timer = 0.0f;
    }
    bool running = input->sprint && can_run && moving;
    if (!running) player->sprint_boost = fmaxf(0.0f, player->sprint_boost - BOOST_DECAY * dt);

    float top = fmaxf(Object_topSpeed(player), JOG_SPEED);
    if (Hands_carries(player, OBJ_CHAINSAW)) top = fminf(top, CAREFUL_SPEED); /* careful with that */
    float goal = running ? lerpf(JOG_SPEED, top, player->sprint_boost) : WALK_SPEED;
    float speed = player->run_speed;
    speed = goal > speed ? fminf(goal, speed + ACCELERATION * dt) : fmaxf(goal, speed - DECELERATION * dt);
    player->run_speed = fmaxf(speed, WALK_SPEED * 0.5f);
    return player->run_speed;
}

void Player_update(Object *player, const Input *input, const Camera *camera, float dt) {
    /* The head follows where you look */
    player->body.look_dir = camera->front;
    player->body.look_weight = 1.0f;
    player->body.urge = clampf(input->move_forward, 0.0f, 1.0f); /* W gets you up sooner after a fall */
    if (Body_isBusy(&player->body) || player->on_ladder || player->riding) { /* (climbing: see the game; in the car) */
        player->movement = vec3_zero();
        player->move_local = vec3_zero();
        return;
    }

    /* Move on the XZ plane. While grabbing, directions follow the grab heading
       (forward pushes, backward pulls) instead of the camera. */
    bool grabbing = Hands_isGrabbing(player);
    Vec3 forward = grabbing ? player->hands.grab_heading
                            : vec3_normalize(vec3(camera->front.x, 0.0f, camera->front.z));
    Vec3 right   = vec3_normalize(vec3_cross(forward, vec3(0, 1, 0)));

    Vec3 move = vec3_add(vec3_scale(forward, input->move_forward),
                         vec3_scale(right, input->move_right * SIDEWAYS_FACTOR));
    /* Pushing, pulling or carrying with both hands slows you down */
    bool busy = grabbing || Hands_moveFactor(player) < 1.0f;
    /* too exhausted to run: until you got your breath back a bit (no on-off-on) */
    static bool spent = false;
    if (player->needs.stamina < 0.1f) spent = true;
    else if (player->needs.stamina > 0.3f) spent = false;
    bool can_run = !busy && !spent;
    bool moving = input->move_forward != 0.0f || input->move_right != 0.0f;
    float speed = runSpeed(player, input, can_run, moving, dt) * Hands_moveFactor(player) * Object_vigor(player);
    speed *= 1.0f - DRUNK_SLOWDOWN * fminf(player->drunk, 1.0f);
    float amount = fminf(vec3_length(move), 1.0f); /* sideways input stays slower */
    if (!grabbing) move = veer(player, move);
    player->movement = vec3_scale(vec3_normalize(move), speed * amount);
    player->move_local = vec3(input->move_right, 0.0f, input->move_forward);

    if (input->jump && player->is_grounded && !grabbing) {
        /* Everybody jumps their own height; a run-up adds a little */
        float run_up = clampf((player->run_speed - WALK_SPEED) / (CAREFUL_SPEED - WALK_SPEED), 0.0f, 1.0f);
        player->velocity.y = Object_jumpSpeed(player) * (1.0f + RUN_JUMP_BONUS * run_up * amount);
        player->is_grounded = false;
    }

    if (!player->sitting) /* sitting, the body stays put while you look around */
        player->rotation.y = atan2f(forward.z, forward.x) + PI_F / 2.0f;
}