Nimbin[12]?Games / Entropie / entropie/src/world/object.h

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/world/object.h 43.9 KB · 622 lines raw
#ifndef WORLD_OBJECT_H
#define WORLD_OBJECT_H

/*
 * World object: mesh, transform, colliders and physics state.
 *
 * position is the base point (bottom center). The mesh origin sits at
 * position + pivot and the object rotates around it. Colliders are defined
 * in the same local space as the mesh, so they always match what you see.
 */

#include <stdbool.h>
#include <stddef.h>

#include "people/ai.h"
#include "people/body.h"
#include "people/hands.h"
#include "world/tools.h"
#include "math/linalg.h"
#include "render/figure.h"
#include "render/mesh.h"
#include "render/shapes.h"

/* Food over a fire: seconds (held right over the flames; on the grate half as fast) */
#define FOOD_DONE   6.0f   /* done */
#define FOOD_DARK   11.0f  /* getting too dark: take it off */
#define FOOD_BURNT  16.0f  /* charcoal */

typedef enum {
    OBJ_BOX,             /* dynamic box,       size = bounding box */
    OBJ_BOX_STATIC,      /* static box,        size = bounding box */
    OBJ_CYLINDER,        /* dynamic cylinder,  size = bounding box (box collider) */
    OBJ_CYLINDER_STATIC, /* static cylinder,   size = bounding box (box collider) */
    OBJ_HUMAN,           /* 2D painted person, size = collider box */
    OBJ_PLAYER,          /* like OBJ_HUMAN; only its shadow is drawn (first person) */
    OBJ_TREE,            /* size.x = canopy diameter, size.y = trunk height */
    OBJ_HOUSE,           /* size = (length, wall height, width), with windows/doors */
    OBJ_BARN,            /* size = (length, wall height, width). PALETTE_WARM: brick barn;
                            farm halls: PALETTE_GROUND plain, PALETTE_STANDARD red roof,
                            PALETTE_MODERN solar roof */
    OBJ_BOTTLE,          /* beer, size = (diameter, height, -); drinkable */
    OBJ_VODKA,           /* like OBJ_BOTTLE, much stronger */
    OBJ_AXE,             /* size.y = length; fells trees, takes branches off, cuts logs */
    OBJ_SHEARS,          /* size.y = length; takes branches off */
    OBJ_CHAINSAW,        /* fells, takes branches off and cuts, fast */
    OBJ_LOG,             /* size = (diameter, length, -), lying along its length; from a tree */
    OBJ_BRANCH,          /* size.y = length; cut off a tree */
    OBJ_WATER,           /* water bottle: thirst, fresh again; no alcohol */
    OBJ_APPLE,           /* size.x = diameter; food */
    OBJ_BREAD,           /* size = loaf; food */
    OBJ_SUPPLY,          /* static: a crate of apples (PALETTE_WARM) or a water barrel
                            (PALETTE_MODERN); clicking it hands you one */
    OBJ_PUDDLE,          /* sick on the ground, size = (radius, seconds until dried up, -); no collision.
                            work = how fresh (1 .. 0). (Water and pee soak into the ground: wetness.h) */
    OBJ_FIREWOOD,        /* split wood, size = (diameter of the log, length, parts):
                            parts 2 = a half, 4 = a quarter */
    OBJ_STINK,           /* stink lines, size.x = radius; work = how strong (0..1). Over sick, or
                            (variant 1) over pee in the ground: as strong as the pee there */
    OBJ_DECAL,           /* static: something painted on the ground (roads, water); mesh given */
    OBJ_WOODPILE,        /* static: two sleepers where firewood gets stacked, size = (length, -, depth) */
    OBJ_FIREPLACE,       /* static: a ring of stones, size.x = diameter. Wood lying in it is fuel;
                            fuel = kg of wood in it, burning = lit (see camp.h) */
    OBJ_SAUSAGE,         /* size.y = length; food, best grilled */
    OBJ_TRASH,           /* litter, palette: PALETTE_WARM paper, PALETTE_MODERN a can,
                            PALETTE_STANDARD plastic, PALETTE_GROUND a broken bit of junk;
                            size.x < 0: a cigarette butt */
    OBJ_BAG,             /* garbage bag; stored = pieces of trash in it */
    OBJ_BIN,             /* static: a big waste container, size = box; trash and bags go in */
    OBJ_BUSH,            /* static: a round bush, size = (diameter, height, -) */
    OBJ_FURNITURE,       /* static: variant = FurnitureKind, size = box (see Object_createFurniture) */
    OBJ_DOG,             /* a dog (see dog.h); name, speech and state of its own */
    OBJ_HOSE,            /* the nozzle of a garden hose; the hose runs to hose_tap */
    OBJ_CAN,             /* a watering can; water = liters in it */
    OBJ_LADDER,          /* a telescopic ladder: slides out (extend) and leans against things */
    OBJ_STONE,           /* a loose stone, size = bounding box; hard: glass breaks on it */
    OBJ_STUMP,           /* static: what's left of a felled tree, size = (diameter, height, -);
                            work = how far it's been chopped out (see tools.h) */
    OBJ_TAPE,            /* a roll of duct tape: mends a melted hose (see hose.h) */
    OBJ_URINELLA,        /* (only in pockets - a box of them is an OBJ_SUPPLY that dispenses these) */
    OBJ_WALL,            /* static, invisible: a solid box of a building made of parts (walls, floors, steps);
                            size = box, position = its bottom middle; variant 1: a locked door (name says which) */
    OBJ_STRUCTURE,       /* static: how a building made of parts looks (one mesh, no collision: see OBJ_WALL) */
    OBJ_TOY,             /* Alice's toys: weight 0 = a ball (size.x across), 1 = a rope to bite (size.x long, size.y thick) */
    OBJ_CLOTHES,         /* somebody's pullover put down (see clothes.h): variant = ClothLay; no collision */
    OBJ_CAR,             /* static (moved by hand while it drives): your estate car, size = (width, height, length), nose to +Z;
                            what's in its boot: load (see car.h) */
    OBJ_CRATE,           /* a crate of bottles: weight 0 = beer (CRATE_BEER bottles), 1 = water (CRATE_WATER); stored = bottles in it */
    OBJ_GEAR,            /* camping things: variant = GearKind (see car.h), owner = whose */
    OBJ_TENT,            /* static: a dome tent, weight 0 small / 1 big (see tent.h); stored = pegs in */
    OBJ_UMBRELLA,        /* an umbrella: size.y long (folded); opens held in the rain (umbrella_open); variant = colour */
    OBJ_HAT,             /* a hat off somebody's head (blown off): variant = HAT_*, look.hat_color, owner = whose */
    OBJ_STAGE,           /* static: a floor's stage being built (world/stage.h): variant = Project, build_stage, build_work */
    OBJ_MATERIAL,        /* building material from the hardware store: variant = MaterialKind (weight given) */
    OBJ_SCYTHE,          /* the scythe: mows the Kartoffelacker's grass (world/field.h); carried in one hand */
    OBJ_SNAIL,           /* a vineyard snail: variant 0 out there, 1 let go at the refuge (weight given: the variant) */
    OBJ_SCULPTURE,       /* static: a log carved into a figure with the chainsaw (Kerry); size = (diameter, height, -), variant = which */
    OBJ_ROPE,            /* the throw line for the big tree: a little bag with a long cord coiled to it (thrown up over a branch) */
} ObjectType;

/* Building material (OBJ_MATERIAL): what the stages are built with besides the logs. */
typedef enum { MATERIAL_BOARDS, MATERIAL_NAILS, MATERIAL_SCREWS, MATERIAL_PAINT, MATERIAL_KINDS } MaterialKind;

/* What a body needs over the day. All 0..1; 0 is fine (stamina: 1 is fine). */
typedef struct {
    float stamina;      /* 1 = fresh .. 0 = exhausted; work drains it, rest brings it back */
    float hunger;
    float thirst;
    float bladder;
    float sleepiness;
} Needs;

/* Oriented box collider. */
typedef struct {
    Vec3 center;       /* local, relative to the pivot (before rotation) */
    Vec3 half_extents;
    Vec3 tilt;         /* local rotation (radians, Y then X then Z) */
    Vec3 offset;       /* cached: world center relative to the object position */
    Vec3 axes[3];      /* cached: world-space orientation */
} Collider;

#define OBJECT_MAX_COLLIDERS 3

#define BOTTLE_SIPS      5    /* a full bottle of beer, water, soda or mate */
#define UMBRELLA_LENGTH  0.9f /* m, folded */
#define UMBRELLA_RADIUS  0.55f /* m across the open canopy */
#define POCKET_URINELLAS 4   /* disposable Urinellas a pocket holds (one-time use) */
#define LIGHTER_USES     60   /* a full lighter lights this many times */
#define POUCH_ROLLUPS    75   /* a pouch of tobacco rolls about this many */
#define PAPERS_PACK      50   /* rolling papers in a packet */
#define FILTERS_BAG      120  /* filter tips in a bag */

/* A pull up in progress (people/climb.c): from where, onto where, over which edge. */
typedef struct {
    float t, time;          /* how far (0..1 over time seconds) */
    Vec3  from, to, edge;
    Vec3  across;           /* along the edge (the hands hold it a shoulder width apart) */
    float hang;             /* feet this high while hanging on before the pull up */
    float face;             /* how far ahead of where it started the face of it is (m) */
    Vec3  ankle0[2], toe0[2]; /* where the feet were when it started (eased from there) */
    bool  sit_way;          /* the careful way up something hip high: turn round, sit on it, legs up, stand up ... */
    bool  sit_now;          /* ... sitting on it right now (the body sits: see world.c) ... */
    float yaw0;             /* ... facing the ledge when it started (rotation.y) */
    float swing;            /* ... which way round the legs come up (+1 / -1) */
    bool  belly_way;        /* chest high, not so sporty (or drunk): a push up onto the belly, lying over the edge, a knee up, stand up ... */
    float belly;            /* ... lying over it this much right now (0..1: the body bends over, see world.c) */
    bool  roll_way;         /* ... (drunk) not a knee up after it, but down flat on the top and rolled over onto it (Body_rollOnto) */
} ClimbMove;

typedef struct Object {
    ObjectType type;
    Palette    palette;       /* as made (Object_create) ... */
    float      make_arg;      /* ... and the weight it was made with (crates, tents, gear: which one) */
    int        scene_id;      /* made by the scene (1..): saves find it again by this ... */
    Vec3       spawn;         /* ... and where it was made ... */
    ObjectType spawn_type;    /* ... as what */
    float      cloth_gap;     /* clothes: hung over an edge this thick */
    Vec3       position;      /* base point (bottom center) */
    Vec3       rotation;      /* Euler radians, applied Y, X, Z */
    Vec3       pivot;         /* mesh origin relative to position */
    bool       has_home;      /* tools: where they belong (the tool room), put back there by careful people */
    Vec3       home_spot, home_rot;
    Mesh       mesh;          /* empty for characters, they are drawn as figures */

    Collider   colliders[OBJECT_MAX_COLLIDERS];
    int        collider_count;

    bool       is_static;     /* never moves */
    bool       is_character;  /* walks; may step onto low ledges, tramples grass */
    float      step_height;
    float      weight;
    float      strength;

    Vec3       velocity;
    Vec3       angular_velocity;
    Vec3       movement;      /* walk velocity requested this frame */
    bool       is_grounded;
    bool       on_terrain;    /* stood on the terrain during the last step */
    float      ground_speed;  /* horizontal speed during the last step */

    bool       is_held;       /* carried: no physics until dropped */
    Vec3       size;          /* as created (trees: trunk height left after a cut) */
    int        sips;          /* drinks / bites left (bottles, food) */
    float      alcohol;       /* drunkenness per sip for an average person (bottles) */
    float      quench;        /* thirst taken away per sip */
    float      nourish;       /* hunger taken away per bite */
    float      refresh;       /* stamina given back per sip / bite */
    ObjectType dispenses;     /* supplies: what you get from it */
    struct CarLoad *load;     /* cars: what's in the boot (malloc'd, freed with the car) */
    bool       boot_open;     /* cars: the boot lid up */
    Mesh       inside;        /* cars: what you see of it from the driver's seat */
    bool       keep;          /* trees: may not be cut down (round the meadow behind your garages); a tent bag: packed on purpose (its owner leaves it packed) */
    float      wake;          /* drinks: sleepiness off per sip (mate, cola) */
    bool       rain_out;      /* characters: out in the rain (no roof over them) */
    bool       lives_here;    /* characters: the house is theirs (Kurti and his sons): in and out of it */
    float      indoors;       /* characters: inside the house this many more seconds (hidden: see ai.c goInside) */
    struct Object *owner;     /* camping gear, tents: whose ... */
    struct Object *owner2;    /* ... tents: and who shares it */
    int        crate_empties; /* crates: of the bottles in it (stored), this many are empties put back ... */
    unsigned char crate_slot[CRATE_SLOTS];
    struct Object *umbrella_guest; /* people: somebody under their umbrella with them (the canopy leans their way) ... */
    float      umbrella_guest_t;   /* ... kept up by them this long */
    bool       umbrella_open; /* umbrellas: open (held up in the rain) */
    float      umbrella_flip;   /* umbrellas: s left blown inside out by a gust (0: as it should be) ... */
    int        umbrella_flips;  /* ... how often that happened ... */
    int        hats_lost;       /* people: how often the wind took their cap (they pull it down tighter each time) */
    bool       umbrella_ruined; /* ... once too often (or in a storm): the ribs bent, it's no use any more */ /* ... and what's in each slot (CRATE_SLOT: kind and sips; see car.c Crate_*) */
    int        tent_stage;    /* tents: TentStage ... */
    float      tent_work;     /* ... how far the stage is (0..1) ... */
    float      tent_idle;     /* ... seconds since anybody worked on it */
    float      tent_blown;    /* tents: seconds since the wind last pushed it about (0: not lately) */
    float      smoke_t;       /* fireplaces: till the next puff of smoke */
    int        tent_beds;     /* ... sleeping bags (bit 0 owner, 2 owner2) and mats (1, 3) put in */
    bool       tent_dragged;  /* ... being moved (no pegs in): nobody works on it meanwhile */
    bool       tent_down;     /* ... being taken down: the stages backwards, then packed into its bag */
    struct Object *in_tent;   /* characters: sleeping in this tent (no collisions with it) */
    bool          rain_nap;  /* in their tent out of the rain, dozing: not woken by being rested, up when it stops */
    struct Object *riding;    /* characters: sitting in this car (hidden, no physics, no AI: see car.c) ... */
    int        ride_seat;     /* ... on this seat (0 the driver) */
    int        branches;      /* trees: branches left on the crown */
    int        stored;        /* woodpiles: pieces stacked on it; firewood: 1 = lies on a woodpile */
    float      work;          /* trees and logs: work done on the current job (see tools.h) */
    float      cut_at;        /* logs: where the current cut is */
    Vec3       mark_at, mark_out; /* trees, logs, stumps: where they're being cut, and outwards there (object space) ... */
    float      mark;          /* ... how far along (0 = no mark) */
    Vec3       fall_dir;      /* trees: direction the top falls */
    Vec3       fell_plan;     /* trees: which way it should come down (room for it there; zero: not looked yet) ... */
    bool       fell_blocked;  /* ... no room anywhere round it */
    float      fall_base_y;   /* trees: where it pivots falling over (the cut) ... */
    Vec3       fall_origin;   /* ... from here ... */
    float      fall_ground_y; /* ... and the ground it comes down on at the end (its butt slides off the stump) */
    float      fall_slide;    /* ... this far along fall_dir */
    struct Object *stump;     /* trees: the stump left behind (the trunk may touch it) */
    struct Object *stink;     /* puddles: the smell over it */
    bool       lifted;        /* carried by a team: no gravity, moved by their hands */
    bool       end_up;        /* ... only one end lifted (long and too heavy for them): the other drags on the ground */
    Vec3       knock;         /* characters: momentum of things that hit them this step */
    Vec3       shoved;        /* characters: a pushed load is shoving them this way (zero: not) ... */
    Vec3       shove_load;    /* ... the load's middle */
    bool       toppled;       /* tipped over (bottles) */
    float      topple;        /* 0..1 while falling over */
    struct Object *thrown_by; /* set while flying after a throw */
    float      thrown_time;
    float      impact;        /* speed lost in collisions during the last step */
    struct Object *touched;   /* the last thing it ran into during the last step (NULL: none, or the ground) */
    struct Object *standing_on; /* characters: what they stood on in the last step (NULL: the ground) */
    Vec3       scramble_hand; /* characters: up a steep bank, a hand put down on it here (world.c scrambleHand) ... */
    bool       scrambling;    /* ... doing that now */
    float      veer_side;     /* the player: drifting sideways past something lying there (share of the speed, eased) */
    bool       broken;        /* shattered bottle: only shards are left */
    int        variant;       /* trees: 1 = fruit tree; trash: what it is (the palette) */
    float      fuel;          /* fireplaces: kg of wood lying in it */
    bool       burning;       /* fireplaces: lit */
    Mesh       flames;        /* fireplaces: drawn while burning */
    Mesh       grill_mesh;    /* fireplaces: chain and grate hanging from the tripod */
    struct Object *on_grill;  /* food: lying on this fireplace's grill */
    struct Object *moved_to;  /* woodpiles: the new one; the firewood here gets carried over */
    struct Object *hose_tap;  /* hoses: the tap it's connected to */
    float      water;         /* watering cans: liters; fireplaces: liters poured on it (see camp.h) */
    bool       spraying;      /* characters: watering (hose or can) this frame */
    float      pour;          /* characters: 0 = can hanging .. 1 = lifted and tipped, pouring */
    bool       female;        /* characters: squats to pee, unless ... */
    bool       urinella;      /* ... she has one of these (a pack of them) ... */
    int        urinella_tried; /* women: 0 never tried one, 1 likes it, -1 not for her */
    int        urinellas;     /* ... fresh ones in the pocket (none: she squats like the others, and gets more) */
    bool       lighter;       /* characters: a lighter in the pocket ... */
    struct Object *lighter_of;/* ... someone else's (borrowed), or NULL: their own ... */
    int        lighter_uses;  /* ... lights left in it (0: empty) */
    int        cigarettes;    /* characters: ready ones in the pocket (a pack, or roll-ups given) ... */
    int        tobacco;       /* ... tobacco for this many roll-ups (a pouch: POUCH_ROLLUPS) ... */
    int        papers;        /* ... rolling papers (a packet: PAPERS_PACK) ... */
    int        filters;       /* ... filter tips (a bag: FILTERS_BAG) ... */
    bool       filter_habit;  /* ... rolls with a filter (without one they ask for one first) */
    bool       pocket_tray;   /* ... a pocket ashtray (a little tin with a lid) ... */
    int        pocket_butts;  /* ... butts in it (or, without one, wrapped up in the pocket): emptied at a bin, the tin or the fire */
    bool       butt_held;     /* characters: a finished cigarette in the fingers, looking for where it goes (ai.c binButt) */
    const char *keys;         /* ... keys in the pocket ("Car keys"), or NULL */
    bool       work_trousers; /* the player: work trousers, more pockets (see Pockets_slots) */
    float      smoker;        /* 0 = doesn't smoke .. 1 = chain smoker: how fast the craving grows */
    float      craving;       /* 0..1: wants a smoke */
    float      puff_timer;    /* smoke breathed out: until the next puff */
    float      smoke_ask;     /* until they ask anybody for a cigarette or a light again */
    float      dirt;          /* characters: 0 = clean .. 1 = filthy (peeing outdoors, lying in the dirt) */
    int        scrapes;       /* characters: shins and knees scraped on things (too fast, or clumsy) */
    float      scrape_cool;   /* (seconds before the next one counts) */
    struct Object *toilet;    /* characters: inside this toilet right now */
    float      grill;         /* food: seconds over a fire (FOOD_DONE, FOOD_DARK, FOOD_BURNT) */
    bool       grilled;       /* food: done over the fire */
    bool       burnt;         /* food: left on too long, charcoal */
    bool       sitting;       /* characters: squatting, e.g. by the fire */
    bool       far_off;       /* characters: far from the player - feet and hands don't look out for things */
    struct Object *seat;      /* characters: sitting on this (no collisions with it meanwhile) */
    Vec3       sit_at;        /* ... stepping over in front of it, turning round and back to here first, */
    Vec3       sit_dir;       /* ... facing this way */
    float      sit_yaw;       /* ... (as rotation.y) */
    bool       sit_moving;
    bool       sit_backing;   /* ... turned round, stepping back to the seat */
    bool       sit_scoot;     /* ... sitting, sliding along the bench to sit_at (moving up for somebody) */
    int        heard_pose;    /* characters: the body's pose last frame (going down: a thud) */
    bool       heard_open;    /* gates, the car's boot: open last frame (opening or shutting: a sound) ... */
    bool       heard_ride;    /* ... characters: riding in the car last frame (getting out: its door) */
    float      rest_time;     /* seconds lying still (physics looks less often) */
    bool       settled;       /* ... and turned to lie on the ground under it (see physics: settleOnGround) */
    bool       climbing;      /* characters: pulling themselves up a ledge (no physics meanwhile) */
    bool       climb_feet;    /* ... the feet go here meanwhile (world; set by whoever moves the climb) */
    Vec3       climb_ankle[2], climb_toe[2];
    ClimbMove  climb_move;    /* ... how the pull up goes */
    /* Stepping over something knee high (walking): a set move, not left to the
       feet - the leading leg over, the body over it, the other leg after (Physics) */
    float      over_t, over_time;  /* 0 = not; else how far (0..1) and how long it takes */
    Vec3       over_from, over_dir;
    float      over_near, over_far, over_top; /* its faces along over_dir from over_from, its top over the feet */
    int        over_lead;          /* the foot that goes first */
    bool       over_up;            /* ... up onto it rather than over it */
    Vec3       over_foot0[2], over_land[2];
    int        sit_style;     /* characters: SIT_GROUND (cross-legged) or SIT_SEAT */
    int        cast_id;       /* people: who in the story (world/cast.c), -1 nobody of it */
    int        build_stage;   /* a stage (OBJ_STAGE): how far it's built (BuildStage) ... */
    float      build_work;    /* ... person-hours of work into the next bit */
    int        stock;         /* a bought food box (OBJ_SUPPLY): portions left (-1: empty); 0: never runs out (the scene's) */
    struct Object *table;     /* a bench of a beer table set: its table (sat at facing it, stepped over) */
    int        snails;        /* characters: vineyard snails in their bucket (let go at the refuge) */
    bool       carving;       /* characters: carving with the chainsaw just now (its sound) */
    float      mowing;        /* characters: > 0 mowing with the scythe: where in the swing (rad) */
    float      commitment;    /* people: 0..1 how much they're into their part of the festival */
    int        guest;         /* people: a guest at the festival (which: world/guests.c), 0 none ... */
    float      fun;           /* ... 0..1 how much they enjoy it ... */
    int        camp_spot;     /* ... got to their place at the camping (1; 0 not yet) ... */
    bool       gone_home;     /* ... left on Sunday (out of sight at the gate, nothing more) ... */
    bool       supporter;     /* ... a supporter: a free ticket for two shifts of work (Sunday, the clean-up on Monday) ... */
    float      clean_sent;    /* people: sent to pick up litter at this story hour (game_cleanup.c: not again at once) */
    bool       heading_home;  /* people: off home now (their tent packed, out at the gate: ai_fest.c) - the crew after the clean-up too */
    bool       storm_told;    /* people: told the storm's coming - in under a roof (the festival) */
    Vec3       inside_door;   /* people: indoors (above), at this door (zero: the main building's) */
    int        injured;       /* people: INJ_* just broken, waiting for the ambulance (lying there) ... */
    int        injury_side;   /* ... which side (0 left, 1 right) ... */
    float      hospital_until; /* ... away at the hospital till this many story hours (0: not away) ... */
    int        cast_until;    /* ... a cast on (look.cast) till this weekend starts */
    float      puller;        /* people: on the big tree's rope (> 0: their place along it, m from its end) */
    int        leads, helps;  /* people: the Project they lead, one they help with (world/cast.h; PROJECT_NONE 0) */
    struct {                  /* dogs */
        int   mode;
        float timer, phase, wag, lie, bark;
        Vec3  goal;
        struct Object *fetch;   /* after this (thrown, or food lying about) ... */
        struct Object *carry;   /* ... got it in her mouth ... */
        struct Object *from;    /* ... thrown by (a stick goes back to them) */
        float chew;             /* seconds with it (an apple: she keeps it, lies down, eats it) */
        float pant, pant_t;     /* 0..1 out of breath (after a run, in the midday sun); the breath going on */
        Vec3  was;              /* where she was last frame: how fast she really goes (not into the car) ... */
        float speed, stuck;     /* ... m/s; s held up by something in the way ... */
        float detour;           /* ... s going round it (sign: which way) */
    } dog;
    float      charred;       /* wood: 0 = fresh .. 1 = burnt to ashes */
    float      glow;          /* wood: 0..1 glowing embers (in a burning fire) */
    float      on_fire;       /* characters: seconds burning (0 = not) */
    float      fire_roll;     /* characters: seconds rolled on the ground while burning */
    float      work_rate;     /* characters: how fast work goes (liking it, condition); 1 = normal */
    float      pace;          /* people: how quick they walk (1 normal; old and calm: slower) */
    float      tempo;         /* characters: their pace, 1 = normal (0 = not set: normal) */
    Vec3       carry_pos;     /* characters: where they were last frame ... */
    float      carry_yaw;     /* ... and which way they faced (what they carry didn't hit anything there) */
    bool       carry_ok;      /* ... (known) */
    bool       carry_blocked; /* this frame: what they carry hit something, so they didn't turn / move */
    float      skill[ORDER_COUNT]; /* characters: how good at each job (0..1), grows with practice */
    int        jobs_seen;     /* characters: finished tool jobs already counted for practice */
    float      bump;          /* characters: walked into something this fast (m/s) this step ... */
    Vec3       bump_normal;   /* ... facing away from it */
    float      bump_top;      /* ... its top this high above the feet ... */
    bool       bump_toe;      /* ... a ladder lying there: it hardly moves, the toe hurts */
    float      bump_cool;     /* seconds until a bump counts again */
    Vec3       pee_hit;       /* characters: where the stream comes down */
    float      extend, extend_goal; /* ladders: 0 = slid together .. 1 = all the way out */
    float      raised, raise_goal; /* ladders carried slid out: held near the foot, this steep up in front (radians, 0 = carried at the side) */
    bool       dipped;        /* ... tipped down flat to get under something (a beam, a doorway) */
    bool       steep;         /* ... stood up straight: a wall in front (flat it would go into it) */
    float      grip_shift;    /* ladders carried at the side: slid back (< 0) or on in the hand (m), off a wall */
    bool       just_caught;   /* ladders: fell against something just now and rests there (the game says so) */
    struct Object *pullover;  /* characters: their pullover as a thing (OBJ_CLOTHES, made the first time it's put down) ... */
    bool       pullover_down; /* ... lying there (else with them: worn - look.top TOP_JACKET - or tied round the waist) */
    bool       pullover_sat;  /* ... put down as they sat (over the chair's back, under them): taken back getting up */
    bool       was_sitting;   /* characters: sitting last frame (see Clothes_update) */
    struct Object *cloth_of;  /* clothes: whose */
    struct Object *cloth_host;/* clothes: over this chair's back (and along with it when it slides) */
    const char *cloth_where;  /* clothes: where, said plainly ("over the chair's back") */
    bool       jacket_told;   /* the player: put the pullover on for you / told where it is, this cold spell */
    int        shown;         /* furniture: what its mesh shows (butts in the tin); -1: not built */
    bool       gate_open;     /* gates: open (or opening) ... */
    float      gate_t;        /* ... how far open (0 shut .. 1 open) */
    Vec3       hinge;         /* ... where it turns */
    Vec3       gate_shut, gate_out; /* ... the way it reaches shut, and open (flat, unit) */
    Vec3       tucked;        /* chairs: where it stands pushed in (set when first pulled out) ... */
    bool       tucked_set;
    bool       trunk_cut;
    float      snag;          /* fallen trees: seconds its branches have been caught on something while dragged */     /* trees: felled (cut off its foot: a sawn end, no roots) */
    float      chair_out, chair_goal; /* ... pulled back from there this far (m), and going to this far */
    bool       seat_scooted;
    bool       drop_tip;      /* bottles: dropped (not set down): it falls over when it lands, mostly */  /* characters: sitting, the chair shuffled in to the table */
    bool       leaning;       /* ladders: leaning against something (stands still) */
    float      air_fall;      /* characters: in the air, falling this fast at most (m/s; for the landing) */
    Vec3       last_ground;   /* characters: where they last stood on something (fell through anyway: back there) */
    float      sprain;        /* characters: game s left limping on a twisted ankle (world/injury.c) */
    float      like;          /* people: 0..1 how much they like you (friendship; people/touch.c) ... */
    float      love;          /* ... 0..1 how drawn to you they are (closeness, more) ... */
    struct Object *led_by;    /* ... walking along with this one, hand in hand (or arm in arm) */
    float      harass_t;      /* people: game s till they might bother somebody again (the festival's drunk ones) */
    float      ladder_skill;  /* characters: practice climbing ladders one-handed (0..1) ... */
    float      ladder_wobble; /* ... nearly slipped: the next slip is a fall (seconds this counts) */
    Vec3       lean_normal;   /* ladders: away from what it leans on */
    bool       spot_checked;  /* wood: somebody looked whether there's room to work on it (once) */
    float      chill;         /* characters: 0..1 cold (night, away from a fire): a jacket on over 0.6 */
    float      shake;         /* ladders: wobbling (seconds left) */
    float      stability;     /* ladders: how steady it stands, leaning (0..1; somebody holding it: steady) */
    struct Object *held_by;   /* ladders: somebody holding it steady at its foot (beside it, hold_side: +1 / -1 along
                                 cross(lean_normal, up), out of the climber's way) ... */
    float      hold_side;
    struct Object *holding_ladder; /* characters: ... this one */
    float      tip, tip_v;    /* ladders: falling over: tipped this far from where it stood (rad), this fast ... */
    Vec3       tip_from, tip_dir; /* ... from that axis, towards that (horizontal) way */
    float      lean_top;      /* ladders: where along it the edge is (0 = against a face, no getting off) */
    float      placing;       /* ladders: being put up against something: 0 = not, else how far (0..1) ... */
    Vec3       place_foot[2], place_axis[2], place_face[2]; /* ... from (carried) to (leaning) */
    float      place_extend[2], place_top;
    struct Object *on_ladder; /* characters: climbing this ladder ... */
    float      ladder_at;     /* ... with the feet this far up along it */
    Vec3       ladder_from[4], ladder_to[4]; /* ... feet and hands on their way from rung to rung */
    float      ladder_t[4];
    Vec3       ladder_shift;  /* ... the body still this far from its place on it (just got on: eases away) */
    float      ladder_off;    /* ... stepping off at the bottom (0 = not, .. 1 = off), from ladder_off_from */
    Vec3       ladder_off_from;
    bool       ladder_limbs_ok;

    /* Characters only */
    FigureLook look;
    Body       body;
    Skeleton   skeleton;      /* solved every frame, figure space */
    Hands      hands;
    Work       work_state;    /* tool strokes */
    Ai         ai;            /* everybody but the player */
    Vec3       move_local;    /* walk intent: x = right, z = forward (-1..1) */
    float      drunk;         /* 0 = sober, 1 = passes out */
    Mood       temperament;   /* how this person carries themselves */
    Needs      needs;
    bool       passed_out;    /* asleep because of drink, not tiredness */
    float      tolerance;     /* how well they hold their drink (1 = average) */
    float      bladder_size;  /* 1 = average; bigger fills slower and takes longer to empty */
    float      last_drunk;
    float      burp_timer;    /* > 0: a burp is coming */
    char       name[16];
    int        main_hand;     /* HAND_RIGHT or HAND_LEFT */
    float      top_speed;     /* sprinting flat out, fresh and sober (m/s) */
    float      fitness;       /* characters: how sporty (0..1), whatever the build: quicker, surer, smoother over things */
    float      wits;          /* characters: 0 = slow on the uptake .. 1 = sharp (sees what's wrong and sorts it out) */
    float      worn;          /* characters: worn by the work of the days (0..1): sleep takes it off, a hard day leaves some for the next */
    float      height_fear;   /* characters: 0 = no head for heights needed .. 1 = won't go up a ladder at all */
    float      jump_speed;    /* take-off speed of a standing jump, fresh and sober (m/s) */
    float      run_speed;     /* player: current speed, eased (m/s) */
    float      sprint_boost;  /* player: 0 = jogging .. 1 = flat out (tapping Shift) */
    float      tap_timer;     /* player: seconds since Shift was last pressed */
} Object;

/* Creates an object, its mesh and matching colliders. See ObjectType for size. */
Object *Object_create(ObjectType type, Palette palette, Vec3 position, Vec3 size, float weight);
void    Object_destroy(Object *obj);

void    Object_setRotation(Object *obj, Vec3 rotation);
/* Recomputes cached collider data from the current rotation. */
void    Object_syncColliders(Object *obj);

Mat4    Object_modelMatrix(const Object *obj);
/* Horizontal forward direction (characters face along it). */
Vec3    Object_facing(const Object *obj);
Vec3    Object_colliderCenter(const Object *obj, int index);
/* Distance along a ray (dir: unit) to an oriented box collider centered at
   center (world), and the face it enters through; -1 if it misses or starts inside. */
float   Collider_ray(const Collider *c, Vec3 center, Vec3 origin, Vec3 dir, Vec3 *normal);
/* The same for collider index of obj, and the face it
   enters through (normal, towards the ray); -1 if it misses or starts inside. */
float   Object_rayToCollider(const Object *obj, int index, Vec3 origin, Vec3 dir, Vec3 *normal);
/* How far obj's colliders reach from its position at most (a bounding sphere). */
float   Object_reach(const Object *obj);

/* Trees: the trunk's wood and the branches (kg). */
float   Object_treeWeight(const Object *obj);
/* What pushing or pulling it along takes (kg): its weight; a fallen tree is
   dragged (one end on the ground) and its branches catch: easier without them. */
float   Object_dragWeight(const Object *obj);

/* Characters: up high (on a roof, or well over the ground)? */
bool    Object_upHigh(const Object *obj, float ground_y);
/* Characters up high: how careful (0 not at all .. 1 creeping): fear, drink and wits (drunk and dim: careless). */
float   Object_caution(const Object *obj);

/* Characters: 1 when fresh, down to 0.5 when exhausted (speed, strength, work). */
float   Object_vigor(const Object *obj);
/* How high (m) someone walks up onto something solid (a block, a crate): the sporty
   and fresh higher, the unfit, worn out or drunk lower; higher than that they go round. */
float   Object_stepUpMost(const Object *obj);
/* A person this tall (m): their body box, how high they step, and how fast their legs carry them
   (longer legs: a little quicker) - everything else scales from look.height. */
void    Object_setHeight(Object *obj, float height);
/* Worn down by the day (sleepiness, felt by personality, fitness and drink): 0 fresh .. 1 done in. */
float   Object_tiredness(const Object *obj);
/* Characters: how fast they can sprint and jump right now (tired or drunk: less). */
float   Object_topSpeed(const Object *obj);
float   Object_jumpSpeed(const Object *obj);
/* Characters: how much the bladder holds right now (1 = average): smaller when worn out. */
float   Object_bladder(const Object *obj);
/* Characters: seconds a pee takes, from how full the bladder is. */
float   Object_peeTime(const Object *obj);
/* Characters: name, main hand and body stats from weight and temperament. */
void    Object_setPerson(Object *obj, const char *name, int main_hand);

bool    Object_isBottle(const Object *obj);
bool    Object_isFood(const Object *obj);
/* Litter, an empty bottle or broken glass: something to clean up. */
bool    Object_isTrash(const Object *obj);
/* Wood that burns: firewood, branches, short logs. */
bool    Object_isFuel(const Object *obj);
/* Goes into the toilet (out of sight) to pee. */
void    Object_useToilet(Object *who, Object *toilet);
/* Furniture (static): workbench, stove, car, log bench, ... */
Object *Object_createFurniture(FurnitureKind kind, Vec3 position, Vec3 size, float yaw);
/* Sits down: on the seat if there is one (on its side towards `toward`,
   facing that way), else cross-legged on the ground. */
/* Sits down (on seat, or the ground with NULL) facing toward. False: no room
   there (somebody sits on that chair, or that spot of the bench or log). */
bool    Object_sit(Object *who, Object *seat, Vec3 toward);
/* Room on a bench or a log for who, as near `near` as can be: ROOM_FREE (there is),
   ROOM_SCOOT (the others there would move up for it), ROOM_NONE. asked: the
   unwilling move up too (they were asked); you never move for anybody. */
enum { ROOM_NONE, ROOM_FREE, ROOM_SCOOT };
int     Object_benchRoom(const Object *who, const Object *seat, Vec3 near, bool asked);
/* ... and they do move up (sliding along it): true if there's room now. */
bool    Object_benchMakeRoom(const Object *who, Object *seat, Vec3 near, bool asked);
/* Sitting, sliding along the bench (moving up): every frame. */
void    Object_scootOnSeat(Object *who, float dt);
/* Who's about (the world each frame): Object_sit keeps people apart. */
void    Object_seePeople(Object *const *objects, int count);
/* Sitting down on a seat: steps over and turns round first (true meanwhile). */
bool    Object_settleOnSeat(Object *who, float dt);
/* Up again: forgets the seat once clear of it (no bumping into it meanwhile). */
void    Object_leaveSeat(Object *who, float dt);
#define CHAIR_PULL  0.38f /* m a chair is pulled out to sit on it */
/* A chair being pulled out or pushed in (towards chair_goal): slides along. */
void    Object_slideChair(Object *chair, float dt);
/* A seat (chair, bench) within reach of pos, or NULL. */
Object *Object_seatNear(Object *const *objects, int count, Vec3 pos, float reach);
/* Houses: the front door at x (meters along the ridge from the middle). */
void    Object_setDoor(Object *obj, float x);
/* Umbrellas: the mesh open or folded, as umbrella_open says (when that changed). */
void    Object_showUmbrella(Object *umbrella);
/* Garbage bags: a new mesh for how full it is. */
void    Object_fillBag(Object *bag, float added_weight);

/* Trees: cut through the trunk this high above the ground (the rest stays as
   the stump), or take branches off the crown. Rebuild mesh and colliders. */
void    Object_cutTrunk(Object *obj, float height);
void    Object_setBranches(Object *obj, int branches);

/* Bottles and trees: starts falling over so the top lands in direction dir (horizontal). */
/* A tree's mesh and colliders again from its trunk, crown and branches (after loading a save). */
void    Object_rebuildTree(Object *obj);
void    Object_topple(Object *obj, Vec3 dir);
/* Turns a fallen tree (or a log) into a log of this length lying along axis,
   centered at center. Weight follows from the wood volume. */
void    Object_makeLog(Object *obj, Vec3 center, Vec3 axis, float length, float diameter);

/* Turns an object into split firewood (see OBJ_FIREWOOD), lying along axis with
   its local Z (the curved side) along up, centered at center. */
void    Object_makeFirewood(Object *obj, Vec3 center, Vec3 axis, Vec3 up, float length, float diameter, int parts);

/* Puddles and stink lines lying on uneven ground (size as for Object_create). */
Object *Object_createOnGround(ObjectType type, Palette palette, Vec3 position, Vec3 size, ShapeGround ground);

/* Something lying on the ground (a road, water): a static object around a ready mesh. */
Object *Object_createDecal(Mesh mesh, Vec3 position);
/* A gate leaf (FURN_GATE) turning about its hinge towards gate_open (dt: 0 puts it right there). */
void    Object_swingGate(Object *gate, float dt);
/* A building made of parts: its look (the solid parts are OBJ_WALLs of their own). */
Object *Object_createStructure(Mesh mesh, Vec3 position, float yaw, Vec3 size);

/* How bad it smells at pos (0 = fine .. 1 = right over something fresh). */
/* Per-frame index of a few kinds that everybody looks for (the world builds it
   at the start of each update; objects are only ever added, so it holds for that
   array as long as the count is the same). */
enum { KIND_CHARACTERS, KIND_FIREPLACES, KIND_STINKS, KIND_COUNT };
void    Object_indexKinds(Object *const *objects, int count);
/* Those of that kind in objects (*n of them) - or all of objects if not indexed for it. */
Object *const *Object_kind(int kind, Object *const *objects, int count, int *n);

float   Object_stinkAt(Object *const *objects, int count, Vec3 pos);

/* Woodpiles: puts a quarter of firewood on the next place of the stack, as
   neatly as care (0 = just thrown on .. 1 = exactly in its place). A loose
   pile getting high may come down: returns how many pieces slid off. */
int     Object_stackFirewood(Object *pile, Object *piece, Object *const *objects, int count, float care);
/* Is this firewood lying on that woodpile? */
bool    Object_onPile(const Object *pile, const Object *o);
/* How many pieces of firewood lie on the woodpile right now. */
int     Object_pileCount(const Object *pile, Object *const *objects, int count);
/* The cut being made shows where point (world) is: progress 0..1 (0 takes the mark away). */
void    Object_markCut(Object *o, Vec3 point, float progress);

/* Hard enough to break glass on: stone, brick and plaster walls, metal. */
bool    Object_isHard(const Object *obj);

/* Lowest point of the main collider (world). */
float   Object_bottom(const Object *obj);

/* A short line about the object ("Beer - 3 sips left"). */
void    Object_describe(const Object *obj, char *out, size_t size);

/* Advances a fall started by Object_topple. */
void    Object_advanceTopple(Object *obj, float dt);
/* A bottle of water (OBJ_WATER) made a soft drink: 0 water, 1 cola, 2 orange soda, 3 mate. */
void    Object_setDrink(Object *b, int drink);
/* Gone (eaten): no mesh, no collision. */
void    Object_useUp(Object *obj);
/* Bottles: turns into shards lying on the ground at ground_y. */
void    Object_shatter(Object *obj, float ground_y);

#endif /* WORLD_OBJECT_H */