Entropie git · main
Entropie festival game; first steps
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.gzentropie 2.17.0: arrival, car, seats, crates, tents
- Arriving: a stretch after the drive, then over to the fire together at a stroll; Kurti walks slowly; "everybody" shouts leave out Kurti and sons - Volkram drives, you sit in the back; a new old estate car that fits the farm; heads no longer through the roof, seats lean back - Nobody sits on the same spot as someone else (benches, logs, chairs) - Kurti: one rounded heavy build (chest into belly, not a ball) - Empty bottles go back into crates (you and the tidy NPCs) - NPCs walk as fast as you and slow down carrying crates like you - Tents sit on the ground (no hovering); move a tent without pegs with F
commit_message.txt | 19 ++--- entropie/Makefile | 2 +- entropie/README.md | 13 ++- entropie/STATE.md | 11 ++- entropie/assets/shaders/figure.frag | 17 ++-- entropie/src/game/game.h | 3 + entropie/src/game/game_actions.c | 62 ++++++++++++++- entropie/src/game/game_arrival.c | 7 +- entropie/src/game/game_talk.c | 4 +- entropie/src/people/ai.c | 4 +- entropie/src/people/ai_help.c | 18 ++++- entropie/src/people/ai_internal.h | 2 +- entropie/src/people/body.c | 8 ++ entropie/src/people/body.h | 2 + entropie/src/render/shapes.h | 1 + entropie/src/render/shapes_props.c | 154 +++++++++++++++++++++++------------- entropie/src/world/car.c | 32 +++++++- entropie/src/world/car.h | 6 +- entropie/src/world/object.c | 76 ++++++++++++++---- entropie/src/world/object.h | 9 ++- entropie/src/world/scene.c | 3 +- entropie/src/world/tent.c | 21 ++++- entropie/src/world/tent.h | 5 ++ entropie/src/world/world.c | 1 + 24 files changed, 370 insertions(+), 110 deletions(-) diff --git a/commit_message.txt b/commit_message.txt index 2791f58..ff953ba 100644 --- a/commit_message.txt +++ b/commit_message.txt @@ -1,10 +1,11 @@ -entropie 2.16.1: car arrival crash and car fixes +entropie 2.17.0: arrival, car, seats, crates, tents -- Fix the segfault when arriving by car: at slow frames the arm springs - blew up (passengers vanished, then a NaN became a terrain index); - springs now substep, NaN never reaches the body or the terrain lookup -- Passengers no longer sit backwards in the car -- Car inside: windscreen pillars lean back; no more looking through under - the steering wheel or beside the dashboard -- People sitting on a bench, chair, log or in the car no longer rotate - their body to follow you (only the head turns) +- Arriving: a stretch after the drive, then over to the fire together at a + stroll; Kurti walks slowly; "everybody" shouts leave out Kurti and sons +- Volkram drives, you sit in the back; a new old estate car that fits the + farm; heads no longer through the roof, seats lean back +- Nobody sits on the same spot as someone else (benches, logs, chairs) +- Kurti: one rounded heavy build (chest into belly, not a ball) +- Empty bottles go back into crates (you and the tidy NPCs) +- NPCs walk as fast as you and slow down carrying crates like you +- Tents sit on the ground (no hovering); move a tent without pegs with F diff --git a/entropie/Makefile b/entropie/Makefile index 2c0266e..ce1d7d2 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 2.16.1 +VERSION := 2.17.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index b86e71d..1c8e93c 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -25,9 +25,10 @@ Painted in the rotoscope style of *Waking Life* (2001): ## Arriving -- The game starts in your car: you, Volkram, Jana and Toni come off the - street onto the gravel road beside the main building and park in the yard - (a short drive; the others sit in the car with you; look around with the mouse) +- The game starts in Volkram's car (he drives, Jana beside him, you and Toni + in the back): off the street onto the gravel road beside the main building, + into the yard (look around with the mouse) +- Out of the car everybody has a good stretch, then you walk over together - Kurti and his sons sit at the fire. First say hello: go over, they welcome you. Then sit down with them (C) for a little chat with Kurti - Then the work: the boot opens, the others unload. Crates go into the @@ -39,7 +40,8 @@ Painted in the rotoscope style of *Waking Life* (2001): put it down for a moment; or two carry it (F to hold on, E to lift together). The others put theirs down for a breather too - Crates: left click takes a bottle out (the others help themselves too), - E / Q carries the whole crate + E / Q carries the whole crate. An empty bottle in your hand: left click at a + crate puts it back in (the tidy bring theirs back too) - The hint line says what's to do next - Nobody asks you for other jobs (wood and so on) until the tents stand and everything is unloaded @@ -79,6 +81,8 @@ Painted in the rotoscope style of *Waking Life* (2001): the pegs come out, then the cover, the poles, and it's folded into its bag. Sleeping bags and mats come out at the door; the owner helps - A tent packed on purpose stays packed (nobody puts it up again on their own) +- Moving one: pull its pegs out (hold left click), then hold F and walk; let + go to set it down, hold left click to peg it again ## The site @@ -701,6 +705,7 @@ say no more often and don't come to you for a bit. | K | Light a cigarette / put it out | | Left click at the car's boot | Take out the next thing | | Left click on a tent | Spread it out; hold: put it up (a standing one: take it down) | +| F at a tent without pegs | Move it (hold F and walk) | | Z | Sleep / get up (at your tent's door: in it) | | C | Sit down / get up; on fire: drop and roll; at a fire with a drink: pour it on | | Space (asleep, hold) | Sleep faster | diff --git a/entropie/STATE.md b/entropie/STATE.md index db3ab52..02a6946 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -257,6 +257,15 @@ Round 2.16.1: - The car from inside (`Shapes_carInside`): the windscreen pillars lean back like the windscreen (were tipped forward); the footwell's front and the dashboard's underside close the hole under the wheel; the floor and the doors go on to the front (you saw the road beside and under the dashboard). - Sitting people (on a bench, chair, log, in the car) no longer turn their whole body to follow you (`turnTowards` leaves them as they sat; the head still turns to look). +Round 2.17: +- Arriving: out of the car everybody stretches (`Body_stretch`: FIDGET_STRETCH x1.3), then (7 s) over to the fire together at a stroll (`goToPoint` greeting: STROLL x1.15). Shouting for everybody leaves out Kurti and his sons (`lives_here`: call them by name). +- Pace (`Object.pace`, scene.c: years x (1 + 0.12 energy)): NPCs walk that much slower (`ai_walk`) - Kurti about half. NPC WALK_SPEED 2.2 (was 3.0: as quick as you walk), and slowed by what they carry like you (`Hands_moveFactor`: a crate in both arms 0.7) - they carried crates at full speed. +- The car: Volkram drives (car keys in his pocket), Jana beside him, you and Toni in the back. A new look (`Shapes_car`, `addProfile`: rounded side profiles made solid): an old faded green estate (CAR_COLOR), glass all round with pillars and roof over it, roof rails, wheel arches with a bit of rust, a rubbing strip, handles, grille, number plate. Taller cabin (roof 1.57, lining 1.51), seats lower (SEAT_UP 0.18), eyes 1.30: heads under the roof. The seats lean back (were tipped forward). The inside matches (bonnet profile, pillars along the windscreen). +- Seats: nobody sits on top of anybody (`Object_sit` now bool; `Object_seePeople` each frame): a bench or log is sat on where you are along it, the nearest spot SEAT_SPACE 0.55 m clear of the others (benches sat everybody in the middle); a taken chair, or no spot left: no sitting ("No room there - somebody's sitting on that spot"). +- Kurti's build (`figure.frag`, belly): one rounded front - a broad low belly, a soft chest on into it, the sides over the hips, wide blends (was a ball on the belly). MAX_PARTS 41. (Step length already scales with height.) +- Bottles back into crates (`Crate_putBottle`, `crate_empties`): left click at a crate with an empty in hand (or both hands full) puts it in; taking one out gives a full one ("Only empties left in this one"). The tidy carry empties to the nearest crate with room within 50 m instead of the bin (`ai_help.c`). +- Tents stood on the sleeping bags lying where they were spread (`World_groundHeight` counted them): on the terrain now (also when blown about). Moving one (`game_actions.c` moveTent, `Tent_movable` / `Tent_startMove`, `tent_dragged`): no pegs in it, hold F: it comes along in front of you, turning with you, set down where you let go; then it goes up again from there (hold left click: the pegs). Nobody works on it while it's moved. + Unfinished building (Shell_build, at SHELL_AT in the frame's frame): brick shell 6.4 x 4.7, walls 2.9, open front (2 m) to the frame (now only the front half: pillars at z -13.8 / -9.2), window holes, bare floor, tiled roof with slabs. Barn: brick courses on BUILDING_BRICK walls (`addBrickCourses`: mortar a shade lighter, so the ink doesn't trace it), ivy (`Shapes_ivy`, OBJ_STRUCTUREs) on both long sides; the woodpile (OBJ_WOODPILE "Woodpile", WOODPILE_A/B) left of the toilet, everybody's woodpile from the start (`scene.c` Scene_finish). Fireplace tripod: wooden poles, feet TRIPOD_OUT 0.9 m outside the stones, TRIPOD_PAST 0.4 m past the top, a lashing; each pole an OBJ_WALL along it ("Tripod pole"). Gates (FURN_GATE, green boards): in your garages' doorways (GATES: hinge, end), `gate_open` / `gate_t`, `hinge`, `gate_shut` / `gate_out`; Object_swingGate (1.2 s), driven by showProps; a click within 1.2 m opens / shuts both leaves. Props that show (1.89, `world.c` showProps every frame): the tin's mesh is built again when its butts change (`Shapes_ashtray`: up to 12 show, the ash rises, a heap from 14; `shown` = what's built). @@ -413,4 +422,4 @@ Most important first. - Drawing many figures on a real graphics card: not measured (software GL: 300 people cost 3x the rest of the scene) --- -State of `entropie 2.16.1 (build 4cd7c31)` +State of `entropie 2.17.0 (build 603c526)` diff --git a/entropie/assets/shaders/figure.frag b/entropie/assets/shaders/figure.frag index e23923d..8216055 100644 --- a/entropie/assets/shaders/figure.frag +++ b/entropie/assets/shaders/figure.frag @@ -72,7 +72,7 @@ const float SELF_SHADOW_SKIP = 0.10; // meters // ---------- Body parts ---------- -const int MAX_PARTS = 38; // (a person with everything: a skirt, a jacket tied round, a cigarette, a Urinella, a stream) +const int MAX_PARTS = 41; // (a person with everything: a skirt, a jacket tied round, a cigarette, a Urinella, a stream) vec3 partA[MAX_PARTS], partB[MAX_PARTS]; float partR1[MAX_PARTS], partR2[MAX_PARTS]; float partBlend[MAX_PARTS]; // smooth-union radius with the rest of the body @@ -137,10 +137,17 @@ void buildBody() { addPart(pelvis + spineDir * (jacket ? 0.0 : 0.05), waist, 0.130 + puff, 0.135 + puff, 0.04, 0.70, sleeve); addPart(waist, chest + spineDir * 0.03 - chestForward * 0.005, 0.135 + puff, 0.160 + puff, 0.04, 0.66, jacket ? M_JACKET : top == 1 ? M_SKIN : M_SHIRT); addPart(joints[J_SHOULDER_L], joints[J_SHOULDER_R], 0.050 + puff, 0.050 + puff, 0.05, 0.75, sleeve); - if (belly > 0.0) { // a belly: round, out in front over the belt - vec3 b = mix(pelvis, chest, 0.3) + chestForward * (0.07 * belly); - addPart(b - spineDir * 0.06, b + spineDir * 0.07, 0.13 + 0.05 * belly, 0.12 + 0.04 * belly, 0.07, 0.95, - jacket ? M_JACKET : top == 1 ? M_SKIN : M_SHIRT); + if (belly > 0.0) { // a heavy build: one soft rounded front from the chest down over the belt (not a ball stuck on) + int front = jacket ? M_JACKET : top == 1 ? M_SKIN : M_SHIRT; + vec3 across = normalize(joints[J_SHOULDER_R] - joints[J_SHOULDER_L]); + vec3 b = mix(pelvis, chest, 0.32) + chestForward * (0.055 * belly); // the belly, low and broad + addPart(b - spineDir * 0.09, b + spineDir * 0.10, 0.14 + 0.045 * belly, 0.13 + 0.04 * belly, 0.11, 0.9, front); + vec3 c = chest - spineDir * 0.07 + chestForward * (0.035 * belly); // the chest, soft, on into the belly + for (int s = -1; s <= 1; s += 2) + addPart(c + across * (float(s) * 0.07) - spineDir * 0.02, c + across * (float(s) * 0.075) + spineDir * 0.03, + 0.075 + 0.02 * belly, 0.07 + 0.015 * belly, 0.10, 0.85, front); + addPart(pelvis + spineDir * 0.12 - across * 0.12, pelvis + spineDir * 0.12 + across * 0.12, // the sides over the hips + 0.07 + 0.025 * belly, 0.07 + 0.025 * belly, 0.09, 0.8, front); } // A jacket tied round the waist: a band over the hips, the sleeves knotted in front, the rest hanging down behind diff --git a/entropie/src/game/game.h b/entropie/src/game/game.h index d2729ce..3d40071 100644 --- a/entropie/src/game/game.h +++ b/entropie/src/game/game.h @@ -75,6 +75,9 @@ typedef struct { Arrival arrival; /* the start: in by car */ struct Object *tent; /* the tent you're working on (left button held) */ float tent_hold; /* ... held this long at a standing one (a moment: you start taking it down) */ + struct Object *tent_drag; /* a tent with no pegs in you're moving (F held) ... */ + Vec3 drag_off; /* ... where it is from you (your right, up, ahead) ... */ + float drag_turn; /* ... and how it's turned from you */ float dt; /* this frame's step */ bool rain_told; /* told it's raining (this shower) */ bool wet_told; /* "wet through" said (till dry again) */ diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c index 0a95bf0..de3c32d 100644 --- a/entropie/src/game/game_actions.c +++ b/entropie/src/game/game_actions.c @@ -574,9 +574,23 @@ static bool takeFromSupply(Game *game) { } int hand = p->hands.state[HAND_RIGHT] == HAND_FREE ? HAND_RIGHT : p->hands.state[HAND_LEFT] == HAND_FREE ? HAND_LEFT : -1; + if (o->type == OBJ_CRATE) { /* a bottle in hand that's empty (or no hand free): back in it goes */ + for (int h = 0; h < 2; h++) { + Object *it = p->hands.item[h]; + if (!it || p->hands.state[h] != HAND_HOLDING || !Object_isBottle(it) || it->broken || (it->sips > 0 && hand >= 0)) continue; + if (o->stored >= Crate_bottles(o->variant)) { say(game, "The crate is full"); return true; } + Hands_drop(p, h); + Hands_touch(p, vec3_add(Object_colliderCenter(o, 0), vec3(0.0f, 0.2f, 0.0f))); + bool empty = it->sips <= 0; + Crate_putBottle(o, it); + say(game, empty ? "The empty back into the crate" : "The bottle back into the crate"); + return true; + } + } if (hand < 0) { say(game, "Your hands are full"); return true; } if (o->type == OBJ_CRATE) { /* a bottle out of the crate (E / Q: the crate itself) */ if (o->stored <= 0) { say(game, "The crate is empty"); return true; } + if (o->stored <= o->crate_empties) { say(game, "Only empties left in this one"); return true; } Object *bottle = Crate_takeBottle(&game->world, o); if (bottle && Hands_pickUpObject(p, hand, bottle)) tell(game, bottle); return true; @@ -770,6 +784,45 @@ static bool workTent(Game *game) { return tent->tent_stage < TENT_UP || tent->tent_down; } +/* F held right at a tent with no pegs in it: it comes along (in front of + you, turning with you), set down on the ground where you let go. */ +static bool moveTent(Game *game, bool held) { + Object *p = game->player, *t = game->tent_drag; + if (t && (!held || t->broken)) { + t->tent_dragged = false; + t->tent_idle = 0.0f; + game->tent_drag = NULL; + say(game, "Put down here - hold left click to peg it again"); + return true; + } + if (!t) { + if (!held || Hands_isGrabbing(p) || game->holding) return false; + t = tentFind(game, false, false); + if (!t) t = tentFind(game, false, true); + if (!t) return false; + if (!Tent_movable(t)) { + static float told; + if (game->daycycle.seconds - told > 4.0f) { say(game, "Its pegs are in - hold left click to take it down (the pegs come out first)"); told = game->daycycle.seconds; } + return false; + } + Tent_startMove(t); + Vec3 f = Object_facing(p), r = vec3(-f.z, 0.0f, f.x), rel = vec3_sub(t->position, p->position); + game->drag_off = vec3(vec3_dot(rel, r), 0.0f, vec3_dot(rel, f)); + game->drag_turn = t->rotation.y - p->rotation.y; + game->tent_drag = t; + say(game, "You pick the tent up - walk to move it, let go of F to set it down"); + } + Vec3 f = Object_facing(p), r = vec3(-f.z, 0.0f, f.x); + Vec3 at = vec3_add(p->position, vec3_add(vec3_scale(r, game->drag_off.x), vec3_scale(f, game->drag_off.z))); + at.y = Terrain_height(&game->world.terrain, at.x, at.z); + t->position = at; + Object_setRotation(t, vec3(0.0f, p->rotation.y + game->drag_turn, 0.0f)); + Object_syncColliders(t); + t->tent_idle = 0.0f; + Hands_touch(p, vec3_add(at, vec3(0.0f, 0.6f, 0.0f))); + return false; /* (walking on as usual) */ +} + /* At the cigarette machine: a pack. */ static bool smokesMachine(Game *game) { Object *m = furnitureNear(game, FURN_SMOKES, 0.9f); @@ -895,7 +948,9 @@ static bool game_bootHint(Game *game, char *out, size_t size) { static bool tentHint(Game *game, char *out, size_t size) { Object *p = game->player, *bag = tentNear(game, true), *tent = tentNear(game, false); if (bag) { snprintf(out, size, "A tent, packed - left click: spread it out here (the door towards you) - E / Q: carry it"); return true; } - if (tent) { snprintf(out, size, "The tent: hold left click - %s%s", Tent_job(tent), tent->tent_stage == TENT_PEGS && !tent->tent_down ? " (a stone in the other hand: quicker)" : ""); return true; } + if (game->tent_drag) { snprintf(out, size, "Moving the tent - let go of F to set it down here"); return true; } + if (tent) { snprintf(out, size, "The tent: hold left click - %s%s%s", Tent_job(tent), tent->tent_stage == TENT_PEGS && !tent->tent_down ? " (a stone in the other hand: quicker)" : "", + Tent_movable(tent) ? " - F (hold): move it" : ""); return true; } Object *mine = Tent_of(game->world.objects, game->world.count, p), *standing = tentFind(game, false, true); Vec3 door = mine ? Tent_door(mine) : p->position; bool at_door = mine && mine->tent_stage >= TENT_COVER && hypotf(door.x - p->position.x, door.z - p->position.z) <= 1.6f; @@ -1241,7 +1296,8 @@ void game_handleActions(Game *game, const Input *input) { else if (input->sit && !Body_isBusy(&p->body) && !p->on_ladder) { if (p->sitting) p->sitting = false; else { - Object_sit(p, Object_seatNear(game->world.objects, game->world.count, p->position, 1.0f), p->position); + if (!Object_sit(p, Object_seatNear(game->world.objects, game->world.count, p->position, 1.0f), p->position)) + say(game, "No room there - somebody's sitting on that spot"); Object *c = p->pullover; if (!p->seat && p->pullover_down && c && c->variant == CLOTH_SPREAD && vec3_length(vec3(c->position.x - p->position.x, 0.0f, c->position.z - p->position.z)) < 0.6f) @@ -1292,6 +1348,8 @@ void game_handleActions(Game *game, const Input *input) { if (!empty || !takeLadder(game)) putDown(game); } + /* F (held) at a tent with no pegs in: move it */ + if (moveTent(game, input->grab)) return; /* F (held) or a click on something heavy: hold on to push, pull or lift */ bool grab = input->grab || game->holding; if (grab && !Hands_isGrabbing(p)) { diff --git a/entropie/src/game/game_arrival.c b/entropie/src/game/game_arrival.c index 9cf16b9..eb3c0fe 100644 --- a/entropie/src/game/game_arrival.c +++ b/entropie/src/game/game_arrival.c @@ -91,7 +91,9 @@ void game_arrivalStart(Game *game) { Object *everybody[4] = { game->player, friends[0], friends[1], friends[2] }; for (int k = 0; k < 4; k++) if (everybody[k]) Car_load(car, CARGO_MAT, everybody[k]); for (int k = 0; k < 4; k++) if (everybody[k]) Car_load(car, CARGO_SLEEPING_BAG, everybody[k]); - for (int k = 0; k < 4; k++) if (everybody[k]) Car_getIn(car, everybody[k], k); + /* Volkram drives (his car), Jana beside him; you and Toni in the back */ + Object *seats[4] = { friends[0], friends[1], game->player, friends[2] }; + for (int k = 0; k < 4; k++) if (seats[k]) Car_getIn(car, seats[k], k); Car_place(&game->world, car, a->path[0], car->rotation.y); a->stage = ARRIVAL_DRIVING; a->speed = 3.0f; @@ -116,6 +118,7 @@ static void getOut(Game *game, const char *name, const char *line) { Object *o = name ? person(game, name) : game->player; if (!o || !o->riding) return; Car_getOut(o); + Body_stretch(&o->body); /* (a long drive: a good stretch first) */ if (line && o->type == OBJ_HUMAN) Ai_say(o, line); } @@ -186,7 +189,7 @@ void game_arrival(Game *game, float dt) { game->third_person = true; a->greeted = true; } - if (a->step == 4 && a->time >= 3.2f) { /* first: over to the fire, hello to Kurti and his sons */ + if (a->step == 4 && a->time >= 7.0f) { /* first: a stretch, then over to the fire together, hello to Kurti and his sons */ Object *fire = fireplace(game); for (int k = 0; k < 3; k++) { Object *o = person(game, FRIENDS[k]); diff --git a/entropie/src/game/game_talk.c b/entropie/src/game/game_talk.c index 76c931e..02c1710 100644 --- a/entropie/src/game/game_talk.c +++ b/entropie/src/game/game_talk.c @@ -129,9 +129,9 @@ static void shout(Game *game, int choice) { int heard = 0; if (choice == 1) { say(game, "Hey, everybody! Over here!"); - for (int i = 0; i < game->world.count; i++) { + for (int i = 0; i < game->world.count; i++) { /* (your lot: not Kurti and his sons - call them by name) */ Object *o = game->world.objects[i]; - if (o->type == OBJ_HUMAN && callOne(game, o)) heard++; + if (o->type == OBJ_HUMAN && !o->lives_here && callOne(game, o)) heard++; } } else if (choice - 2 < game->shout_count) { Object *o = game->shout_to[choice - 2]; diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c index 99b144f..cad732e 100644 --- a/entropie/src/people/ai.c +++ b/entropie/src/people/ai.c @@ -63,6 +63,8 @@ void ai_walk(Object *o, Vec3 dir, float speed, float dt) { } } ai->walk_dir = dir; + if (o->pace > 0.05f) speed *= fminf(o->pace, 1.1f); /* (the old and the calm walk slower) */ + speed *= Hands_moveFactor(o); /* (a crate in both arms, something dragged: slower, like you) */ o->movement = vec3_scale(dir, speed); turnTowards(o, dir, dt); Vec3 f = Object_facing(o); @@ -1707,7 +1709,7 @@ static bool goToPoint(Object *self, Vec3 p, float reach, Object *const *objects, if (!self->ai.path_len && d > 6.0f) findWay(self, p); /* (further: the way round from the start - hedges, walls) */ Vec3 dir = ai_steer(self, NULL, objects, count, p); if (vec3_length(dir) < 0.5f) { stand(self); return false; } - ai_walk(self, dir, fminf(WALK_SPEED, d * 2.0f + 0.3f), dt); + ai_walk(self, dir, fminf(self->ai.greet ? STROLL_SPEED * 1.15f : WALK_SPEED, d * 2.0f + 0.3f), dt); /* (going over to say hello: no hurry) */ return false; } diff --git a/entropie/src/people/ai_help.c b/entropie/src/people/ai_help.c index e95491b..6c25a9e 100644 --- a/entropie/src/people/ai_help.c +++ b/entropie/src/people/ai_help.c @@ -5,6 +5,7 @@ */ #include "people/ai_internal.h" +#include "world/car.h" #include "world/ladder.h" #include "people/climb.h" @@ -290,7 +291,22 @@ bool ai_tidyUp(Object *self, Object *const *objects, int count, float dt) { return true; } } - /* nothing better to do: to the container with it */ + /* nothing better to do: an empty bottle back into a crate, other trash to the container */ + Object *held = trashInHands(self); + if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && held && Object_isBottle(held) && !held->broken) { + Object *crate = Crate_withRoom(objects, count, self->position, TIDY_BIN); + if (crate && !ai_skipped(ai, crate)) { + if ((ai->tray_time += dt) > 45.0f) { skipTarget(ai, crate); ai->tray_time = 0.0f; return false; } /* (can't get there) */ + if (goTo(self, crate, objects, count, 0.8f, dt)) { + ai->tray_time = 0.0f; + for (int h = 0; h < 2; h++) if (self->hands.item[h] == held) Hands_drop(self, h); + Hands_touch(self, vec3_add(Object_colliderCenter(crate, 0), vec3(0.0f, 0.2f, 0.0f))); + Crate_putBottle(crate, held); + ai->tidy_pause = 0.6f; + } + return true; + } + } if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && trashInHands(self)) { Object *bin = ai_nearestOf(self, objects, count, OBJ_BIN, OBJ_BOX); if (!bin || flatDistance(bin->position, self->position) > TIDY_BIN) return false; diff --git a/entropie/src/people/ai_internal.h b/entropie/src/people/ai_internal.h index e10fe72..bebcce4 100644 --- a/entropie/src/people/ai_internal.h +++ b/entropie/src/people/ai_internal.h @@ -13,7 +13,7 @@ #include <string.h> #define STROLL_SPEED 1.3f -#define WALK_SPEED 3.0f +#define WALK_SPEED 2.2f /* (as quick as you walk: they hurried along at 3) */ #define WALK_TURN 5.0f /* rad/s the way someone walks can turn */ #define RUN_SPEED 5.0f #define TURN_RATE 5.0f /* radians per second */ diff --git a/entropie/src/people/body.c b/entropie/src/people/body.c index ff58004..2657a5f 100644 --- a/entropie/src/people/body.c +++ b/entropie/src/people/body.c @@ -408,6 +408,14 @@ void Body_lieDown(Body *b) { b->face_down = false; } +void Body_stretch(Body *b) { + b->fidget = FIDGET_STRETCH; + b->fidget_side = 0; + b->fidget_len = b->fidget_time = FIDGET_LENGTH[FIDGET_STRETCH] * 1.3f; + b->fidget_fade = 1.0f; + b->fidget_timer = b->fidget_len + 2.0f; +} + void Body_getUp(Body *b) { b->asleep = false; if (b->pose != POSE_UP) b->down_time = 0.0f; /* still going down: up again right after */ diff --git a/entropie/src/people/body.h b/entropie/src/people/body.h index 96540ba..ee71f41 100644 --- a/entropie/src/people/body.h +++ b/entropie/src/people/body.h @@ -319,6 +319,8 @@ bool Body_takeLanding(Body *body, int *foot, Vec3 *at); /* Lies down to sleep until Body_getUp. */ void Body_lieDown(Body *body); void Body_getUp(Body *body); +/* A good stretch now (arms up over the head: out of the car, up from a long sit). */ +void Body_stretch(Body *body); /* Not standing: falling, lying or getting up (no walking, no hands). */ bool Body_isDown(const Body *body); void Body_burp(Body *body); diff --git a/entropie/src/render/shapes.h b/entropie/src/render/shapes.h index 065bb34..0e6b33a 100644 --- a/entropie/src/render/shapes.h +++ b/entropie/src/render/shapes.h @@ -130,6 +130,7 @@ Mesh Shapes_ivy(float width, float height, unsigned seed); the hatch up. */ #define CAR_WIDTH 1.78f #define CAR_LENGTH 4.6f +#define CAR_COLOR ((Vec3){ 0.50f, 0.55f, 0.46f }) /* (an old faded green) */ Mesh Shapes_car(Vec3 color, bool boot_open); /* ... seen from the driver's seat: the bonnet, the dashboard, the wheel, the pillars, the roof. */ Mesh Shapes_carInside(Vec3 color); diff --git a/entropie/src/render/shapes_props.c b/entropie/src/render/shapes_props.c index 414dafb..aace874 100644 --- a/entropie/src/render/shapes_props.c +++ b/entropie/src/render/shapes_props.c @@ -872,87 +872,127 @@ Mesh Shapes_tent(int big, int stage, float progress, int pegs, Vec3 inner, Vec3 return Mesh_fromBuilder(&b); } -/* Your estate car (see shapes.h). */ +/* A shape drawn side on (points (z, y), going round, convex) made solid from + x0 to x1: the two sides and the faces between the points. */ +static void addProfile(MeshBuilder *b, const float (*pt)[2], int n, float x0, float x1, Vec3 color) { + float cz = 0.0f, cy = 0.0f; + for (int i = 0; i < n; i++) { cz += pt[i][0]; cy += pt[i][1]; } + cz /= (float)n; cy /= (float)n; + for (int i = 0; i < n; i++) { + const float *p = pt[i], *q = pt[(i + 1) % n]; + Vec3 n_out = vec3_normalize(vec3(0.0f, -(q[0] - p[0]), q[1] - p[1])); /* (perpendicular in the side view) */ + if (vec3_dot(n_out, vec3(0.0f, (p[1] + q[1]) * 0.5f - cy, (p[0] + q[0]) * 0.5f - cz)) < 0.0f) n_out = vec3_scale(n_out, -1.0f); + MeshBuilder_gridQuad(b, vec3(x0, p[1], p[0]), vec3(x1, p[1], p[0]), vec3(x1, q[1], q[0]), vec3(x0, q[1], q[0]), n_out, color, 0.6f); + for (int s = 0; s < 2; s++) { + float x = s ? x1 : x0; + MeshBuilder_gridTriangle(b, vec3(x, cy, cz), vec3(x, p[1], p[0]), vec3(x, q[1], q[0]), vec3(s ? 1.0f : -1.0f, 0.0f, 0.0f), color, 0.6f); + } + } +} + +/* Volkram's old estate car (see shapes.h): rounded off, a faded paint, rust + at the arches, roof rails; the cabin glass all round with the pillars and + the roof over it. Nose to +Z. */ Mesh Shapes_car(Vec3 color, bool boot_open) { MeshBuilder b; MeshBuilder_init(&b); - const float w = CAR_WIDTH, l = CAR_LENGTH, wheel = 0.31f, back = -l * 0.5f; - const Vec3 glass = { 0.30f, 0.40f, 0.52f }, dark = { 0.10f, 0.10f, 0.11f }, trim = vec3_scale(color, 0.7f); - addBlock(&b, vec3(0.0f, 0.62f, 0.0f), vec3(w, 0.6f, l), color); /* the body */ - addBlock(&b, vec3(0.0f, 0.35f, 0.0f), vec3(w * 0.96f, 0.1f, l * 0.98f), dark); /* under it */ - const float cab0 = back + 0.12f, cab1 = 0.45f; /* the cabin, right to the back: an estate */ - addBlock(&b, vec3(0.0f, 1.18f, (cab0 + cab1) * 0.5f), vec3(w * 0.9f, 0.54f, cab1 - cab0), color); - addBlock(&b, vec3(0.0f, 1.2f, (cab0 + cab1) * 0.5f + 0.05f), vec3(w * 0.91f, 0.34f, cab1 - cab0 - 0.3f), glass); /* side windows */ - size_t first = b.vertex_count; /* the windscreen, leaning back */ - addBlock(&b, vec3(0.0f, 0.0f, 0.0f), vec3(w * 0.86f, 0.58f, 0.03f), glass); - transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, 1.16f, cab1 + 0.12f)), mat4_rotate_x(0.55f))); - addBlock(&b, vec3(0.0f, 1.2f, 0.4f), vec3(w * 0.92f, 0.04f, 0.05f), trim); /* door pillars */ - addBlock(&b, vec3(0.0f, 1.2f, -0.65f), vec3(w * 0.92f, 0.34f, 0.07f), trim); - addBlock(&b, vec3(0.0f, 0.55f, l * 0.5f + 0.03f), vec3(w * 0.92f, 0.13f, 0.07f), dark); /* bumpers */ - addBlock(&b, vec3(0.0f, 0.55f, back - 0.03f), vec3(w * 0.92f, 0.13f, 0.07f), dark); + const float w = CAR_WIDTH, hw = w * 0.5f, l = CAR_LENGTH, wheel = 0.31f, back = -l * 0.5f; + const Vec3 glass = { 0.34f, 0.42f, 0.48f }, dark = { 0.12f, 0.12f, 0.12f }, trim = { 0.30f, 0.30f, 0.29f }; + const Vec3 rust = { 0.45f, 0.27f, 0.16f }, chrome = { 0.62f, 0.63f, 0.62f }; + const float BODY[][2] = { { 2.12f, 0.36f }, { 2.30f, 0.48f }, { 2.32f, 0.66f }, { 2.24f, 0.82f }, { 1.95f, 0.90f }, + { 0.80f, 0.97f }, { -2.26f, 0.97f }, { -2.32f, 0.82f }, { -2.32f, 0.50f }, { -2.18f, 0.36f } }; + addProfile(&b, BODY, (int)(sizeof BODY / sizeof BODY[0]), -hw, hw, color); + addBlock(&b, vec3(0.0f, 0.34f, 0.0f), vec3(w * 0.94f, 0.08f, l * 0.92f), dark); /* under it */ + /* the cabin: glass all round ... */ + const float GLASS[][2] = { { 0.80f, 0.96f }, { 0.04f, 1.52f }, { -2.06f, 1.52f }, { -2.27f, 1.38f }, { -2.27f, 0.96f } }; + addProfile(&b, GLASS, 5, -hw * 0.88f, hw * 0.88f, glass); + /* ... the roof, the pillars, the sills over it */ + const float ROOF[][2] = { { 0.08f, 1.50f }, { 0.02f, 1.57f }, { -2.06f, 1.57f }, { -2.16f, 1.50f } }; + addProfile(&b, ROOF, 4, -hw * 0.89f, hw * 0.89f, color); for (int s = -1; s <= 1; s += 2) { - addBlock(&b, vec3((float)s * w * 0.36f, 0.75f, l * 0.5f + 0.005f), vec3(0.26f, 0.1f, 0.02f), (Vec3){ 0.95f, 0.93f, 0.80f }); /* headlights */ - addBlock(&b, vec3((float)s * w * 0.42f, 0.95f, back - 0.005f), vec3(0.14f, 0.2f, 0.02f), (Vec3){ 0.75f, 0.12f, 0.10f }); /* tail lights */ - addBlock(&b, vec3((float)s * (w * 0.5f + 0.06f), 1.0f, 0.35f), vec3(0.1f, 0.08f, 0.14f), dark); /* mirrors */ + const float x = (float)s * hw * 0.885f; + addStick(&b, vec3(x, 0.97f, 0.80f), vec3(x, 1.53f, 0.05f), 0.055f, color); /* windscreen pillar */ + addBlock(&b, vec3(x, 1.24f, -0.62f), vec3(0.03f, 0.56f, 0.09f), color); /* middle pillar */ + addBlock(&b, vec3(x, 1.24f, -1.48f), vec3(0.03f, 0.56f, 0.12f), color); /* back door's pillar */ + addStick(&b, vec3(x, 0.97f, -2.26f), vec3(x, 1.52f, -2.14f), 0.08f, color); /* the last one, at the back */ + addStick(&b, vec3(x * 0.92f, 1.61f, -0.1f), vec3(x * 0.92f, 1.61f, -1.95f), 0.025f, trim); /* roof rails */ + addBlock(&b, vec3(x * 0.92f, 1.585f, -0.2f), vec3(0.03f, 0.04f, 0.05f), trim); + addBlock(&b, vec3(x * 0.92f, 1.585f, -1.85f), vec3(0.03f, 0.04f, 0.05f), trim); + for (int k = 0; k < 2; k++) { /* the wheel arches, dark, a bit of rust round them */ + float z = k ? l * 0.31f : -l * 0.31f; + addBlock(&b, vec3((float)s * (hw + 0.005f), 0.52f, z), vec3(0.01f, 0.32f, 0.86f), dark); + addBlock(&b, vec3((float)s * (hw + 0.008f), 0.70f, z + (k ? 0.30f : -0.32f)), vec3(0.01f, 0.07f, 0.16f), rust); + } + addBlock(&b, vec3((float)s * (hw + 0.006f), 0.66f, 0.15f), vec3(0.01f, 0.03f, 1.9f), trim); /* a rubbing strip along the doors */ + addBlock(&b, vec3((float)s * (hw + 0.006f), 0.92f, 0.4f), vec3(0.012f, 0.025f, 0.14f), chrome); /* door handles */ + addBlock(&b, vec3((float)s * (hw + 0.006f), 0.92f, -0.85f), vec3(0.012f, 0.025f, 0.14f), chrome); + addBlock(&b, vec3((float)s * w * 0.33f, 0.66f, l * 0.5f + 0.01f), vec3(0.30f, 0.14f, 0.03f), (Vec3){ 0.93f, 0.91f, 0.78f }); /* headlights */ + addBlock(&b, vec3((float)s * w * 0.40f, 0.78f, back - 0.01f), vec3(0.16f, 0.24f, 0.03f), (Vec3){ 0.66f, 0.14f, 0.11f }); /* tail lights */ + addBlock(&b, vec3((float)s * (hw + 0.08f), 1.04f, 0.62f), vec3(0.1f, 0.09f, 0.15f), dark); /* mirrors */ } + addBlock(&b, vec3(0.0f, 0.66f, l * 0.5f + 0.02f), vec3(w * 0.32f, 0.12f, 0.03f), dark); /* the grille */ + addBlock(&b, vec3(0.0f, 0.44f, l * 0.5f + 0.05f), vec3(w * 0.96f, 0.12f, 0.09f), trim); /* bumpers */ + addBlock(&b, vec3(0.0f, 0.44f, back - 0.05f), vec3(w * 0.96f, 0.12f, 0.09f), trim); + addBlock(&b, vec3(0.0f, 0.62f, back - 0.022f), vec3(0.42f, 0.11f, 0.01f), (Vec3){ 0.92f, 0.92f, 0.88f }); /* the number plate */ for (int k = 0; k < 4; k++) addWheel(&b, vec3(k % 2 ? w * 0.43f : -w * 0.43f, wheel, k / 2 ? l * 0.31f : -l * 0.31f), wheel, 0.22f); if (boot_open) { /* the hatch up (hinged at the roof's back edge), the boot open: dark inside */ - addBlock(&b, vec3(0.0f, 1.0f, back + 0.005f), vec3(w * 0.82f, 0.62f, 0.02f), dark); - first = b.vertex_count; - addBlock(&b, vec3(0.0f, 0.0f, -0.36f), vec3(w * 0.86f, 0.04f, 0.72f), color); - addBlock(&b, vec3(0.0f, -0.025f, -0.38f), vec3(w * 0.72f, 0.02f, 0.5f), glass); - transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, 1.45f, cab0)), mat4_rotate_x(-0.35f))); + addBlock(&b, vec3(0.0f, 1.17f, back + 0.02f), vec3(w * 0.80f, 0.44f, 0.02f), dark); + size_t first = b.vertex_count; + addBlock(&b, vec3(0.0f, 0.0f, -0.40f), vec3(w * 0.84f, 0.04f, 0.80f), color); + addBlock(&b, vec3(0.0f, -0.025f, -0.36f), vec3(w * 0.7f, 0.02f, 0.5f), glass); + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, 1.55f, -2.12f)), mat4_rotate_x(-0.35f))); } else { - addBlock(&b, vec3(0.0f, 1.0f, back + 0.005f), vec3(w * 0.86f, 0.62f, 0.02f), color); - addBlock(&b, vec3(0.0f, 1.2f, cab0 - 0.005f), vec3(w * 0.72f, 0.3f, 0.02f), glass); /* the back window */ + addBlock(&b, vec3(0.0f, 1.20f, back + 0.035f), vec3(w * 0.84f, 0.40f, 0.02f), glass); /* the back window */ } return Mesh_fromBuilder(&b); } -/* The car seen from the driver's seat (the body round you would hide it all): - the bonnet, the dashboard, the wheel, the pillars and the roof. */ +/* The car seen from inside (the body round you would hide it all): the + bonnet, the dashboard, the wheel, the pillars, the roof, the seats. */ Mesh Shapes_carInside(Vec3 color) { MeshBuilder b; MeshBuilder_init(&b); - const float w = CAR_WIDTH, l = CAR_LENGTH; - const Vec3 dash = { 0.16f, 0.16f, 0.17f }, roof = { 0.62f, 0.60f, 0.56f }; - addBlock(&b, vec3(0.0f, 0.84f, (0.8f + l * 0.5f) * 0.5f), vec3(w, 0.06f, l * 0.5f - 0.8f), color); /* the bonnet */ - addBlock(&b, vec3(0.0f, 0.86f, 0.66f), vec3(w * 0.92f, 0.12f, 0.3f), dash); /* the dashboard */ - size_t first = b.vertex_count; /* the wheel, the driver's side */ + const float w = CAR_WIDTH, hw = w * 0.5f, l = CAR_LENGTH; + const Vec3 dash = { 0.18f, 0.17f, 0.16f }, roof = { 0.70f, 0.67f, 0.60f }; + const float BONNET[][2] = { { 2.24f, 0.82f }, { 1.95f, 0.90f }, { 0.80f, 0.97f }, { 0.80f, 0.86f }, { 2.24f, 0.78f } }; + addProfile(&b, BONNET, 5, -hw, hw, color); /* the bonnet */ + addBlock(&b, vec3(0.0f, 0.90f, 0.70f), vec3(w * 0.92f, 0.12f, 0.28f), dash); /* the dashboard */ + addBlock(&b, vec3(0.0f, 0.74f, 0.74f), vec3(w * 0.92f, 0.2f, 0.2f), dash); /* ... its underside */ + addBlock(&b, vec3(0.0f, 0.58f, 0.86f), vec3(w * 0.92f, 0.52f, 0.06f), dash); /* the footwell's front */ + size_t first = b.vertex_count; /* the wheel, the driver's side */ shapes_addTube(&b, -0.02f, 0.19f, 0.19f, 0.03f, 18, dash, false); shapes_addTube(&b, -0.02f, 0.03f, 0.03f, 0.04f, 8, dash, true); - transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.38f, 0.95f, 0.42f)), mat4_rotate_x(PI_F * 0.5f - 0.45f))); - for (int s = -1; s <= 1; s += 2) { /* the pillars beside the windscreen */ - first = b.vertex_count; - addBlock(&b, vec3(0.0f, 0.3f, 0.0f), vec3(0.05f, 0.62f, 0.05f), color); - transformSince(&b, first, mat4_mul(mat4_translate(vec3((float)s * w * 0.44f, 0.9f, 0.8f)), mat4_rotate_x(0.55f))); /* (leaning back, like the windscreen) */ - } - addBlock(&b, vec3(0.0f, 0.58f, 0.86f), vec3(w * 0.92f, 0.52f, 0.06f), dash); /* the footwell's front: under the dashboard */ - addBlock(&b, vec3(0.0f, 0.70f, 0.74f), vec3(w * 0.92f, 0.12f, 0.2f), dash); /* the dashboard's underside */ - const float back = -l * 0.5f, cab0 = back + 0.12f, cab1 = 0.45f, cab = cab1 - cab0, mid = (cab0 + cab1) * 0.5f; - const Vec3 door = vec3_scale(color, 0.75f), seat = { 0.24f, 0.24f, 0.27f }, floor = { 0.12f, 0.12f, 0.13f }; - addBlock(&b, vec3(0.0f, 1.47f, mid), vec3(w * 0.92f, 0.04f, cab), roof); /* the roof lining */ - addBlock(&b, vec3(0.0f, 1.44f, 0.2f), vec3(0.25f, 0.05f, 0.06f), dash); /* the mirror */ - addBlock(&b, vec3(0.0f, 0.33f, (cab0 + 0.9f) * 0.5f), vec3(w * 0.9f, 0.04f, 0.9f - cab0), floor); /* the floor, on under the dashboard */ + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.38f, 0.96f, 0.46f)), mat4_rotate_x(PI_F * 0.5f - 0.45f))); + for (int s = -1; s <= 1; s += 2) /* the pillars beside the windscreen, leaning back with it */ + addStick(&b, vec3((float)s * hw * 0.86f, 0.97f, 0.78f), vec3((float)s * hw * 0.86f, 1.52f, 0.06f), 0.055f, color); + const float back = -l * 0.5f, cab0 = back + 0.12f; + const Vec3 door = vec3_scale(color, 0.78f), seat = { 0.30f, 0.28f, 0.26f }, floor = { 0.14f, 0.13f, 0.12f }; + addBlock(&b, vec3(0.0f, 1.53f, (0.05f - 2.1f) * 0.5f), vec3(w * 0.9f, 0.04f, 2.15f), roof); /* the roof lining */ + addBlock(&b, vec3(0.0f, 1.48f, 0.12f), vec3(0.25f, 0.05f, 0.06f), dash); /* the mirror */ + addBlock(&b, vec3(0.0f, 0.33f, (cab0 + 0.9f) * 0.5f), vec3(w * 0.9f, 0.04f, 0.9f - cab0), floor); /* the floor, on under the dashboard */ for (int s = -1; s <= 1; s += 2) { - float x = (float)s * (w * 0.5f - 0.04f); - addBlock(&b, vec3(x, 0.66f, (cab0 + 0.9f) * 0.5f), vec3(0.06f, 0.64f, 0.9f - cab0), door); /* the doors, up to the windows (on past the dashboard) */ - addBlock(&b, vec3(x, 1.2f, -0.6f), vec3(0.07f, 0.52f, 0.12f), door); /* the middle pillar ... */ - addBlock(&b, vec3(x, 1.2f, cab0 + 0.12f), vec3(0.07f, 0.52f, 0.24f), door); /* ... and the back one */ - addBlock(&b, vec3((float)s * 0.38f, 0.52f, -0.02f), vec3(0.5f, 0.12f, 0.5f), seat); /* the front seats */ + float x = (float)s * (hw - 0.04f); + addBlock(&b, vec3(x, 0.66f, (cab0 + 0.9f) * 0.5f), vec3(0.06f, 0.64f, 0.9f - cab0), door); /* the doors, up to the windows */ + addBlock(&b, vec3(x, 1.24f, -0.62f), vec3(0.07f, 0.56f, 0.12f), door); /* the middle pillar ... */ + addBlock(&b, vec3(x, 1.24f, -1.48f), vec3(0.07f, 0.56f, 0.14f), door); /* ... the back door's ... */ + addBlock(&b, vec3(x, 1.24f, cab0 + 0.1f), vec3(0.07f, 0.56f, 0.2f), door); /* ... and the last one */ + addBlock(&b, vec3((float)s * 0.38f, 0.47f, 0.0f), vec3(0.5f, 0.12f, 0.5f), seat); /* the front seats */ first = b.vertex_count; - addBlock(&b, vec3(0.0f, 0.28f, 0.0f), vec3(0.48f, 0.56f, 0.1f), seat); - addBlock(&b, vec3(0.0f, 0.62f, 0.0f), vec3(0.22f, 0.14f, 0.1f), seat); /* (the headrest) */ - transformSince(&b, first, mat4_mul(mat4_translate(vec3((float)s * 0.38f, 0.55f, -0.3f)), mat4_rotate_x(-0.2f))); + addBlock(&b, vec3(0.0f, 0.30f, 0.0f), vec3(0.48f, 0.6f, 0.1f), seat); + addBlock(&b, vec3(0.0f, 0.68f, 0.0f), vec3(0.22f, 0.16f, 0.1f), seat); /* (the headrest) */ + transformSince(&b, first, mat4_mul(mat4_translate(vec3((float)s * 0.38f, 0.5f, -0.3f)), mat4_rotate_x(0.22f))); /* (leaning back) */ } - addBlock(&b, vec3(0.0f, 0.52f, -1.05f), vec3(w * 0.84f, 0.12f, 0.5f), seat); /* the back seat */ - addBlock(&b, vec3(0.0f, 0.87f, -1.34f), vec3(w * 0.84f, 0.62f, 0.1f), seat); - addBlock(&b, vec3(0.0f, 0.6f, back + 0.55f), vec3(w * 0.84f, 0.04f, 0.8f), floor); /* the boot, full */ + addBlock(&b, vec3(0.0f, 0.47f, -0.98f), vec3(w * 0.84f, 0.12f, 0.5f), seat); /* the back seat */ + first = b.vertex_count; + addBlock(&b, vec3(0.0f, 0.31f, 0.0f), vec3(w * 0.84f, 0.62f, 0.1f), seat); + transformSince(&b, first, mat4_mul(mat4_translate(vec3(0.0f, 0.5f, -1.28f)), mat4_rotate_x(0.18f))); + addBlock(&b, vec3(0.0f, 0.6f, back + 0.55f), vec3(w * 0.84f, 0.04f, 0.8f), floor); /* the boot, full */ static const Vec3 LOAD[] = { { 0.62f, 0.16f, 0.12f }, { 0.22f, 0.42f, 0.72f }, { 0.70f, 0.18f, 0.16f }, { 0.30f, 0.45f, 0.25f }, { 0.85f, 0.75f, 0.2f } }; for (int k = 0; k < 5; k++) addBlock(&b, vec3(-0.6f + 0.3f * (float)k, 0.78f, back + 0.45f + 0.1f * (float)(k % 2)), vec3(0.27f, 0.32f, 0.36f), LOAD[k]); - addBlock(&b, vec3(0.0f, 1.0f, back + 0.04f), vec3(w * 0.86f, 0.62f, 0.04f), door); /* the hatch from inside */ + addBlock(&b, vec3(0.0f, 1.2f, back + 0.06f), vec3(w * 0.84f, 0.44f, 0.04f), (Vec3){ 0.34f, 0.42f, 0.48f }); /* the back window from inside */ + addBlock(&b, vec3(0.0f, 0.75f, back + 0.05f), vec3(w * 0.86f, 0.42f, 0.04f), door); /* the hatch below it */ return Mesh_fromBuilder(&b); } diff --git a/entropie/src/world/car.c b/entropie/src/world/car.c index 3c1373a..0772dca 100644 --- a/entropie/src/world/car.c +++ b/entropie/src/world/car.c @@ -8,13 +8,13 @@ #define SEAT_X 0.38f /* seats this far left (+X: the driver's side) or right of the middle */ #define SEAT_Z -0.95f /* the back seats this far back from the front ones */ -#define EYE_UP 1.18f /* the eyes this high, sitting */ +#define EYE_UP 1.30f /* the eyes this high, sitting */ #define DOOR_OUT 0.6f /* out of the door: standing this far beside the car */ #define BOOT_BACK 0.8f /* standing this far behind it to reach into the boot */ #define SET_DOWN 0.3f /* what comes out is set down this far behind it */ #define BOTTLE_KG 0.6f #define CAR_FLOOR 0.35f /* the floor inside this high */ -#define SEAT_UP 0.28f /* the seats this high over it */ +#define SEAT_UP 0.18f /* the seats this high over it (heads under the roof) */ static const Vec3 CRATE_BOTTLE = { 0.07f, 0.25f, 0.0f }; @@ -188,7 +188,7 @@ void Car_openBoot(Object *car, bool open) { if (car->boot_open == open) return; car->boot_open = open; Mesh_destroy(&car->mesh); - car->mesh = Shapes_car(vec3(0.24f, 0.34f, 0.52f), open); + car->mesh = Shapes_car(CAR_COLOR, open); } /* Where the camping things of this many go: by whose tent (you, Volkram and Jana, Toni). */ @@ -217,7 +217,7 @@ Vec3 Car_dropSpot(const World *world, const Object *item) { Vec3 Car_campDoor(void) { return Site_campDoor(); } Object *Crate_takeBottle(World *world, Object *crate) { - if (crate->type != OBJ_CRATE || crate->stored <= 0) return NULL; + if (crate->type != OBJ_CRATE || crate->stored - crate->crate_empties <= 0) return NULL; Vec3 top = vec3_add(Object_colliderCenter(crate, 0), vec3(0.0f, CRATE_SIZE_Y * 0.5f + 0.16f, 0.0f)); Object *b = Object_create(crate->variant ? OBJ_WATER : OBJ_BOTTLE, PALETTE_WARM, top, CRATE_BOTTLE, BOTTLE_KG); if (!b) return NULL; @@ -228,6 +228,30 @@ Object *Crate_takeBottle(World *world, Object *crate) { return b; } +#define EMPTY_KG 0.35f /* an empty bottle */ + +bool Crate_putBottle(Object *crate, Object *bottle) { + if (!crate || crate->type != OBJ_CRATE || !bottle || !Object_isBottle(bottle) || bottle->broken) return false; + if (crate->stored >= Crate_bottles(crate->variant)) return false; + bool empty = bottle->sips <= 0; + crate->stored++; + if (empty) crate->crate_empties++; + crate->weight += empty ? EMPTY_KG : Crate_bottleKg(crate->variant); + Object_useUp(bottle); + return true; +} + +Object *Crate_withRoom(Object *const *objects, int count, Vec3 at, float range) { + Object *best = NULL; + for (int i = 0; i < count; i++) { + Object *c = objects[i]; + if (c->type != OBJ_CRATE || c->is_held || c->broken || c->stored >= Crate_bottles(c->variant)) continue; + float d = hypotf(c->position.x - at.x, c->position.z - at.z); + if (d < range) { range = d; best = c; } + } + return best; +} + void Car_serve(World *world) { for (int i = 0; i < world->count; i++) { Object *o = world->objects[i]; diff --git a/entropie/src/world/car.h b/entropie/src/world/car.h index 1e14e70..6157cad 100644 --- a/entropie/src/world/car.h +++ b/entropie/src/world/car.h @@ -55,7 +55,11 @@ Vec3 Car_campDoor(void); Vec3 Car_dropSpot(const World *world, const Object *item); /* A bottle out of a crate (on top of it, for the taking): NULL if it's empty. */ -Object *Crate_takeBottle(World *world, Object *crate); +Object *Crate_takeBottle(World *world, Object *crate); /* a full one (NULL: none, or only empties) */ +/* A bottle (empty or not) back into a crate with room: true if it went in. */ +bool Crate_putBottle(Object *crate, Object *bottle); +/* The nearest crate within range with room for one more (not carried), or NULL. */ +Object *Crate_withRoom(Object *const *objects, int count, Vec3 at, float range); /* Every frame: whoever asked for a bottle from a crate or the next thing from the boot (ai.take_from) gets it lying there for them (ai.target). */ void Car_serve(World *world); diff --git a/entropie/src/world/object.c b/entropie/src/world/object.c index f40f8f2..61d6043 100644 --- a/entropie/src/world/object.c +++ b/entropie/src/world/object.c @@ -353,12 +353,12 @@ Object *Object_create(ObjectType type, Palette palette, Vec3 position, Vec3 size case OBJ_CAR: { /* (see car.c Car_create) */ const float back = -size.z * 0.5f; - obj->mesh = Shapes_car(vec3(0.24f, 0.34f, 0.52f), false); - obj->inside = Shapes_carInside(vec3(0.24f, 0.34f, 0.52f)); + obj->mesh = Shapes_car(CAR_COLOR, false); + obj->inside = Shapes_carInside(CAR_COLOR); obj->is_static = true; obj->weight = 1300.0f; addCollider(obj, vec3(0.0f, 0.62f, 0.0f), vec3(size.x, 0.74f, size.z), vec3_zero()); - addCollider(obj, vec3(0.0f, 1.2f, (back + 0.12f + 0.45f) * 0.5f), vec3(size.x * 0.9f, 0.5f, 0.45f - back - 0.12f), vec3_zero()); + addCollider(obj, vec3(0.0f, 1.27f, (back + 0.05f + 0.5f) * 0.5f), vec3(size.x * 0.9f, 0.6f, 0.5f - back - 0.05f), vec3_zero()); break; } @@ -778,20 +778,53 @@ static void handOnChair(Object *who, const Object *chair) { } } -void Object_sit(Object *who, Object *seat, Vec3 toward) { +/* Everybody (set each frame by the world): who sits where already. */ +static Object *const *g_people; +static int g_people_n; +void Object_seePeople(Object *const *objects, int count) { g_people = objects; g_people_n = count; } + +#define SEAT_SPACE 0.55f /* m along a bench or a log between two people sitting on it */ + +/* Where along a bench or log (from its middle, axis along it) somebody can + sit nearest to `want`, clear of everybody already on it; NAN: full. */ +static float freeAlong(const Object *who, const Object *seat, Vec3 c, Vec3 axis, float want, float half) { + for (int k = 0; k <= 2 * (int)(half / 0.1f) + 2; k++) { + float cand = want + 0.1f * (float)((k + 1) / 2) * (k % 2 ? 1.0f : -1.0f); + if (cand < -half || cand > half) continue; + bool clear = true; + for (int i = 0; i < g_people_n && clear; i++) { + const Object *o = g_people[i]; + if (o == who || !o->is_character || o->seat != seat || !o->sitting) continue; + clear = fabsf(vec3_dot(vec3_sub(o->sit_at, c), axis) - cand) >= SEAT_SPACE; + } + if (clear) return cand; + } + return NAN; +} + +static bool seatTaken(const Object *who, const Object *seat) { + for (int i = 0; i < g_people_n; i++) + if (g_people[i] != who && g_people[i]->is_character && g_people[i]->seat == seat && g_people[i]->sitting) return true; + return false; +} + +static bool noRoom(Object *who) { who->sitting = false; who->seat = NULL; return false; } + +bool Object_sit(Object *who, Object *seat, Vec3 toward) { who->sitting = true; who->seat = seat; who->body.crossing = false; /* (not stepping over it: onto it) */ who->seat_scooted = false; who->sit_style = SIT_GROUND; who->body.seat_height = SEAT_HEIGHT; - if (!seat) return; + if (!seat) return true; if (seat->type == OBJ_LOG) { /* on a log: beside it, facing away from it */ Vec3 c = Object_colliderCenter(seat, 0), axis = seat->colliders[0].axes[1]; Vec3 rel = vec3_sub(who->position, c); Vec3 across = vec3_normalize(vec3(-axis.z, 0.0f, axis.x)); if (vec3_dot(across, rel) < 0.0f) across = vec3_scale(across, -1.0f); - float along = clampf(vec3_dot(rel, axis), -seat->size.y * 0.35f, seat->size.y * 0.35f); + float along = freeAlong(who, seat, c, axis, clampf(vec3_dot(rel, axis), -seat->size.y * 0.35f, seat->size.y * 0.35f), seat->size.y * 0.35f); + if (isnan(along)) return noRoom(who); /* (somebody's there already: no sitting on top of them) */ Vec3 at = vec3_add(vec3_add(c, vec3_scale(axis, along)), vec3_scale(across, SEAT_BACK - 0.02f)); /* the bottom on top of it */ who->sit_at = vec3(at.x, who->position.y, at.z); who->sit_yaw = atan2f(across.z, across.x) + PI_F / 2.0f; @@ -801,8 +834,9 @@ void Object_sit(Object *who, Object *seat, Vec3 toward) { who->velocity = vec3_zero(); who->sit_style = SIT_SEAT; who->body.seat_height = fmaxf(seat->size.x, 0.15f); - return; + return true; } + if (seat->variant == FURN_CHAIR && seatTaken(who, seat)) return noRoom(who); Vec3 n = seat->colliders[0].axes[2]; /* across the seat */ Vec3 to = vec3_sub(toward, seat->position); if (seat->variant == FURN_CHAIR || vec3_dot(n, to) < 0.0f) n = seat->variant == FURN_CHAIR ? n : vec3_scale(n, -1.0f); @@ -813,7 +847,17 @@ void Object_sit(Object *who, Object *seat, Vec3 toward) { seat->chair_goal = CHAIR_PULL; base = vec3_sub(seat->tucked, vec3_scale(n, CHAIR_PULL)); } - /* the bottom in the middle of the seat (a chair: a little back, against the backrest) */ + /* the bottom in the middle of the seat (a chair: a little back, against the backrest); a bench: where + you are along it, clear of whoever sits on it already */ + if (seat->variant != FURN_CHAIR && seat->collider_count > 0) { + Vec3 along_axis = seat->colliders[0].axes[0]; + along_axis = vec3_normalize(vec3(along_axis.x, 0.0f, along_axis.z)); + float half = fmaxf(seat->colliders[0].half_extents.x - 0.28f, 0.0f); + float along = freeAlong(who, seat, seat->position, along_axis, + clampf(vec3_dot(vec3_sub(who->position, seat->position), along_axis), -half, half), half); + if (isnan(along)) return noRoom(who); + base = vec3_add(base, vec3_scale(along_axis, along)); + } Vec3 at = vec3_add(base, vec3_scale(n, SEAT_BACK + (seat->variant == FURN_CHAIR ? -0.04f : 0.0f))); who->sit_at = vec3(at.x, who->position.y, at.z); who->sit_yaw = atan2f(n.z, n.x) + PI_F / 2.0f; /* facing n */ @@ -823,6 +867,7 @@ void Object_sit(Object *who, Object *seat, Vec3 toward) { who->velocity = vec3_zero(); who->sit_style = SIT_SEAT; who->body.seat_height = SEAT_HEIGHT; + return true; } #define SETTLE_SPEED 1.1f /* m/s stepping over to the seat */ @@ -1226,7 +1271,7 @@ void Object_describe(const Object *obj, char *out, size_t size) { case OBJ_VODKA: case OBJ_WATER: if (obj->sips > 0) snprintf(out, size, "%s - %d sip%s left", bottleName(obj), obj->sips, obj->sips == 1 ? "" : "s"); - else snprintf(out, size, "%s - empty", bottleName(obj)); + else snprintf(out, size, "%s - empty (back into a crate with it)", bottleName(obj)); break; case OBJ_APPLE: case OBJ_BREAD: @@ -1285,13 +1330,16 @@ void Object_describe(const Object *obj, char *out, size_t size) { break; case OBJ_STONE: snprintf(out, size, kg >= 1 ? "A stone - %d kg" : "A pebble", kg); break; case OBJ_CAR: - if (!obj->load || obj->load->n == 0) snprintf(out, size, "Your car - the boot is empty"); - else if (!obj->boot_open) snprintf(out, size, "Your car - full of your things"); - else snprintf(out, size, "Your car - left click at the boot: take out %s", Car_nextName(obj)); + if (!obj->load || obj->load->n == 0) snprintf(out, size, "Volkram's car - the boot is empty"); + else if (!obj->boot_open) snprintf(out, size, "Volkram's car - full of your things"); + else snprintf(out, size, "Volkram's car - left click at the boot: take out %s", Car_nextName(obj)); break; case OBJ_CRATE: - snprintf(out, size, "Crate of %s - %d bottle%s left%s", Crate_name(obj->variant), obj->stored, obj->stored == 1 ? "" : "s", - obj->stored > 0 && !obj->is_held ? " (left click: take one)" : ""); + if (obj->crate_empties > 0) + snprintf(out, size, "Crate of %s - %d full, %d empt%s%s", Crate_name(obj->variant), obj->stored - obj->crate_empties, obj->crate_empties, + obj->crate_empties == 1 ? "y" : "ies", obj->stored > obj->crate_empties && !obj->is_held ? " (left click: take one)" : ""); + else snprintf(out, size, "Crate of %s - %d bottle%s left%s", Crate_name(obj->variant), obj->stored, obj->stored == 1 ? "" : "s", + obj->stored > 0 && !obj->is_held ? " (left click: take one)" : ""); break; case OBJ_TENT: { static const char *const STAGE[] = { "spread out - left click: put the poles in", "the poles going in - keep clicking", diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h index e57d2f4..9e04483 100644 --- a/entropie/src/world/object.h +++ b/entropie/src/world/object.h @@ -168,12 +168,14 @@ typedef struct Object { 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 */ 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 */ @@ -284,6 +286,7 @@ typedef struct Object { 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) */ @@ -444,7 +447,11 @@ void Object_useToilet(Object *who, Object *toilet); 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. */ -void Object_sit(Object *who, Object *seat, Vec3 toward); +/* 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); +/* 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). */ diff --git a/entropie/src/world/scene.c b/entropie/src/world/scene.c index 972d136..4d40091 100644 --- a/entropie/src/world/scene.c +++ b/entropie/src/world/scene.c @@ -84,7 +84,7 @@ static const int PAPERS[] = { PAPERS_PACK, 20, 5, 0, 30, 0, 0 }; /* (Ton static const int FILTERS[] = { FILTERS_BAG, 60, 0, 15, 0, 0, 0 }; /* (Jana is out) */ static const bool FILTER_HABIT[] = { true, true, true, true, false, false, false }; /* (Kurti: none, ever) */ static const bool POCKET_TRAY[] = { true, false, true, false, false, false, false }; /* a pocket ashtray: you and Jana */ -static const char *const KEYS_IN_POCKET[] = { "Car keys", NULL, NULL, NULL, "House keys", NULL, NULL }; +static const char *const KEYS_IN_POCKET[] = { NULL, "Car keys", NULL, NULL, "House keys", NULL, NULL }; /* (Volkram's car) */ static const int GARMENT[] = { GARMENT_TROUSERS, GARMENT_TROUSERS, GARMENT_SKIRT, GARMENT_SHORTS, GARMENT_TROUSERS, GARMENT_TROUSERS, GARMENT_SHORTS }; /* Years: past 45 slower on their feet (Kurti is 74). */ static const int AGE[] = { 28, 31, 29, 26, 74, 30, 25 }; @@ -274,6 +274,7 @@ Object *Scene_load(World *world) { float years = clampf(1.0f - (float)(AGE[characters % TEMPERAMENT_COUNT] - 45) / 60.0f, 0.5f, 1.0f); /* (old legs) */ obj->top_speed *= years; obj->jump_speed *= years; + obj->pace = years * (1.0f + 0.12f * obj->temperament.energy); /* (Kurti: old and calm - he takes his time) */ characters++; } diff --git a/entropie/src/world/tent.c b/entropie/src/world/tent.c index 77132f8..fd013df 100644 --- a/entropie/src/world/tent.c +++ b/entropie/src/world/tent.c @@ -58,7 +58,7 @@ Object *Tent_unpack(World *world, Object *bag, Vec3 door_dir) { if (!bag || bag->type != OBJ_GEAR || bag->variant < GEAR_TENT_SMALL || bag->is_held || bag->broken) return NULL; int big = bag->variant == GEAR_TENT_BIG; Vec3 at = bag->position; - at.y = World_groundHeight(world, at.x, at.z, at.y + 0.3f); + at.y = Terrain_height(&world->terrain, at.x, at.z); /* (on the ground: not on the sleeping bags lying there - it hovered) */ Object *tent = Object_create(OBJ_TENT, PALETTE_STANDARD, at, vec3_zero(), big ? 1.0f : 0.0f); if (!tent) return NULL; tent->owner = bag->owner; @@ -96,6 +96,21 @@ void Tent_takeDown(Object *const *objects, int count, Object *tent) { reshape(tent); } +bool Tent_movable(const Object *tent) { + return tent && tent->type == OBJ_TENT && !tent->broken && + (tent->tent_stage < TENT_PEGS || (tent->tent_stage == TENT_PEGS && tent->stored == 0) || (tent->tent_down && tent->tent_stage <= TENT_COVER)); +} + +void Tent_startMove(Object *tent) { + if (tent->tent_down) { /* (the pegs out on the way down: now it's going up again where it's put) */ + tent->tent_down = false; + if (tent->tent_stage == TENT_COVER) { tent->tent_stage = TENT_PEGS; tent->stored = 0; } + tent->tent_work = 0.0f; + reshape(tent); + } + tent->tent_dragged = true; +} + /* Coming down: the pegs out, the cover off, the poles down, folded up (packed in Tent_update). */ static int workDown(Object *tent, float rate, float dt) { int big = tent->variant; @@ -120,7 +135,7 @@ static int workDown(Object *tent, float rate, float dt) { } int Tent_work(Object *tent, Object *who, float dt) { - if (tent->type != OBJ_TENT || tent->broken || (tent->tent_stage >= TENT_UP && !tent->tent_down)) return -1; + if (tent->type != OBJ_TENT || tent->broken || tent->tent_dragged || (tent->tent_stage >= TENT_UP && !tent->tent_down)) return -1; int big = tent->variant; float rate = (0.75f + 0.5f * who->wits) * (1.0f - 0.5f * fminf(who->drunk, 1.0f)); if (tent->tent_down) return workDown(tent, rate, dt); @@ -289,7 +304,7 @@ static void blowAbout(World *world, Object *tent, float dt) { Vec3 from_p = vec3_add(tent->position, vec3(0.0f, 0.3f, 0.0f)); if (World_rayCast(world, from_p, dir, reach, notIt, tent, NULL, NULL) >= 0.0f) return; /* (snagged on something) */ tent->position = vec3_add(tent->position, vec3_scale(dir, push * dt)); - tent->position.y = World_groundHeight(world, tent->position.x, tent->position.z, tent->position.y + 0.5f); + tent->position.y = Terrain_height(&world->terrain, tent->position.x, tent->position.z); Object_setRotation(tent, vec3(0.0f, tent->rotation.y + 0.06f * push * sinf(tent->position.x * 1.7f + tent->position.z) * dt, 0.0f)); Object_syncColliders(tent); world->tent_blowing = tent; diff --git a/entropie/src/world/tent.h b/entropie/src/world/tent.h index 493253b..aa91650 100644 --- a/entropie/src/world/tent.h +++ b/entropie/src/world/tent.h @@ -24,6 +24,11 @@ int Tent_work(Object *tent, Object *who, float dt); #define TENT_DOWN 10 #define TENT_DOWN_PACKED 20 void Tent_takeDown(Object *const *objects, int count, Object *tent); +/* No pegs in it (spread out, on its poles, the cover on, the pegs out): it can be moved. */ +bool Tent_movable(const Object *tent); +/* Picked up to move it: from now on it goes up again (a tent being taken down + with its pegs out stays as it is, waiting for its pegs). */ +void Tent_startMove(Object *tent); /* What working on it means now ("putting the poles in") and where the hands go. */ const char *Tent_job(const Object *tent); Vec3 Tent_workSpot(const Object *tent, float side); /* where to stand (side -1 / 0 / +1) */ diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c index 0d3a7bb..6ce2142 100644 --- a/entropie/src/world/world.c +++ b/entropie/src/world/world.c @@ -1276,6 +1276,7 @@ void World_update(World *world, float dt, float game_dt) { Object_indexKinds(world->objects, world->count); world->dt_props = dt; world->game_dt = game_dt; + Object_seePeople(world->objects, world->count); Clothes_update(world); Weather_update(world, dt, game_dt); Car_serve(world);