Nimbin[12]?Games / Entropie / commits / 3977cd7

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 1.65.0: smooth off the ladder and taking it down

Christian Immanuel · 2026-10-15 02:50 · 3977cd76a6fa53b3a3927e0519b98617e26e6a45

- Stepping off a ladder at the bottom: a short step back, one foot down
  after the other (the feet jumped 18 cm)
- Taking a leaning ladder down: the hand reaches for the rail at chest
  height (it reached for the middle of the ladder, high up the wall) and
  the ladder stays as steep as it leaned
- Getting up onto a roof from a ladder: no hop at the end
- Audit ladder: joint jumps 65 -> 45 (163 in 1.63)

Includes 1.64.0 (if not committed separately):
- Smooth ladder and climbs: getting on, over the edge at the top, feet
  from where they are; the easy 1 m climb no longer drops you off again

Includes 1.63.0 (if not committed separately):
- The hose melts in the fire (leaks, burns through); duct tape and the
  shears mend it

Includes 1.62.0 (if not committed separately):
- Smooth pull-ups: no hop at the end, feet and knees ease over the edge,
  elbows and hands taking hold blend in (audit walk: joint jumps 161 -> 58)

Includes 1.61.0 (if not committed separately):
- A step ladder in the tool room: stands on its own, at a wall with its
  back to it; climb onto the wall from its top

Includes 1.60.0 (if not committed separately):
- A folding ladder (two 2.5 m halves on a hinge, 5 m open) beside the
  telescopic one; one click at a wall unfolds it and leans it there

Includes 1.59.0 (if not committed separately):
- Stepping up onto blocks above knee high; set moves and jumps only for
  things standing on the ground in front of you

Includes 1.58.0 (if not committed separately):
- Walking over knee-high things is one smooth set move that always
  finishes; at a run: jump up onto blocks, catch climbable edges
 entropie/Makefile                |  2 +-
 entropie/STATE.md                |  6 ++---
 entropie/commit_message.txt      | 23 ++++++++++---------
 entropie/src/game/game_actions.c | 28 +++++++++++++----------
 entropie/src/people/hands.c      |  2 ++
 entropie/src/world/ladder.c      | 48 +++++++++++++++++++++++++++++++++-------
 entropie/src/world/object.h      |  2 ++
 7 files changed, 77 insertions(+), 34 deletions(-)

diff --git a/entropie/Makefile b/entropie/Makefile
index 7ef31b1..0090557 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 1.64.0
+VERSION   := 1.65.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/STATE.md b/entropie/STATE.md
index daa8fb8..6ef0b2d 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -84,7 +84,7 @@ Talk topics: offer, ask, suggest, where (new woodpile spot), ask for a hand, ask
 - **Bushes**: trying to get over one ends in it, twigs snap
 - **Half way over something**: from when a foot is well into a step over something (lifted 12 cm+, 15 % into the step) until the body is over there (6 cm behind where that foot came down, at most 20 cm past it), you're crossing (`body.c` crossOver): stopping (or slower than 0.3 m/s) the body carries on over it at 0.6 m/s (`physics.c` carryOver); the foot over there stays down, the other comes after. Walking back takes you back. Standing still half over costs energy (2.5 %/s, a leg held up 6 %/s; less sporty, more); worn out, held longer than 1-6 s (fitness, energy) or legs over 0.95 m apart: you're made to go over; still stuck 1.5 s later: you fall (`world.c` halfOver)
 - **Sure-footedness**: one factor (`world.c`, unsure: ~0.2 sporty and careful .. ~1 average .. ~2.7 clumsy and unfit) scales every trip, slip, scrape and run-into, and how bad it gets; clumsy and unfit, you also stumble over your own feet jogging or running
-- **Climbing**: hand on it (up to 1.35 m), hang and pull up (higher, both hands free), jump for the edge; ladder up and down, over the edge at the top. Walking up to something 0.7–2 m high, the hands go onto its edge first; pulling up, the feet push against the face below the edge, then the right knee and foot come over onto it, then the left (scripted in `game_actions.c`, climbFeet); knees bend up. A foot below the edge goes back until its (forward bent) knee is 8 cm clear of the face (`game_actions.c`, kneeClear); onto something low (under 1 m) the body rises quickly first, so the knee comes up above the edge. Walking up to anything higher than 0.5 m, the feet stay 0.35 m back from it (room for the knee; `body.c`, footSpot). Climb test: 0.8-1.5 m, walking or jumping, legs at most 5 cm into it (was 19 cm at 0.8 m). High pull-ups: pulled up till the arms press down (hips near the edge), then the knee comes up and you rise; the hands go on ahead onto the top and stay there, the back bends over the edge as far as it takes to keep them on it (body_pose.c, reach_bend). Jumping: holding Space (or W) grabs an edge the hands reach (about 2.4 m); up to 0.35 m higher, the fingertips touch it ("Just out of reach"); hands go up wide, then onto the edge. Smooth pull-ups (audit walk: joint jumps 161 -> 58, the worst 22 -> 10 cm): the climb ends at the highest point under the body's footprint (where the physics stands you: no hop), each foot lands on what's under it, no replanting at the end (the feet are there already; body velocity zeroed: no stride ahead); feet over the edge over longer windows (lead 0.56-0.86, trail 0.74-1.0 of a high climb); kneeClear eases the foot back in 1 cm steps and lets go gradually as the knee comes over the top; the knee pole for pulling up eases in and out (`pull_knees`); an elbow swinging up over an edge blends between the two solves (no flip at half way); a hand taking hold eases out of the arm's "move out" (`arm_hold`). Every climb starts from where the feet are (`noteFeet`, eased in over the first 15 %); each foot's height on top is taken under where it ends up (clamped to 0.3 under / 0.15 over the top: roofs slope); below the edge the knee room (kneeClear) blends out as the foot gets over the face or above the top. Ladder: getting on, the body eases over onto it (`ladder_shift`, 1.6 m/s) and hands and feet go onto the rungs from where they are; over the edge at the top is a pull-up (1.6 s, `climb_face` where the top rests). Audit ladder: joint jumps 163 -> 65; climb test 458 -> 180 (the easy 1 m climb no longer drops you off again)
+- **Climbing**: hand on it (up to 1.35 m), hang and pull up (higher, both hands free), jump for the edge; ladder up and down, over the edge at the top. Walking up to something 0.7–2 m high, the hands go onto its edge first; pulling up, the feet push against the face below the edge, then the right knee and foot come over onto it, then the left (scripted in `game_actions.c`, climbFeet); knees bend up. A foot below the edge goes back until its (forward bent) knee is 8 cm clear of the face (`game_actions.c`, kneeClear); onto something low (under 1 m) the body rises quickly first, so the knee comes up above the edge. Walking up to anything higher than 0.5 m, the feet stay 0.35 m back from it (room for the knee; `body.c`, footSpot). Climb test: 0.8-1.5 m, walking or jumping, legs at most 5 cm into it (was 19 cm at 0.8 m). High pull-ups: pulled up till the arms press down (hips near the edge), then the knee comes up and you rise; the hands go on ahead onto the top and stay there, the back bends over the edge as far as it takes to keep them on it (body_pose.c, reach_bend). Jumping: holding Space (or W) grabs an edge the hands reach (about 2.4 m); up to 0.35 m higher, the fingertips touch it ("Just out of reach"); hands go up wide, then onto the edge. Smooth pull-ups (audit walk: joint jumps 161 -> 58, the worst 22 -> 10 cm): the climb ends at the highest point under the body's footprint (where the physics stands you: no hop), each foot lands on what's under it, no replanting at the end (the feet are there already; body velocity zeroed: no stride ahead); feet over the edge over longer windows (lead 0.56-0.86, trail 0.74-1.0 of a high climb); kneeClear eases the foot back in 1 cm steps and lets go gradually as the knee comes over the top; the knee pole for pulling up eases in and out (`pull_knees`); an elbow swinging up over an edge blends between the two solves (no flip at half way); a hand taking hold eases out of the arm's "move out" (`arm_hold`). Every climb starts from where the feet are (`noteFeet`, eased in over the first 15 %); each foot's height on top is taken under where it ends up (clamped to 0.3 under / 0.15 over the top: roofs slope); below the edge the knee room (kneeClear) blends out as the foot gets over the face or above the top. Ladder: getting on, the body eases over onto it (`ladder_shift`, 1.6 m/s) and hands and feet go onto the rungs from where they are; over the edge at the top is a pull-up (1.6 s, `climb_face` where the top rests; the body ends at its stand height on the roof, `standHeight`); stepping off at the bottom is a 0.6 s step back, one foot down after the other, the hands let go half way (`ladder_off`); taking a leaning ladder down, the hand reaches for its rail at chest height (not its middle up the wall) and it stays held as steep as it leaned. Audit ladder: joint jumps 163 -> 45; climb test 458 -> 180 (the easy 1 m climb no longer drops you off again)
 - **Drink and the rest**: sips, burps, sways, veers, sick, passes out, sleeps it off; pees (stream lands on the real ground, soaks in or puddles)
 - **Throwing**: underhand lob, overhand when wound up long enough
 - **Carrying one-handed**: hangs at the side by its top (a bag by its neck), wide things with the arm a little out; only long things (ladder, logs, axe) stop you at walls, bags and bottles squeeze by
@@ -137,7 +137,7 @@ A cell grid of the site (2 m cells, `src/world/grid.c`, built once a frame) answ
 Most important first.
 
 **Bugs**
-- Joint jumps left in the audit: jog+stop 191, sprint 358 (worst 28 cm) - running into things; ladder 65: stepping off at the bottom (feet replanted, 18 cm), taking it down (the hand flies up to the grip), toes after getting up on the roof
+- Joint jumps left in the audit: jog+stop 191, sprint 358 (worst 28 cm) - running into things; ladder 45: toes on the sloping roof (a landing step's toes snap onto the slope, 16 cm; blending them in made all walking worse), toes turning onto the rungs getting on from the top (14 cm)
 
 **Body and moving**
 - Obstacle approach, still to do: prefer a step in between on top of something stable (or unstable, if clumsy) over a long stride; NPCs: pull up or jump onto things on their own (the player does when running at them)
@@ -162,4 +162,4 @@ Most important first.
 - The walking map rebuild takes ~9 ms when fixed things change (a felled tree)
 
 ---
-State of `entropie 1.64.0 (build 55638db)`
+State of `entropie 1.65.0 (build 7f81926)`
diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt
index b6d185d..67cd231 100644
--- a/entropie/commit_message.txt
+++ b/entropie/commit_message.txt
@@ -1,13 +1,16 @@
-entropie 1.64.0: smooth ladder and climbs (no jumping feet)
-
-- Getting on a ladder: the body eases over onto it, hands and feet go
-  onto the rungs from where they were (feet jumped 20 cm)
-- Over the edge at the top of a ladder: a proper pull-up, the feet from
-  the rungs onto the roof without snapping
-- Every climb starts from where the feet are; the foot height on top
-  follows a sloping roof; the knee room blends out instead of snapping
-- Audit: ladder joint jumps 163 -> 65, climb test 458 -> 180; the easy
-  1 m climb no longer drops you off again
+entropie 1.65.0: smooth off the ladder and taking it down
+
+- Stepping off a ladder at the bottom: a short step back, one foot down
+  after the other (the feet jumped 18 cm)
+- Taking a leaning ladder down: the hand reaches for the rail at chest
+  height (it reached for the middle of the ladder, high up the wall) and
+  the ladder stays as steep as it leaned
+- Getting up onto a roof from a ladder: no hop at the end
+- Audit ladder: joint jumps 65 -> 45 (163 in 1.63)
+
+Includes 1.64.0 (if not committed separately):
+- Smooth ladder and climbs: getting on, over the edge at the top, feet
+  from where they are; the easy 1 m climb no longer drops you off again
 
 Includes 1.63.0 (if not committed separately):
 - The hose melts in the fire (leaks, burns through); duct tape and the
diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c
index 3180c32..a2221b7 100644
--- a/entropie/src/game/game_actions.c
+++ b/entropie/src/game/game_actions.c
@@ -69,6 +69,20 @@ static void noteFeet(Game *game, const Object *p) {
     }
 }
 
+/* Where the body will stand up there: the highest point under its footprint,
+   as the physics stands you - no hop once up. */
+static float standHeight(Game *game, const Object *p, Vec3 to, float s) {
+    const Collider *pc = &p->colliders[0];
+    float under = to.y;
+    for (int cx = -1; cx <= 1; cx += 2)
+        for (int cz = -1; cz <= 1; cz += 2) {
+            Vec3 q = vec3_add(to, vec3_add(vec3_scale(pc->axes[0], (float)cx * pc->half_extents.x), vec3_scale(pc->axes[2], (float)cz * pc->half_extents.z)));
+            float g = World_groundHeight(&game->world, q.x, q.z, to.y + 0.4f * s);
+            if (g > under && g < to.y + 0.3f * s) under = g;
+        }
+    return under;
+}
+
 /* Hands on the edge: climb up. Easy ledges with a hand on them; higher up
    (as far as the hands reach, standing or jumping) hanging on and pulling up. */
 static bool startClimb(Game *game, float top, Vec3 probe) {
@@ -94,17 +108,7 @@ static bool startClimb(Game *game, float top, Vec3 probe) {
     Vec3 to = vec3(probe.x + f.x * 0.25f * s, top, probe.z + f.z * 0.25f * s);
     to.y = World_groundHeight(&game->world, to.x, to.z, top + 0.6f * s); /* (a sloping roof goes on up) */
     if (to.y < top - 0.3f || to.y > top + 0.4f * s) to.y = top;
-    { /* (where the body will stand up there: the highest point under its footprint - no hop once up) */
-        const Collider *pc = &p->colliders[0];
-        float under = to.y;
-        for (int cx = -1; cx <= 1; cx += 2)
-            for (int cz = -1; cz <= 1; cz += 2) {
-                Vec3 q = vec3_add(to, vec3_add(vec3_scale(pc->axes[0], (float)cx * pc->half_extents.x), vec3_scale(pc->axes[2], (float)cz * pc->half_extents.z)));
-                float g = World_groundHeight(&game->world, q.x, q.z, to.y + 0.4f * s);
-                if (g > under && g < to.y + 0.3f * s) under = g;
-            }
-        to.y = under;
-    }
+    to.y = standHeight(game, p, to, s);
     game->climb_from = p->position;
     game->climb_to = to;
 
@@ -364,7 +368,7 @@ void game_climbLadder(Game *game, const Input *in, float dt) {
     Ladder_getOff(p);
     p->climbing = true; /* pulled over the edge like any ledge (see game_climb) */
     game->climb_from = p->position;
-    game->climb_to = vec3(over.x, top, over.z);
+    game->climb_to = vec3(over.x, standHeight(game, p, vec3(over.x, top, over.z), p->look.height / 1.75f), over.z);
     game->climb_edge = edge;
     game->climb_hang = p->position.y; /* (already up there: no hanging on) */
     game->climb_across = vec3_normalize(vec3_cross(Object_facing(p), vec3(0.0f, 1.0f, 0.0f)));
diff --git a/entropie/src/people/hands.c b/entropie/src/people/hands.c
index 244d9f3..c4a2568 100644
--- a/entropie/src/people/hands.c
+++ b/entropie/src/people/hands.c
@@ -848,6 +848,8 @@ void Hands_update(Object *who, float dt) {
             Vec3 target = twoHanded(h)
                 ? vec3_add(c, vec3_scale(right, sign * (extentAlong(item, right) + 0.01f)))
                 : vec3_add(c, vec3(0.0f, extentAlong(item, UP), 0.0f));
+            if (!twoHanded(h) && (item->type == OBJ_LADDER || target.y > who->position.y + 1.5f * s)) /* (long, or up high: where it's in reach) */
+                target = closestPoint(item, vec3(target.x, who->position.y + 1.0f * s, target.z));
             b->hand_active[i] = true;
             b->hand_target[i] = target;
             b->crouch_goal = fmaxf(b->crouch_goal, crouchFor(who, target.y));
diff --git a/entropie/src/world/ladder.c b/entropie/src/world/ladder.c
index 2fb14e6..507497f 100644
--- a/entropie/src/world/ladder.c
+++ b/entropie/src/world/ladder.c
@@ -15,6 +15,7 @@
 #define HANDS_ABOVE   1.05f   /* hands this far above the feet (along the ladder) */
 #define MOUNT_REACH   1.2f
 #define LADDER_ONTO   1.6f    /* m/s the body moves over onto it, getting on */
+#define STEP_OFF_TIME 0.6f    /* seconds to step off at the bottom */
 #define RAISE_ANGLE   1.05f   /* a raised ladder: this steep (rad) looking straight ahead ... */
 #define RAISE_FOLLOW  0.9f    /* ... steeper looking up, flatter looking down ... */
 #define RAISE_LOW     0.75f
@@ -433,6 +434,7 @@ bool Ladder_take(Object *who, Object *l) {
     l->lean_top = 0.0f;
     l->rest_time = 0.0f;
     if (stepLadder(l)) l->extend_goal = 0.0f; /* (folds up as you take it) */
+    else l->raised = clampf(asinf(clampf(upAlong(l).y, -1.0f, 1.0f)), 0.15f, RAISE_HIGH); /* (held up as steep as it leaned: no swing down and up again) */
     return Hands_pickUpObject(who, hand, l);
 }
 
@@ -477,8 +479,12 @@ void Ladder_mount(Object *who, Object *l, bool at_top) {
     Object_syncColliders(who);
 }
 
+/* Where you stand once off it at the bottom. */
+static Vec3 offSpot(const Object *l) { return vec3_add(l->position, vec3_scale(l->lean_normal, 0.45f)); }
+
 void Ladder_getOff(Object *who) {
     who->ladder_shift = vec3_zero();
+    who->ladder_off = 0.0f;
     who->on_ladder = NULL;
     who->climbing = false;
 }
@@ -486,19 +492,28 @@ void Ladder_getOff(Object *who) {
 int Ladder_climb(Object *who, float input, float dt) {
     Object *l = who->on_ladder;
     if (!l) return CLIMB_OFF_BOTTOM;
+    if (who->ladder_off > 0.0f) { /* stepping off backwards onto the ground, one foot after the other */
+        who->ladder_off = fminf(1.0f, who->ladder_off + dt / STEP_OFF_TIME);
+        Vec3 off = offSpot(l);
+        who->position = vec3_lerp(who->ladder_off_from, off, smoothstepf(0.0f, 1.0f, who->ladder_off));
+        Object_syncColliders(who);
+        if (who->ladder_off < 1.0f) return CLIMB_ON;
+        Ladder_getOff(who);
+        who->is_grounded = true;
+        who->body.velocity = vec3_zero(); /* (the feet are down there already: they stay) */
+        return CLIMB_OFF_BOTTOM;
+    }
     who->ladder_at += input * CLIMB_SPEED * Object_vigor(who) * dt;
     { /* just got on: the body eases over onto it */
         float left = vec3_length(who->ladder_shift), step = LADDER_ONTO * dt;
         who->ladder_shift = left > step ? vec3_scale(who->ladder_shift, (left - step) / left) : vec3_zero();
     }
-    if (who->ladder_at < 0.0f) { /* down: step off backwards onto the ground */
-        Vec3 off = vec3_add(l->position, vec3_scale(l->lean_normal, 0.45f));
-        Ladder_getOff(who);
-        who->position = off;
-        who->is_grounded = true;
-        who->body.replant = true;
-        Object_syncColliders(who);
-        return CLIMB_OFF_BOTTOM;
+    if (who->ladder_at < 0.0f) { /* down: step off backwards onto the ground (see above) */
+        who->ladder_at = 0.0f;
+        who->ladder_off = 1e-4f;
+        who->ladder_off_from = who->position;
+        for (int i = 0; i < 2; i++) who->ladder_from[i] = who->body.feet[i].ankle; /* (the feet go down from there) */
+        return CLIMB_ON;
     }
     float top = highest(l);
     bool at_edge = who->ladder_at >= top && input > 0.0f && l->lean_top > 0.0f;
@@ -550,6 +565,7 @@ void Ladder_poseHands(Object *who, float dt) {
     if (!l) return;
     const float s = who->look.height / 1.75f;
     Vec3 n = l->lean_normal, across = vec3_normalize(vec3_cross(n, UP));
+    if (who->ladder_off > 0.5f) return; /* (stepping off: let go) */
     int k = rungAbove(l, who->ladder_at + HANDS_ABOVE * s);
     for (int h = 0; h < 2; h++) {
         /* hand over hand: one a rung above the other, swapping as you go */
@@ -571,6 +587,22 @@ void Ladder_poseFeet(Object *who, float dt) {
     if (!l) return;
     const float s = who->look.height / 1.75f;
     Vec3 n = l->lean_normal, across = vec3_normalize(vec3_cross(n, UP));
+    if (who->ladder_off > 0.0f) { /* stepping off: one foot down onto the ground, then the other */
+        for (int i = 0; i < 2; i++) {
+            Foot *foot = &who->body.feet[i];
+            float side = i == 0 ? 1.0f : -1.0f;
+            float u = smoothstepf(i == 0 ? 0.0f : 0.4f, i == 0 ? 0.6f : 1.0f, who->ladder_off);
+            Vec3 down = vec3_add(offSpot(l), vec3_add(vec3_scale(across, side * 0.11f * s), vec3_scale(n, -0.05f * s)));
+            down.y = l->position.y + 0.075f * s; /* (ankle height over the ground) */
+            Vec3 at = vec3_lerp(who->ladder_from[i], down, u);
+            at.y += 0.08f * s * sinf(PI_F * u); /* (lifted off the rung, set down) */
+            foot->ankle = at;
+            foot->dir = vec3_scale(n, -1.0f);
+            foot->toe = vec3_add(at, vec3_add(vec3_scale(n, -0.14f * s), vec3(0.0f, -lerpf(0.05f, 0.055f, u) * s, 0.0f)));
+            foot->stepping = false;
+        }
+        return;
+    }
     int k = rungAbove(l, who->ladder_at - 0.02f);
     for (int i = 0; i < 2; i++) {
         /* one foot a rung higher than the other, swapping as you climb */
diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h
index b6e4275..3e3cfd7 100644
--- a/entropie/src/world/object.h
+++ b/entropie/src/world/object.h
@@ -225,6 +225,8 @@ typedef struct Object {
     Vec3       ladder_from[4], ladder_to[4]; /* ... feet and hands on their way from rung to rung */
     float      ladder_t[4];
     Vec3       ladder_shift;  /* ... the body still this far from its place on it (just got on: eases away) */
+    float      ladder_off;    /* ... stepping off at the bottom (0 = not, .. 1 = off), from ladder_off_from */
+    Vec3       ladder_off_from;
     bool       ladder_limbs_ok;
 
     /* Characters only */