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 2.5.0: cigarette butts go somewhere
- Finished cigarettes go into the fire when close, else the tin or the bin when near; tidy people walk there with the butt - Pocket ashtrays (you and Jana): the butt goes in there - No bin near: the tidy ask for a pocket ashtray (from you too: B, look at them, Enter holds yours out), else they keep it wrapped up till they pass a bin; the others still flick it away - Your pocket ashtray: B, Enter at the tin, a bin or the fire empties it
entropie/Makefile | 2 +- entropie/README.md | 10 +++-- entropie/STATE.md | 10 ++++- entropie/commit_message.txt | 18 ++++----- entropie/src/game/game_actions.c | 60 +++++++++++++++++++++++++++--- entropie/src/people/ai.c | 80 ++++++++++++++++++++++++++++++++++++++++ entropie/src/people/ai.h | 2 + entropie/src/people/pockets.c | 12 +++++- entropie/src/people/pockets.h | 7 +++- entropie/src/world/object.h | 3 ++ entropie/src/world/scene.c | 2 + entropie/src/world/world.c | 40 +++++++++++++++----- 12 files changed, 211 insertions(+), 35 deletions(-) diff --git a/entropie/Makefile b/entropie/Makefile index cac588a..f2a1637 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 2.4.0 +VERSION := 2.5.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index 89a05b9..360b6f4 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -542,9 +542,13 @@ say no more often and don't come to you for a bit. then the lighter to it - theirs, or "Got a light?" from somebody near - A drag now and then (the tip glows), smoke breathed out, the hand up in front between drags. Hands needed: it goes into the mouth -- Done: flicked away or dropped as a butt (into the fire when close; the - tidy and you use the old tin on the end of a bench by the fire as ashtray; - you see it fill up) +- Done: into the fire when close, else the tin on the end of a bench by the + fire or the bin when near (you see the tin fill up). You and Jana carry a + pocket ashtray: the butt goes in there. The tidy walk to the tin, a bin or + the fire with it; none near, they ask for a pocket ashtray ("got your pocket + ashtray?" - B, look at them, Enter: hold it out) or keep it in a scrap of + paper till they pass a bin. The others flick it away or drop it +- Your pocket ashtray: B, Enter at the tin, a bin or the fire empties it - A full ashtray gets emptied by somebody tidy (into a scrap of paper, off to the bin) - K: light one (yours, or somebody next to you gives you a light), K again diff --git a/entropie/STATE.md b/entropie/STATE.md index 7030def..b23e4f6 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -175,6 +175,11 @@ Round 2.4: - You (B, pocket rows "Pullover", "Put your pullover down"): put on / take off (round your hips), put it down where the verb line says; click it (within 1.3 m, `takePullover`) to take it back; somebody else's: "That's Jana's pullover". C at your spread pullover: you sit on it. Cold: put on for you once a cold spell (`jacket_told`), after that it's up to you; put down: told where it is. - People (`Clothes_update`): sitting down (settled), warm, theirs with them: over the chair's back (0.8 x tidiness) or under them on the ground (tidiness: 0.35 + 0.35 liking for clearing up + 0.2 pride - 0.3 drunk), "Not sitting in the dirt."; getting up, it comes along. Cold with it put down: `ai.c` fetchPullover (idle, wander, at a table; on their own chair or under them: just take it; else goTo it, on with it; gives up after 90 s, 80 m off or warm). +Round 2.5: +- Cigarette butts (`world.c` smoke, at the end of one): a burning fire within 3 m: in; the tin or a bin within BUTT_REACH 4 m: in (the tidy and you; the others 40 %); a pocket ashtray (`pocket_tray`: you and Jana): `Pockets_keepButt` (`pocket_butts`); the tidy (likes clearing up over 0.2 or pride over 0.4, under 0.6 drunk): `butt_held`; the rest: dropped or flicked (as before). +- `ai.c` binButt (idle, wander, at a table): with `butt_held`, the tin, a bin or a burning fire within BUTT_WALK 30 m: there (gives up after 40 s: skipped) and in; none: asks somebody within 6 m with a pocket ashtray (NPCs: "Sure, put it in." - into theirs; you: `asking_you` POCKET_ASHTRAY, the hint line, B Enter "hold it out"); nothing after BUTT_WAIT 25 s or held BUTT_KEEP 90 s: wrapped up into the pocket ("I'll keep it till I find a bin."). Pocket butts are emptied passing within BUTT_PASS 2 m of the tin, a bin or the fire; a pocket ashtray with TRAY_FULL 8 is taken there on purpose. +- Your pockets: row "Pocket ashtray - n butts": Enter looking at somebody holding a butt: it goes into yours ("Thanks - much better than on the ground."); else at the tin, a bin or the fire (within 1 m): emptied. + 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). @@ -306,6 +311,8 @@ Most important first. **Bugs** - Joint jumps left in the audit: jog+stop 171, sprint 297 (worst 26 cm) - running into things: a hurried catch step swings through in a few frames (step 0.07 -> 0.34, the ankle up 20 cm). Capping the swing speed helped sprint (278) but made jumps, drunk walk and the NPCs worse - tried and reverted in 1.83; ladder 45: toes on the sloping roof (a landing step's toes snap onto the slope, 16 cm; blending them in made all walking worse), toes turning onto the rungs getting on from the top (14 cm) +- NPC audit: Kurti idles by the fire with the chainsaw in his hand; it knocks against the tripod poles and he jitters (about 1400 of the 3500 joint jumps) + **Body and moving** - Obstacle approach, still to do: thin round logs (under 0.2 m) are still stepped over; a step in between on top of something unstable (if clumsy); NPCs: pull up on their own for their own reasons (now only following a leader), jumping onto things - Climbing rework: hands first (one reaches first, shoulder ahead), a knee up, the other foot next to it; or sit on it and lift the legs; or belly first and roll; by height, surface and person @@ -319,7 +326,6 @@ 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 -- Butts, next: into a bin or the fire too, a pocket ashtray for some, the tidy ask for an ashtray with no bin near - Arrival, next: the first start by car (you, Volkram, Jana, Toni), unloading 2 crates of beer, 2 of water, sleeping bags, mats, tents - Tents: Volkram and Jana share one, Toni and you a small one each; pitching together (poles, the cover, pegs); sleeping in them - Rain: showers; people go into the house or their tents @@ -331,4 +337,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.4.0 (build 6d78a96)` +State of `entropie 2.5.0 (build 74f2023)` diff --git a/entropie/commit_message.txt b/entropie/commit_message.txt index d96e1f1..f1efa7b 100644 --- a/entropie/commit_message.txt +++ b/entropie/commit_message.txt @@ -1,11 +1,9 @@ -entropie 2.4.0: pullovers instead of the coat rack +entropie 2.5.0: cigarette butts go somewhere -- The coat rack is gone (code too); the jacket is now a pullover -- Your pullover (B): put it on, take it off (tied round your hips), or put - it down where you look: over a chair's back, a railing, a wall or a bench, - folded on a table, or spread on the ground; click it to take it back -- Sitting on the ground: spread it out and sit on it (C at it sits on it) -- Others hang theirs over their chair or sit on it on the ground, take it - along when they get up, and go and get it when it gets cold -- Cold: it goes on for you once, after that it's up to you; put down - somewhere, you're told where it is +- Finished cigarettes go into the fire when close, else the tin or the bin + when near; tidy people walk there with the butt +- Pocket ashtrays (you and Jana): the butt goes in there +- No bin near: the tidy ask for a pocket ashtray (from you too: B, look at + them, Enter holds yours out), else they keep it wrapped up till they pass + a bin; the others still flick it away +- Your pocket ashtray: B, Enter at the tin, a bin or the fire empties it diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c index 1f21af6..aa4cbfa 100644 --- a/entropie/src/game/game_actions.c +++ b/entropie/src/game/game_actions.c @@ -164,7 +164,7 @@ static void smokeOrStop(Game *game) { /* ---------- Your pockets (B): what's in them, taken out, given ---------- */ -enum { ROW_CIGS, ROW_TOBACCO, ROW_PAPERS, ROW_FILTERS, ROW_LIGHTER, ROW_KEYS, ROW_URINELLAS, ROW_PULLOVER, ROW_PUT_DOWN, POCKET_ROWS }; +enum { ROW_CIGS, ROW_TOBACCO, ROW_PAPERS, ROW_FILTERS, ROW_LIGHTER, ROW_ASHTRAY, ROW_KEYS, ROW_URINELLAS, ROW_PULLOVER, ROW_PUT_DOWN, POCKET_ROWS }; static bool rowHas(const Object *p, int row) { switch (row) { @@ -173,6 +173,7 @@ static bool rowHas(const Object *p, int row) { case ROW_PAPERS: return p->papers > 0; case ROW_FILTERS: return p->filters > 0; case ROW_LIGHTER: return p->lighter; + case ROW_ASHTRAY: return p->pocket_tray || p->pocket_butts > 0; case ROW_KEYS: return p->keys != NULL; case ROW_URINELLAS: return p->urinellas > 0; case ROW_PULLOVER: return true; @@ -189,6 +190,7 @@ static void rowText(const Object *p, int row, char *out, size_t size) { case ROW_FILTERS: snprintf(out, size, "Filter tips - %d", p->filters); break; case ROW_LIGHTER: snprintf(out, size, "%s - %s", p->lighter_of ? "A borrowed lighter" : "Lighter", p->lighter_uses > 0 ? "works" : "empty"); break; + case ROW_ASHTRAY: snprintf(out, size, p->pocket_tray ? "Pocket ashtray - %d butts" : "Butts wrapped up - %d", p->pocket_butts); break; case ROW_KEYS: snprintf(out, size, "%s", p->keys); break; case ROW_URINELLAS: snprintf(out, size, "Urinellas - %d", p->urinellas); break; case ROW_PULLOVER: snprintf(out, size, "Pullover - %s", p->pullover_down ? Clothes_where(p) : p->look.top == TOP_JACKET ? "on" @@ -213,6 +215,33 @@ static int pocketRow(Game *game) { return -1; } +/* The tin, a bin or a burning fire within reach: where butts go. */ +static Object *buttPlaceNear(const Game *game) { + const Object *p = game->player; + for (int i = 0; i < game->world.count; i++) { + Object *o = game->world.objects[i]; + bool takes = (o->type == OBJ_FURNITURE && o->variant == FURN_ASHTRAY) || o->type == OBJ_BIN || (o->type == OBJ_FIREPLACE && o->burning); + if (takes && Hands_gapFrom(p->position, p->look.height, o) < 1.0f) return o; + } + return NULL; +} + +static const char *placeName(const Object *o) { + return o->type == OBJ_BIN ? "bin" : o->type == OBJ_FIREPLACE ? "fire" : "tin"; +} + +/* Your pocket ashtray (or wrapped-up butts) emptied into the tin, a bin or the fire. */ +static void emptyButts(Game *game) { + Object *p = game->player, *place = buttPlaceNear(game); + if (!place || p->pocket_butts == 0) { say(game, p->pocket_butts ? "No bin, tin or fire right here to empty it" : "It's empty"); return; } + if (place->type == OBJ_FURNITURE) place->stored += p->pocket_butts; + Hands_touch(p, Object_colliderCenter(place, 0)); + p->pocket_butts = 0; + snprintf(game->info, sizeof(game->info), "You empty your butts into the %s", placeName(place)); + game->info_about = NULL; + say(game, game->info); +} + /* What Enter does with that row now: given to who's in front, or used. */ static void pocketVerb(Game *game, int row, char *out, size_t size) { Object *person = game_personInFront(game); @@ -223,6 +252,13 @@ static void pocketVerb(Game *game, int row, char *out, size_t size) { case ROW_PAPERS: snprintf(out, size, name ? "Enter: give %s a few papers" : "Enter: roll one and light it", name); break; case ROW_FILTERS: snprintf(out, size, name ? "Enter: give %s a few filters" : "Enter: roll one and light it", name); break; case ROW_LIGHTER: snprintf(out, size, name ? "Enter: lend %s your lighter" : "Enter: click - does it work?", name); break; + case ROW_ASHTRAY: { + const Object *place = buttPlaceNear(game); + if (person && person->butt_held && game->player->pocket_tray) snprintf(out, size, "Enter: hold it out for %s's butt", name); + else if (place && game->player->pocket_butts > 0) snprintf(out, size, "Enter: empty it into the %s", placeName(place)); + else snprintf(out, size, "Your butts go in here when there's no bin near"); + break; + } case ROW_KEYS: snprintf(out, size, "Keys stay in your pocket"); break; case ROW_URINELLAS: snprintf(out, size, name ? "Enter: give %s one" : "They're for peeing standing (P)", name); break; case ROW_PULLOVER: @@ -271,6 +307,8 @@ static void usePocket(Game *game, int row) { else say(game, "You're smoking one already"); } else if (row == ROW_LIGHTER) { say(game, p->lighter_uses > 0 ? "Click - a flame. It works" : "Click, click... it's empty"); + } else if (row == ROW_ASHTRAY) { + emptyButts(game); } return; } @@ -290,6 +328,12 @@ static void usePocket(Game *game, int row) { given = 1; snprintf(line, sizeof line, "You lend %s your lighter", person->name); break; + case ROW_ASHTRAY: + if (!p->pocket_tray || !person->butt_held) { emptyButts(game); return; } + what = POCKET_ASHTRAY; + given = Pockets_give(p, person, what); + snprintf(line, sizeof line, "%s puts the butt in your pocket ashtray", person->name); + break; case ROW_URINELLAS: if (p->urinellas > 0) { p->urinellas--; person->urinellas++; given = 1; } snprintf(line, sizeof line, "You give %s a Urinella", person->name); @@ -304,8 +348,8 @@ static void usePocket(Game *game, int row) { say(game, game->info); if (person->ai.asking_you && (person->ai.asking_you == (int)what || row == ROW_CIGS)) { /* (what they asked for) */ person->ai.asking_you = 0; - person->smoke_ask = 2.0f; /* (and rolls one in a moment) */ - Ai_say(person, "Thanks, you're a star!"); + if (what != POCKET_ASHTRAY) person->smoke_ask = 2.0f; /* (and rolls one in a moment) */ + Ai_say(person, what == POCKET_ASHTRAY ? "Thanks - much better than on the ground." : "Thanks, you're a star!"); } else if (person->ai.speech_time <= 0.0f) { Ai_say(person, "Oh - thanks."); } @@ -747,7 +791,10 @@ static const Object *askingYou(const Game *game) { for (int i = 0; i < game->world.count; i++) { Object *o = game->world.objects[i]; if (o->type != OBJ_HUMAN || !o->ai.asking_you) continue; - if (Pockets_hasSmoke(o) && Pockets_need(o) == POCKET_NOTHING) { o->ai.asking_you = 0; continue; } /* (got it elsewhere) */ + if (o->ai.asking_you == POCKET_ASHTRAY ? !o->butt_held : Pockets_hasSmoke(o) && Pockets_need(o) == POCKET_NOTHING) { + o->ai.asking_you = 0; /* (got it elsewhere) */ + continue; + } if (hypotf(o->position.x - p->position.x, o->position.z - p->position.z) > 8.0f) { o->ai.asking_you = 0; continue; } /* (walked off) */ return o; } @@ -761,8 +808,9 @@ const char *Game_hint(Game *game) { if (game->player->on_fire > 0.0f) snprintf(game->hint, sizeof(game->hint), "On fire! C: drop and roll - left click: pour your drink over you - water or pee puts it out"); else if ((asking = askingYou(game))) - snprintf(game->hint, sizeof(game->hint), "%s asks you for %s - B: your pockets, look at %s, Enter: give it", - asking->name, Pockets_needName((PocketNeed)asking->ai.asking_you), asking->female ? "her" : "him"); + snprintf(game->hint, sizeof(game->hint), "%s asks you for %s - B: your pockets, look at %s, Enter: %s", + asking->name, Pockets_needName((PocketNeed)asking->ai.asking_you), asking->female ? "her" : "him", + asking->ai.asking_you == POCKET_ASHTRAY ? "hold it out" : "give it"); else if (shown) snprintf(game->hint, sizeof(game->hint), "%s follows you: walk to where the firewood should go and left click - Backspace: never mind", shown->name); else if (watcher) diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c index 3ee2260..9b9ec72 100644 --- a/entropie/src/people/ai.c +++ b/entropie/src/people/ai.c @@ -1665,6 +1665,84 @@ static bool fetchPullover(Object *self, Object *const *objects, int count, float return true; } +/* A finished cigarette in the fingers (the tidy): to the tin, a bin or the + fire with it; none near: somebody's pocket ashtray (asked for), else wrapped + up and into the pocket. Butts in the pocket (ashtray) go into the tin, a + bin or the fire when passing by - or, the ashtray filling up, on purpose. */ +#define BUTT_WALK 30.0f /* m: a place for it this near: walked over */ +#define BUTT_ASK 6.0f /* m: somebody this near with a pocket ashtray: asked */ +#define BUTT_WAIT 25.0f /* s: asked and nothing came: into the pocket */ +#define BUTT_KEEP 90.0f /* s: still holding it: into the pocket */ +#define BUTT_PASS 2.0f /* m: passing this near, the pocket's emptied */ +#define TRAY_FULL 8 /* butts: off to empty the pocket ashtray */ + +static bool buttGoesIn(const Object *o) { + return (o->type == OBJ_FURNITURE && o->variant == FURN_ASHTRAY) || o->type == OBJ_BIN || (o->type == OBJ_FIREPLACE && o->burning); +} + +static Object *buttPlaceNear(const Object *self, Object *const *objects, int count, float range) { + Object *best = NULL; + for (int i = 0; i < count; i++) { + Object *o = objects[i]; + if (!buttGoesIn(o) || ai_skipped(&self->ai, o)) continue; + float d = flatDistance(o->position, self->position); + if (d < range) { range = d; best = o; } + } + return best; +} + +static void buttsInto(Object *self, Object *place, int n) { + if (place->type == OBJ_FURNITURE) place->stored += n; /* (the tin fills up; a bin or the fire: gone) */ + Hands_touch(self, vec3_add(Object_colliderCenter(place, 0), vec3(0.0f, place->type == OBJ_FIREPLACE ? 0.0f : 0.3f, 0.0f))); +} + +static bool binButt(Object *self, Object *const *objects, int count, float dt) { + Ai *ai = &self->ai; + if (self->pocket_butts > 0) { /* passing by: emptied */ + Object *at = buttPlaceNear(self, objects, count, BUTT_PASS); + if (at && !self->sitting) { buttsInto(self, at, self->pocket_butts); self->pocket_butts = 0; } + } + if (!self->butt_held) { + if (!self->pocket_tray || self->pocket_butts < TRAY_FULL || ai->mode == AI_TABLE) return false; + Object *place = buttPlaceNear(self, objects, count, BUTT_WALK); /* (the ashtray full: emptied) */ + if (!place) return false; + if (goTo(self, place, objects, count, 0.6f, dt)) { buttsInto(self, place, self->pocket_butts); self->pocket_butts = 0; } + return true; + } + ai->butt_time += dt; + if (ai->butt_time > BUTT_KEEP || (ai->butt_asked && ai->butt_time > BUTT_WAIT && !ai->asking_you)) { + Pockets_keepButt(self); + if (random01(ai) < 0.4f) Ai_say(self, "I'll keep it till I find a bin."); + return false; + } + if (ai->butt_asked) { /* (waiting for the player's ashtray) */ + if (ai->butt_time > BUTT_WAIT) { ai->asking_you = 0; Pockets_keepButt(self); Ai_say(self, "Never mind, I'll keep it."); } + return false; + } + Object *place = buttPlaceNear(self, objects, count, BUTT_WALK); + if (place) { + if (self->sitting) self->sitting = false; + if (ai->butt_time > 40.0f) { skipTarget(ai, place); return false; } /* (can't get there) */ + if (!goTo(self, place, objects, count, place->type == OBJ_FIREPLACE ? 0.4f : 0.6f, dt)) return true; + buttsInto(self, place, 1); + self->butt_held = false; + if (place->type == OBJ_BIN && random01(ai) < 0.25f) Ai_say(self, "There we go."); + return true; + } + ai->butt_asked = true; + Object *giver = Pockets_hasNear(self, POCKET_ASHTRAY, objects, count, BUTT_ASK); + if (!giver) return false; /* (keeps it a while: maybe they pass a bin) */ + if (giver->type == OBJ_PLAYER) { + Ai_say(self, "No bin anywhere - got your pocket ashtray?"); + ai->asking_you = (int)POCKET_ASHTRAY; /* (see the pockets: B, hold it out) */ + return false; + } + Ai_say(self, withName("%s, can I use your pocket ashtray?", giver)); + Pockets_give(giver, self, POCKET_ASHTRAY); + if (giver->type == OBJ_HUMAN) Ai_say(giver, "Sure, put it in."); + return false; +} + void Ai_update(Object *self, Object *const *objects, int count, float dt) { ai_ensureReady(self); Ai *ai = &self->ai; @@ -1721,6 +1799,8 @@ 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; + /* A finished cigarette in the fingers: to the tin, a bin, the fire */ + if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || ai->mode == AI_TABLE) && binButt(self, objects, count, dt)) return; /* Cold, the pullover put down somewhere: off to get it */ if ((ai->mode == AI_IDLE || ai->mode == AI_WANDER || ai->mode == AI_TABLE) && fetchPullover(self, objects, count, dt)) return; /* Out of cigarettes: to the machine */ diff --git a/entropie/src/people/ai.h b/entropie/src/people/ai.h index 83ad489..72a3659 100644 --- a/entropie/src/people/ai.h +++ b/entropie/src/people/ai.h @@ -176,6 +176,8 @@ typedef struct { bool want_smokes; /* out of cigarettes, nobody gives one: off to the machine */ float smokes_time; /* ... on the way this long */ bool asked_you; /* asked the player for one (nothing came of it: the machine next time) */ + float butt_time; /* holding a finished cigarette (Object.butt_held) this long ... */ + bool butt_asked; /* ... and asked somebody for their pocket ashtray already */ int asking_you; /* asked the player for this (PocketNeed: a paper, a filter, tobacco), 0: nothing */ bool filter_asked; /* asked for a filter this time already (none: rolls one without) */ float jacket_time; /* ... on the way this long */ diff --git a/entropie/src/people/pockets.c b/entropie/src/people/pockets.c index c3776d0..9b77049 100644 --- a/entropie/src/people/pockets.c +++ b/entropie/src/people/pockets.c @@ -96,6 +96,7 @@ static int spare(const Object *o, PocketNeed what) { case POCKET_PAPERS: return o->papers; case POCKET_FILTERS: return o->filters; case POCKET_TOBACCO: return o->tobacco; + case POCKET_ASHTRAY: return o->pocket_tray ? 2 : 0; case POCKET_NOTHING: break; } return 0; @@ -113,7 +114,7 @@ Object *Pockets_hasNear(Object *who, PocketNeed what, Object *const *objects, in } int Pockets_give(Object *from, Object *to, PocketNeed what) { - static const int SOME[] = { [POCKET_NOTHING] = 0, [POCKET_PAPERS] = 3, [POCKET_FILTERS] = 5, [POCKET_TOBACCO] = 2 }; + static const int SOME[] = { [POCKET_NOTHING] = 0, [POCKET_PAPERS] = 3, [POCKET_FILTERS] = 5, [POCKET_TOBACCO] = 2, [POCKET_ASHTRAY] = 1 }; int n = spare(from, what); if (n > SOME[what]) n = SOME[what]; if (n <= 0) return 0; @@ -121,6 +122,7 @@ int Pockets_give(Object *from, Object *to, PocketNeed what) { case POCKET_PAPERS: from->papers -= n; to->papers += n; break; case POCKET_FILTERS: from->filters -= n; to->filters += n; break; case POCKET_TOBACCO: from->tobacco -= n; to->tobacco += n; break; + case POCKET_ASHTRAY: if (!to->butt_held) return 0; to->butt_held = false; from->pocket_butts++; break; case POCKET_NOTHING: break; } return n; @@ -131,6 +133,7 @@ const char *Pockets_needName(PocketNeed what) { case POCKET_PAPERS: return "a paper"; case POCKET_FILTERS: return "a filter"; case POCKET_TOBACCO: return "a bit of tobacco"; + case POCKET_ASHTRAY: return "your pocket ashtray"; case POCKET_NOTHING: break; } return "something"; @@ -147,5 +150,10 @@ int Pockets_slots(const Object *who) { int Pockets_used(const Object *who) { return (who->cigarettes > 0) + (who->tobacco > 0) + (who->papers > 0) + (who->filters > 0) + - (who->lighter ? 1 : 0) + (who->keys ? 1 : 0) + (who->urinellas > 0); + (who->lighter ? 1 : 0) + (who->keys ? 1 : 0) + (who->urinellas > 0) + (who->pocket_tray || who->pocket_butts > 0); +} + +void Pockets_keepButt(Object *who) { + who->pocket_butts++; + who->butt_held = false; } diff --git a/entropie/src/people/pockets.h b/entropie/src/people/pockets.h index 7768a4d..54aab56 100644 --- a/entropie/src/people/pockets.h +++ b/entropie/src/people/pockets.h @@ -32,15 +32,18 @@ bool Pockets_giveSmoke(Object *from, Object *to); /* What who lacks to roll one (they have tobacco or papers, not both - or no filter and they use them): POCKET_NOTHING when they can, or have ready ones. */ -typedef enum { POCKET_NOTHING, POCKET_PAPERS, POCKET_FILTERS, POCKET_TOBACCO } PocketNeed; +typedef enum { POCKET_NOTHING, POCKET_PAPERS, POCKET_FILTERS, POCKET_TOBACCO, POCKET_ASHTRAY } PocketNeed; PocketNeed Pockets_need(const Object *who); /* Somebody within range who has that to spare (not who). */ Object *Pockets_hasNear(Object *who, PocketNeed what, Object *const *objects, int count, float range); -/* Gives some of it (3 papers, 5 filters, a pinch for 2): how many. */ +/* Gives some of it (3 papers, 5 filters, a pinch for 2; the pocket ashtray: to's butt goes into from's): how many. */ int Pockets_give(Object *from, Object *to, PocketNeed what); /* "a paper", "a filter", "a bit of tobacco" */ const char *Pockets_needName(PocketNeed what); +/* A finished cigarette: into who's pocket ashtray (or wrapped up in the pocket: tidy ones without one). */ +void Pockets_keepButt(Object *who); + /* Pockets: how many (by what they wear) and how many have something in them. */ int Pockets_slots(const Object *who); int Pockets_used(const Object *who); diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h index 2329f8e..718d281 100644 --- a/entropie/src/world/object.h +++ b/entropie/src/world/object.h @@ -203,6 +203,9 @@ typedef struct Object { int papers; /* ... rolling papers (a packet: PAPERS_PACK) ... */ int filters; /* ... filter tips (a bag: FILTERS_BAG) ... */ bool filter_habit; /* ... rolls with a filter (without one they ask for one first) */ + bool pocket_tray; /* ... a pocket ashtray (a little tin with a lid) ... */ + int pocket_butts; /* ... butts in it (or, without one, wrapped up in the pocket): emptied at a bin, the tin or the fire */ + bool butt_held; /* characters: a finished cigarette in the fingers, looking for where it goes (ai.c binButt) */ const char *keys; /* ... keys in the pocket ("Car keys"), or NULL */ bool work_trousers; /* the player: work trousers, more pockets (see Pockets_slots) */ float smoker; /* 0 = doesn't smoke .. 1 = chain smoker: how fast the craving grows */ diff --git a/entropie/src/world/scene.c b/entropie/src/world/scene.c index d0bb9ec..7c41ad9 100644 --- a/entropie/src/world/scene.c +++ b/entropie/src/world/scene.c @@ -97,6 +97,7 @@ static const int TOBACCO[] = { POUCH_ROLLUPS, 40, 10, 25, 60, 0, 0 }; static const int PAPERS[] = { PAPERS_PACK, 20, 5, 0, 30, 0, 0 }; /* (Toni forgot his) */ static const int FILTERS[] = { FILTERS_BAG, 60, 0, 15, 0, 0, 0 }; /* (Jana is out) */ static const bool FILTER_HABIT[] = { true, true, true, true, false, false, false }; /* (Kurti: none, ever) */ +static const bool POCKET_TRAY[] = { true, false, true, false, false, false, false }; /* a pocket ashtray: you and Jana */ static const char *const KEYS_IN_POCKET[] = { "Car keys", NULL, NULL, NULL, "House keys", NULL, NULL }; static const int GARMENT[] = { GARMENT_TROUSERS, GARMENT_TROUSERS, GARMENT_SKIRT, GARMENT_SHORTS, GARMENT_TROUSERS, GARMENT_TROUSERS, GARMENT_SHORTS }; /* Years: past 45 slower on their feet (Kurti is 74). */ @@ -252,6 +253,7 @@ Object *Scene_load(World *world) { obj->papers = PAPERS[characters % TEMPERAMENT_COUNT]; obj->filters = FILTERS[characters % TEMPERAMENT_COUNT]; obj->filter_habit = FILTER_HABIT[characters % TEMPERAMENT_COUNT]; + obj->pocket_tray = POCKET_TRAY[characters % TEMPERAMENT_COUNT]; obj->keys = KEYS_IN_POCKET[characters % TEMPERAMENT_COUNT]; obj->work_trousers = characters == 0; /* (you: work trousers, plenty of pockets) */ obj->craving = 0.3f + 0.15f * (float)(characters % 3); diff --git a/entropie/src/world/world.c b/entropie/src/world/world.c index 09d08d1..f03aba6 100644 --- a/entropie/src/world/world.c +++ b/entropie/src/world/world.c @@ -1,6 +1,7 @@ #include "world/world.h" #include "world/camp.h" #include "world/clothes.h" +#include "people/pockets.h" #include "people/condition.h" #include "people/dog.h" #include "world/forestry.h" @@ -659,8 +660,24 @@ static void overFeet(Object *o) { #define FAR_DETAIL 30.0f /* m from the player: beyond, feet and hands don't look out for things */ -/* Smoking: puffs breathed out and off the tip, the butt when it's done (into a - burning fire close by, else on the ground). */ +/* Smoking: puffs breathed out and off the tip, the butt when it's done: into a + burning fire close by, the tin or a bin near (the tidy, and you), into a + pocket ashtray; the tidy keep it in the fingers and go and find a place for + it (ai.c binButt); the others drop it or flick it away. */ +#define BUTT_REACH 4.0f /* m: the tin or a bin this near */ + +/* Where a butt goes: the tin or a bin within reach. */ +static Object *buttPlace(const World *world, const Object *o, float reach) { + Object *best = NULL; + for (int i = 0; i < world->count; i++) { + Object *t = world->objects[i]; + if (!(t->type == OBJ_FURNITURE && t->variant == FURN_ASHTRAY) && t->type != OBJ_BIN) continue; + float d = vec3_length(vec3_sub(t->position, o->position)); + if (d < reach) { reach = d; best = t; } + } + return best; +} + #define CRAVE_TIME 300.0f /* s from nothing to a strong craving, for a chain smoker */ static void smoke(World *world, Object *o, Vec3 facing, float dt) { @@ -670,14 +687,19 @@ static void smoke(World *world, Object *o, Vec3 facing, float dt) { if (Body_takeButt(&o->body)) { Vec3 at = Figure_toWorld(o->position, facing, sk->cig_glow >= 0.0f ? sk->cig_tip : sk->joints[JOINT_HAND_R]); Object *fire = Camp_fireNear(world->objects, world->count, o->position, 3.0f, true); - Object *tray = NULL; /* (an ashtray near: the tidy and you put it in there) */ + Object *tray = buttPlace(world, o, BUTT_REACH); bool tidy = o->type == OBJ_PLAYER || (o->type == OBJ_HUMAN && (o->ai.character.likes[ORDER_TRASH] > 0.2f || o->temperament.pride > 0.4f) && o->drunk < 0.6f); - for (int i = 0; i < world->count && tidy; i++) { - Object *t = world->objects[i]; - if (t->type == OBJ_FURNITURE && t->variant == FURN_ASHTRAY && vec3_length(vec3_sub(t->position, o->position)) < 4.0f) tray = t; - } - if (tray) tray->stored++; - else if (!fire) { + if (fire) { + /* (flicked into the flames) */ + } else if (tray && (tidy || worldDice(world) < 0.4f)) { + if (tray->type == OBJ_FURNITURE) tray->stored++; /* (a bin: in it, gone) */ + } else if (o->pocket_tray) { + Pockets_keepButt(o); + } else if (tidy && o->type == OBJ_HUMAN) { + o->butt_held = true; /* (off to find a place for it, or ask) */ + o->ai.butt_time = 0.0f; + o->ai.butt_asked = false; + } else { Object *butt = Object_create(OBJ_TRASH, PALETTE_WARM, at, vec3(-1.0f, 0.0f, 0.0f), 0.005f); if (butt) { snprintf(butt->name, sizeof(butt->name), "Cigarette butt");