Skip to content

NPC needs: executor system with BT integration#86243

Open
dobbry-vechur wants to merge 1 commit intoCleverRaven:masterfrom
dobbry-vechur:improve/npc-bt
Open

NPC needs: executor system with BT integration#86243
dobbry-vechur wants to merge 1 commit intoCleverRaven:masterfrom
dobbry-vechur:improve/npc-bt

Conversation

@dobbry-vechur
Copy link
Copy Markdown
Contributor

Summary

Features "NPC needs: executor system with BT integration"

Purpose of change

NPCs' need-seeking (food, water, warmth, sleep, fire) was handled by scattered ad-hoc logic in address_needs() with no persistent state across turns. This caused several problems: NPCs dithered between equidistant targets, gave up too early, walked through walls (bashing) for routine food, oscillated between guard post and need-seeking, got stuck in infinite forage/harvest loops, and couldn't eat camp food when in caloric deficit with low short-term hunger.

Fixes #86221, fixes #86216.

Describe the solution

An executor layer sits between BT goal selection and the actual need-fulfillment actions. Each need goal (eat_food, drink_water, seek_warmth, go_to_sleep) gets a need_plan state machine that tracks the source kind, concrete target, executor result, and a no-progress counter for sticky multi-turn pursuit.

Key pieces:

  • need_goal_id enum with centralized helpers, replacing string-based if/else dispatch at 6 sites in npc::move()
  • Same-category preemption so urgent needs can interrupt less-urgent ones (e.g. thirst interrupts warmth hold) using BT urgency scores via tree::tick_full()
  • Failed-target blacklist prevents immediate reacquisition of bad targets
  • Shared candidate query layer (need_candidate + find_*_candidates()) so BT predicates and executors use the same scan
  • Warmth goals collapsed into a single seek_warmth node; fire folded into warmth as a source kind
  • consume_filter enum (any/food_only/drink_only) replaces the bool food_only parameter
  • Forage/harvest activities yield to danger, finish before BT re-runs, and restrict auto-eat to self-care goals
  • Wielded weapon protected from fire fuel searches
  • Stale guard_pos no longer intercepts non-guard NPCs

Describe alternatives you've considered

Could have kept patching address_needs() incrementally, but the lack of persistent state was the root cause of most bugs -- sticky targets and executor results are necessary for non-dithering multi-turn behavior. A full planner/blackboard system would be more general but way overkill for the 4-5 need types that exist.

Testing

~2700 lines of new tests in npc_test.cpp:

  • Executor contract tests for all four need types (sticky target, retarget on invalidation, no-progress timeout, follow non-regression)
  • Water executor contract and predicate tests
  • Sleep and warmth executor characterization
  • Guard warmth routing, danger interruption, camp food/caloric deficit tests
  • Preemption and commitment lifecycle tests

In-game testing:

Additional context

Related to #28681 -- this is a step toward the BT/executor split described there.

@github-actions github-actions bot added NPC / Factions NPCs, AI, Speech, Factions, Ownership [JSON] Changes (can be) made in JSON Code: Tests Measurement, self-control, statistics, balancing. [C++] Changes (can be) made in C++. Previously named `Code` <Bugfix> This is a fix for a bug (or closes open issue) <Enhancement / Feature> New features, or enhancements on existing astyled astyled PR, label is assigned by github actions json-styled JSON lint passed, label assigned by github actions labels Apr 4, 2026
@dobbry-vechur dobbry-vechur force-pushed the improve/npc-bt branch 3 times, most recently from 19dc8a0 to 65c206e Compare April 4, 2026 22:25
Executor layer:
- need_plan state machine tracks goal, source, target, result,
  and no-progress counter for sticky multi-turn pursuit
- need_goal_id enum with centralized helpers replacing string-based
  dispatch at 6 sites in npc::move()
- Same-category preemption lets urgent needs interrupt holding/blocked
  executors using BT urgency scores via tree::tick_full()
- Failed-target blacklist prevents immediate reacquisition of bad
  targets, cleared on generation reset
- clear_committed_goal() helper unifies external clear sites

Executors:
- execute_eat_food: camp food, inventory, ground items, harvestable
  terrain with food-only filter and sticky target
- execute_drink_water: camp water, inventory drinks, ground drinks,
  water terrain sources with sticky target
- execute_seek_warmth: inventory wear, ground clothing, shelter
  movement with indoor hold and timeout
- execute_go_to_sleep: candidate scoring, sticky target, danger gate,
  effect_lying_down management
- Fold start_fire into seek_warmth as fire_spot source kind with
  shared is_usable_npc_firestarter helper

Shared candidate query layer:
- need_candidate struct with find_food/water/warmth/sleep_candidates()
  wrapping scored helpers into unified scans
- BT predicates and executor target acquisition share the same scan
- Camp food/water as predicate-only sources in candidate layer

BT changes:
- Collapse warmth goals into single seek_warmth node
- Restructure npc_thirst with obtain/seek fallback
- needs_food_badly triggers on caloric deficit, not just hunger
- can_obtain_food/water predicates cover all source types
- Commitment completion uses executor result

Engine fixes:
- NPCs skip query_yn in examine actions so forage/harvest start
- Activities return npc_player_activity, self_activity() restores
  attitude/mission for self-initiated activities
- In-progress forage/harvest finish before BT re-runs
- Forage/harvest yield to danger instead of blindly continuing
- Harvest auto-eat restricted to self-care committed goals
- Protect wielded weapon from fire fuel searches
- Fix idle dispatch intercepting non-guard NPCs with stale guard_pos
- Fix lying_down persistence on sleep interruption
- Delegate firestarter validation to firestarter_actor::can_use()
- consume_food/find_nearby_food with food_only/drink_only filter via
  consume_filter enum
- move_to_and_verify no-bash parameter for routine need-seeking

Tests:
- Executor contract tests for food-seeking (sticky target, retarget,
  no-progress timeout, follow non-regression)
- Water executor contract and predicate tests
- Sleep and warmth executor characterization
- Guard warmth routing, danger interruption, camp food tests
@github-actions github-actions bot added the BasicBuildPassed This PR builds correctly, label assigned by github actions label Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

astyled astyled PR, label is assigned by github actions BasicBuildPassed This PR builds correctly, label assigned by github actions <Bugfix> This is a fix for a bug (or closes open issue) [C++] Changes (can be) made in C++. Previously named `Code` Code: Tests Measurement, self-control, statistics, balancing. <Enhancement / Feature> New features, or enhancements on existing [JSON] Changes (can be) made in JSON json-styled JSON lint passed, label assigned by github actions NPC / Factions NPCs, AI, Speech, Factions, Ownership

Projects

None yet

Development

Successfully merging this pull request may close these issues.

npc dance when told to study books in zone Follower Npc gets stuck in a loop trying to forage and faints

1 participant