Nimbin[12]?Games / Entropie / commits / 94b6203

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.84.0: helpers get out of the way

Christian Immanuel · 2026-10-15 16:52 · 94b6203e67a9610c6509b83651cbdbbd70c11c56

- A helper standing where the pushed or pulled load wants to go notices it,
  lets go and comes round to another side; quick-witted ones soon, slow
  ones (Ben) not at all ("Why won't it move?")
- New trait: wits (0 slow .. 1 sharp), also in the dev menu

Includes 1.83.0 (if not committed separately):
- Peeing standing: one hand holds it (the Urinella to the body), the other
  stays at the belt
- A full ashtray (12 butts) gets emptied by somebody tidy: into a scrap of
  paper, off to the bin
- Fix: a tidy person could stand still for minutes, reaching again and
  again for trash just out of reach

Includes 1.82.0 (if not committed separately):
- Walking styles: feet wider or narrower, toes out more or less, bouncier
  or flatter (livelier when lively), hips swaying more or less
- The ashtray stands on the cool box by the fire (on the ground it was in
  everybody's way)

Includes 1.81.0 (if not committed separately):
- Fix: following someone on a chat break (and walking up to things) went
  walk-stop-walk every frame, the whole body shook; now they slow down
  coming up and stay with them
- Fix: an elbow could flip over when a hand went up past the shoulder
  (scratching the head); it turns round the arm now

Includes 1.80.0 (if not committed separately):
- An old tin by the fire as ashtray: you and the tidy put butts in there
- The Urinella is drawn while it's used; the dress is lifted higher
- After peeing you take up again what you put down
- The others can slip off a wobbly ladder too

Includes 1.79.0 (if not committed separately):
- Helpers (G) follow you up a leaning ladder onto a roof or wall and down
  again; without a ladder they pull themselves up after you (if they reach)
  and jump down when you're down
- Pulling up is a module for anybody now (people/climb.c), not only you

Includes 1.78.0 (if not committed separately):
- Cold nights: away from the fire a jacket goes on (each their own colour)
- Working hard in the midday sun your shirt comes off too
- Women who never tried a Urinella may try one: some like it, some don't
- Dying for a smoke makes restless; a wobbling ladder rocks
- Moving heavy wood: tested with a called helper; the player is asked when
  nobody else is around

Includes 1.77.0 (if not committed separately):
- Ladders: carried nearly upright when out; stood up on their own they
  fall over; leaned, how steady they stand depends on angle, ground, feet
  and what holds them; a wobbly one wobbles, then slips and falls with you
- Somebody holds it (hands on the rails, a foot on the bottom rung):
  helpers (G) when you climb, helpful people when it looks wobbly
- The work place: the tidy clear away what lies where they'd stand; no room
  to swing, they take the piece somewhere with room (carried, or dragged
  with somebody called to help)
- The sporty pull up and climb ladders much quicker

Includes 1.76.0 (if not committed separately):
- Smoking: packs and tobacco in the pockets (used up); smokers get a
  craving and light up in a quiet moment - rolling one first, the lighter
  to it, drags (the tip glows), smoke breathed out, the butt flicked away
- Shared: "Got a light?", bumming a cigarette (or a no); talk to ask for
  one or offer one; K lights yours / puts it out; Tab shows your pockets
- Shadows: soft, smooth edges instead of stair steps

Includes 1.75.0 (if not committed separately):
- Urinellas: one-time use, up to 4 in the pocket; a box by the toilet
  refills it (the others fetch more when they run out, you click it)
- Peeing: what's in the hands is put down first (the others take it up
  again); standing, the fly is open or the skirt or dress lifted
- Lighter: needed to light the fire, runs empty; borrowed when there's
  none, given back (or not), owners ask for it; talk: borrow, give back,
  ask for yours back
- Working hard in the midday sun the shirt comes off (women keep a top)
- Dropping from high up: knees pulled up, legs reach for the ground, deep
  into the knees, a roll or a crash
- Ladders: one-handed climbing (you may slip), a lying ladder is stepped
  over, kicked it hardly moves and your toe hurts
- Dev menu: teleport to the parkour / yard; the player spawns 1 m up

Includes 1.74.0 (if not committed separately):
- Fix: legs showed through a skirt or dress standing normally; the thighs
  under it are cloth now and it's wider over the hips
- Mila's Urinellas come in a pack of 3 (then she squats); now and then a
  mishap (on the shoes); used ones go into the bag, are dropped or flung
  away (drunk) as trash

Includes 1.73.0 (if not committed separately):
- Clothes: trousers, shorts, skirt, dress; squatting to pee trousers come
  down, a skirt or dress is gathered up

Includes 1.72.0 (if not committed separately):
- Fix: walking off the other way from something half stepped over left
  one foot planted behind, sliding along

Includes 1.71.0 (if not committed separately):
- Two-handed fidgets: rubbing the hands, hands behind the head, both
  hands on the hips

Includes 1.70.0 (if not committed separately):
- More one-hand gestures (neck, nose, ear, hair, thumb in the belt, hand
  on the chest, shading the eyes); mood and personal habits pick them

Includes 1.69.0 (if not committed separately):
- Faster with a crowd: 750 people 22 -> 14 ms a frame (kind index, cell
  grid for company, no foot checks far from you)

Includes 1.68.0 (if not committed separately):
- The walking map is updated in place when fixed things change
  (0.3 ms instead of 10 ms)

Includes 1.67.0 (if not committed separately):
- Pour your drink on the fire (C): a small fire goes out, a big one
  hisses, vodka flares up

Includes 1.66.0 (if not committed separately):
- Careful people sort the woodpile by length (front even) and put the
  branches lying about onto a heap at the end of the woodpile

Includes 1.65.0 (if not committed separately):
- Smooth off the ladder (a step back, one foot after the other) and
  taking it down (the hand reaches for the rail at chest height)

Includes 1.64.0 (if not committed separately):
- Smooth ladder and climbs: getting on, over the edge at the top, feet
  from where they are; 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/README.md            |  2 ++
 entropie/STATE.md             |  5 +++-
 entropie/commit_message.txt   |  8 +++++-
 entropie/src/game/game_dev.c  |  4 ++-
 entropie/src/people/ai.h      |  4 +++
 entropie/src/people/ai_help.c | 57 +++++++++++++++++++++++++++++++++++++++++++
 entropie/src/world/object.c   |  1 +
 entropie/src/world/object.h   |  1 +
 entropie/src/world/scene.c    |  2 ++
 10 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/entropie/Makefile b/entropie/Makefile
index db3f986..5eb61c0 100644
--- a/entropie/Makefile
+++ b/entropie/Makefile
@@ -1,5 +1,5 @@
 APP       := entropie
-VERSION   := 1.83.0
+VERSION   := 1.84.0
 
 PREFIX    ?= /usr
 BINDIR    ?= $(PREFIX)/bin
diff --git a/entropie/README.md b/entropie/README.md
index 78c5fb3..8e29525 100644
--- a/entropie/README.md
+++ b/entropie/README.md
@@ -225,6 +225,8 @@ and satellite pictures (about 340 x 340 m).
 - Tidy people (Ida) pick up trash they pass and take it to the container
 - Press G and the nearest one helps: walks beside you, finds a free spot
   at what you push and pushes along. Press again for more helpers, H sends them off
+- A helper standing where the load wants to go notices it, lets go and comes
+  round to another side (the quick-witted soon, the slow never)
 - Not sure what you're up to, helpers copy you: they take something like what
   you carry, walk along at your side and do the same with it (put it down
   next to yours, into the fire, into the bin)
diff --git a/entropie/STATE.md b/entropie/STATE.md
index 05a874e..55341dd 100644
--- a/entropie/STATE.md
+++ b/entropie/STATE.md
@@ -20,6 +20,7 @@ Updated with every version (see the version line at the end).
 | Pees | standing | standing | squatting | standing (Urinella, 3 in the pocket) | standing |
 | Lighter in the pocket (lights) | 20 | 40 | – | – | 40 |
 | Smokes (craving speed) / pack / roll-ups | .3 / 10 / 0 | .6 / 0 / 12 | – | .25 / 0 / 0 | .9 / 6 / 20 |
+| Wits | .6 | .45 | .8 | .6 | .25 |
 
 Alice the dog: follows, can be petted and called.
 
@@ -66,6 +67,8 @@ Smoke puffs (`chips.c` Chips_puff): small blocks drifting up, growing, drawn wit
 
 Shadows: 3x3 PCF with each tap blended bilinearly between its 4 texels (`common.glsl` shadowVisibility, 16 lookups): no stair steps at the edges.
 
+Making room (`ai_help.c` makeRoom, every frame in AI_HELP): the leader wants to go (`movement` 0.2+ m/s) and the helper stands in the load's way (`inLoadsWay`: in front of it along that direction, within its width + 0.35 m). After 0.8 s + 2.5 x (1 - wits) the load's progress is checked (from `jam_from`): holding on and it goes 35 %+ of the speed: fine; else (or not holding on: shoved along) wits under 0.35 (minus 0.6 x drunk): "Why won't it move?" and nothing; else let go, "Wait - I'm in the way", and for 6 s (`clear_time`, `clear_dir`) spots in the load's way are no good spots. `wits` (object.h, 0.5 by default; table in scene.c; dev menu row).
+
 Climbing for anybody (`people/climb.c`, moved out of the game): `Climb_ledgeAhead`, `Climb_start` (says why not: hands, strength, drunk), `Climb_update` (the pull up, its feet and hands), `Climb_overLadderTop`; the move lives in the person (`climb_move`). Helpers (`ai_help.c` ai_followByLadder): the leader 1.2 m higher (standing, not climbing): a leaning ladder within 25 m from their level to the leader's (top within 1.3 m) - to its foot, up (`ai_climbing`: on a ladder or pulling over its top this runs first, whatever the mode), over the edge; none, and the leader within reach (CLIMB_REACH): walk at it and pull up where the edge ahead is at the leader's level ("Hup!"). The leader 1.2 m lower: to the top of a ladder from up there and down it; none: they just walk off (the landing does the rest). A ladder leading nowhere (no top to get off at): down again after 8 s. `Ai_useWorld` gives the AI the world for this.
 
 Walking styles (`Body.style[4]`, -1..1 per person, made from the quirks - no extra dice, so nothing else changes): feet apart x (1 + 0.15 style0), toes out + 0.04 rad x style1, the bob x (1 + 0.35 style2 + 0.25 lively), the hips swaying over the standing foot x (1 + 0.35 style3). (The audit swings both ways with it - it changes where everybody goes; overall the player's numbers are lower.)
@@ -204,4 +207,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.83.0 (build a45a9ec)`
+State of `entropie 1.84.0 (build 0f5b1e3)`
diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt
index 152a468..1c8e7a3 100644
--- a/entropie/commit_message.txt
+++ b/entropie/commit_message.txt
@@ -1,5 +1,11 @@
-entropie 1.83.0: a hand on it, and the ashtray gets emptied
+entropie 1.84.0: helpers get out of the way
 
+- A helper standing where the pushed or pulled load wants to go notices it,
+  lets go and comes round to another side; quick-witted ones soon, slow
+  ones (Ben) not at all ("Why won't it move?")
+- New trait: wits (0 slow .. 1 sharp), also in the dev menu
+
+Includes 1.83.0 (if not committed separately):
 - Peeing standing: one hand holds it (the Urinella to the body), the other
   stays at the belt
 - A full ashtray (12 butts) gets emptied by somebody tidy: into a scrap of
diff --git a/entropie/src/game/game_dev.c b/entropie/src/game/game_dev.c
index a6431ed..4145e7c 100644
--- a/entropie/src/game/game_dev.c
+++ b/entropie/src/game/game_dev.c
@@ -11,7 +11,7 @@
 typedef enum {
     DEV_WHO, DEV_TELEPORT, DEV_FEMALE, DEV_URINELLA, DEV_SPORTY, DEV_DRUNK, DEV_ENERGY,
     DEV_BLADDER, DEV_HUNGER, DEV_THIRST, DEV_SLEEPY, DEV_STRENGTH, DEV_ANNOYED,
-    DEV_PRIDE, DEV_LIVELY, DEV_NERVES, DEV_CLUMSY, DEV_WILLING,
+    DEV_PRIDE, DEV_LIVELY, DEV_NERVES, DEV_CLUMSY, DEV_WITS, DEV_WILLING,
     DEV_LIKES,                       /* likes for each job ... */
     DEV_SKILLS = DEV_LIKES + 4,      /* ... skill at each job */
     DEV_ROWS = DEV_SKILLS + 4
@@ -121,6 +121,7 @@ void game_dev(Game *game, const Input *input) {
     case DEV_LIVELY:   o->temperament.energy = stepSigned(o->temperament.energy, dir, 0.1f); break;
     case DEV_NERVES:   o->temperament.nerves = step01(o->temperament.nerves, dir, 0.1f); break;
     case DEV_CLUMSY:   o->body.quirk[3] = stepSigned(o->body.quirk[3], dir, 0.2f); break; /* (clumsiness = 0.5 + 0.5 x this) */
+    case DEV_WITS:     o->wits = step01(o->wits, dir, 0.05f); break;
     case DEV_WILLING:  c->willing = step01(c->willing, dir, 0.1f); break;
     default: break;
     }
@@ -156,6 +157,7 @@ static void devLine(const Object *o, int row, char *out) {
     case DEV_LIVELY:   snprintf(out, 96, "Lively (tired .. lively):  %+.1f", o->temperament.energy); break;
     case DEV_NERVES:   snprintf(out, 96, "Nerves (calm .. restless):  %.1f", o->temperament.nerves); break;
     case DEV_CLUMSY:   snprintf(out, 96, "Clumsy:  %.0f%%", (0.5f + 0.5f * o->body.quirk[3]) * 100.0f); break;
+    case DEV_WITS:     snprintf(out, 96, "Wits:  %.2f", o->wits); break;
     case DEV_WILLING:  snprintf(out, 96, "Willing:  %.0f%%", c->willing * 100.0f); break;
     default:           out[0] = '\0'; break;
     }
diff --git a/entropie/src/people/ai.h b/entropie/src/people/ai.h
index 7d79c2d..057f858 100644
--- a/entropie/src/people/ai.h
+++ b/entropie/src/people/ai.h
@@ -144,6 +144,10 @@ typedef struct {
     struct Object *skip[AI_SKIPS];      /* didn't work out: leave these a while ... */
     float          skip_time[AI_SKIPS]; /* ... this much longer each */
     float          retry;       /* lost the grip: wait a moment before trying again */
+    float          jam_time;    /* AI_HELP: the load doesn't move and we stand where it wants to go (seconds) */
+    float          clear_time;  /* ... so we keep off that side a while */
+    Vec3           clear_dir;   /* ... (the way the load wants to go) */
+    Vec3           jam_from;    /* (where the load was when we started to wonder) */
 
     /* Talking */
     Topic          topic;       /* AI_APPROACH: what they want to talk about */
diff --git a/entropie/src/people/ai_help.c b/entropie/src/people/ai_help.c
index 0369dcf..43d13d8 100644
--- a/entropie/src/people/ai_help.c
+++ b/entropie/src/people/ai_help.c
@@ -11,11 +11,27 @@
 /* ---------- Helping ---------- */
 
 /* A free spot right at the object, not on top of the leader. */
+/* Standing at p, where the load wants to go (dir, flat and unit): in front of it, not beside it. */
+static bool inLoadsWay(const Object *target, Vec3 dir, Vec3 p) {
+    const Collider *c = &target->colliders[0];
+    const float half[3] = { c->half_extents.x, c->half_extents.y, c->half_extents.z };
+    Vec3 across = vec3_cross(dir, UP);
+    float length = 0.0f, width = 0.0f;
+    for (int k = 0; k < 3; k++) {
+        length += fabsf(vec3_dot(c->axes[k], dir)) * half[k];
+        width  += fabsf(vec3_dot(c->axes[k], across)) * half[k];
+    }
+    Vec3 rel = flat(vec3_sub(p, Object_colliderCenter(target, 0)));
+    float along = vec3_dot(rel, dir), side = fabsf(vec3_dot(rel, across));
+    return along > 0.0f && along < length + 1.0f && side < width + 0.35f; /* (a body in front, not beside) */
+}
+
 static bool goodSpot(Object *self, Object *const *objects, int count, const Object *target, Vec3 p) {
     const Object *leader = self->ai.leader;
     if (flatDistance(p, leader->position) < PERSON_RADIUS + 0.1f) return false;
     float gap = Hands_gapFrom(p, self->look.height, target);
     if (gap > SPOT_GAP || gap < 0.05f) return false; /* at it, not in it */
+    if (self->ai.clear_time > 0.0f && inLoadsWay(target, self->ai.clear_dir, p)) return false; /* (we were in the way there) */
     return !ai_blockedBy(self, leader, target, objects, count, p);
 }
 
@@ -481,6 +497,46 @@ static bool helpImitate(Object *self, Object *const *objects, int count, float d
     return true;
 }
 
+/* Pushing or pulling together and the load won't budge, because we stand
+   where it wants to go: the quick-witted see it, let go and come round to
+   another side; the slow keep at it (and wonder). Returns true while letting go. */
+#define JAM_SPEED   0.2f  /* m/s: the leader wants to go at least this fast ... */
+#define JAM_MOVING  0.35f /* ... and the load does less than this share of it */
+#define JAM_NOTICE  0.8f  /* s before the sharpest notice (the slow take longer) */
+#define JAM_WITS    0.35f /* less (drunk takes off): they don't get it */
+#define CLEAR_TIME  6.0f  /* s keeping off that side */
+
+static bool makeRoom(Object *self, const Object *leader, const Object *target, float dt) {
+    Ai *ai = &self->ai;
+    ai->clear_time = fmaxf(0.0f, ai->clear_time - dt);
+    Vec3 want = flat(leader->movement);
+    float speed = vec3_length(want);
+    if (!target || speed < JAM_SPEED) { ai->jam_time = 0.0f; return false; }
+    Vec3 dir = vec3_scale(want, 1.0f / speed);
+    if (!inLoadsWay(target, dir, self->position)) { ai->jam_time = 0.0f; return false; }
+    if (ai->jam_time <= 0.0f) ai->jam_from = target->position;
+    ai->jam_time += dt;
+    float wits = self->wits - 0.6f * fminf(self->drunk, 1.0f);
+    if (ai->jam_time < JAM_NOTICE + 2.5f * (1.0f - clampf(wits, 0.0f, 1.0f))) return false;
+    float moved = vec3_dot(flat(vec3_sub(target->position, ai->jam_from)), dir);
+    float time = ai->jam_time;
+    ai->jam_time = 0.0f;
+    if (ai->pushing && moved > JAM_MOVING * speed * time) return false; /* (holding on and it goes: all fine; not holding on, we're just shoved along) */
+    if (wits < JAM_WITS) { /* doesn't get it: keeps at it */
+        if (random01(ai) < 0.4f) Ai_say(self, random01(ai) < 0.5f ? "Why won't it move?" : "Push harder!");
+        return false;
+    }
+    Ai_say(self, moved > 0.3f ? "Whoa - I'm in the way!" : random01(ai) < 0.5f ? "Wait - I'm in the way." : "Hang on, I'll come round.");
+    if (Hands_isGrabbing(self)) Hands_release(self);
+    ai->pushing    = false;
+    ai->retry      = 0.0f;
+    ai->closest    = 1e9f;
+    ai->clear_dir  = dir;
+    ai->clear_time = CLEAR_TIME;
+    stand(self);
+    return true;
+}
+
 void ai_updateHelp(Object *self, Object *const *objects, int count, float dt) {
     Ai *ai = &self->ai;
     Object *leader = ai->leader;
@@ -517,6 +573,7 @@ void ai_updateHelp(Object *self, Object *const *objects, int count, float dt) {
         ai->pushing = false;
         ai->closest = 1e9f;
     }
+    if (makeRoom(self, leader, target, dt)) return;
     if (ai->retry > 0.0f) {
         ai->retry -= dt;
         stand(self);
diff --git a/entropie/src/world/object.c b/entropie/src/world/object.c
index e0dd5de..75761dd 100644
--- a/entropie/src/world/object.c
+++ b/entropie/src/world/object.c
@@ -100,6 +100,7 @@ Object *Object_create(ObjectType type, Palette palette, Vec3 position, Vec3 size
     obj->main_hand = HAND_RIGHT;
     obj->top_speed = SPRINT_SPEED;
     obj->fitness = 0.5f;
+    obj->wits    = 0.5f;
     obj->jump_speed = JUMP_SPEED;
     obj->tap_timer = 1e3f;
     obj->position = position;
diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h
index 30e9d55..aaffa42 100644
--- a/entropie/src/world/object.h
+++ b/entropie/src/world/object.h
@@ -289,6 +289,7 @@ typedef struct Object {
     int        main_hand;     /* HAND_RIGHT or HAND_LEFT */
     float      top_speed;     /* sprinting flat out, fresh and sober (m/s) */
     float      fitness;       /* characters: how sporty (0..1), whatever the build: quicker, surer, smoother over things */
+    float      wits;          /* characters: 0 = slow on the uptake .. 1 = sharp (sees what's wrong and sorts it out) */
     float      jump_speed;    /* take-off speed of a standing jump, fresh and sober (m/s) */
     float      run_speed;     /* player: current speed, eased (m/s) */
     float      sprint_boost;  /* player: 0 = jogging .. 1 = flat out (tapping Shift) */
diff --git a/entropie/src/world/scene.c b/entropie/src/world/scene.c
index 8225027..120ca46 100644
--- a/entropie/src/world/scene.c
+++ b/entropie/src/world/scene.c
@@ -87,6 +87,7 @@ static const bool URINELLA[] = { false, false, false, true, false };
 static const bool LIGHTER[]  = { true, true, false, false, true }; /* (a lighter in the pocket: You, Jonas, Ben) */
 /* Smoking: how much (0 = not at all), a pack, tobacco to roll (Mila smokes now and then, and bums them). */
 static const float SMOKER[]     = { 0.3f, 0.6f, 0.0f, 0.25f, 0.9f };
+static const float WITS[]       = { 0.6f, 0.45f, 0.8f, 0.6f, 0.25f }; /* (Ben is slow on the uptake) */
 static const int   CIGARETTES[] = { 10, 0, 0, 0, 6 };
 static const int   TOBACCO[]    = { 0, 12, 0, 0, 20 };
 static const int  GARMENT[]  = { GARMENT_TROUSERS, GARMENT_SHORTS, GARMENT_SKIRT, GARMENT_DRESS, GARMENT_TROUSERS };
@@ -145,6 +146,7 @@ Object *Scene_load(World *world) {
             obj->lighter = LIGHTER[characters % TEMPERAMENT_COUNT];
             obj->lighter_uses = obj->lighter ? LIGHTER_USES * (1 + characters % 3) / 3 : 0; /* (some nearly empty) */
             obj->smoker     = SMOKER[characters % TEMPERAMENT_COUNT];
+            obj->wits       = WITS[characters % TEMPERAMENT_COUNT];
             obj->cigarettes = CIGARETTES[characters % TEMPERAMENT_COUNT];
             obj->tobacco    = TOBACCO[characters % TEMPERAMENT_COUNT];
             obj->craving    = 0.3f + 0.15f * (float)(characters % 3);