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.11.0: taking tents down
- Hold left click right at a standing tent: after a moment it comes down (pegs out, cover off, poles down, folded into its bag) - Sleeping bags and mats that were inside come out at the door - The owner and people nearby help; a tent packed on purpose stays packed - New Gear_create (car.c) for camping things
commit_message.txt | 12 ++--- entropie/Makefile | 2 +- entropie/README.md | 6 ++- entropie/STATE.md | 8 +++- entropie/src/game/game.h | 1 + entropie/src/game/game_actions.c | 51 +++++++++++++++------ entropie/src/people/ai.c | 10 ++-- entropie/src/world/car.c | 16 ++++--- entropie/src/world/car.h | 3 ++ entropie/src/world/object.h | 3 +- entropie/src/world/tent.c | 99 ++++++++++++++++++++++++++++++++++++---- entropie/src/world/tent.h | 6 +++ 12 files changed, 174 insertions(+), 43 deletions(-) diff --git a/commit_message.txt b/commit_message.txt index 327e736..e487023 100644 --- a/commit_message.txt +++ b/commit_message.txt @@ -1,7 +1,7 @@ -entropie 2.10.0: skirt when sitting, rain in tents +entropie 2.11.0: taking tents down -- A skirt or dress lies over the lap when sitting (knees and shins show), - instead of a ball round the thighs -- Rain: the friends shelter in their own tent at night or when it's nearer - than the garage kitchen; they doze and come out when it stops -- Kurti no longer wanders with the chainsaw (checked in the NPC audit) +- Hold left click right at a standing tent: after a moment it comes down + (pegs out, cover off, poles down, folded into its bag) +- Sleeping bags and mats that were inside come out at the door +- The owner and people nearby help; a tent packed on purpose stays packed +- New Gear_create (car.c) for camping things diff --git a/entropie/Makefile b/entropie/Makefile index c5c4d9a..4185ade 100644 --- a/entropie/Makefile +++ b/entropie/Makefile @@ -1,5 +1,5 @@ APP := entropie -VERSION := 2.10.0 +VERSION := 2.11.0 PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin diff --git a/entropie/README.md b/entropie/README.md index 6849ced..9764bd5 100644 --- a/entropie/README.md +++ b/entropie/README.md @@ -67,6 +67,10 @@ Painted in the rotoscope style of *Waking Life* (2001): - Z at your tent's door: crawl in and sleep (better with your sleeping bag in it: warm, and you're rested sooner). Z again: you crawl out - Sleepy, the others go to bed in their tents +- Taking one down: hold left click right at a standing tent. After a moment + the pegs come out, then the cover, the poles, and it's folded into its bag. + Sleeping bags and mats come out at the door; the owner helps +- A tent packed on purpose stays packed (nobody puts it up again on their own) ## The site @@ -683,7 +687,7 @@ say no more often and don't come to you for a bit. | P | Pee (next to the toilet: use it); Shift: further, mouse: aim | | K | Light a cigarette / put it out | | Left click at the car's boot | Take out the next thing | -| Left click on a tent | Spread it out; hold: put it up | +| Left click on a tent | Spread it out; hold: put it up (a standing one: take it down) | | Z | Sleep / get up (at your tent's door: in it) | | C | Sit down / get up; on fire: drop and roll; at a fire with a drink: pour it on | | Space (asleep, hold) | Sleep faster | diff --git a/entropie/STATE.md b/entropie/STATE.md index b10b338..4007350 100644 --- a/entropie/STATE.md +++ b/entropie/STATE.md @@ -220,6 +220,10 @@ Round 2.10: - Rain, into the tent (`ai.c` shelter): the friends go into their own tent (up) at night (21-7) or when it's nearer than the kitchen; in, they doze (`rain_nap`: not woken by being rested, `condition.c`); `tent.c` Tent_update gets them up once the rain is under RAIN_NAP_END 0.15 (sleepy, over 0.6: they sleep on). - Kurti and the chainsaw: gone with "no helping" (2.8) - NPC audit: Kurti 33 joint pops (was 300-900), no tools in hand in 10 min. +Round 2.11: +- Taking a tent down (`tent.c` Tent_takeDown, `tent_down`): anybody in it comes out (woken); then Tent_work runs the stages backwards (workDown: each DOWN_QUICKER 0.6 of the time up, the pegs out one by one from the last, the cover off, the poles down, FOLD_TIME 7 s folding) and returns TENT_DOWN + the stage (TENT_DOWN_PACKED folded). Tent_update packs it (`pack`): its bag (`Gear_create`, from car.c) where it lay, `keep` (its owner doesn't put it up again on their own: `ai.c` pitchTents), the sleeping bags and mats that were in it at the door; the tent used up (no owner: Tent_of skips it). Drawn the other way round (`shownWork`). +- You: hold left click right at a standing tent (0.6 m, so a click near it still takes things): TAKE_DOWN_HOLD 1 s, then it comes down while you hold (`game_actions.c` workTent). The owner and anybody near help (helpers drop a packed job). Hints: "hold left click: take it down". + 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). @@ -366,7 +370,7 @@ 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: taking them down again; wind blowing an unpegged one about; people busy (work) when sleepy go to bed late +- Tents, still to do: wind blowing an unpegged one about; people busy (work) when sleepy go to bed late; NPCs taking theirs down on their own (leaving) - Rain, still to do: thunder; wet clothes - Main building: "the left building's roof peak is too far left" - to be clarified @@ -377,4 +381,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.10.0 (build 4d58e30)` +State of `entropie 2.11.0 (build 7d62268)` diff --git a/entropie/src/game/game.h b/entropie/src/game/game.h index 0c133f6..ce199bb 100644 --- a/entropie/src/game/game.h +++ b/entropie/src/game/game.h @@ -74,6 +74,7 @@ typedef struct { Object *putting_up; /* the ladder you are putting up (hands on it, standing still) */ Arrival arrival; /* the start: in by car */ struct Object *tent; /* the tent you're working on (left button held) */ + float tent_hold; /* ... held this long at a standing one (a moment: you start taking it down) */ float dt; /* this frame's step */ bool rain_told; /* told it's raining (this shower) */ } Game; diff --git a/entropie/src/game/game_actions.c b/entropie/src/game/game_actions.c index 97a15c9..0a95bf0 100644 --- a/entropie/src/game/game_actions.c +++ b/entropie/src/game/game_actions.c @@ -703,16 +703,16 @@ static bool takePullover(Game *game) { #define TENT_REACH 1.2f /* m: a tent this near to work on it */ -/* A packed tent (its bag lying) or a tent not up yet, near and in front. */ -static Object *tentNear(const Game *game, bool packed) { +/* A packed tent (its bag lying) or a tent not up yet (or a standing one), near and in front. */ +static Object *tentFind(const Game *game, bool packed, bool standing) { const Object *p = game->player; Vec3 facing = Object_facing(p); Object *best = NULL; - float best_gap = packed ? 0.9f : TENT_REACH; + float best_gap = packed ? 0.9f : standing ? 0.6f : TENT_REACH; /* (a standing one: right at it, so a click near it still takes things) */ for (int i = 0; i < game->world.count; i++) { Object *o = game->world.objects[i]; bool fits = packed ? o->type == OBJ_GEAR && o->variant >= GEAR_TENT_SMALL && !o->is_held && !o->broken - : o->type == OBJ_TENT && o->tent_stage < TENT_UP; + : o->type == OBJ_TENT && !o->broken && (standing ? o->tent_stage >= TENT_UP && !o->tent_down : o->tent_stage < TENT_UP || o->tent_down); if (!fits) continue; Vec3 to = vec3_sub(o->position, p->position); to.y = 0.0f; @@ -722,6 +722,8 @@ static Object *tentNear(const Game *game, bool packed) { return best; } +static Object *tentNear(const Game *game, bool packed) { return tentFind(game, packed, false); } + /* Left click: a packed tent in front spread out (the door towards you), or a tent going up: work on it (held). */ static int tentClick(Game *game) { Object *p = game->player, *bag = tentNear(game, true); @@ -736,22 +738,36 @@ static int tentClick(Game *game) { return CLICK_NONE; } Object *tent = tentNear(game, false); + if (!tent) tent = tentFind(game, false, true); /* (standing: held a moment, it comes down) */ if (!tent) return -1; + game->tent_hold = 0.0f; if (p->hands.state[HAND_LEFT] != HAND_FREE && p->hands.state[HAND_RIGHT] != HAND_FREE) { say(game, "A hand free for that"); return CLICK_NONE; } game->tent = tent; return CLICK_TENT; } /* Left button held at a tent: the next bit of it. */ +#define TAKE_DOWN_HOLD 1.0f /* s: held this long at a standing tent, you start taking it down */ + static bool workTent(Game *game) { Object *p = game->player, *tent = game->tent; - if (!tent || tent->tent_stage >= TENT_UP || Hands_gapFrom(p->position, p->look.height, tent) > TENT_REACH + 0.6f) return false; + if (!tent || tent->broken || Hands_gapFrom(p->position, p->look.height, tent) > TENT_REACH + 0.6f) return false; + if (tent->tent_stage >= TENT_UP && !tent->tent_down) { /* standing: a moment first (no accidents) */ + if (game->tent_hold == 0.0f) say(game, "Keep holding to take the tent down"); + if ((game->tent_hold += game->dt) < TAKE_DOWN_HOLD) return true; + Tent_takeDown(game->world.objects, game->world.count, tent); + say(game, Tent_isTheirs(tent, p) || !tent->owner ? "You start taking the tent down - pegs out first" + : "You start taking their tent down - pegs out first"); + } Hands_touch(p, Tent_handsAt(tent, p, tent->tent_work * 18.0f)); int got = Tent_work(tent, p, game->dt); static const char *const SAID[] = { "", "Spread out - now the poles in and up", "The poles are in - now pull the cover over it", "The cover's on - now peg it down (a stone helps)", "All pegged - the tent stands" }; - if (got >= 0) say(game, SAID[got % 5]); - return tent->tent_stage < TENT_UP; + static const char *const DOWN[] = { "Poles down - now fold it up", "The cover's off - now the poles down", "The pegs are out - now pull the cover off" }; + if (got == TENT_DOWN_PACKED) { say(game, "Folded up and in its bag - E / Q: carry it"); return false; } + if (got >= TENT_DOWN) say(game, DOWN[(got - TENT_DOWN) % 3]); + else if (got >= 0) say(game, SAID[got % 5]); + return tent->tent_stage < TENT_UP || tent->tent_down; } /* At the cigarette machine: a pack. */ @@ -879,14 +895,19 @@ static bool game_bootHint(Game *game, char *out, size_t size) { static bool tentHint(Game *game, char *out, size_t size) { Object *p = game->player, *bag = tentNear(game, true), *tent = tentNear(game, false); if (bag) { snprintf(out, size, "A tent, packed - left click: spread it out here (the door towards you) - E / Q: carry it"); return true; } - if (tent) { snprintf(out, size, "The tent: hold left click - %s%s", Tent_job(tent), tent->tent_stage == TENT_PEGS ? " (a stone in the other hand: quicker)" : ""); return true; } - Object *mine = Tent_of(game->world.objects, game->world.count, p); - if (!mine || mine->tent_stage < TENT_COVER) return false; - Vec3 door = Tent_door(mine); - if (hypotf(door.x - p->position.x, door.z - p->position.z) > 1.6f) return false; - if (Hands_carries(p, OBJ_GEAR)) { snprintf(out, size, "Your tent's door: right click - put it down here, it goes in"); return true; } - if (mine->tent_stage >= TENT_UP) { - snprintf(out, size, "Your tent%s - Z: crawl in and sleep", Tent_hasBed(mine, p) ? "" : " (your sleeping bag isn't in it: put it down at the door)"); + if (tent) { snprintf(out, size, "The tent: hold left click - %s%s", Tent_job(tent), tent->tent_stage == TENT_PEGS && !tent->tent_down ? " (a stone in the other hand: quicker)" : ""); return true; } + Object *mine = Tent_of(game->world.objects, game->world.count, p), *standing = tentFind(game, false, true); + Vec3 door = mine ? Tent_door(mine) : p->position; + bool at_door = mine && mine->tent_stage >= TENT_COVER && hypotf(door.x - p->position.x, door.z - p->position.z) <= 1.6f; + if (at_door && Hands_carries(p, OBJ_GEAR)) { snprintf(out, size, "Your tent's door: right click - put it down here, it goes in"); return true; } + if (at_door && mine->tent_stage >= TENT_UP && !mine->tent_down) { + snprintf(out, size, "Your tent%s - Z: crawl in and sleep%s", Tent_hasBed(mine, p) ? "" : " (your sleeping bag isn't in it: put it down at the door)", + standing == mine ? " - hold left click: take it down" : ""); + return true; + } + if (standing) { + snprintf(out, size, "%s%s tent - hold left click: take it down", standing->owner == p ? "Your" : standing->owner ? standing->owner->name : "A", + standing->owner && standing->owner != p ? "'s" : ""); return true; } return false; diff --git a/entropie/src/people/ai.c b/entropie/src/people/ai.c index 1753c8d..4ca5f8a 100644 --- a/entropie/src/people/ai.c +++ b/entropie/src/people/ai.c @@ -1822,7 +1822,11 @@ static void workOnTent(Object *self, Object *tent, Object *const *objects, int c turnTowards(self, vec3_sub(hands, self->position), dt); Hands_touch(self, hands); int got = Tent_work(tent, self, dt); - if (got >= 0 && random01(ai) < 0.6f) { + if (got >= TENT_DOWN) { + static const char *const DOWN[] = { "Folding it up now.", "Poles down.", "Cover's off.", "", "" }; + if (got < TENT_DOWN_PACKED && random01(ai) < 0.5f) Ai_say(self, DOWN[got - TENT_DOWN]); + else if (got == TENT_DOWN_PACKED && random01(ai) < 0.7f) Ai_say(self, "Packed. That one's done."); + } else if (got >= 0 && random01(ai) < 0.6f) { static const char *const SAY[] = { "", "Poles in - up it goes.", "Now the cover over it.", "Pegs next - who's got a stone?", "There - it stands!" }; Ai_say(self, SAY[got % 5]); } @@ -1839,7 +1843,7 @@ static bool pitchTents(Object *self, Object *const *objects, int count, float dt } if (!tent) { /* still in its bag: spread it out where it lies */ Object *bag = ownGear(self, objects, count, true); - if (bag && !bag->is_held && flatDistance(bag->position, self->position) < TENT_WALK) { + if (bag && !bag->is_held && !bag->keep && flatDistance(bag->position, self->position) < TENT_WALK) { if (self->sitting) self->sitting = false; if (!goToPoint(self, bag->position, 0.6f, objects, count, dt)) return true; freeHands(self, NULL); @@ -1850,7 +1854,7 @@ static bool pitchTents(Object *self, Object *const *objects, int count, float dt } /* somebody pitching one near: a hand */ Object *job = ai->tent_job; - if (job && (job->tent_stage >= TENT_UP || (ai->tent_time += dt) > HELP_LONG || job->tent_idle > 20.0f)) job = ai->tent_job = NULL; + if (job && (job->broken || (job->tent_stage >= TENT_UP && !job->tent_down) || (ai->tent_time += dt) > HELP_LONG || job->tent_idle > 20.0f)) job = ai->tent_job = NULL; if (!job && (ai->mode == AI_IDLE || ai->mode == AI_WANDER)) { for (int i = 0; i < count && !job; i++) { Object *t = objects[i]; diff --git a/entropie/src/world/car.c b/entropie/src/world/car.c index 4082104..0e5c72e 100644 --- a/entropie/src/world/car.c +++ b/entropie/src/world/car.c @@ -88,6 +88,15 @@ static Vec3 gearColor(GearKind kind, const Object *owner) { return vec3(0.5f, 0.5f, 0.5f); } +Object *Gear_create(GearKind kind, Object *owner, Vec3 at) { + Object *item = Object_create(OBJ_GEAR, PALETTE_STANDARD, at, vec3_zero(), (float)kind); + if (!item) return NULL; + item->owner = owner; + Mesh_destroy(&item->mesh); + item->mesh = Shapes_gear((int)kind, gearColor(kind, owner)); + return item; +} + Object *Car_takeOut(World *world, Object *car) { CarLoad *l = car->load; if (!l || l->n == 0 || !car->boot_open) return NULL; @@ -101,12 +110,7 @@ Object *Car_takeOut(World *world, Object *car) { } else { GearKind g = kind == CARGO_SLEEPING_BAG ? GEAR_SLEEPING_BAG : kind == CARGO_MAT ? GEAR_MAT : kind == CARGO_TENT_SMALL ? GEAR_TENT_SMALL : GEAR_TENT_BIG; - item = Object_create(OBJ_GEAR, PALETTE_STANDARD, at, vec3_zero(), (float)g); - if (item) { - item->owner = owner; - Mesh_destroy(&item->mesh); - item->mesh = Shapes_gear((int)g, gearColor(g, owner)); - } + item = Gear_create(g, owner, at); } if (!item) return NULL; item->rotation.y = car->rotation.y + PI_F * 0.5f; /* (lying across the back of it) */ diff --git a/entropie/src/world/car.h b/entropie/src/world/car.h index c106c79..1e14e70 100644 --- a/entropie/src/world/car.h +++ b/entropie/src/world/car.h @@ -60,4 +60,7 @@ Object *Crate_takeBottle(World *world, Object *crate); the boot (ai.take_from) gets it lying there for them (ai.target). */ void Car_serve(World *world); +/* Somebody's camping thing (OBJ_GEAR) lying at `at` (not added to the world yet), or NULL. */ +Object *Gear_create(GearKind kind, Object *owner, Vec3 at); + #endif /* WORLD_CAR_H */ diff --git a/entropie/src/world/object.h b/entropie/src/world/object.h index 7491639..144e876 100644 --- a/entropie/src/world/object.h +++ b/entropie/src/world/object.h @@ -161,7 +161,7 @@ typedef struct Object { struct CarLoad *load; /* cars: what's in the boot (malloc'd, freed with the car) */ bool boot_open; /* cars: the boot lid up */ Mesh inside; /* cars: what you see of it from the driver's seat */ - bool keep; /* trees: may not be cut down (round the meadow behind your garages) */ + bool keep; /* trees: may not be cut down (round the meadow behind your garages); a tent bag: packed on purpose (its owner leaves it packed) */ float wake; /* drinks: sleepiness off per sip (mate, cola) */ bool rain_out; /* characters: out in the rain (no roof over them) */ bool lives_here; /* characters: the house is theirs (Kurti and his sons): in and out of it */ @@ -172,6 +172,7 @@ typedef struct Object { float tent_work; /* ... how far the stage is (0..1) ... */ float tent_idle; /* ... seconds since anybody worked on it */ 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 */ struct Object *in_tent; /* characters: sleeping in this tent (no collisions with it) */ bool rain_nap; /* in their tent out of the rain, dozing: not woken by being rested, up when it stops */ struct Object *riding; /* characters: sitting in this car (hidden, no physics, no AI: see car.c) ... */ diff --git a/entropie/src/world/tent.c b/entropie/src/world/tent.c index ae10995..513d025 100644 --- a/entropie/src/world/tent.c +++ b/entropie/src/world/tent.c @@ -14,6 +14,13 @@ #define PEG_TIME 2.5f /* ... each peg (with a stone to hit it: twice as quick) */ #define DOOR_OUT 0.45f /* in front of the door: this far out from the cover */ #define BED_REACH 0.8f /* a sleeping bag or mat put down this near the door goes in */ +#define DOWN_QUICKER 0.6f /* taking it down: each bit this much of the time putting it up took ... */ +#define FOLD_TIME 7.0f /* ... and folding it up into its bag at the end */ + +/* How far the stage shows: up, its work; coming down, the other way round. */ +static float shownWork(const Object *tent) { + return tent->tent_down ? 1.0f - tent->tent_work : tent->tent_work; +} static Vec3 local(const Object *tent, Vec3 p) { return vec3_add(tent->position, mat4_mul_dir(mat4_rotate_y(tent->rotation.y), p)); @@ -34,13 +41,13 @@ static Vec3 flyColor(const Object *tent) { /* Its look and its box for how far it's up (built again only when that shows). */ static void reshape(Object *tent) { - int big = tent->variant, steps = tent->tent_stage == TENT_POLES || tent->tent_stage == TENT_COVER ? (int)(tent->tent_work * 12.0f) : 0; + int big = tent->variant, steps = tent->tent_stage == TENT_POLES || tent->tent_stage == TENT_COVER ? (int)(shownWork(tent) * 12.0f) : 0; int shown = tent->tent_stage * 1000 + steps * 10 + tent->stored; if (shown == tent->shown) return; tent->shown = shown; Mesh_destroy(&tent->mesh); tent->mesh = Shapes_tent(big, tent->tent_stage, (float)steps / 12.0f, tent->stored, vec3(0.90f, 0.86f, 0.62f), flyColor(tent)); - float h = tent->tent_stage == TENT_LAID ? 0.04f : tent->tent_stage == TENT_POLES ? fmaxf(0.04f, TENT_H(big) * tent->tent_work) : TENT_H(big); + float h = tent->tent_stage == TENT_LAID ? 0.04f : tent->tent_stage == TENT_POLES ? fmaxf(0.04f, TENT_H(big) * shownWork(tent)) : TENT_H(big); tent->colliders[0].center = vec3(0.0f, h * 0.5f, 0.0f); tent->colliders[0].half_extents = vec3(TENT_W(big) * 0.5f, h * 0.5f, TENT_L(big) * 0.5f); Object_syncColliders(tent); @@ -72,10 +79,50 @@ static bool hasStone(const Object *who) { return false; } +void Tent_takeDown(Object *const *objects, int count, Object *tent) { + if (tent->type != OBJ_TENT || tent->tent_down || tent->broken) return; + for (int i = 0; i < count; i++) { /* (anybody in it: out) */ + Object *o = objects[i]; + if (o->in_tent != tent) continue; + o->rain_nap = false; + if (o->body.asleep) Body_getUp(&o->body); + Tent_crawlOut(o); + } + tent->tent_down = true; + tent->tent_work = 0.0f; + if (tent->tent_stage >= TENT_UP) { tent->tent_stage = TENT_PEGS; tent->stored = TENT_PEGS(tent->variant); } + else if (tent->tent_stage == TENT_PEGS && tent->stored == 0) tent->tent_stage = TENT_COVER; + reshape(tent); +} + +/* Coming down: the pegs out, the cover off, the poles down, folded up (packed in Tent_update). */ +static int workDown(Object *tent, float rate, float dt) { + int big = tent->variant; + float time = tent->tent_stage == TENT_LAID ? FOLD_TIME + : DOWN_QUICKER * (tent->tent_stage == TENT_POLES ? (big ? POLES_BIG : POLES_SMALL) + : tent->tent_stage == TENT_COVER ? (big ? COVER_BIG : COVER_SMALL) : PEG_TIME); + tent->tent_work += dt * rate / time; + tent->tent_idle = 0.0f; + int done = -1; + if (tent->tent_work >= 1.0f) { + if (tent->tent_stage == TENT_LAID) { tent->tent_work = 1.0f; return TENT_DOWN_PACKED; } /* (Tent_update packs it) */ + tent->tent_work = 0.0f; + if (tent->tent_stage == TENT_PEGS) { + if (--tent->stored <= 0) { tent->stored = 0; tent->tent_stage = TENT_COVER; done = TENT_DOWN + TENT_COVER; } + } else { + tent->tent_stage--; + done = TENT_DOWN + tent->tent_stage; + } + } + reshape(tent); + return done; +} + int Tent_work(Object *tent, Object *who, float dt) { - if (tent->type != OBJ_TENT || tent->tent_stage >= TENT_UP) return -1; + if (tent->type != OBJ_TENT || tent->broken || (tent->tent_stage >= TENT_UP && !tent->tent_down)) return -1; int big = tent->variant; float rate = (0.75f + 0.5f * who->wits) * (1.0f - 0.5f * fminf(who->drunk, 1.0f)); + if (tent->tent_down) return workDown(tent, rate, dt); float time = tent->tent_stage == TENT_LAID ? LAY_TIME : tent->tent_stage == TENT_POLES ? (big ? POLES_BIG : POLES_SMALL) : tent->tent_stage == TENT_COVER ? (big ? COVER_BIG : COVER_SMALL) : PEG_TIME / (hasStone(who) ? 2.0f : 1.0f); @@ -96,6 +143,12 @@ int Tent_work(Object *tent, Object *who, float dt) { } const char *Tent_job(const Object *tent) { + if (tent->tent_down) switch (tent->tent_stage) { + case TENT_LAID: return "folding it up into its bag"; + case TENT_POLES: return "taking the poles down"; + case TENT_COVER: return "pulling the cover off"; + default: return "pulling the pegs out"; + } switch (tent->tent_stage) { case TENT_LAID: return "spreading it out"; case TENT_POLES: return "putting the poles in"; @@ -107,7 +160,7 @@ const char *Tent_job(const Object *tent) { static Vec3 nextPeg(const Object *tent) { float x, z; - Shapes_tentPeg(tent->variant, tent->stored, &x, &z); + Shapes_tentPeg(tent->variant, tent->tent_down ? (tent->stored > 0 ? tent->stored - 1 : 0) : tent->stored, &x, &z); return vec3(x, 0.0f, z); } @@ -131,8 +184,8 @@ Vec3 Tent_handsAt(const Object *tent, const Object *who, float clock) { float s = me.x < 0.0f ? -1.0f : 1.0f; switch (tent->tent_stage) { case TENT_LAID: return local(tent, vec3(s * hw, 0.08f, clampf(me.z, -hl, hl))); - case TENT_POLES: return local(tent, vec3(s * hw * 0.7f, fmaxf(0.1f, 0.55f * h * tent->tent_work), clampf(me.z, -hl * 0.5f, hl * 0.5f))); - case TENT_COVER: return local(tent, vec3(s * 0.3f, h * 0.8f, -hl + 2.0f * hl * tent->tent_work)); + case TENT_POLES: return local(tent, vec3(s * hw * 0.7f, fmaxf(0.1f, 0.55f * h * shownWork(tent)), clampf(me.z, -hl * 0.5f, hl * 0.5f))); + case TENT_COVER: return local(tent, vec3(s * 0.3f, h * 0.8f, -hl + 2.0f * hl * shownWork(tent))); case TENT_PEGS: { Vec3 p = nextPeg(tent); p.y = 0.06f + 0.14f * fabsf(sinf(clock * 7.0f)); /* (hitting it in) */ @@ -152,7 +205,7 @@ bool Tent_isTheirs(const Object *tent, const Object *who) { Object *Tent_of(Object *const *objects, int count, const Object *who) { for (int i = 0; i < count; i++) - if (objects[i]->type == OBJ_TENT && Tent_isTheirs(objects[i], who)) return objects[i]; + if (objects[i]->type == OBJ_TENT && !objects[i]->broken && Tent_isTheirs(objects[i], who)) return objects[i]; return NULL; } @@ -188,12 +241,42 @@ void Tent_crawlOut(Object *who) { Object_syncColliders(who); } +/* Folded up: its bag where it lay (left packed), the sleeping bags and mats that were in it at the door. */ +static void pack(World *world, Object *tent) { + const int big = tent->variant; + Object *bag = Gear_create(big ? GEAR_TENT_BIG : GEAR_TENT_SMALL, tent->owner, vec3_add(tent->position, vec3(0.0f, 0.02f, 0.0f))); + if (bag) { + bag->keep = true; + bag->rotation.y = tent->rotation.y; + Object_syncColliders(bag); + if (!World_add(world, bag)) Object_destroy(bag); + } + Object *sleeper[2] = { tent->owner, tent->owner2 }; + for (int k = 0; k < 4; k++) { + if (!(tent->tent_beds & (1 << k)) || !sleeper[k / 2]) continue; + Vec3 at = local(tent, vec3((big ? (k / 2 ? 0.45f : -0.45f) : 0.0f) + (k % 2 ? 0.3f : -0.3f), 0.0f, TENT_L(big) * 0.5f * 1.14f + DOOR_OUT + 0.3f)); + at.y = World_groundHeight(world, at.x, at.z, at.y + 0.5f) + 0.02f; + Object *thing = Gear_create(k % 2 ? GEAR_MAT : GEAR_SLEEPING_BAG, sleeper[k / 2], at); + if (!thing) continue; + thing->rotation.y = tent->rotation.y; + Object_syncColliders(thing); + if (!World_add(world, thing)) Object_destroy(thing); + } + tent->tent_beds = 0; + tent->owner = tent->owner2 = NULL; + Object_useUp(tent); +} + #define RAIN_NAP_END 0.15f /* rain: under this, out of the tent again */ void Tent_update(World *world, float dt) { for (int i = 0; i < world->count; i++) { Object *o = world->objects[i]; - if (o->type == OBJ_TENT) { o->tent_idle += dt; continue; } + if (o->type == OBJ_TENT) { + o->tent_idle += dt; + if (o->tent_down && !o->broken && o->tent_stage == TENT_LAID && o->tent_work >= 1.0f) pack(world, o); + continue; + } if (o->rain_nap && (!o->body.asleep || world->rain < RAIN_NAP_END)) { /* in out of the rain: up when it stops */ o->rain_nap = false; if (o->body.asleep && o->needs.sleepiness < 0.6f) Body_getUp(&o->body); diff --git a/entropie/src/world/tent.h b/entropie/src/world/tent.h index 34cf5b9..493253b 100644 --- a/entropie/src/world/tent.h +++ b/entropie/src/world/tent.h @@ -18,6 +18,12 @@ Object *Tent_unpack(World *world, Object *bag, Vec3 door_dir); /* Somebody working on it this frame (both hands free, at it): the next bit done. Returns the stage it got to when one was finished just now, else -1. */ int Tent_work(Object *tent, Object *who, float dt); +/* Taking a tent down (anybody in it comes out): Tent_work then goes the other + way, returning TENT_DOWN + the stage it's down to (or TENT_DOWN_PACKED: folded + up; Tent_update packs it into its bag, left packed, its beds at the door). */ +#define TENT_DOWN 10 +#define TENT_DOWN_PACKED 20 +void Tent_takeDown(Object *const *objects, int count, Object *tent); /* What working on it means now ("putting the poles in") and where the hands go. */ const char *Tent_job(const Object *tent); Vec3 Tent_workSpot(const Object *tent, float side); /* where to stand (side -1 / 0 / +1) */