[#766] Adventure mode: possess one agent + WASD + right-click verbs#776
Merged
Conversation
Foundation for adventure mode (#766). Marks one agent as player-driven so AI brain decisions stop firing for it while every other system (biology, perception, action execution, conversations, ...) keeps running normally. Subsequent issues land input handling, the action context menu, camera follow, and the menu entry on top of this. The marker filters two systems: * arbitrate_every_tick — Without<PlayerControlled> on its main query * update_rational_planning — same Wakeup emitters and the per-tick alertness/state-hash bookkeeping all run unconditionally. Player input (next sub-issue) writes directly into BrainState.chosen_actions, reusing the existing execution pipeline. Three tests covering: (1) AI never touches a possessed agent, (2) biology/perception keep ticking for a possessed agent, (3) removing the marker restores AI control. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adventure-mode movement on top of the #767 marker. Held WASD/arrow keys build a Walk template aimed at the adjacent tile and write it into BrainState.chosen_actions; the existing execution pipeline (start_actions → tick_actions → movement) does the rest. The "movement-state cooldown" falls out naturally — start_actions skips Walk while one is already active, so held keys re-fire as soon as the previous step lands and the existing speed model (calculate_speed × intensity × terrain) keeps authority over per-step pacing. Camera follow lerps toward the possessed agent each frame at alpha=0.15. Suspended while middle-click drag is held so manual pan still works; release lets the lerp pull the camera back. Lerp factored out into a pure helper for unit tests. `player_input` takes `Option<Res<ButtonInput<KeyCode>>>` because TestWorld's MinimalPlugins and headless runs don't include InputPlugin — the system silently no-ops there instead of panicking on missing resource validation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Main menu gets a third button. Clicking it lands directly on the create-world form with `SimMode::Adventure` selected, skipping the mode picker — there's only one Adventure variant so the picker would be a noise step. Adventure reuses the Debug spawn population entirely (a real, populated world is what makes possessing one body interesting); after spawn, `possess_first_person_for_adventure` marks the first Person as PlayerControlled. AI keeps running for everyone else. Empty population is logged as a warning rather than silently leaving Adventure without a body — a future "no humans in this preset" miswire should surface loudly. ESC pause menu and "Release control" are out of scope for the MVP — quitting to main menu already despawns the world via DespawnOnExit, which is sufficient until we want mid-session swap-bodies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Right-click a sprite (or empty space near yourself) to bring up an egui popup of verbs the player can invoke. Selecting one writes the matching ActionTemplate into BrainState.chosen_actions, where the existing execution pipeline picks it up — no new action machinery, just a new way to drive the same one. Verb whitelist per concept covers the playable surface for MVP: * Self: Eat (only when inventory has food), Sleep, Rest, Wait * Person: Wave, Talk, Attack * AppleTree/BerryBush/StoneNode/WoodLog: Harvest * Wolf/Deer: Attack * StorageChest: Take, Deposit * Campfire: WarmUp * LeanTo/House: RestInShelter Empty-space right-click within ~24px of the player opens the self menu — gives the player a forgiving target without having to land the cursor exactly on their sprite. Out of scope for the MVP: greyed-out infeasible entries with tooltips explaining the failed precondition. The current path hides infeasible verbs entirely; surfacing them as teaching tools is a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Original held-key model spawned one Walk action per tile-step: walk to center, action ends with MoveResult::Arrived, next held-key tick admits a fresh Walk. The gap between actions is the visible step / pause / step rhythm — not smooth. Fix: keep a single Walk running for the entire held-key burst. Each player_input tick mutates the active Walk's target_position to a point ~1.5 tiles ahead of the agent in the held direction. move_toward never sees Arrived while a key is held, so movement stays continuous at the existing speed model. Releasing the key freezes the target where it landed; the agent finishes the trailing step and stops naturally. Wall clamp: if the lookahead point isn't walkable, the target falls back to a 0.4-tile-ahead nudge so a player held against rock doesn't push movement into an unwalkable tile (also caught by Gate::TileReachable on first admission, but the runtime nudge keeps held-key feel stable). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…f-click Three things the user hit on the live build: 1. Right-clicking yourself opened a concept-less "Target" menu. Cause: the pick loop hit one of the silhouette child sprites (head/torso/ eyes) that share the player's transform but have no EntityType. Filter the loop to require EntityType so only real world objects match — silhouette parts no longer steal the click and the SELF_- PICK_RADIUS fallback fires correctly. 2. Even with berries in inventory, the self-menu showed no Eat verb because the original `has_edible(ontology)` check could fail in ways that aren't easy to reproduce from a test. Replace the single "Eat" entry with one "Eat <Concept> (qty)" entry per food in the inventory — checking BOTH `has_trait(_, Edible)` AND `is_a(_, Food)` for the membership signal. Belt and suspenders. Player now sees exactly what they have to eat. 3. Fish and Drink are now offered in the self-menu when adjacent to water (reuses the AdjacentToWater gate's predicate). They were missing entirely from the verb whitelist. 4. Adventure mode now auto-pins the character sheet to the possessed agent at spawn time — the player sees their own hunger/energy/pain without having to left-click themselves first. Implemented by selecting the player into `UiState.selected_entities` from the same system that inserts PlayerControlled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues from live playtesting: 1. Tap WASD = move ~1.5 tiles. Cause: my smoother-walk lookahead set the active Walk's target ~1.5 tiles ahead so it never "arrived" while held — but on release, the agent kept walking to that stale target. Combined with the 2.5 hop/sec animation, each tap coincidentally produced one full hop, hence "one press = one unit". Fix: on release, pin the active Walk's target_position to current_pos so it arrives within a tick. Also drop the lookahead from 1.5 tiles to 0.6 tiles — the only requirement is "agent doesn't reach it while keys held," and 0.6 is enough for that while making the brief release-overshoot imperceptible. 2. No build verbs. Add BuildLeanTo / BuildHouse / BuildStorageChest to the self-menu, gated on inventory wood/stone amounts so the menu only offers what the player can actually start. 3. Adventure mode now starts at orthographic scale 0.5 instead of 1.0 — single-character POV cares about immediate surroundings, not a 50×50 overview. The existing scroll-wheel/pinch zoom still works, just from a closer-in baseline. (The "river displacement" the user noticed is the existing terrain elevation lift in sprite_animation.rs, not a bug — sprites get raised/lowered to sit on the visual relief, and water tiles drop the sprite. Logical position is unchanged. Leaving as-is for now.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-tile elevation lookup made sprite Y "teleport" at tile boundaries: crossing the seam between two tiles of different elevation produced an instant jump in sprite height. Replace the lookup with a bilinear sample of the four tile-center elevations surrounding the agent's sub-tile position. Elevation now varies continuously across the map, so an agent walking from grass into a riverbank glides down instead of snapping. Tile rendering itself still discretizes to per-tile heights — this only affects the entity sprite layer, which is what reads `elevation_lift_at`. Map-edge holes (any of the four corner tiles missing) fall back to the average of the present corners rather than 0, so sprites don't plummet at the world boundary. Three new tests cover the bilinear sampler: - exact value at a tile center - midway interpolation between adjacent centers - no discontinuity across the boundary (the bug this commit fixes) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regression from the smooth-walk fix: when no directional key was held, player_input cleared `BrainState.chosen_actions` outright every tick. That clobbered any template the right-click menu had just written — Harvest, Eat, Fish, Build, etc. all got wiped one tick later before `start_actions` could admit them. The user noticed: "i cant harvest any shit anymore from anything." Fix: filter the clear to Walk only. Other action types persist long enough to be admitted on the next FixedUpdate. Walk's stop-on-release behavior is preserved because the active Walk's target_position still gets pinned to current_pos. Also stop blanking the agent's `TargetPosition` component on release — Harvest and friends route through it for proximity navigation, and unconditional clearing was a second source of dropped actions. Regression test: drop a Harvest template into BrainState, hold no keys, tick 5 times, assert Harvest is still there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…o-walk + Construct + Campfire Pile of UX fixes from live playtesting: * Hold Shift while moving = sprint. Bumps locomotion intensity from 0.5 (1.2× base speed) to 1.0 (2.0× base speed) and burns anaerobic stamina the same way Flee does, so sprinting tires the player like it would tire any other agent. Toggle works mid-step without re-admitting Walk. * Menu rows are now full-width clickable (egui::Button with min_size matching the popup width) instead of a tiny button inside a wide popup whose padding wasn't a click target. * Greyed-out verbs with hover tooltips. Builds always show — when materials are missing, the tooltip names the resource and shows have/need (e.g. "Wood: 2/5"). Fish/Drink show greyed when not adjacent to water with "Need to be next to water". Teaches the player what's possible without making them experiment to discover the requirements. * Click outside the popup dismisses it. Same pattern every other context menu uses; matches user expectation. * Build Campfire is in the menu now (forgotten earlier — needs 3 wood, uses ActionType::Build). * ConstructionSite entities offer "Construct" — without this verb, placing a Lean-to / House / Storage Chest just sat there since finishing requires labor on the site. The user noticed: "it just goes into my doing now but it never gets built." * Entity-targeted actions (Harvest, Attack, Talk, Take, Deposit, …) auto-prepend a Walk step when the player isn't on the target tile. start_actions admits Walk first; the targeted action sits in chosen_actions waiting for proximity, then admits when the agent arrives. The user noticed: "i can harvest, attack blah blah from ANY range" — because the original menu wrote raw templates whose proximity preconditions weren't auto-injected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gacy stamina helper
1. Idle now actually restores stamina. Before: Idle had intensity 0.0,
which the effort model multiplies the primitive's recovery profile by,
zeroing it out. So a "standing still" agent got no stamina regen at
all. Bumped Idle intensity to 0.4 (matches Rest). The functional
difference between Idle and Rest stays in completion: Rest auto-ends
when stamina tops up, Idle continues until something else takes
priority.
2. Construction sites are visible now. They were spawned headless (no
Sprite, no Visibility components) by the Build / BuildLeanTo /
BuildHouse / BuildStorageChest action handlers — the entity existed
but had nothing to render. Added Visibility plumbing on the root
spawn and a `sync_construction_site_visuals` system that attaches a
sandy outlined-square placeholder to any site without one. The
visual despawns with its parent when becomes_system transforms the
site into the finished entity.
3. Removed `Stamina::recover()` and the four tests that pinned it. Only
tests called the function — the production stamina recovery path
goes through the effort model in `effort.rs`. Per the testing rule
("test logic, not plumbing"), the deleted tests were pinning a
parallel implementation that was about to drift from production
anyway.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #767
closes #768
closes #771
closes #769
Bundle landing the four foundation sub-issues of the adventure-mode epic (#766) in one PR. The user explicitly asked for one PR rather than four. The epic itself stays open — HUD auto-pin, ESC "release control", and greyed-out tooltips are deferred follow-ups noted in the relevant commits.
What lands
arbitrate_every_tickandupdate_rational_planningfilterWithout<PlayerControlled>. The AI stops thinking for marked entities; biology, perception, action execution, conversations, etc. all keep running normally.SimMode::Adventure. Adventure reuses the Debug spawn population and then possesses the first Person.No new actions, no new rendering, no new world content — adventure mode is an input-shim layer on top of the existing simulation pipeline.
Test plan
tests/test_player_controlled_marker.rs(4 passed): AI never plans/arbitrates, biology+perception still tick, held-D walks east, release restores AI controlsrc/agent/player.rs::tests(6): direction reading, diagonal normalization, opposite-key cancel, arrow alias, lerp boundariessrc/world/spawner.rs::tests(2): possess marker on existing Person, no-op when emptysrc/menu.rs::testsfor SimMode::Adventure configsrc/ui/adventure_menu.rs::tests(5): self-menu inventory gating, per-concept verb tables, fallback to emptycargo build --profile cisucceeds🤖 Generated with Claude Code