Nimbin[12]?Games / Entropie / commits / 34f5c93

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (17 hours ago) · synced 8 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 2.17.0: arrival, car, seats, crates, tents

Christian Immanuel · 2026-10-20 17:05 · 34f5c93cd636812b85b830ee581af4375d872eab

- 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);