Entropie git · main
Entropie festival game; first steps
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.gzentropie 1.97.0: jackets tied round the waist, back onto the rack
- A jacket taken off (by the fire, in the morning) is tied round the waist by its sleeves: a band over the hips, the knot in front, the rest hanging down the back - Warm again, people take it back to the coat rack sooner or later (the tidy sooner, the drunk later)
entropie/Makefile | 2 +- entropie/README.md | 3 ++- entropie/STATE.md | 8 ++++++-- entropie/assets/shaders/figure.frag | 17 ++++++++++++++++- entropie/commit_message.txt | 14 ++++++-------- entropie/src/people/ai.c | 37 +++++++++++++++++++++++++++++++++++++ entropie/src/people/ai.h | 1 + entropie/src/render/figure.h | 1 + entropie/src/render/renderer.c | 2 ++ entropie/src/render/renderer.h | 2 +- entropie/src/world/world.c | 1 + 11 files changed, 74 insertions(+), 14 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index c46d9a3..549e3b1 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 1.96.0 +VERSION := 1.97.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index e30e9e0..15dad1c 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -500,7 +500,8 @@ say no more often and don't come to you for a bit. - Working hard in the midday sun the shirt comes off (women keep a top on; the shy keep it all on), yours too; it goes back on when cooled down - Cold nights: away from the fire a jacket goes on (each their own colour), - by the fire or in the day it comes off (and stays with them) + by the fire or in the day it comes off and gets tied round the waist; + warm again, they take it back to the rack sooner or later (the tidy sooner) - Everybody's jacket starts on the coat rack in your kitchen: getting cold, they walk over and put it on. Yours too: click the rack (on / back on it) - Ask them what they're doing, send them back to helping you, or give them a break diff --git a/entropie/STATE.md b/entropie/STATE.md index 9f96501..a194c05 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -134,6 +134,10 @@ Round 1.96: - Steering (`ai.c` ai_blockedBy): a building's walls kept WALL_RADIUS 0.24 m off (BODY_RADIUS 0.35 left no way through a 0.9 m doorway); a building part over the head (bottom 0.3 m+ over the feet) more than 0.6 m off doesn't block (a floor over a stair going down). Wandering counts progress along the way round (`stillToGo`), not straight to the goal. - Sitting at a table (`body_pose.c` keepBodyOut): the legs aren't lifted onto anything higher than the hips (they lay on the table top). +Round 1.97: +- Jacket off and with them (`jacket_hung` false, top not TOP_JACKET): `FigureLook.jacket_tied` (set in `world.c` dressForNight) -> `jacketTied` in figure.frag: a band over the hips, a knot in front with the two sleeve ends hanging (over a skirt's top), the rest hanging flat down the back. MAX_PARTS 31 -> 35 (and addPart stops at it). +- Back on the rack (`ai.c` hangJacket): idle or wandering, warm (chill under 0.05), the rack within RACK_WALK 60 m: RACK_CHANCE 0.004/s x tidiness (0.5 + liking for clearing up - 0.4 drunk); there (`goTo` 0.6 m) a hand to it, `jacket_hung`. Gives up after JACKET_GIVE_UP or when getting cold again. + 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); the coat rack (FURN_COATRACK, in your kitchen by the doors, garage coords 20.6 / 2.4) shows the jackets hanging on it (`Shapes_coatRack`, up to 6, a bitmask of whose in `shown`). `jacket_hung` (characters, true at the start): cold (chill 0.6+) and the jacket with them: on it goes (as before); hanging: NPCs `ai.want_jacket` -> `ai.c` fetchJacket (idle / wander: goTo the rack, on with it, "Ah, better."; gives up after 90 s or warm again); you are told once ("your jacket hangs on the rack in your garage") and click the rack (`game_actions.c` coatRack: on, or back onto it). Warm again: it comes off and stays with them. @@ -277,7 +281,7 @@ Most important first. - Ladders, still to do: a holder for a ladder standing free; NPCs taking a ladder somewhere for their own work - Smoking, still to do: buying more (a kiosk) - "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); hanging the jacket back on the rack +- Clothing, still to do: more outfits for extra people (only the performance test has any: they all wear trousers) **Tools for us** - Optional local AI (llama.cpp, small model) for talking and tasks; the menu stays as fallback @@ -286,4 +290,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 1.96.0 (build d8ab965)` +State of `entropie 1.97.0 (build 51512aa)` diff --git a/entropie/assets/shaders/figure.frag b/entropie/assets/shaders/figure.frag index 57f49e4..b33aee3 100644 --- a/entropie/assets/shaders/figure.frag +++ b/entropie/assets/shaders/figure.frag @@ -33,6 +33,7 @@ uniform vec3 skinColor; uniform vec3 hairColor; uniform vec3 shirtColor; uniform vec3 jacketColor; // (top == 3: a jacket over it) +uniform bool jacketTied; // the jacket off, tied round the waist by its sleeves uniform vec3 pantsColor; uniform vec3 shoeColor; uniform vec3 stream[4]; // pee or sick: an arc from the body to the ground (figure space) @@ -69,7 +70,7 @@ const float SELF_SHADOW_SKIP = 0.10; // meters // ---------- Body parts ---------- -const int MAX_PARTS = 31; +const int MAX_PARTS = 35; // (a person with everything: a skirt, a jacket tied round, a cigarette, a Urinella, a stream) vec3 partA[MAX_PARTS], partB[MAX_PARTS]; float partR1[MAX_PARTS], partR2[MAX_PARTS]; float partBlend[MAX_PARTS]; // smooth-union radius with the rest of the body @@ -86,6 +87,7 @@ int candidates = 0; // bit mask of parts near the current ray bool hairCandidate = false; void addPart(vec3 a, vec3 b, float r1, float r2, float blend, float depth, int mat) { + if (partCount >= MAX_PARTS) return; partA[partCount] = a; partB[partCount] = b; partR1[partCount] = r1; partR2[partCount] = r2; partBlend[partCount] = blend; partDepth[partCount] = depth; @@ -134,6 +136,19 @@ void buildBody() { addPart(waist, chest + spineDir * 0.03 - chestForward * 0.005, 0.135 + puff, 0.160 + puff, 0.04, 0.66, jacket ? M_JACKET : top == 1 ? M_SKIN : M_SHIRT); addPart(joints[J_SHOULDER_L], joints[J_SHOULDER_R], 0.050 + puff, 0.050 + puff, 0.05, 0.75, sleeve); + // A jacket tied round the waist: a band over the hips, the sleeves knotted in front, the rest hanging down behind + if (jacketTied) { + vec3 belt = pelvis + spineDir * 0.07; + addPart(belt - spineDir * 0.025, belt + spineDir * 0.025, 0.142, 0.142, 0.02, 0.72, M_JACKET); + vec3 knot = belt + chestForward * (garment >= 2 ? 0.168 : 0.125); // (over a skirt's top) + addPart(knot, knot, 0.026, 0.026, 0.01, 1.0, M_JACKET); + vec3 across = normalize(joints[J_HIP_R] - joints[J_HIP_L]); + addPart(knot, knot - spineDir * 0.17 + chestForward * 0.02 - across * 0.03, 0.022, 0.018, 0.01, 1.0, M_JACKET); // (the sleeves' ends) + addPart(knot, knot - spineDir * 0.14 + chestForward * 0.02 + across * 0.03, 0.022, 0.018, 0.01, 1.0, M_JACKET); + vec3 back = belt - chestForward * 0.085; + addPart(back, back - spineDir * 0.27 - chestForward * 0.02, 0.125, 0.135, 0.015, 0.25, M_JACKET); + } + // What's on the legs: trousers, shorts, a skirt or a dress (squatting to pee: // trousers pulled down to the knees, a skirt or dress gathered up) bool down = undress > 0.5, lifted = undress < -0.5; diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index 75f0ca3..5433932 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,9 +1,7 @@ -entropie 1.96.0: people find the basement, legs under the table +entropie 1.97.0: jackets tied round the waist, back onto the rack -- The walking map has two levels: people find their way down the stairs to - the basement and back up -- Narrow doorways and the landing at the top of the basement stairs no - longer stop people; the floor over the stairs doesn't count as a wall - on the way down -- Wandering somewhere the long way round no longer gives up halfway -- Sitting at a table, people's legs go under it (they lay on the table top) +- A jacket taken off (by the fire, in the morning) is tied round the waist + by its sleeves: a band over the hips, the knot in front, the rest hanging + down the back +- Warm again, people take it back to the coat rack sooner or later (the + tidy sooner, the drunk later) diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c index fc34ef8..a365285 100644 --- a/entropie/src/people/ai.c +++ b/entropie/src/people/ai.c @@ -1577,6 +1577,41 @@ static bool fetchJacket(Object *self, Object *const *objects, int count, float d return true; } +#define RACK_WALK 60.0f /* m: the rack this near ... */ +#define RACK_CHANCE 0.004f /* ... the jacket back onto it this often a second (the tidy more, the lazy less) */ + +static Object *coatRack(Object *const *objects, int count) { + for (int i = 0; i < count; i++) + if (objects[i]->type == OBJ_FURNITURE && objects[i]->variant == FURN_COATRACK) return objects[i]; + return NULL; +} + +/* Warm again, the jacket tied round the waist: now and then back onto the rack with it. */ +static bool hangJacket(Object *self, Object *const *objects, int count, float dt) { + Ai *ai = &self->ai; + if (!ai->hang_jacket) { + if (self->jacket_hung || self->look.top == TOP_JACKET || self->chill > 0.05f || ai->want_jacket) return false; + float tidy = clampf(0.5f + ai->character.likes[ORDER_TRASH] - 0.4f * self->drunk, 0.1f, 1.5f); + if (random01(ai) > RACK_CHANCE * tidy * dt) return false; + Object *rack = coatRack(objects, count); + if (!rack || flatDistance(rack->position, self->position) > RACK_WALK) return false; + ai->hang_jacket = true; + ai->jacket_time = 0.0f; + if (random01(ai) < 0.3f) Ai_say(self, "I'll hang my jacket up."); + } + Object *rack = coatRack(objects, count); + if (!rack || self->jacket_hung || self->look.top == TOP_JACKET || self->chill > 0.3f || (ai->jacket_time += dt) > JACKET_GIVE_UP) { + ai->hang_jacket = false; + return false; + } + if (!goTo(self, rack, objects, count, 0.6f, dt)) return true; + Hands_touch(self, Object_colliderCenter(rack, 0)); /* (onto a hook) */ + self->jacket_hung = true; + ai->hang_jacket = false; + rest(ai, 1.0f, 2.0f); + return true; +} + void Ai_update(Object *self, Object *const *objects, int count, float dt) { ai_ensureReady(self); Ai *ai = &self->ai; @@ -1635,6 +1670,8 @@ void Ai_update(Object *self, Object *const *objects, int count, float dt) { if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && ai_putAway(self, objects, count, dt)) return; /* Cold, the jacket on the rack: off to get it */ if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && fetchJacket(self, objects, count, dt)) return; + /* Warm again: the jacket back onto the rack */ + if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER) && hangJacket(self, objects, count, dt)) return; /* Tidy people pick things up on the way */ if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || (ai->mode == AI_HELP && !ai->pushing && !ai->copied) || ai->mode == AI_SUPPLY || ai->mode == AI_FETCH) && ai_tidyUp(self, objects, count, dt)) diff --git a/entropie/src/people/ai.h b/entropie/src/people/ai.h index 413f01f..00a5518 100644 --- a/entropie/src/people/ai.h +++ b/entropie/src/people/ai.h @@ -173,6 +173,7 @@ typedef struct { float tidy_pause; /* bending down for trash on the way (seconds left) */ float tray_time; /* on the way to empty the ashtray this long */ bool want_jacket; /* cold, the jacket on the rack: off to get it */ + bool hang_jacket; /* warm again, the jacket tied round the waist: back onto the rack with it */ float jacket_time; /* ... on the way this long */ bool tool_waiting; /* waiting for the player to put the axe down */ unsigned char banned; /* tools (1 << ToolKind) they were told to leave alone ... */ diff --git a/entropie/src/render/figure.h b/entropie/src/render/figure.h index 2d7550d..d99787b 100644 --- a/entropie/src/render/figure.h +++ b/entropie/src/render/figure.h @@ -29,6 +29,7 @@ typedef struct { int garment; /* GARMENT_*: what's on the legs */ int top; /* TOP_*: the shirt on, or off (working in the sun), or a jacket over it (cold nights) */ Vec3 jacket; /* the jacket's colour */ + bool jacket_tied; /* the jacket off and along with them: tied round the waist by its sleeves */ } FigureLook; enum { TOP_SHIRT, TOP_BARE, TOP_CROP, TOP_JACKET }; diff --git a/entropie/src/render/renderer.c b/entropie/src/render/renderer.c index 9087168..ee14cd8 100644 --- a/entropie/src/render/renderer.c +++ b/entropie/src/render/renderer.c @@ -102,6 +102,7 @@ static bool loadShaders(Renderer *r) { f->garment = glGetUniformLocation(f->program, "garment"); f->undress = glGetUniformLocation(f->program, "undress"); f->top = glGetUniformLocation(f->program, "top"); + f->jacket_tied = glGetUniformLocation(f->program, "jacketTied"); f->cig_a = glGetUniformLocation(f->program, "cigA"); f->uri_a = glGetUniformLocation(f->program, "uriA"); f->uri_b = glGetUniformLocation(f->program, "uriB"); @@ -542,6 +543,7 @@ void Renderer_drawFigure(Renderer *r, const FigurePose *pose) { glUniform1i(f->garment, pose->look.garment); glUniform1f(f->undress, sk->undress); glUniform1i(f->top, pose->look.top); + glUniform1i(f->jacket_tied, pose->look.jacket_tied && pose->look.top != TOP_JACKET ? 1 : 0); setVec3(f->cig_a, sk->cig_a); setVec3(f->uri_a, sk->uri_a); setVec3(f->uri_b, sk->uri_b); diff --git a/entropie/src/render/renderer.h b/entropie/src/render/renderer.h index bf5bbd4..d39d4b8 100644 --- a/entropie/src/render/renderer.h +++ b/entropie/src/render/renderer.h @@ -68,7 +68,7 @@ typedef struct { GLint skin, hair, shirt, pants, shoes, hair_style, long_sleeves; GLint head_forward, head_up, chest_forward, waist_bend, face, expression; GLint stream, stream_kind, stream_width; - GLint garment, undress, top; + GLint garment, undress, top, jacket_tied; GLint cig_a, cig_tip, cig_glow, jacket, uri_a, uri_b, uri; GLint species, tail_a, tail_b; GLint view_pos, light_dir, light_color, shadow_color, sky_top, sky_horizon; diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c index f7fd015..e40209c 100644 --- a/entropie/src/world/world.c +++ b/entropie/src/world/world.c @@ -723,6 +723,7 @@ static void dressForNight(World *world, Object *o, float dt) { o->look.top = TOP_SHIRT; /* (off, and along with them: round the waist) */ } if (o->chill < 0.3f) o->jacket_told = false; + o->look.jacket_tied = !o->jacket_hung && o->look.top != TOP_JACKET; /* (off and with them) */ } /* A chair waits to be pushed in while somebody stands where it goes. */