Nimbin[12]?Games / Entropie / entropie/src/game/game_injury.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/game_injury.c 9.5 KB · 230 lines raw
/*
 * Broken bones (world/injury.h): somebody lies there hurt - an ambulance is
 * called. A while later it comes in along the way in by car (lights, siren),
 * stops by the yard, the paramedics fetch them, off it goes. They're at the
 * hospital (out of sight: in Berlin with the others between weekends) and
 * come back a day and a half later - or with the next weekend, whichever is
 * first - with a cast (an arm: weaker, slower at work; a leg: slow on their
 * feet). The cast comes off two weekends on.
 *
 * You: the ambulance takes you; six hours later you're back with a cast.
 * A guest: the ambulance takes them, and that's their festival over.
 */

#include <math.h>

#include "game/game_internal.h"
#include "world/car.h"
#include "world/cast.h"
#include "world/injury.h"
#include "world/manor.h"

#define AMB_COMES     1200.0f /* game s till it's here (out in the country) */
#define AMB_SPEED     9.0f    /* m/s along the way in */
#define AMB_STOP      12.0f   /* m short of where your car stops */
#define AMB_LOADING   10.0f   /* real s the paramedics take */
#define AWAY_HOURS    36.0f   /* story hours at the hospital (a cast on, then back) */
#define YOU_AWAY      6.0f    /* ... you: hours */
#define CAST_WEEKENDS 2       /* the cast comes off this many weekends on */
#define YOU_WAIT      4.0f    /* real s lying there before you're taken */

static const char *limb(int kind) { return kind == INJ_ARM ? "arm" : "leg"; }

static void putOnGround(Game *game, Object *o) {
    Vec3 path[16];
    int n = 0;
    float yaw;
    Site_arrivalPath(path, &n, &yaw);
    Vec3 at = n > 4 ? path[4] : Site_yard();
    at.y = World_groundHeight(&game->world, at.x, at.z, at.y + 3.0f);
    o->sitting = false;
    o->seat = NULL;
    o->ai.meeting = false;
    o->position = at;
    o->velocity = o->movement = vec3_zero();
    o->body.replant = true;
    Object_syncColliders(o);
}

/* Back from the hospital: the cast on. */
static void back(Game *game, Object *o, bool to_ground) {
    int kind = o->injured ? o->injured : INJ_LEG;
    Injury_setCast(o, kind, o->injury_side, true);
    o->injured = INJ_NONE;
    o->hospital_until = 0.0f;
    o->cast_until = game->weekend + CAST_WEEKENDS;
    if (to_ground) putOnGround(game, o);
    if (o->type == OBJ_HUMAN) Ai_say(o, kind == INJ_ARM ? "I'm back! Arm in plaster - I'll do what I can one-handed." : "Back again! Slow on this leg, but I'm here.");
    snprintf(game->story_line, sizeof game->story_line, "%s %s back from the hospital - with a cast on the %s", o->type == OBJ_PLAYER ? "You" : o->name,
             o->type == OBJ_PLAYER ? "are" : "is", limb(kind));
    say(game, game->story_line);
    game->message_time = 6.0f;
}

/* You: taken to the hospital, back hours later with the cast. */
static void youHurt(Game *game, float dt) {
    Object *p = game->player;
    if (!p->injured) { game->you_hurt_t = 0.0f; return; }
    if (game->you_hurt_t == 0.0f) {
        snprintf(game->story_line, sizeof game->story_line, "Your %s - it's broken. Somebody calls the ambulance...", limb(p->injured));
        say(game, game->story_line);
        game->message_time = YOU_WAIT + 1.0f;
    }
    if ((game->you_hurt_t += dt) < YOU_WAIT) return;
    game->you_hurt_t = 0.0f;
    float add = YOU_AWAY;
    game->story_hours += add;
    game->daycycle.seconds = fmodf(game->daycycle.seconds + add * 3600.0f, 24.0f * 3600.0f);
    p->body.down_time = 0.0f;
    p->body.pose = POSE_UP;
    p->body.replant = true;
    Vec3 at = Site_yard();
    at.y = World_groundHeight(&game->world, at.x, at.z, at.y + 3.0f);
    p->position = at;
    p->velocity = p->movement = vec3_zero();
    Object_syncColliders(p);
    game->on_rope = false;
    back(game, p, false);
}

static Object *waiting(const Game *game) {
    for (int i = 0; i < game->world.count; i++) {
        Object *o = game->world.objects[i];
        if (o->type == OBJ_HUMAN && o->injured && o->hospital_until <= 0.0f && !o->gone_home) return o;
    }
    return NULL;
}

static Object *crewNear(const Game *game, const Object *hurt) {
    Object *best = NULL;
    float best_d = 60.0f;
    for (int i = 0; i < game->world.count; i++) {
        Object *o = game->world.objects[i];
        if (o == hurt || o->type != OBJ_HUMAN || o->cast_id <= 0 || o->injured || Body_isDown(&o->body) || Game_inBerlin(o)) continue;
        float d = hypotf(o->position.x - hurt->position.x, o->position.z - hurt->position.z);
        if (d < best_d) { best_d = d; best = o; }
    }
    return best;
}

static void ambulance(Game *game, float dt) {
    Ambulance *a = &game->ambulance;
    World *w = &game->world;
    switch (a->stage) {
    case AMB_NONE: {
        Object *hurt = waiting(game);
        if (!hurt) return;
        a->who = hurt;
        a->stage = AMB_CALLED;
        a->t = 0.0f;
        Object *c = crewNear(game, hurt);
        if (c) Ai_say(c, "Don't move - I'm calling 112! The ambulance is on its way.");
        snprintf(game->story_line, sizeof game->story_line, "%s is hurt - a broken %s. The ambulance is called; out here it takes a while", hurt->name, limb(hurt->injured));
        say(game, game->story_line);
        game->message_time = 6.0f;
        return;
    }
    case AMB_CALLED:
        if ((a->t += w->game_dt) < AMB_COMES || !game_drivePath(game)) return;
        {
            Vec3 pos, dir;
            game_driveAt(&game->arrival, 0.0f, &pos, &dir);
            Object *car = Car_create(pos, yaw_facing(dir));
            if (!car) return;
            car->variant = 1;
            Mesh_destroy(&car->mesh);
            car->mesh = Shapes_ambulance();
            snprintf(car->name, sizeof car->name, "Ambulance");
            if (!World_add(w, car)) return;
            a->car = car;
            a->s = 0.0f;
            a->stage = AMB_IN;
            say(game, "Blue lights on the gravel road - the ambulance is here");
        }
        return;
    case AMB_IN: case AMB_OUT: {
        float stop = game->arrival.length - AMB_STOP;
        a->s += (a->stage == AMB_IN ? 1.0f : -1.0f) * AMB_SPEED * dt;
        if (a->stage == AMB_IN && a->s >= stop) { a->s = stop; a->stage = AMB_PARKED; a->t = 0.0f; }
        Vec3 pos, dir;
        game_driveAt(&game->arrival, a->s, &pos, &dir);
        Car_place(w, a->car, pos, yaw_facing(a->stage == AMB_OUT ? vec3_scale(dir, -1.0f) : dir));
        if (a->stage == AMB_OUT && a->s <= 0.0f) { /* (gone: to the hospital) */
            Object *o = a->who;
            if (o && o->riding == a->car) Car_getOut(o);
            if (o && o->guest) { o->gone_home = true; o->inside_door = pos; o->indoors = 1e7f; o->injured = INJ_NONE; }
            else if (o) {
                o->hospital_until = game->story_hours + AWAY_HOURS;
                o->position = vec3(BERLIN_X + BERLIN_BAR + 1.5f, 0.05f, 2.0f);
                o->velocity = o->movement = vec3_zero();
                o->ai.meeting = true;
                Object_syncColliders(o);
            }
            Object_useUp(a->car);
            memset(a, 0, sizeof *a);
        }
        return;
    }
    case AMB_PARKED: {
        Object *o = a->who;
        if ((a->t += dt) < AMB_LOADING) return;
        if (o && !o->riding) {
            o->body.down_time = 0.0f; /* (on the stretcher, in) */
            o->body.pose = POSE_UP;
            Car_getIn(a->car, o, 2);
            snprintf(game->story_line, sizeof game->story_line, "The paramedics carry %s to the ambulance on a stretcher", o->name);
            say(game, game->story_line);
        }
        a->stage = AMB_OUT;
        return;
    }
    default: return;
    }
}

void game_injury(Game *game, float dt) {
    World *w = &game->world;
    if (game->meeting.kind) return;
    Object *you = game->player;
    if (you->sprain > 0.0f && !game->sprain_told) { game->sprain_told = true; say(game, "Ow - you twisted your ankle. A few hours of limping"); }
    else if (you->sprain <= 0.0f && game->sprain_told) { game->sprain_told = false; say(game, "Your ankle's fine again"); }
    youHurt(game, dt);
    ambulance(game, dt);
    for (int i = 0; i < w->count; i++) {
        Object *o = w->objects[i];
        if (!o->is_character || o->type == OBJ_DOG) continue;
        if (o->hospital_until > 0.0f) { /* at the hospital: back when it's time (or the next weekend brought them) */
            if (!Game_inBerlin(o)) back(game, o, false);
            else if (game->story_hours >= o->hospital_until) back(game, o, true);
        }
        if (o->look.cast && game->weekend >= o->cast_until && o->cast_until > 0) {
            int kind = Injury_castKind(o), side = (o->look.cast - 1) % 2;
            Injury_setCast(o, kind, side, false);
            o->cast_until = 0;
            if (!Game_inBerlin(o)) {
                snprintf(game->story_line, sizeof game->story_line, "%s%s cast is off - good as new", o->type == OBJ_PLAYER ? "Your" : o->name, o->type == OBJ_PLAYER ? "" : "'s");
                say(game, game->story_line);
            }
        }
    }
}

/* Dev: all well again - out of the ambulance, back from the hospital, the cast and the sprain off. */
void game_injuryHeal(Game *game, Object *o) {
    Ambulance *a = &game->ambulance;
    if (a->who == o) {
        if (a->car && o->riding == a->car) Car_getOut(o);
        a->who = NULL;
        if (a->stage == AMB_CALLED) { a->stage = AMB_NONE; a->t = 0.0f; } /* (not on its way yet: called off) */
    }
    if (o == game->player) game->you_hurt_t = 0.0f;
    if (o->hospital_until > 0.0f) {
        o->hospital_until = 0.0f;
        o->ai.meeting = false;
        if (Game_inBerlin(o) && !game->meeting.kind) putOnGround(game, o);
    }
    Injury_heal(o);
    game->sprain_told = false;
}

bool game_ambulanceHere(const Game *game) { return game->ambulance.stage >= AMB_IN; }