Nimbin[12]?Games / Entropie / commits / a4c9aba

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.14.0: wind follow-ups

Christian Immanuel · 2026-10-20 15:22 · a4c9abad6f40e6b33610682740a6f68dc71b0bfd

- Trees and bushes sway with the wind
- Paper and plastic litter and pullovers lying about get blown along
- Owners run after their tent when the wind blows it about and hold it down
 commit_message.txt          | 11 ++++-------
 entropie/Makefile           |  2 +-
 entropie/README.md          |  3 ++-
 entropie/STATE.md           | 11 ++++++++---
 entropie/src/people/ai.c    | 16 ++++++++++++++++
 entropie/src/world/object.h |  1 +
 entropie/src/world/tent.c   |  2 ++
 entropie/src/world/world.c  | 41 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 75 insertions(+), 12 deletions(-)

diff --git a/commit_message.txt b/commit_message.txt
index fdf2961..3f639fc 100644
--- a/commit_message.txt
+++ b/commit_message.txt
@@ -1,8 +1,5 @@
-entropie 2.13.0: wind
+entropie 2.14.0: wind follow-ups
 
-- Wind with calm and windy spells and gusts (windier with rain and storms)
-- Rain slants and drifts with it; fires now smoke, and smoke and flames
-  go with the wind
-- Tents not pegged down (under half the pegs) blow about in strong wind
-  until they snag or someone works on them; the hint line warns you
-- Dev menu: wind (on its own, calm, a breeze, a storm)
+- Trees and bushes sway with the wind
+- Paper and plastic litter and pullovers lying about get blown along
+- Owners run after their tent when the wind blows it about and hold it down
diff --git a/entropie/Makefile b/entropie/Makefile
index 796f03f..f18e08d 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 2.13.0
+VERSION   := 2.14.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/README.md b/entropie/README.md
index f839f4c..e375a77 100644
--- a/entropie/README.md
+++ b/entropie/README.md
@@ -59,7 +59,8 @@ Painted in the rotoscope style of *Waking Life* (2001):
 - Wind: calm or windy spells, gusts on top (windier with rain). The rain
   slants, the fire's smoke and flames go with it
 - A tent not pegged down (under half its pegs) blows about in strong wind
-  until it snags on something or somebody works on it
+  until it snags on something or somebody works on it; its owner comes running
+- Trees and bushes sway; paper, plastic and a pullover lying about get blown along
 
 ## Tents
 
diff --git a/entropie/STATE.md b/entropie/STATE.md
index 3bd2853..06b09cd 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -236,6 +236,11 @@ Round 2.13:
 - Rain drops drift with it and slant (`Shapes_rain` lean: the top upwind). Smoke and sawdust slow to the wind's speed (`Chips_update` wind). Fires smoke now (`Chips_smoke`, a big soft grey puff every 0.22 s / flame size, rising 0.9, spreading 0.22 m/s). Flames sheared downwind (`drawFlames`, 0.07 x wind, at most 0.8).
 - Tents in the wind (`tent.c` blowAbout): not up and under half the pegs in, nobody working on it for BLOW_HELD 1.5 s: on its poles from 4 m/s, flat from 6.5 m/s, it slides 0.13 m/s per m/s over that (flat half), turning a little, on the ground; a ray along the wind (0.3 m up) hitting something static: snagged, it stays. `world.tent_blowing` -> the hint line: "Volkram's tent is blowing about in the wind - peg it down!" (not again for 60 s).
 
+Round 2.14:
+- Trees (standing) and bushes sway with the wind (`world.c` windSway: sheared from their foot, SWAY_TREE 0.06 / SWAY_BUSH 0.10 per m of height in the strongest wind, x a beat of their own; a little even when calm). Drawing only: their shadows and colliders stay.
+- Light things blown along (`world.c` windBlows, before the physics step): paper and plastic litter from 5 m/s, a pullover put down from 9 m/s; BLOW_SHOVE 0.9 m/s^2 per m/s over that against the ground's grip; paper spins.
+- Owners chase their tent (`ai.c` chaseTent, before the rain: idle, wander, at a table, working, fetching): the wind pushed it lately (`tent_blown` 6 s) and it's within 70 m: "Hey - my tent!", over to it and on with it (working on it holds it: it doesn't blow while worked on).
+
 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).
@@ -382,8 +387,8 @@ Most important first.
 - Smoking, still to do: paying for a pack (no money in the game yet)
 - "No shirt no service" policy (decision open)
 - Clothing, still to do: more outfits for extra people (only the performance test has any: they all wear trousers); the pullover over a railing NPCs never do on their own
-- Tents, still to do: NPCs taking theirs down on their own (leaving); owners running after theirs when it blows away
-- Weather, still to do: a sound for thunder, wind and rain (no sound in the game yet); trees and bushes swaying; loose light things (paper, a pullover) blown about
+- Tents, still to do: NPCs taking theirs down on their own (leaving)
+- Weather, still to do: a sound for thunder, wind and rain (no sound in the game yet); a hat or anything held blown out of the hands
 - Main building: "the left building's roof peak is too far left" - to be clarified
 
 **Tools for us**
@@ -393,4 +398,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.13.0 (build f0f0b53)`
+State of `entropie 2.14.0 (build 11cb1c9)`
diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c
index bda7612..d959418 100644
--- a/entropie/src/people/ai.c
+++ b/entropie/src/people/ai.c
@@ -1832,6 +1832,19 @@ static void workOnTent(Object *self, Object *tent, Object *const *objects, int c
     }
 }
 
+/* Their tent blowing away: after it (whatever they were doing), and hold it down. */
+static bool chaseTent(Object *self, Object *const *objects, int count, float dt) {
+    Ai *ai = &self->ai;
+    if (self->body.asleep || self->in_tent || self->indoors > 0.0f || self->riding || ai->unload) return false;
+    Object *tent = Tent_of(objects, count, self);
+    if (!tent || tent->tent_blown <= 0.0f || flatDistance(tent->position, self->position) > TENT_WALK) return false;
+    if (tent->tent_idle > 3.0f && ai->speech_time <= 0.0f && random01(ai) < 0.5f * dt)
+        Ai_say(self, random01(ai) < 0.5f ? "Hey - my tent!" : "No no no - come back here!");
+    if (self->sitting) self->sitting = false;
+    workOnTent(self, tent, objects, count, dt);
+    return true;
+}
+
 static bool pitchTents(Object *self, Object *const *objects, int count, float dt) {
     Ai *ai = &self->ai;
     if (ai->unload || self->body.asleep || self->in_tent) return false;
@@ -2158,6 +2171,9 @@ void Ai_update(Object *self, Object *const *objects, int count, float dt) {
     if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || ai->mode == AI_HELP) && ai_holdLadder(self, objects, count, dt)) return;
     /* Done with a tool: careful people put it back where it belongs, the others leave it lying */
     if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && ai_putAway(self, objects, count, dt)) return;
+    /* Their tent blowing away: after it */
+    if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || ai->mode == AI_TABLE || ai->mode == AI_WORK || ai->mode == AI_FETCH) &&
+        chaseTent(self, objects, count, dt)) return;
     /* Rain: inside */
     if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || ai->mode == AI_TABLE || ai->mode == AI_WORK || ai->mode == AI_FETCH) &&
         shelter(self, objects, count, dt)) return;
diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h
index 493b6d0..8cb0ab2 100644
--- a/entropie/src/world/object.h
+++ b/entropie/src/world/object.h
@@ -171,6 +171,7 @@ typedef struct Object {
     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_down;     /* ... being taken down: the stages backwards, then packed into its bag */
diff --git a/entropie/src/world/tent.c b/entropie/src/world/tent.c
index 6e13496..77132f8 100644
--- a/entropie/src/world/tent.c
+++ b/entropie/src/world/tent.c
@@ -293,6 +293,7 @@ static void blowAbout(World *world, Object *tent, float dt) {
     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;
+    tent->tent_blown = 6.0f; /* (its owner comes running) */
 }
 
 #define RAIN_NAP_END 0.15f /* rain: under this, out of the tent again */
@@ -303,6 +304,7 @@ void Tent_update(World *world, float dt) {
         Object *o = world->objects[i];
         if (o->type == OBJ_TENT) {
             o->tent_idle += dt;
+            o->tent_blown = fmaxf(0.0f, o->tent_blown - dt);
             if (o->tent_down && !o->broken && o->tent_stage == TENT_LAID && o->tent_work >= 1.0f) pack(world, o);
             else if (!o->broken) blowAbout(world, o, dt);
             continue;
diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c
index 658a95b..0d3a7bb 100644
--- a/entropie/src/world/world.c
+++ b/entropie/src/world/world.c
@@ -1253,6 +1253,25 @@ static void emptyTray(World *world, Object *o) {
     if (!Hands_pickUpObject(o, hand, butts)) Hands_receive(o, hand, butts); /* (the tin a bit far: tipped into the hand all the same) */
 }
 
+/* Light things lying about (paper and plastic litter, a pullover put down)
+   blown along the ground by a strong wind, tumbling as they go. */
+#define BLOW_PAPER   5.0f /* m/s: paper and plastic go from this wind ... */
+#define BLOW_CLOTH   9.0f /* ... a pullover from this */
+#define BLOW_SHOVE   0.9f /* m/s^2 for each m/s over it (against the ground's grip) */
+
+static void windBlows(World *world, Object *o, float dt) {
+    if (o->is_static || o->is_held || o->broken || !o->is_grounded || o->lifted) return;
+    bool paper = o->type == OBJ_TRASH && o->size.x > 0.0f && ((o->variant & 3) == PALETTE_WARM || (o->variant & 3) == PALETTE_STANDARD);
+    bool cloth = o->type == OBJ_CLOTHES;
+    if (!paper && !cloth) return;
+    float speed = hypotf(world->wind.x, world->wind.z), from = paper ? BLOW_PAPER : BLOW_CLOTH;
+    if (speed <= from) return;
+    float shove = (speed - from) * BLOW_SHOVE * (cloth ? 0.6f : 1.0f) * dt / speed;
+    o->velocity.x += world->wind.x * shove;
+    o->velocity.z += world->wind.z * shove;
+    if (paper) o->angular_velocity.y += (sinf(o->position.x * 3.1f) > 0.0f ? 1.0f : -1.0f) * 2.0f * shove * speed;
+}
+
 void World_update(World *world, float dt, float game_dt) {
     Object_indexKinds(world->objects, world->count);
     world->dt_props = dt;
@@ -1307,6 +1326,7 @@ void World_update(World *world, float dt, float game_dt) {
     Physics_useGrid(&world->grid);
     for (int i = 0; i < world->count; i++) {
         Object *o = world->objects[i];
+        windBlows(world, o, dt);
         Physics_step(o, world->objects, world->count, &world->terrain, dt);
         landThrown(o);
     }
@@ -1478,6 +1498,25 @@ static void drawMark(Renderer *renderer, const Mesh *block, const Object *o) {
     }
 }
 
+/* Trees and bushes bending with the wind (a lean growing with the height,
+   swaying with the gusts; each its own beat). Their shadows stay put. */
+#define SWAY_TREE 0.06f  /* lean per m of height in the strongest wind */
+#define SWAY_BUSH 0.10f
+
+static Mat4 windSway(const World *world, const Object *o, float t) {
+    float k = o->type == OBJ_BUSH ? SWAY_BUSH : SWAY_TREE;
+    float speed = hypotf(world->wind.x, world->wind.z), level = fminf(speed / WIND_MAX, 1.4f);
+    float phase = o->position.x * 0.37f + o->position.z * 0.61f;
+    float beat = 0.65f + 0.35f * sinf(t * (o->type == OBJ_BUSH ? 2.6f : 1.4f) + phase);
+    Vec3 dir = speed > 0.01f ? vec3_scale(world->wind, 1.0f / speed) : vec3(1.0f, 0.0f, 0.0f);
+    float lean = k * (level * beat + 0.04f * sinf(t * 0.8f + phase)); /* (a little even when it's calm) */
+    Mat4 shear = mat4_identity();
+    shear.m[4] = dir.x * lean;
+    shear.m[6] = dir.z * lean;
+    Vec3 base = o->position;
+    return mat4_mul(mat4_translate(base), mat4_mul(shear, mat4_translate(vec3_scale(base, -1.0f))));
+}
+
 void World_draw(World *world, Renderer *renderer, bool draw_player) {
     Terrain_syncTexture(&world->terrain);
     {
@@ -1523,6 +1562,8 @@ void World_draw(World *world, Renderer *renderer, bool draw_player) {
             Vec3 tint = vec3_lerp(vec3_lerp(vec3(1.0f, 1.0f, 1.0f), vec3(0.9f, 0.78f, 0.62f), obj->type == OBJ_SAUSAGE ? 0.0f : brown),
                                   vec3(0.2f, 0.17f, 0.15f), black);
             Renderer_drawMeshTinted(renderer, &obj->mesh, Object_modelMatrix(obj), tint, 0.0f);
+        } else if ((obj->type == OBJ_TREE && !obj->toppled) || obj->type == OBJ_BUSH) {
+            Renderer_drawMesh(renderer, &obj->mesh, mat4_mul(windSway(world, obj, renderer->boil_time), Object_modelMatrix(obj)));
         } else {
             Renderer_drawMesh(renderer, &obj->mesh, Object_modelMatrix(obj));
             if (obj->type == OBJ_FIREPLACE && obj->grill_mesh.vao) { /* the grate swings a little on its chain */