Nimbin[12]?Games / Entropie / commits / 4b5d825

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.16.1: car arrival crash and car fixes

Christian Immanuel · 2026-10-20 16:10 · 4b5d825d9650095d169d4bb99251e4f09ec2efa7

- 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)
 commit_message.txt                 | 15 +++++++++------
 entropie/Makefile                  |  2 +-
 entropie/STATE.md                  |  8 +++++++-
 entropie/src/people/ai_internal.h  |  1 +
 entropie/src/people/body_pose.c    | 11 +++++++++--
 entropie/src/render/shapes_props.c |  8 +++++---
 entropie/src/world/car.c           |  4 ++--
 entropie/src/world/terrain.c       |  2 +-
 8 files changed, 35 insertions(+), 16 deletions(-)

diff --git a/commit_message.txt b/commit_message.txt
index 05ea22b..2791f58 100644
--- a/commit_message.txt
+++ b/commit_message.txt
@@ -1,7 +1,10 @@
-entropie 2.16.0: felling room
+entropie 2.16.1: car arrival crash and car fixes
 
-- NPCs check where a tree will come down before felling it and pick a
-  clear direction (no houses, walls, cars, tents, furniture, fire); no room
-  anywhere: they leave that tree
-- Nearly through, anyone in the way gets "Timber!" and steps aside; the
-  feller waits until the way is clear (also for you)
+- 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)
diff --git a/entropie/Makefile b/entropie/Makefile
index d5edb5e..2c0266e 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 2.16.0
+VERSION   := 2.16.1
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/STATE.md b/entropie/STATE.md
index 309d852..db3ab52 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -251,6 +251,12 @@ Round 2.16:
 - Felling room (`ai_work.c`): before an NPC fells a tree (`ai_taskFor` JOB_FELL -> `fellPlan`, once per tree: `fell_plan`, `fell_blocked`), FELL_DIRS 12 ways round it, nearest to the way they'd fell it first; `fallHits` looks down (rays from 14 m) at spots every metre from the stump to its top (`treeTall`), across the trunk near the stump and a crown wide further out: a house, barn, building, wall, car, tent, furniture, fireplace or woodpile hit counts. The first clear way (or the least bad): the feller stands on the other side (it falls away from them). No clear way at all: "No room for that one to come down - I'll leave it" (skipped).
 - Nearly through (`Tools_fellDone` over FELL_WARN 0.6): anybody where it'll come down (from the stump to its top + 1.5 m, a crown + 0.6 m wide) is warned ("Timber! Out of the way!", you: "...it's coming down there!"); NPCs step out sideways past the crown (`ai.dodge_to`, `dodge_t` 8 s, first thing in Ai_update); the feller waits (`ai.timber`, counts as fine waiting for the watchdog) till nobody's there.
 
+Round 2.16.1:
+- The crash arriving by car: at slow frames (10 fps, or a spike up to MAX_FRAME_DT 0.1) the arm springs (`body_pose.c` spring: ARM_STIFFNESS x4 running, explicit damping 22 x 0.1 s > 2) blew up - a passenger's hand went NaN (they vanished), then the ground lookup took an index from the NaN (`terrain.c` wearAt: SEGV). `spring` now splits a frame into steps of SPRING_STEP 0.02 s (60 fps: one, nothing changes there) and never hands a NaN on; `Terrain_naturalHeight` treats a NaN position as outside. Checked: the drive and the hello at 10, ~16 and 60 fps (jittered), and under AddressSanitizer.
+- Passengers sat backwards: a person's yaw is turned round from a thing's (`Object_facing` vs `yaw_front`): `Car_place` gives them car yaw + pi.
+- 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).
+
 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).
@@ -407,4 +413,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.0 (build ceace5d)`
+State of `entropie 2.16.1 (build 4cd7c31)`
diff --git a/entropie/src/people/ai_internal.h b/entropie/src/people/ai_internal.h
index 6e98ce9..e10fe72 100644
--- a/entropie/src/people/ai_internal.h
+++ b/entropie/src/people/ai_internal.h
@@ -125,6 +125,7 @@ static inline void setFacing(Object *o, Vec3 dir) {
 
 /* Turns towards dir at a limited rate. */
 static inline void turnTowards(Object *o, Vec3 dir, float dt) {
+    if (o->sitting && (o->seat || o->riding)) return; /* (on a bench, a chair, a log, in the car: they stay sat as they sat - the head turns, see look_dir) */
     dir = flat(dir);
     if (vec3_length(dir) < 1e-4f) return;
     Vec3 f = Object_facing(o);
diff --git a/entropie/src/people/body_pose.c b/entropie/src/people/body_pose.c
index af3906b..3e0495f 100644
--- a/entropie/src/people/body_pose.c
+++ b/entropie/src/people/body_pose.c
@@ -113,9 +113,16 @@ static Vec3 reachPole(Vec3 shoulder, Vec3 palm, float sign) {
 }
 
 /* Damped spring step towards a target. */
+#define SPRING_STEP 0.02f  /* s: a slow frame is split into steps this short (60 fps: one) (a stiff spring blew up at 10 fps: NaN arms, a crash) */
+
 static void spring(float *value, float *velocity, float target, float stiffness, float damping, float dt) {
-    *velocity += (stiffness * (target - *value) - damping * *velocity) * dt;
-    *value += *velocity * dt;
+    int n = dt > SPRING_STEP ? (int)ceilf(dt / SPRING_STEP) : 1;
+    float h = dt / (float)n;
+    for (int i = 0; i < n; i++) {
+        *velocity += (stiffness * (target - *value) - damping * *velocity) * h;
+        *value += *velocity * h;
+    }
+    if (!isfinite(*value) || !isfinite(*velocity)) { *value = target; *velocity = 0.0f; } /* (never a NaN into the body: it would vanish, then crash) */
 }
 
 /* Head frame from yaw (> 0 = right), pitch (> 0 = up) and roll. */
diff --git a/entropie/src/render/shapes_props.c b/entropie/src/render/shapes_props.c
index 405d145..414dafb 100644
--- a/entropie/src/render/shapes_props.c
+++ b/entropie/src/render/shapes_props.c
@@ -926,16 +926,18 @@ Mesh Shapes_carInside(Vec3 color) {
     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)));
+        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, mid), vec3(w * 0.9f, 0.04f, cab), floor);                               /* the floor */
+    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, mid), vec3(0.06f, 0.64f, cab), door);                                   /* the doors, up to the windows */
+        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 */
diff --git a/entropie/src/world/car.c b/entropie/src/world/car.c
index 0e5c72e..3c1373a 100644
--- a/entropie/src/world/car.c
+++ b/entropie/src/world/car.c
@@ -151,8 +151,8 @@ void Car_place(World *world, Object *car, Vec3 at, float yaw) {
         Vec3 p = local(car, vec3_add(seatAt(o->ride_seat), vec3(0.0f, 0.0f, 0.18f))); /* (the feet under the dashboard) */
         o->position = vec3(p.x, at.y + CAR_FLOOR, p.z);
         o->sit_at = o->position;
-        o->sit_yaw = yaw;
-        o->rotation.y = yaw;
+        o->sit_yaw = yaw + PI_F; /* (a person's yaw faces the other way round from a thing's: see Object_facing / yaw_front) */
+        o->rotation.y = yaw + PI_F;
         o->velocity = vec3_zero();
         Object_syncColliders(o);
     }
diff --git a/entropie/src/world/terrain.c b/entropie/src/world/terrain.c
index 125f89a..b607a04 100644
--- a/entropie/src/world/terrain.c
+++ b/entropie/src/world/terrain.c
@@ -354,7 +354,7 @@ float Terrain_height(const Terrain *t, float x, float z) {
 }
 
 float Terrain_naturalHeight(const Terrain *t, float x, float z) {
-    if (x < -HALF || x > HALF || z < -HALF || z > HALF) return OUTSIDE_HEIGHT;
+    if (!(x >= -HALF && x <= HALF && z >= -HALF && z <= HALF)) return OUTSIDE_HEIGHT; /* (written so a NaN is outside too: never an index from it) */
     float gx = fminf((x + HALF) / GRID_CELL, (float)TERRAIN_GRID - 0.001f);
     float gz = fminf((z + HALF) / GRID_CELL, (float)TERRAIN_GRID - 0.001f);
     int ix = (int)gx, iz = (int)gz;