Nimbin[12]?Games / Entropie / commits / 5ae6bcd

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.64.0: smooth ladder and climbs (no jumping feet)

Christian Immanuel · 2026-10-15 02:38 · 5ae6bcd65a3fdef64d42e7d2092b908795857155

- 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

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      | 21 +++++++++++++--------
 entropie/src/game/game.h         |  1 +
 entropie/src/game/game_actions.c | 38 +++++++++++++++++++++++++++++---------
 entropie/src/world/ladder.c      | 24 ++++++++++++++++++++++--
 entropie/src/world/object.h      |  1 +
 7 files changed, 70 insertions(+), 23 deletions(-)

diff --git a/entropie/Makefile b/entropie/Makefile
index a9d96b6..7ef31b1 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 1.63.0
+VERSION   := 1.64.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/STATE.md b/entropie/STATE.md
index 1c378a3..daa8fb8 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`)
+- **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)
 - **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 373 (worst 28 cm), ladder 163 (27 cm) - running into things and the ladder climb; walk is down to 58 (10 cm)
+- 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
 
 **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.63.0 (build 1f95dd5)`
+State of `entropie 1.64.0 (build 55638db)`
diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt
index 2eb3320..b6d185d 100644
--- a/entropie/commit_message.txt
+++ b/entropie/commit_message.txt
@@ -1,12 +1,17 @@
-entropie 1.63.0: the hose melts in the fire; duct tape and shears mend it
+entropie 1.64.0: smooth ladder and climbs (no jumping feet)
 
-- Lying in or by a burning fire the hose gets hot (glows), melts and
-  leaks (less water, a shorter arc; it squirts onto the ground there),
-  then burns through (nothing at the nozzle, the loose end can be taken
-  anywhere)
-- Duct tape (new, on the workbench) clicked at a melted bit tapes it up
-- Shears clicked at a burnt-through bit cut it out and join the ends:
-  the hose gets shorter (and reaches less far)
+- 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
+
+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,
diff --git a/entropie/src/game/game.h b/entropie/src/game/game.h
index fae2f7c..c1f18e2 100644
--- a/entropie/src/game/game.h
+++ b/entropie/src/game/game.h
@@ -53,6 +53,7 @@ typedef struct {
     float    brace_hold;    /* hands on an edge you walked up to: kept there this much longer ... */
     Vec3     brace_grip;    /* ... here */
     float    climb_face;    /* how far ahead of where you started the face of it is (m) */
+    Vec3     climb_ankle0[2], climb_toe0[2]; /* where the feet were when the climb started (eased from there) */
     Object  *putting_up;    /* the ladder you are putting up (hands on it, standing still) */
 } Game;
 
diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c
index a04fb38..3180c32 100644
--- a/entropie/src/game/game_actions.c
+++ b/entropie/src/game/game_actions.c
@@ -61,6 +61,14 @@ static void reachForIt(Game *game, Object *p, float s) {
     }
 }
 
+/* Where the feet are as a climb starts: they go from there (no jump onto the climb's own path). */
+static void noteFeet(Game *game, const Object *p) {
+    for (int i = 0; i < 2; i++) {
+        game->climb_ankle0[i] = p->body.feet[i].ankle;
+        game->climb_toe0[i] = p->body.feet[i].toe;
+    }
+}
+
 /* 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) {
@@ -116,6 +124,7 @@ static bool startClimb(Game *game, float top, Vec3 probe) {
     game->climb_time = (hard ? 1.3f + 0.7f * fmaxf(rise / s - CLIMB_EASY, 0.0f) : 0.6f + rise * 0.6f) / fmaxf(vigor, 0.4f);
     game->climb_t = 0.0f;
     game->climb_push = 0.0f;
+    noteFeet(game, p);
     p->climbing = true;
     p->velocity = vec3_zero();
     if (hard) p->needs.stamina = fmaxf(0.0f, p->needs.stamina - 0.04f * rise / s);
@@ -172,10 +181,10 @@ static void climbFeet(Game *game, Object *p, float t, bool high, float s) {
         /* on top: a little over the edge, or under you once you're up there */
         float along_top = fmaxf(face + 0.2f * s, root_along);
         float y_top = top + 0.08f * s;
-        { /* (on what's right under that foot up there - the top isn't always level) */
-            Vec3 q = vec3_add(a, vec3_add(vec3_scale(f, along_top), vec3_scale(side, sign * 0.11f * s)));
-            float g = World_groundHeight(&game->world, q.x, q.z, top + 0.3f * s);
-            if (g > top - 0.25f * s && g < top + 0.05f * s) y_top = g + 0.08f * s;
+        { /* (on what's under that foot where it ends up - the top isn't always level; the same all along: no jumps) */
+            Vec3 q = vec3_add(b, vec3_scale(side, sign * 0.11f * s));
+            float g = World_groundHeight(&game->world, q.x, q.z, top + 1.0f * s); /* (from well above: a roof slope may rise there) */
+            if (g > top - 0.6f * s) y_top = clampf(g, top - 0.3f * s, top + 0.15f * s) + 0.08f * s; /* (clamped, not cut off: no jumps on a slope) */
         }
         /* up first (clear of the edge), then over */
         float up = smoothstepf(0.0f, 0.6f, u), across = smoothstepf(0.35f, 1.0f, u);
@@ -185,15 +194,21 @@ static void climbFeet(Game *game, Object *p, float t, bool high, float s) {
         }
         float along = lerpf(along_below, along_top, across);
         float yy = lerpf(y_below, y_top, up) + 0.12f * s * sinf(PI_F * u);
-        if (along > face - 0.05f * s) yy = fmaxf(yy, y_top); /* (over it: on it, not in it) */
-        else along = kneeClear(along, yy, root_along, p->position.y, face, top, s);
+        /* over it: on it, not in it; below the edge: the knee kept clear of the face - one into the other, no jump */
+        float over = smoothstepf(face - 0.14f * s, face - 0.03f * s, along);
+        yy = lerpf(yy, fmaxf(yy, y_top), over);
+        /* (the foot up over the top already needs no room for the knee) */
+        float free = fmaxf(over, smoothstepf(top - 0.04f * s, top + 0.08f * s, yy));
+        if (free < 1.0f) along = lerpf(kneeClear(along, yy, root_along, p->position.y, face, top, s), along, free);
         Vec3 ankle = vec3_add(a, vec3_add(vec3_scale(f, along), vec3_scale(side, sign * 0.11f * s)));
         ankle.y = yy;
         /* toes: at the wall pointing into it (on the toes), on top flat ahead */
         Vec3 toe = vec3_add(ankle, vec3_scale(f, 0.14f * s));
         toe.y = ankle.y - lerpf(0.06f, 0.05f, across) * s;
-        p->climb_ankle[i] = ankle;
-        p->climb_toe[i] = toe;
+        /* (from where they were when it started: eased onto the climb's path) */
+        float onto = smoothstepf(0.0f, 0.15f, t);
+        p->climb_ankle[i] = vec3_lerp(game->climb_ankle0[i], ankle, onto);
+        p->climb_toe[i] = vec3_lerp(game->climb_toe0[i], toe, onto);
     }
     p->climb_feet = true;
 }
@@ -345,6 +360,7 @@ void game_climbLadder(Game *game, const Input *in, float dt) {
         if (game->message_time <= 0.0f) say(game, "Nothing to stand on up there");
         return;
     }
+    noteFeet(game, p);
     Ladder_getOff(p);
     p->climbing = true; /* pulled over the edge like any ledge (see game_climb) */
     game->climb_from = p->position;
@@ -352,7 +368,11 @@ void game_climbLadder(Game *game, const Input *in, float dt) {
     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)));
-    game->climb_time = 1.0f;
+    { /* (the face of it: where the ladder's top rests) */
+        Vec3 to_edge = vec3_sub(edge, p->position);
+        game->climb_face = fmaxf(0.05f, -(to_edge.x * n.x + to_edge.z * n.z));
+    }
+    game->climb_time = 1.6f; /* (pulled up over it like a high ledge: from the ladder the feet start well below) */
     game->climb_t = 0.0f;
     say(game, "You climb over the edge");
 }
diff --git a/entropie/src/world/ladder.c b/entropie/src/world/ladder.c
index 6377464..2fb14e6 100644
--- a/entropie/src/world/ladder.c
+++ b/entropie/src/world/ladder.c
@@ -14,6 +14,7 @@
 #define CLIMB_SPEED   0.8f    /* meters along the ladder per second */
 #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 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
@@ -446,7 +447,7 @@ static float highest(const Object *l) {
 static void place(Object *who) {
     const Object *l = who->on_ladder;
     Vec3 n = l->lean_normal;
-    who->position = vec3_add(pointAt(l, who->ladder_at), vec3_scale(n, STAND_OUT));
+    who->position = vec3_add(vec3_add(pointAt(l, who->ladder_at), vec3_scale(n, STAND_OUT)), who->ladder_shift);
     who->rotation.y = atan2f(-n.z, -n.x) + PI_F / 2.0f;
     who->velocity = who->movement = vec3_zero();
     who->is_grounded = false;
@@ -454,15 +455,30 @@ static void place(Object *who) {
 }
 
 void Ladder_mount(Object *who, Object *l, bool at_top) {
+    /* hands and feet go onto the rungs from where they are now (eased, see limbTo) */
+    const Vec3 f = Object_facing(who);
+    for (int i = 0; i < 2; i++) who->ladder_from[i] = who->body.feet[i].ankle;
+    who->ladder_from[2] = Figure_toWorld(who->position, f, who->skeleton.joints[JOINT_HAND_L]);
+    who->ladder_from[3] = Figure_toWorld(who->position, f, who->skeleton.joints[JOINT_HAND_R]);
+    for (int k = 0; k < 4; k++) {
+        who->ladder_to[k] = who->ladder_from[k];
+        who->ladder_t[k] = 1.0f;
+    }
+    who->ladder_limbs_ok = true;
     who->on_ladder = l;
     who->ladder_at = at_top ? highest(l) : 0.0f;
-    who->ladder_limbs_ok = false;
     who->climbing = true;
     who->sitting = false;
+    const Vec3 was = who->position;
+    who->ladder_shift = vec3_zero();
     place(who);
+    who->ladder_shift = vec3_sub(was, who->position); /* (from where you stood onto it: eased, see Ladder_climb) */
+    who->position = was;
+    Object_syncColliders(who);
 }
 
 void Ladder_getOff(Object *who) {
+    who->ladder_shift = vec3_zero();
     who->on_ladder = NULL;
     who->climbing = false;
 }
@@ -471,6 +487,10 @@ int Ladder_climb(Object *who, float input, float dt) {
     Object *l = who->on_ladder;
     if (!l) 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);
diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h
index 2fb3d92..b6e4275 100644
--- a/entropie/src/world/object.h
+++ b/entropie/src/world/object.h
@@ -224,6 +224,7 @@ typedef struct Object {
     float      ladder_at;     /* ... with the feet this far up along it */
     Vec3       ladder_from[4], ladder_to[4]; /* ... feet and hands on their way from rung to rung */
     float      ladder_t[4];
+    Vec3       ladder_shift;  /* ... the body still this far from its place on it (just got on: eases away) */
     bool       ladder_limbs_ok;
 
     /* Characters only */