Skip to content

Cover re-pick veto callback (npc_on_best_cover_repick)#607

Open
damiansirbu wants to merge 1 commit into
themrdemonized:all-in-one-vs2022-wpofrom
damiansirbu:feat-cover-stable
Open

Cover re-pick veto callback (npc_on_best_cover_repick)#607
damiansirbu wants to merge 1 commit into
themrdemonized:all-in-one-vs2022-wpofrom
damiansirbu:feat-cover-stable

Conversation

@damiansirbu

@damiansirbu damiansirbu commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Lua veto for best-cover re-picks, matching the action-switch veto in #595. CStalkerCombatPlanner::on_best_cover_changed clears InCover, LookedOut and PositionHolded whenever the NPC's best cover point changes. That reset is the in-combat cover strafe (NPCs sliding between near-equal cover) and it restarts flanks, since detour_enemy requires LookedOut and PositionHolded.

on_best_cover_changed has one call site, in best_cover(), reached when find_best_cover returns a point different from the one held. The engine now calls _G.CAI_Stalker__AllowCoverRepick(npc) there first; returning false keeps the current cover and skips the reset. With no subscriber, or no cover currently held, behaviour is identical to vanilla. callbacks_gameobject.script registers npc_on_best_cover_repick and forwards it with a flags table, the same shape as AllowCombatActionSwitch.

The engine sets no policy on which re-picks to keep; the subscribing mod decides, as in #595.

Use cases

A combat-AI mod keeps an NPC on its current cover while it is winning and firing, and allows a re-pick when the spot is compromised. Removes the lateral cover strafe and stops flanks restarting mid-move.

Tested

Built locally on all-in-one-vs2022-wpo (DX11, 0 errors). Run in-game with a Lua subscriber that vetoes re-picks while the NPC keeps a clear shot: it holds the cover as intended and cut about 70% of the useless in-combat cover shuffles. With no subscriber, behaviour is unchanged.

Cost

The functor runs only when find_best_cover returns a different point than the one held. For the event invalidators (enemy re-select, danger add/remove, the held cover's value degrading) m_best_cover_actual stays true after a denial, so a denied NPC is re-asked on its next invalidation; the value invalidator also stabilizes because the denial path refreshes m_best_cover_value for the held cover. Two invalidators are standing state rather than events: an enemy within MIN_SUITABLE_ENEMY_DISTANCE (3m) of the held cover, and a smart cover whose loophole is gone, re-invalidate on each combat-action execute, so a subscriber that keeps denying through one of those is re-asked at that rate. Subscribers should stay cheap and cap a continuous denial; with no subscriber the added cost is one functor lookup on the change path.

Files changed

  • src/xrGame/ai/stalker/ai_stalker_cover.cpp: functor call at the on_best_cover_changed site
  • gamedata/scripts/callbacks_gameobject.script: AddScriptCallback + forwarder

Lua usage

RegisterScriptCallback("npc_on_best_cover_repick", function(npc, flags)
    flags.allow = false  -- keep the current cover
end)

@damiansirbu damiansirbu changed the title Per-NPC cover stability (set_cover_stable): suppress best-cover re-pick noise Cover re-pick veto callback (npc_on_best_cover_repick) Jul 15, 2026
CStalkerCombatPlanner::on_best_cover_changed clears InCover, LookedOut and
PositionHolded on every best-cover change, which drives the in-combat cover
strafe and restarts detour_enemy flanks. best_cover() now calls
_G.CAI_Stalker__AllowCoverRepick before that reset; a subscriber returning
false keeps the current cover. No subscriber, or no cover held, is vanilla.
Mirrors the themrdemonized#595 action-switch veto.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant