--- a/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeCrashStartProbe.c +++ b/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeCrashStartProbe.c @@ -3,6 +3,28 @@ // actor relocation, damage or inventory creation in this probe. class ERR_NativeCrashStartProbe { + protected static SCR_AIGroup s_ClueGroup; + protected static ref array s_ClueAgents = {}; + protected static ref array s_ClueBodies = {}; + static bool NearbyEnemies(ERR_GameMode mode, IEntity player) + { + ERR_SiteRuntime clue = mode.Sites().Find("everon-supply-1"); + if (!clue || clue.Group != s_ClueGroup || !s_ClueGroup || s_ClueAgents.IsEmpty() || s_ClueAgents.Count() != s_ClueBodies.Count()) return false; + array current = {}; s_ClueGroup.GetAgents(current); + int nearby, active; + for (int index = 0; index < s_ClueAgents.Count(); index++) + { + SCR_ChimeraAIAgent agent = s_ClueAgents[index]; IEntity actor = s_ClueBodies[index]; + if (!agent || !actor || agent.GetControlledEntity() != actor || !current.Contains(agent) || !ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor)) continue; + if (vector.DistanceXZ(actor.GetOrigin(), player.GetOrigin()) > 100) continue; + nearby++; + bool awake = agent.IsAIActivated(); + PrintFormat("ERR CRASH NEARBY AI: index=%1 active=%2 lod=%3 permanentLod=%4 prisonHeld=%5", index, awake, agent.GetLOD(), agent.GetPermanentLOD(), agent.ERR_PrisonHeldForTest()); + if (awake && !agent.ERR_PrisonHeldForTest()) active++; + } + PrintFormat("ERR CRASH NEARBY AI TOTAL: retained=%1 nearby=%2 active=%3; ordinary native activation after scripted cache approach, no activation override", s_ClueAgents.Count(), nearby, active); + return active > 0; + } protected static bool Check(bool condition, string message) { if (condition) { Print("ERR SMOKE PASS: crash start " + message); return true; } @@ -24,6 +46,18 @@ static bool Run(ERR_GameMode mode, IEntity player) { if (!Check(mode && mode.StartMode() == 1 && mode.Phase() == ERR_Phase.RADIO_SEARCH, "frozen choice enters radio search directly")) return false; + s_ClueAgents.Clear(); s_ClueBodies.Clear(); s_ClueGroup = null; + ERR_SiteRuntime clue = mode.Sites().Find("everon-supply-1"); + if (!Check(clue && clue.Group, "retains original clue defenders before approach")) return false; + s_ClueGroup = clue.Group; + array guards = {}; s_ClueGroup.GetAgents(guards); + if (guards.IsEmpty() || guards.Count() > 16) return false; + foreach (AIAgent guard : guards) + { + SCR_ChimeraAIAgent original = SCR_ChimeraAIAgent.Cast(guard); + if (!original || !original.GetControlledEntity()) return false; + s_ClueAgents.Insert(original); s_ClueBodies.Insert(original.GetControlledEntity()); + } ERR_PlayerLife life = mode.Players().CurrentActor(player); CompartmentAccessComponent access = CompartmentAccessComponent.Cast(player.FindComponent(CompartmentAccessComponent)); if (!Check(life && life.UsedLife && access && !access.IsInCompartment() && ERR_Actors.CanFight(player), "ordinary canonical one-life survivor starts conscious on foot")) return false; --- a/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c +++ b/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c @@ -985,6 +985,7 @@ } else if (m_ERRSmokeStep == 3) { + if (m_ERRTestCrashStart && !ERR_Check(ERR_NativeCrashStartProbe.NearbyEnemies(this, m_ERRSmokePlayer), "an original nearby clue defender has native AI active after crash-route approach")) return; if (!ERR_CheckRouteExtras(now)) return; if (!ERR_Check(m_ERRSmokeRadio.Knowledge() == 2, "carried intel identifies radio through intel service")) return; if (!ERR_Check(!TuneRadio(m_ERRSmokePlayer, m_ERRSmokeRadio), "radio rejects distant player")) return;