Nimbin[12]?Games / Entropie / commits / 9b03502

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.30.0: waving, creaking gates

Christian Immanuel · 2026-10-26 15:22 · 9b035025fb205b13798ee1d8a0b854ae9dfa0477

- Talking, people wave hello and goodnight, shrug saying no, point over
  there
- Sounds: gates creaking, car doors and the boot, pushing through a bush
 commit_message.txt                  | 17 ++++-------------
 entropie/Makefile                   |  2 +-
 entropie/README.md                  |  2 +-
 entropie/STATE.md                   | 13 ++++++++++---
 entropie/commit_message.txt         |  8 ++++----
 entropie/src/game/sound.c           | 15 +++++++++++++++
 entropie/src/people/ai.c            | 10 ++++++++++
 entropie/src/people/body.c          | 13 ++++++++++++-
 entropie/src/people/body.h          |  3 +++
 entropie/src/people/body_internal.h |  3 +++
 entropie/src/people/body_pose.c     | 14 ++++++++++++++
 entropie/src/world/forestry.c       |  1 +
 entropie/src/world/object.h         |  6 ++++--
 entropie/src/world/world.c          |  7 +++++++
 entropie/src/world/world.h          |  3 +++
 15 files changed, 92 insertions(+), 25 deletions(-)

diff --git a/commit_message.txt b/commit_message.txt
index ff4e5ee..caeb652 100644
--- a/commit_message.txt
+++ b/commit_message.txt
@@ -1,14 +1,5 @@
-entropie 2.28.0: sound, sorted crates, hoods and umbrellas
+entropie 2.30.0: waving, creaking gates
 
-- Sound: steps on every ground, wind, rain, thunder, fire, axe, chainsaw,
-  the car, bottles, glass, falls, a tree coming down, birds and crickets -
-  all made up on the fly (M: off / on)
-- Arrival: everybody gets a seat by the fire (people move up on the bench;
-  no room: they stand with the others)
-- Crates know each bottle: put a bottle down next to a crate and it goes in;
-  right click at a crate takes out one that does not belong; the tidy sort
-  the crates
-- Rain and smoke show against the sky; the sky clouds over with the weather
-- Rain soaks the ground wherever people walk: mud on trodden paths
-- Hoods go up in the rain; two umbrellas to fetch and carry
-- The very sporty roll off drops of 5 m and more
+- Talking, people wave hello and goodnight, shrug saying no, point over
+  there
+- Sounds: gates creaking, car doors and the boot, pushing through a bush
diff --git a/entropie/Makefile b/entropie/Makefile
index 061dc47..2e4aeaa 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 2.29.0
+VERSION   := 2.30.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/README.md b/entropie/README.md
index bf3dbde..0a802bc 100644
--- a/entropie/README.md
+++ b/entropie/README.md
@@ -124,5 +124,5 @@ Dependencies point downward: `main → game → world/people → render → core
 
 ## Version
 
-`make version` prints e.g. `entropie 2.29.0 (build 58db6f4)`. The build id is
+`make version` prints e.g. `entropie 2.30.0 (build 1fccf58)`. The build id is
 an md5 of all sources and assets.
diff --git a/entropie/STATE.md b/entropie/STATE.md
index 540d40f..84d13ed 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -617,6 +617,8 @@ and satellite pictures (about 340 x 340 m).
   the unfit bent over with the hands on the knees, the sporty with the hands
   on top of the head or a light stretch, anyone hands on the hips. Breathing
   is calm and slow, and quicker the more out of breath
+- Talking, a hand goes with it now and then: waving hello or goodnight, a
+  shrug saying no, pointing over there
 - One hand now and then: on the hip, scratching the head, at the chin, in the
   pocket, rubbing the neck or the nose, at the ear, back through the hair,
   thumb in the belt, on the chest, shading the eyes. How someone feels
@@ -932,7 +934,8 @@ say no more often and don't come to you for a bit.
   bottles clinking, glass breaking, somebody hitting the ground, a tree
   coming down, twigs snapping, the hose, birds by day, crickets at night;
   people talking (a murmur, deeper or higher by who it is, the words are
-  on screen), Alice barking and crunching, sips and bites, the toilet
+  on screen), Alice barking and crunching, sips and bites, the toilet, the
+  gates creaking, car doors and the boot, pushing through a bush
 - Further away is quieter and duller, and comes from its side
 - M: sound off / on; the menus pause it
 
@@ -1173,6 +1176,10 @@ Round 2.29:
 - Smoke soft and see-through (`Shapes_puff`: a ball; `Renderer_drawPuff`: blended, no depth, no outline; `scene.frag` `puff`: thickest facing you, fading at the rim, SMOKE_ALPHA 0.5, thinning out at the end). Checked: the fire's smoke against the sky and the house.
 - More sounds: voices (`Ai_say` -> NOISE_VOICE: a murmur of saw syllables, deep / man / woman, by the length of the line; nothing for "*...*"), Alice (Woof -> NOISE_BARK, crunch), a sip or a bite (`world.c`: the moment `drink_sipped` turns on -> NOISE_GULP), the toilet (NOISE_FLUSH). Checked: compiles, the game's sound stays clean (no NaN, no clipping); not listened to.
 
+Round 2.30:
+- Talking gestures (`Body_talkGesture`: GESTURE_WAVE, SHRUG, POINT; picked in `Ai_say` by words in the line - Hello / Night / Bye, No / can't / Sorry, over there / Look - 80 % of the time; they go on walking up to 2.5 m/s). Checked: Toni waving and shrugging.
+- Sounds: a bush pushed into (NOISE_RUSTLE, `forestry.c`), a gate opening or shutting (NOISE_CREAK) and the boot (NOISE_SLAM) when that changes (`world.c` showProps, `heard_open`), getting out of the car (NOISE_SLAM, `heard_ride`).
+
 ## Open tasks
 
 Most important first.
@@ -1196,7 +1203,7 @@ Most important first.
 - Clothing, still to do: more outfits for extra people (only the performance test has any: they all wear trousers)
 - Tents, still to do: NPCs taking theirs down on their own (leaving)
 - Weather, still to do: hats (nobody wears one yet) blown off; umbrellas while working (now only idle people fetch one); an umbrella blown inside out
-- Sound, still to do: doors and gates, the car's doors and boot, rustling bushes walking through
+- Sound, still to do: the house's doors (they have no doors that open yet), Alice panting
 
 **Saves, still to do**
 - Things being done come back fresh: a person's part of a job half way (its progress on the thing is kept, and their order), carrying something together
@@ -1208,4 +1215,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.29.0 (build 58db6f4)`
+State of `entropie 2.30.0 (build 1fccf58)`
diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt
index bf15895..caeb652 100644
--- a/entropie/commit_message.txt
+++ b/entropie/commit_message.txt
@@ -1,5 +1,5 @@
-entropie 2.29.0: soft smoke, voices
+entropie 2.30.0: waving, creaking gates
 
-- Smoke is soft and see-through, fading at its edges
-- Sounds: people talking (a murmur), Alice barking and crunching, sips
-  and bites, the toilet
+- Talking, people wave hello and goodnight, shrug saying no, point over
+  there
+- Sounds: gates creaking, car doors and the boot, pushing through a bush
diff --git a/entropie/src/game/sound.c b/entropie/src/game/sound.c
index 36f00b2..da17583 100644
--- a/entropie/src/game/sound.c
+++ b/entropie/src/game/sound.c
@@ -126,6 +126,21 @@ static void noiseOut(const Noise *n, const Ear *ear) {
         play(WAVE_NOISE, 0.0f, 0.0f, 500.0f * dull, 60.0f, 0.4f, 1.4f, 3.5f, 0.5f * g, pan, 0.3f);
         play(WAVE_CRACKLE, 60.0f, 0.0f, 1200.0f * dull, 100.0f, 0.5f, 1.2f, 3.0f, 0.3f * g, pan, 0.6f);
         break;
+    case NOISE_RUSTLE: /* leaves and twigs */
+        play(WAVE_NOISE, 0.0f, 0.0f, 5000.0f * dull, 900.0f, 0.03f, 0.25f, 0.7f, 0.6f * g, pan, 0.0f);
+        play(WAVE_CRACKLE, 500.0f, 0.0f, 6000.0f * dull, 1200.0f, 0.02f, 0.2f, 0.6f, 0.5f * g, pan, 0.05f);
+        break;
+    case NOISE_CREAK: { /* old hinges: a wavering squeak, then the leaf against the stop */
+        float f = 500.0f + 300.0f * dice();
+        play(WAVE_SAW, f, f * (0.7f + 0.2f * dice()), 2500.0f * dull, 300.0f, 0.08f, 0.25f, 0.7f, 0.12f * g, pan, 0.0f);
+        play(WAVE_SINE, 120.0f, 80.0f, 0.0f, 0.0f, 0.002f, 0.08f, 0.25f, 0.4f * g, pan, 0.8f);
+        break;
+    }
+    case NOISE_SLAM: /* a car door or the boot: a solid thunk */
+        play(WAVE_SINE, 90.0f, 55.0f, 0.0f, 0.0f, 0.002f, 0.09f, 0.3f, 0.8f * g, pan, 0.0f);
+        play(WAVE_NOISE, 0.0f, 0.0f, 1200.0f * dull, 80.0f, 0.002f, 0.05f, 0.2f, 0.5f * g, pan, 0.0f);
+        play(WAVE_CRACKLE, 300.0f, 0.0f, 4000.0f * dull, 1500.0f, 0.001f, 0.03f, 0.08f, 0.3f * g, pan, 0.0f); /* (the latch) */
+        break;
     case NOISE_SNAP: /* a dry branch underfoot */
         play(WAVE_CRACKLE, 1500.0f, 0.0f, 7000.0f * dull, 900.0f, 0.001f, 0.03f, 0.08f, 0.8f * g, pan, 0.0f);
         play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * dull, 400.0f, 0.001f, 0.02f, 0.06f, 0.5f * g, pan, 0.0f);
diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c
index 623edb0..1fa504f 100644
--- a/entropie/src/people/ai.c
+++ b/entropie/src/people/ai.c
@@ -1276,6 +1276,16 @@ void Ai_say(Object *self, const char *text) {
         return;
     }
     if (text[0] == '*') return; /* (a sound written out, not words) */
+    { /* a hand goes with it: waving hello / goodbye, a shrug saying no, pointing over there */
+        static const char *const WAVE[]  = { "Hello", "hello", "Hi ", "Hey ", "Night", "Bye", "Morning" };
+        static const char *const SHRUG[] = { "No ", "no idea", "Not ", "can't", "Can't", "don't", "Never mind", "forget it", "Nope", "Sorry" };
+        static const char *const POINT[] = { "over there", "Over there", "Look", "That one", "this way", "This way" };
+        int kind = -1;
+        for (unsigned k = 0; k < sizeof WAVE / sizeof WAVE[0] && kind < 0; k++) if (strstr(text, WAVE[k])) kind = TALK_WAVE;
+        for (unsigned k = 0; k < sizeof SHRUG / sizeof SHRUG[0] && kind < 0; k++) if (strstr(text, SHRUG[k])) kind = TALK_SHRUG;
+        for (unsigned k = 0; k < sizeof POINT / sizeof POINT[0] && kind < 0; k++) if (strstr(text, POINT[k])) kind = TALK_POINT;
+        if (kind >= 0 && random01(ai) < 0.8f) Body_talkGesture(&self->body, kind);
+    }
     int syllables = (int)strlen(text) / 5;
     syllables = syllables < 2 ? 2 : syllables > 10 ? 10 : syllables;
     int voice = self->female ? VOICE_WOMAN : self->look.height > 1.82f || self->weight > 85.0f ? VOICE_DEEP : VOICE_MAN;
diff --git a/entropie/src/people/body.c b/entropie/src/people/body.c
index 7d97476..d778147 100644
--- a/entropie/src/people/body.c
+++ b/entropie/src/people/body.c
@@ -721,7 +721,8 @@ static void updateExpression(Body *b, const BodyFrame *f, float speed) {
     /* Now and then one hand does something: on the hip, scratching the head,
        at the chin, in the pocket. Moving or busy hands end it. */
     b->gesture_time = fmaxf(0.0f, b->gesture_time - dt);
-    if (speed > 0.3f || b->pose != POSE_UP || b->hand_active[b->gesture_side]) { /* fades out from where it is */
+    bool talking = b->gesture >= GESTURE_WAVE; /* (said in passing: walking on, it goes on) */
+    if (speed > (talking ? 2.5f : 0.3f) || b->pose != POSE_UP || b->hand_active[b->gesture_side]) { /* fades out from where it is */
         if ((b->gesture_fade -= dt / GESTURE_FADE) <= 0.0f) b->gesture_time = 0.0f;
     }
     if ((b->gesture_timer -= dt) <= 0.0f && b->gesture_time <= 0.0f) {
@@ -824,6 +825,16 @@ static void updateExpression(Body *b, const BodyFrame *f, float speed) {
     }
 }
 
+void Body_talkGesture(Body *b, int kind) {
+    int side = !b->hand_active[1] ? 1 : !b->hand_active[0] ? 0 : -1;
+    if (side < 0 || b->pose != POSE_UP) return;
+    b->gesture = kind == TALK_WAVE ? GESTURE_WAVE : kind == TALK_SHRUG ? GESTURE_SHRUG : GESTURE_POINT;
+    b->gesture_side = side;
+    b->gesture_len = b->gesture_time = kind == TALK_WAVE ? 1.8f : 1.4f;
+    b->gesture_fade = 1.0f;
+    b->gesture_timer = b->gesture_len + 4.0f;
+}
+
 void Body_think(Body *b, float seconds) {
     int side = !b->hand_active[1] ? 1 : !b->hand_active[0] ? 0 : -1;
     if (side < 0 || b->pose != POSE_UP || seconds < 0.5f) return;
diff --git a/entropie/src/people/body.h b/entropie/src/people/body.h
index 3e7ca6e..ea9995a 100644
--- a/entropie/src/people/body.h
+++ b/entropie/src/people/body.h
@@ -332,6 +332,9 @@ void  Body_vomit(Body *body);
 bool  Body_takeVomit(Body *body);
 /* Thinking it over for a moment: a hand to the chin or scratching the head (if one is free). */
 void  Body_think(Body *body, float seconds);
+/* Something said: a hand goes with it (a free hand, standing): waving, a shrug, pointing. */
+enum { TALK_WAVE, TALK_SHRUG, TALK_POINT };
+void  Body_talkGesture(Body *body, int kind);
 /* Stands still for a pee (this many seconds); Body_takePee reports when it's done. */
 void  Body_pee(Body *body, float seconds);
 bool  Body_takePee(Body *body);
diff --git a/entropie/src/people/body_internal.h b/entropie/src/people/body_internal.h
index fc3668f..f401252 100644
--- a/entropie/src/people/body_internal.h
+++ b/entropie/src/people/body_internal.h
@@ -117,6 +117,9 @@ enum { GESTURE_NONE, GESTURE_HIP, GESTURE_HEAD, GESTURE_CHIN, GESTURE_POCKET,
        GESTURE_THUMB,  /* thumb hooked in the belt (proud, at ease) */
        GESTURE_CHEST,  /* hand on the chest (shy) */
        GESTURE_SHADE,  /* shading the eyes, looking out (lively) */
+       GESTURE_WAVE,   /* talking: waving hello or goodbye (Body_talkGesture) */
+       GESTURE_SHRUG,  /* ... a palm up and out: "no idea", "never mind" */
+       GESTURE_POINT,  /* ... pointing ahead: "over there" */
        GESTURE_COUNT };
 #define GESTURE_FADE      0.4f
 
diff --git a/entropie/src/people/body_pose.c b/entropie/src/people/body_pose.c
index da0d17d..834958a 100644
--- a/entropie/src/people/body_pose.c
+++ b/entropie/src/people/body_pose.c
@@ -312,6 +312,20 @@ static void gesturePalm(const Body *b, const Vec3 *j, const Skeleton *sk, float
         *palm = vec3_add(head, vec3_add(vec3_scale(fwd, 0.15f), vec3_add(vec3_scale(up, 0.12f), vec3_scale(side, sign * 0.06f))));
         *pole = vec3(sign, 0.5f, -0.2f); /* (the elbow up and out to the side: the face stays free) */
         break;
+    case GESTURE_WAVE: { /* the hand up beside the head, waving side to side */
+        float wave = 0.07f * sinf(b->clock * 10.0f);
+        *palm = vec3_add(head, vec3_add(vec3_scale(fwd, 0.06f), vec3_add(vec3_scale(up, 0.10f), vec3_scale(side, sign * (0.30f + wave)))));
+        *pole = vec3(sign, -0.8f, -0.2f);
+        break;
+    }
+    case GESTURE_SHRUG:  /* the forearm out to the side, the palm up */
+        *palm = vec3_add(j[JOINT_CHEST], vec3(sign * 0.34f, -0.20f, 0.20f));
+        *pole = vec3(sign * 0.3f, -1.0f, -0.4f);
+        break;
+    case GESTURE_POINT:  /* the arm out ahead, pointing */
+        *palm = vec3_add(j[JOINT_CHEST], vec3(sign * 0.12f, 0.12f, 0.58f));
+        *pole = vec3(sign, -1.0f, 0.0f);
+        break;
     default:             /* hand in the trouser pocket */
         *palm = vec3_add(j[JOINT_PELVIS], vec3(sign * 0.15f, -0.13f, 0.05f));
         *pole = vec3(sign * 0.6f, -0.2f, -1.0f);
diff --git a/entropie/src/world/forestry.c b/entropie/src/world/forestry.c
index 2014506..4e56df8 100644
--- a/entropie/src/world/forestry.c
+++ b/entropie/src/world/forestry.c
@@ -120,6 +120,7 @@ int Forestry_intoBush(World *world, Object *who, Object *bush) {
     to.y = 0.0f;
     Vec3 dir = vec3_length(to) > 1e-3f ? vec3_normalize(to) : Object_facing(who);
     Body_trip(&who->body, 0, dir, 0.85f); /* (badly: a fall) */
+    World_noise(world, NOISE_RUSTLE, bush->position, 1.0f);
     if (bush->stored >= BUSH_TWIGS) return 0; /* (nothing much left to break) */
     int n = 1 + (int)(dice(who->position, bush->stored) * 2.0f), made = 0;
     for (int k = 0; k < n && bush->stored < BUSH_TWIGS; k++) {
diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h
index 5181084..4f7ba45 100644
--- a/entropie/src/world/object.h
+++ b/entropie/src/world/object.h
@@ -277,8 +277,10 @@ typedef struct Object {
     float      sit_yaw;       /* ... (as rotation.y) */
     bool       sit_moving;
     bool       sit_backing;   /* ... turned round, stepping back to the seat */
-    bool       sit_scoot;
-    int        heard_pose;    /* characters: the body's pose last frame (going down: a thud) */     /* ... sitting, sliding along the bench to sit_at (moving up for somebody) */
+    bool       sit_scoot;     /* ... sitting, sliding along the bench to sit_at (moving up for somebody) */
+    int        heard_pose;    /* characters: the body's pose last frame (going down: a thud) */
+    bool       heard_open;    /* gates, the car's boot: open last frame (opening or shutting: a sound) ... */
+    bool       heard_ride;    /* ... characters: riding in the car last frame (getting out: its door) */
     float      rest_time;     /* seconds lying still (physics looks less often) */
     bool       settled;       /* ... and turned to lie on the ground under it (see physics: settleOnGround) */
     bool       climbing;      /* characters: pulling themselves up a ledge (no physics meanwhile) */
diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c
index 0e7ef2c..0736334 100644
--- a/entropie/src/world/world.c
+++ b/entropie/src/world/world.c
@@ -868,6 +868,11 @@ static float scootTo(const World *world, const Object *chair) {
 static void showProps(World *world) {
     for (int i = 0; i < world->count; i++) {
         Object *f = world->objects[i];
+        if ((f->type == OBJ_FURNITURE && f->variant == FURN_GATE) || f->type == OBJ_CAR) { /* (opening or shutting: heard) */
+            bool open = f->type == OBJ_CAR ? f->boot_open : f->gate_open;
+            if (open != f->heard_open) World_noise(world, f->type == OBJ_CAR ? NOISE_SLAM : NOISE_CREAK, f->position, 0.8f);
+            f->heard_open = open;
+        }
         if (f->type == OBJ_UMBRELLA) { /* (folded once it's put down) */
             if (!f->is_held) f->umbrella_open = false;
             Object_showUmbrella(f);
@@ -947,6 +952,8 @@ static void animateCharacter(World *world, Object *o, float dt) {
     scrambleHand(world, o);
     if (o->body.pose == POSE_DOWN && o->heard_pose == POSE_FALLING) World_noise(world, NOISE_THUD, o->position, clampf(o->weight / 80.0f, 0.4f, 1.0f)); /* (down on the ground) */
     o->heard_pose = o->body.pose;
+    if (o->heard_ride && !o->riding) World_noise(world, NOISE_SLAM, o->position, 0.7f); /* (out of the car: its door) */
+    o->heard_ride = o->riding != NULL;
     if (o->climbing || o->on_ladder || o->is_held) o->air_fall = 0.0f;
     else if (!o->is_grounded) o->air_fall = fmaxf(o->air_fall, -o->velocity.y);
     else if (o->air_fall > 0.0f) landFromHigh(world, o);
diff --git a/entropie/src/world/world.h b/entropie/src/world/world.h
index 3efb320..d4cd5ae 100644
--- a/entropie/src/world/world.h
+++ b/entropie/src/world/world.h
@@ -27,6 +27,9 @@ typedef enum {
     NOISE_BARK,   /* Alice (surface: how many woofs) */
     NOISE_GULP,   /* a sip (surface 0), a bite (1) */
     NOISE_FLUSH,  /* the toilet */
+    NOISE_RUSTLE, /* into a bush: leaves and twigs */
+    NOISE_CREAK,  /* a gate opening or shutting */
+    NOISE_SLAM,   /* a car door, the boot */
     NOISE_COUNT
 } NoiseKind;
 enum { STEP_GRASS, STEP_GRAVEL, STEP_HARD, STEP_EARTH, STEP_MUD, STEP_WOOD, STEP_WATER };