diff --git a/gamedata/scripts/callbacks_gameobject.script b/gamedata/scripts/callbacks_gameobject.script index 4fec6a5e9..4bfa25384 100644 --- a/gamedata/scripts/callbacks_gameobject.script +++ b/gamedata/scripts/callbacks_gameobject.script @@ -446,6 +446,20 @@ _G.CAI_Stalker__AllowCombatActionSwitch = function(npc, from_op, to_op) return flags.allow ~= false end +-- NPC best-cover re-pick veto. Fires BEFORE the NPC replaces its current best cover +-- (on_best_cover_changed, which clears InCover/LookedOut/PositionHolded, runs after an +-- allowed change). Set flags.allow = false to keep the current cover. Only fires when a +-- cover is already held; a change to a different point and losing the cover entirely both count. +-- Re-asked on the next invalidation while denied; two invalidators are standing state (enemy +-- within 3m of the held cover, smart cover without a loophole) and re-ask every combat action +-- update, so keep subscribers cheap and cap a continuous denial. +AddScriptCallback("npc_on_best_cover_repick") +_G.CAI_Stalker__AllowCoverRepick = function(npc) + local flags = { allow = true } + SendScriptCallback("npc_on_best_cover_repick", npc, flags) + return flags.allow ~= false +end + -- NPC TakeCover destination callback. Only fires when a real cover point is found, not on fallback paths AddScriptCallback("npc_on_take_cover_destination") _G.CAI_Stalker__OnTakeCoverDestination = function(npc, cover_pos, enemy) diff --git a/src/xrGame/ai/stalker/ai_stalker_cover.cpp b/src/xrGame/ai/stalker/ai_stalker_cover.cpp index e856f7259..511539a72 100644 --- a/src/xrGame/ai/stalker/ai_stalker_cover.cpp +++ b/src/xrGame/ai/stalker/ai_stalker_cover.cpp @@ -298,10 +298,22 @@ const CCoverPoint* CAI_Stalker::best_cover(const Fvector& position_to_cover_from const CCoverPoint* best_cover = find_best_cover(position_to_cover_from); if (best_cover != m_best_cover) { - on_best_cover_changed(best_cover, m_best_cover); - m_best_cover = best_cover; - m_best_cover_advance_cover = 0; - m_best_cover_can_try_advance = false; + // Let Lua veto replacing the held cover before on_best_cover_changed clears the cover props. + // allow = false keeps m_best_cover. Only when a cover is already held; no functor means allow. + bool allow = true; + if (m_best_cover) + { + luabind::functor repick_funct; + if (ai().script_engine().functor("_G.CAI_Stalker__AllowCoverRepick", repick_funct)) + allow = repick_funct(lua_game_object()); + } + if (allow) + { + on_best_cover_changed(best_cover, m_best_cover); + m_best_cover = best_cover; + m_best_cover_advance_cover = 0; + m_best_cover_can_try_advance = false; + } } m_best_cover_value = m_best_cover ? best_cover_value(position_to_cover_from) : flt_max;