Nimbin[12]?Games / Entropie / commits / 786780b

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.38.0: a shelter in the yard

Christian Immanuel · 2026-10-27 11:29 · 786780b08d2d73df67390ce8c582d77135bd8593

- A shelter by the fire: a roof on posts, two log benches and a table
- In hard rain the guests sit it out there or in your garages' kitchen,
  whichever is nearer with a seat free
 commit_message.txt          | 10 ++++------
 entropie/Makefile           |  2 +-
 entropie/README.md          |  2 +-
 entropie/STATE.md           | 15 ++++++++++-----
 entropie/commit_message.txt | 10 ++++------
 entropie/src/people/ai.c    | 25 ++++++++++++++++++++++---
 entropie/src/people/ai.h    |  3 ++-
 entropie/src/world/manor.c  | 38 ++++++++++++++++++++++++++++++++++++++
 entropie/src/world/manor.h  |  2 ++
 entropie/src/world/site.c   | 10 ++++++++++
 entropie/src/world/site.h   |  2 ++
 11 files changed, 96 insertions(+), 23 deletions(-)

diff --git a/commit_message.txt b/commit_message.txt
index a418266..c9d5195 100644
--- a/commit_message.txt
+++ b/commit_message.txt
@@ -1,7 +1,5 @@
-entropie 2.37.0: umbrellas inside out, hats
+entropie 2.38.0: a shelter in the yard
 
-- A strong gust can blow an open umbrella inside out for a moment; the
-  third time (or in a storm) it's broken for good
-- Hats: Toni and Kurti wear caps, Jonas a beanie. The wind can take a cap
-  off; it bowls along the ground, they chase it and put it back on
-  (pulled down tighter each time)
+- A shelter by the fire: a roof on posts, two log benches and a table
+- In hard rain the guests sit it out there or in your garages' kitchen,
+  whichever is nearer with a seat free
diff --git a/entropie/Makefile b/entropie/Makefile
index 0c05bb2..15eaaa1 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 2.37.0
+VERSION   := 2.38.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/README.md b/entropie/README.md
index 3fc8f72..67c8158 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.37.0 (build 1a6ad0e)`. The build id is
+`make version` prints e.g. `entropie 2.38.0 (build ee5055b)`. The build id is
 an md5 of all sources and assets.
diff --git a/entropie/STATE.md b/entropie/STATE.md
index bffff4d..7837b1a 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -297,9 +297,10 @@ Painted in the rotoscope style of *Waking Life* (2001):
   there for me?") and they walk along side by side under it (yours too); it
   leans over the two of them
 - People get out of it: Kurti and his sons into the house, the others into
-  their tent (at night, or when it's nearer) or your garages' kitchen. Raining
-  hard, they sit it out in there (the two chairs, a log bench by the back
-  wall, else the floor); out again when it stops
+  their tent (at night, or when it's nearer), your garages' kitchen or the
+  shelter in the yard (a roof on posts by the fire, two log benches and a
+  table under it) - the nearest with a seat free. Raining hard, they sit it
+  out there (else on the floor); out again when it stops
 - Out in the rain your clothes get wet (darker; Tab: Wet) and you get cold.
   They dry by a fire quickest, in the day slower, at night slowest. Wet
   through, the others go and sit by a burning fire
@@ -1230,6 +1231,10 @@ Round 2.37 (umbrellas blown inside out, hats):
 - Hats (`FigureLook.hat` HAT_CAP / HAT_BEANIE, `hat_color`; `figure.frag` hatDistance: a cap's crown and a brim tipped down over the brow, a beanie down to the ears with a knot and ribs; under a hood: none): Toni a red cap, Kurti an old green one, Jonas a blue beanie (`Figure_wardrobe` by index % 7: 3, 4, 6). Saved with the person.
 - Blown off (`world.c` windHat): in a gust (HAT_GUST 0.4) over HAT_WIND 9 m/s, HAT_RATE 0.3/s for every 3 m/s over it x gust (a beanie a tenth), divided by 1 + 3 x `hats_lost` (pulled down tighter each time): an OBJ_HAT (`Shapes_hat`, its colour, `owner`) flies off with the wind and bowls along like paper (`windBlows`); NPCs go after it (`ai.blown`, as for paper); picked up by its owner it goes back on (`hatBackOn`). Checked: Toni at wind level 0.8 - off, chased 7-14 m, back on in 3-14 s; less often each time.
 
+Round 2.38 (a shelter in the yard):
+- `manor.c` Shelter_build: a roof on six posts (5 x 3.6 m, eaves 2.25 m, a gable 1 m up; its roof slabs solid, so `Weather_covered` holds under it), paving slabs, a log bench along each long side (3.4 m) and a table between them; in the yard at court(SHELTER_A 24.5, SHELTER_B 3.5), about 11 m from the fire towards the barn, along the garages.
+- Rain places (`Site_rainPlaces`: the garages' kitchen, the shelter): sheltering guests pick the nearest with a free seat (`ai.rain_place`, chosen once per shower); a bench is walked up to from the front where along it they are (round its end they got stuck outside a post and never sat). Checked: hard rain at midday - all three guests on one shelter bench within 20-60 s, dry, up when it stopped.
+
 ## Open tasks
 
 Most important first.
@@ -1252,7 +1257,7 @@ Most important first.
 - "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)
 - Tents, still to do: NPCs taking theirs down on their own (leaving)
-- Weather, still to do: umbrellas while working (now only idle people fetch one); more roofed places to sit out the rain together (outdoors: a shelter, a pavilion - `Site_rainRoom` is the one place now)
+- Weather, still to do: umbrellas while working (now only idle people fetch one)
 - Sound, still to do: the house's doors (they have no doors that open yet)
 
 **Saves, still to do**
@@ -1265,4 +1270,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.37.0 (build 1a6ad0e)`
+State of `entropie 2.38.0 (build ee5055b)`
diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt
index a418266..c9d5195 100644
--- a/entropie/commit_message.txt
+++ b/entropie/commit_message.txt
@@ -1,7 +1,5 @@
-entropie 2.37.0: umbrellas inside out, hats
+entropie 2.38.0: a shelter in the yard
 
-- A strong gust can blow an open umbrella inside out for a moment; the
-  third time (or in a storm) it's broken for good
-- Hats: Toni and Kurti wear caps, Jonas a beanie. The wind can take a cap
-  off; it bowls along the ground, they chase it and put it back on
-  (pulled down tighter each time)
+- A shelter by the fire: a roof on posts, two log benches and a table
+- In hard rain the guests sit it out there or in your garages' kitchen,
+  whichever is nearer with a seat free
diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c
index e7e46cb..ab6b836 100644
--- a/entropie/src/people/ai.c
+++ b/entropie/src/people/ai.c
@@ -2156,7 +2156,16 @@ static bool rainRoom(Object *self, Object *const *objects, int count, float dt)
     if (self->sitting && ai->rain_seated) { rest(ai, 3.0f, 6.0f); return true; } /* (sitting it out) */
     ai->rain_seated = false;
     if (self->sitting) self->sitting = false;
-    Vec3 room = Site_rainRoom();
+    Vec3 places[4];
+    int n = Site_rainPlaces(places, 4);
+    if (ai->rain_place <= 0 || ai->rain_place > n) { /* where to: the nearest with a seat free (else just the nearest) */
+        float best = 1e9f;
+        for (int k = 0; k < n; k++) {
+            float d = flatDistance(places[k], self->position) + (freeSeatIn(self, objects, count, places[k]) ? 0.0f : 60.0f);
+            if (d < best) { best = d; ai->rain_place = k + 1; }
+        }
+    }
+    Vec3 room = places[ai->rain_place - 1];
     Vec3 spot = vec3_add(room, vec3(0.8f * ai->side, 0.0f, 0.8f * ai->side));
     if (flatDistance(room, self->position) > 3.5f || g_world->rain < RAIN_SIT) {
         if (goToPoint(self, spot, 0.7f, objects, count, dt)) rest(ai, 3.0f, 6.0f);
@@ -2164,8 +2173,17 @@ static bool rainRoom(Object *self, Object *const *objects, int count, float dt)
     }
     Object *seat = freeSeatIn(self, objects, count, room);
     if (seat) {
-        float near = 0.6f + 0.5f * seat->size.x;
-        if (flatDistance(seat->position, self->position) > near && !goToPoint(self, seat->position, near, objects, count, dt)) return true;
+        Vec3 at = seat->position; /* (a chair: up to it; a bench: in front of it, where along it we are - not round its end, through a post) */
+        float near = 0.9f;
+        if (seat->variant == FURN_BENCH) {
+            Vec3 axis = seat->colliders[0].axes[0], front = vec3_sub(room, seat->position);
+            float half = fmaxf(0.0f, 0.5f * seat->size.x - 0.4f), along = clampf(vec3_dot(vec3_sub(self->position, seat->position), axis), -half, half);
+            front = flat(vec3_sub(front, vec3_scale(axis, vec3_dot(front, axis))));
+            front = vec3_length(front) > 1e-3f ? vec3_normalize(front) : vec3(0.0f, 0.0f, 1.0f);
+            at = vec3_add(vec3_add(seat->position, vec3_scale(axis, along)), vec3_scale(front, 0.5f));
+            near = 0.3f;
+        }
+        if (flatDistance(at, self->position) > near && !goToPoint(self, at, near, objects, count, dt)) return true;
         stand(self);
         if (Object_sit(self, seat, room)) {
             ai->rain_seated = true;
@@ -2205,6 +2223,7 @@ static bool shelter(Object *self, Object *const *objects, int count, float dt) {
     if (!g_world || g_world->rain < SHELTER_RAIN || self->indoors > 0.0f || self->in_tent || self->body.asleep || self->riding) {
         if (!g_world || g_world->rain < SHELTER_RAIN * 0.5f) {
             ai->sheltering = false;
+            ai->rain_place = 0;
             if (ai->rain_seated) { /* (over: up again) */
                 ai->rain_seated = false;
                 if (self->sitting && random01(ai) < 0.4f) Ai_say(self, random01(ai) < 0.5f ? "It's stopped raining." : "Right, the rain's over.");
diff --git a/entropie/src/people/ai.h b/entropie/src/people/ai.h
index 553e18f..b2cd12c 100644
--- a/entropie/src/people/ai.h
+++ b/entropie/src/people/ai.h
@@ -182,7 +182,8 @@ typedef struct {
     bool           asked_you;   /* asked the player for one (nothing came of it: the machine next time) */
     struct Object *take_from;   /* a bottle from this crate, or the next thing out of this car's boot: please (see car.c Car_serve) */
     bool           sheltering;  /* in out of the rain (ai.c shelter) */
-    bool           rain_seated; /* ... sat down to wait it out (in the garages' kitchen) */
+    bool           rain_seated; /* ... sat down to wait it out (the garages' kitchen, the shelter) ... */
+    int            rain_place;  /* ... which of those (Site_rainPlaces), + 1 (0: not chosen yet) */
     bool           greet;       /* just arrived: over to the fire, saying hello, sitting there till the work starts ... */
     bool           greet_stand; /* ... no room on the benches: standing with the others here instead ... */
     Vec3           greet_at;
diff --git a/entropie/src/world/manor.c b/entropie/src/world/manor.c
index 1282775..fee4c7e 100644
--- a/entropie/src/world/manor.c
+++ b/entropie/src/world/manor.c
@@ -479,6 +479,44 @@ bool Shell_build(World *world, Vec3 origin, float yaw) {
     return look && World_add(world, look);
 }
 
+/* ---------- The shelter: a roof on posts in the yard, benches under it ---------- */
+#define SHELTER_LEN   5.0f  /* m along the ridge */
+#define SHELTER_WID   3.6f  /* m across */
+#define SHELTER_EAVES 2.25f /* m: the eaves (headroom at the sides) */
+#define SHELTER_RISE  1.0f  /* m: the ridge over the eaves */
+
+bool Shelter_build(World *world, Vec3 origin, float yaw) {
+    static Plan plan;
+    Plan *p = &plan;
+    memset(p, 0, sizeof(*p));
+    p->world = world;
+    p->origin = origin;
+    p->yaw = yaw;
+    p->ok = true;
+    const Vec3 WOOD = { 0.40f, 0.29f, 0.18f }, FLOOR = { 0.52f, 0.49f, 0.44f };
+    const float hx = SHELTER_LEN * 0.5f - 0.1f, hz = SHELTER_WID * 0.5f - 0.1f, w = 0.08f;
+    for (int i = -1; i <= 1; i++) /* six posts: the corners and the middle of each long side */
+        for (int s = -1; s <= 1; s += 2)
+            box(p, vec3((float)i * hx - w, 0.0f, (float)s * hz - w), vec3((float)i * hx + w, SHELTER_EAVES, (float)s * hz + w), WOOD, SOLID);
+    for (int s = -1; s <= 1; s += 2) /* the beams along the eaves */
+        box(p, vec3(-hx - 0.15f, SHELTER_EAVES - 0.16f, (float)s * hz - w), vec3(hx + 0.15f, SHELTER_EAVES, (float)s * hz + w), WOOD, LOOK_ONLY);
+    box(p, vec3(-hx, 0.0f, -hz), vec3(hx, 0.03f, hz), FLOOR, LOOK_ONLY); /* (paving slabs under it) */
+    p->roofs[p->n_roofs++] = (ShapeRoof){ vec3(0.0f, 0.0f, 0.0f), SHELTER_LEN + 0.6f, SHELTER_WID + 0.6f, SHELTER_EAVES, SHELTER_RISE,
+                                          BUILDING_PLASTER_DARK, false, 0.0f };
+    roofSlabs(p, &p->roofs[0]);
+    if (!p->ok) return false;
+    Mesh mesh = Shapes_structure(p->slabs, p->n_slabs, p->openings, p->n_openings, p->roofs, p->n_roofs);
+    if (!mesh.vao) return false;
+    Object *look = Object_createStructure(mesh, origin, yaw, vec3(SHELTER_LEN, SHELTER_EAVES + SHELTER_RISE, SHELTER_WID));
+    if (!look || !World_add(world, look)) return false;
+    for (int s = -1; s <= 1; s += 2) { /* a log bench along each long side, a table between them */
+        Object *bench = Object_createFurniture(FURN_BENCH, toWorld(p, vec3(0.0f, 0.0f, (float)s * (hz - 0.4f))), vec3(3.4f, SEAT_HEIGHT, 0.36f), yaw);
+        if (!bench || !World_add(world, bench)) return false;
+    }
+    Object *table = Object_createFurniture(FURN_TABLE, toWorld(p, vec3(0.0f, 0.0f, 0.0f)), vec3(2.6f, 0.75f, 0.8f), yaw);
+    return table && World_add(world, table);
+}
+
 bool Manor_build(World *world, Vec3 origin, float yaw) {
     static Plan plan; /* (big: not on the stack) */
     Plan *p = &plan;
diff --git a/entropie/src/world/manor.h b/entropie/src/world/manor.h
index c609180..96ab35d 100644
--- a/entropie/src/world/manor.h
+++ b/entropie/src/world/manor.h
@@ -26,5 +26,7 @@ bool Manor_build(World *world, Vec3 origin, float yaw);
 /* Your unfinished building: a brick shell under a roof, open at the front
    (-z) to its extension (the frame where the bar gets built); +z against the barn. */
 bool Shell_build(World *world, Vec3 origin, float yaw);
+/* A shelter in the yard: a roof on six posts, two log benches and a table under it (out of the rain together). */
+bool Shelter_build(World *world, Vec3 origin, float yaw);
 
 #endif /* WORLD_MANOR_H */
diff --git a/entropie/src/world/site.c b/entropie/src/world/site.c
index f00574a..5b26bc2 100644
--- a/entropie/src/world/site.c
+++ b/entropie/src/world/site.c
@@ -314,6 +314,8 @@ static const CourtDoorBuilding COURT[] = {
 #define YARD_B   12.0f
 #define TAP_A     3.0f   /* the tap: on the right end of the main building */
 #define TAP_B    17.25f
+#define SHELTER_A 24.5f  /* the shelter: a roof on posts by the fire, towards the barn */
+#define SHELTER_B  3.5f
 #define FIRE_A   16.0f   /* the fireplace: near the main building and your garages */
 #define FIRE_B   -4.0f
 #define BENCH_RADIUS 2.8f /* log benches round it */
@@ -578,6 +580,13 @@ Vec3 Site_rainRoom(void) { /* the middle of the kitchen in your garages: where t
     return placed(vec3(23.6f, 0.0f, 1.2f), WALLED_CENTER, g_garages.origin, g_garages.yaw, g_garages.zsign);
 }
 
+int Site_rainPlaces(Vec3 *out, int max) { /* under a roof together: the kitchen, the shelter in the yard */
+    int n = 0;
+    if (n < max) out[n++] = Site_rainRoom();
+    if (n < max) out[n++] = court(SHELTER_A, SHELTER_B);
+    return n;
+}
+
 Vec3 Site_houseDoor(void) { return court(MANOR_A + 6.0f, MANOR_B - 7.6f); } /* (out in front of the main building's left door) */
 
 bool Site_inHouse(float x, float z) {
@@ -1093,6 +1102,7 @@ bool Site_build(World *world) {
     if (!placePieces(world, WALLED, COUNT(WALLED), WALLED_CENTER, walled, wyaw, zsign) ||
         !placePieces(world, FRAME, COUNT(FRAME), FRAME_CENTER, court(FRAME_A, FRAME_B), across, 1.0f) ||
         !Shell_build(world, placed(SHELL_AT, FRAME_CENTER, court(FRAME_A, FRAME_B), across, 1.0f), across) ||
+        !Shelter_build(world, court(SHELTER_A, SHELTER_B), yaw_along(mapDir(COURT_ANGLE))) ||
         !furnishGarages(world, walled, wyaw, zsign) || !hangGates(world, walled, wyaw, zsign))
         return false;
     if (!buildGarages(world) || !plantOrchard(world) || !plantMeadows(world) || !furnishYard(world)) return false;
diff --git a/entropie/src/world/site.h b/entropie/src/world/site.h
index e6f4af5..4fb3265 100644
--- a/entropie/src/world/site.h
+++ b/entropie/src/world/site.h
@@ -29,6 +29,8 @@
 Vec3  Site_drinksSpot(void);
 /* The middle of the kitchen in your garages: a roof to wait out the rain under, seats round it. */
 Vec3 Site_rainRoom(void);
+/* Every place like that (the kitchen, the shelter in the yard): their middles, how many. */
+int  Site_rainPlaces(Vec3 *out, int max);
 Vec3  Site_campSpot(int k);
 Vec3  Site_campAlong(void);
 Vec3  Site_campDoor(void);