759 lines
37 KiB
C
759 lines
37 KiB
C
// Explicit, local Workbench diagnostic only. Never compiled into game/server builds.
|
|
#ifdef WORKBENCH
|
|
modded class ERR_GameMode
|
|
{
|
|
protected bool m_ERRTestPatrolHQ;
|
|
protected ref ERR_NativePatrolProbe m_ERRHQPatrolMovement;
|
|
protected ref ERR_NativePatrolHQProbe m_ERRHQPatrolOrder;
|
|
protected int m_ERRSmokeStep;
|
|
protected float m_ERRSmokeDeadline;
|
|
protected float m_ERRSmokeWait;
|
|
protected IEntity m_ERRSmokePlayer;
|
|
protected BaseDoorComponent m_ERRGate;
|
|
protected ERR_LocationComponent m_ERRSmokeRadio;
|
|
protected bool m_ERRSmokeDone;
|
|
protected bool m_ERRTestLostHelicopter;
|
|
protected bool m_ERRTestPlayerDeath;
|
|
protected bool m_ERRTestRadioNetwork;
|
|
protected bool m_ERRTestHQResponse;
|
|
protected bool m_ERRTestHQMemory;
|
|
protected bool m_ERRTestConvoy;
|
|
protected ref ERR_NativeConvoyProbe m_ERRConvoyProbe;
|
|
protected ref ERR_NativeHQMemoryProbe m_ERRHQMemoryProbe;
|
|
protected bool m_ERRTestMortar;
|
|
protected bool m_ERRTestMortarFire;
|
|
protected bool m_ERRTestMapOwnership;
|
|
protected bool m_ERRTestAircraftField;
|
|
protected ref ERR_NativeAircraftFieldProbe m_ERRAircraftField;
|
|
protected bool m_ERRTestAircraftRoute;
|
|
protected ref ERR_NativeAircraftRouteProbe m_ERRAircraftRoute;
|
|
protected bool m_ERRTestAircraftControl;
|
|
protected bool m_ERRTestAircraftCancel;
|
|
protected bool m_ERRTestAircraftTranslate;
|
|
protected ref ERR_NativeAircraftControlProbe m_ERRAircraftControl;
|
|
protected ref ERRM_NativeMapOwnershipProbe m_ERRMapOwnership;
|
|
protected bool m_ERRTestMortarSalvo;
|
|
protected ref ERR_NativeMortarSalvoProbe m_ERRMortarSalvo;
|
|
protected bool m_ERRTestMortarCourier;
|
|
protected ref ERR_NativeMortarCourierProbe m_ERRMortarCourier;
|
|
protected bool m_ERRTestMortarTransfer;
|
|
protected ref ERR_NativeMortarTransferProbe m_ERRMortarTransfer;
|
|
protected ref ERR_NativeMortarFiringProbe m_ERRMortarFire;
|
|
protected float m_ERRNextProbeLog;
|
|
protected ref ERR_NativeRadioProbe m_ERRRadioProbe;
|
|
protected ref ERR_NativeHQResponseProof m_ERRHQResponseProof;
|
|
protected ref array<IEntity> m_ERRCacheFolders = {};
|
|
protected int m_ERRIntelRerollStage;
|
|
protected float m_ERRIntelRerollDeadline;
|
|
protected ref array<IEntity> m_ERRIntelActors = {};
|
|
protected ref array<IEntity> m_ERRRetiredIntel = {};
|
|
|
|
|
|
protected override void EOnInit(IEntity owner)
|
|
{
|
|
super.EOnInit(owner);
|
|
string enabled;
|
|
if (SCR_Global.IsEditMode() || !Replication.IsServer() || !System.GetCLIParam("errSmoke", enabled) || enabled != "1") return;
|
|
string outcome;
|
|
m_ERRTestLostHelicopter = System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "lost-helicopter";
|
|
m_ERRTestRadioNetwork = outcome == "radio-network";
|
|
m_ERRTestHQResponse = outcome == "hq-response";
|
|
m_ERRTestHQMemory = outcome == "hq-memory";
|
|
m_ERRTestConvoy = outcome == "convoy";
|
|
m_ERRTestMortar = outcome == "mortar-inventory";
|
|
m_ERRTestMapOwnership = outcome == "map-ownership";
|
|
m_ERRTestAircraftField = outcome == "aircraft-field";
|
|
m_ERRTestAircraftRoute = outcome == "aircraft-route";
|
|
m_ERRTestAircraftCancel = outcome == "aircraft-cancel";
|
|
m_ERRTestAircraftTranslate = outcome == "aircraft-translate";
|
|
m_ERRTestAircraftControl = outcome == "aircraft-control" || m_ERRTestAircraftCancel || m_ERRTestAircraftTranslate;
|
|
m_ERRTestMortarFire = outcome == "mortar-fire" || outcome == "mortar-expiry";
|
|
m_ERRTestMortarCourier = outcome == "mortar-courier";
|
|
m_ERRTestMortarSalvo = outcome == "mortar-salvo" || outcome == "mortar-refresh";
|
|
m_ERRTestMortarTransfer = outcome == "mortar-transfer";
|
|
m_ERRTestPatrolHQ = outcome == "patrol-hq";
|
|
m_ERRTestPlayerDeath = outcome == "player-death" || m_ERRTestRadioNetwork || m_ERRTestHQResponse || m_ERRTestMortar || m_ERRTestHQMemory || m_ERRTestConvoy;
|
|
m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 150000;
|
|
if (m_ERRTestConvoy) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 540000;
|
|
if (m_ERRTestHQMemory) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 240000;
|
|
if (m_ERRTestAircraftControl || m_ERRTestAircraftRoute || m_ERRTestAircraftField) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
|
if (m_ERRTestMortarCourier) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 450000;
|
|
if (m_ERRTestMortarTransfer) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
|
if (m_ERRTestMortarSalvo) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
|
if (m_ERRTestMortarFire) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;
|
|
if (m_ERRTestPatrolHQ) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 420000;
|
|
OnStop().Insert(ERR_StopHQPatrol);
|
|
OnPhaseChanged().Insert(ERR_SmokePhase);
|
|
GetGame().GetCallqueue().CallLater(ERR_SmokeTick, 500, true);
|
|
Print("ERR SMOKE: scripted integration test enabled; gameplay acceptance remains separate");
|
|
}
|
|
|
|
// Ordinary victory fixture additionally exercises the actual administrator
|
|
// reroll against native enemy inventories. Scripted, not remote UI evidence.
|
|
protected bool ERR_CheckIntelReroll(float now)
|
|
{
|
|
if (m_ERRIntelRerollStage == 3) return true;
|
|
if (m_ERRIntelRerollStage == 0)
|
|
{
|
|
if (!ERR_Check(Sites() && Sites().Find("everon-supply-2") && Sites().Find("everon-supply-2").Group, "reroll cache group is ready")) return false;
|
|
array<AIAgent> guards = {};
|
|
Sites().Find("everon-supply-2").Group.GetAgents(guards);
|
|
foreach (AIAgent guard : guards)
|
|
{
|
|
if (!guard) continue;
|
|
IEntity actor = guard.GetControlledEntity();
|
|
if (ERR_Actors.Alive(actor) && !ERR_Actors.Player(actor)) m_ERRIntelActors.Insert(actor);
|
|
}
|
|
if (!ERR_Check(!m_ERRIntelActors.IsEmpty(), "reroll observes original living cache guards")) return false;
|
|
array<string> replies = {};
|
|
string before = m_Intel.RerollStatus();
|
|
m_Admin.Execute(this, false, "intel reroll", replies);
|
|
if (!ERR_Check(m_Intel.RerollStatus() == before && !m_Intel.RerollRunning(), "unauthorized reroll cannot start a batch")) return false;
|
|
if (!ERR_Check(m_Settings.Set("aiIntelChancePercent", 100), "native reroll captured chance configured")) return false;
|
|
m_Admin.Execute(this, true, "intel reroll", replies);
|
|
if (!ERR_Check(m_Intel.RerollRunning() && !m_Intel.BeginReroll(), "administrator starts one batch; overlapping reroll rejected")) return false;
|
|
if (!ERR_Check(m_Settings.Set("aiIntelChancePercent", 0), "live setting change does not alter captured batch chance")) return false;
|
|
m_ERRIntelRerollDeadline = now + 90000;
|
|
m_ERRIntelRerollStage = 1;
|
|
return false;
|
|
}
|
|
if (now >= m_ERRIntelRerollDeadline) return ERR_Check(false, "native reroll exceeded bounded fixture deadline");
|
|
if (m_Intel.RerollRunning()) return false;
|
|
Print("ERR INTEL REROLL: " + m_Intel.RerollStatus());
|
|
if (!ERR_Check(m_Intel.RerollStatus().IndexOf(", 0 failed,") >= 0, "native inventory reroll completes without failed operations")) return false;
|
|
foreach (IEntity actor : m_ERRIntelActors)
|
|
{
|
|
if (!ERR_Check(ERR_Actors.Alive(actor), "original cache guard survives the reroll fixture")) return false;
|
|
array<IEntity> folders = {};
|
|
ERR_DiscoveryService.Folders(InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent)), folders);
|
|
if (m_ERRIntelRerollStage == 1)
|
|
{
|
|
if (!ERR_Check(folders.Count() == 1, "captured 100 percent batch assigns one native folder despite live zero setting")) return false;
|
|
m_ERRRetiredIntel.Insert(folders[0]);
|
|
}
|
|
else if (!ERR_Check(folders.IsEmpty(), "zero percent reroll removes original native AI folders without replacements")) return false;
|
|
}
|
|
array<IEntity> carried = {};
|
|
ERR_DiscoveryService.Folders(InventoryStorageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(InventoryStorageManagerComponent)), carried);
|
|
foreach (IEntity document : m_ERRCacheFolders)
|
|
if (!ERR_Check(carried.Contains(document), "reroll preserves player's exact collected clue documents")) return false;
|
|
if (!ERR_Check(m_ERRSmokeRadio.Knowledge() == 2, "reroll preserves already identified radio")) return false;
|
|
if (m_ERRIntelRerollStage == 1)
|
|
{
|
|
if (!ERR_Check(m_Intel.BeginReroll(), "next independent batch uses current zero probability")) return false;
|
|
m_ERRIntelRerollStage = 2;
|
|
return false;
|
|
}
|
|
foreach (IEntity retired : m_ERRRetiredIntel)
|
|
if (!ERR_Check(retired && Discovery().IsSpent(retired) && m_OwnedEntities.Contains(retired), "retired native document remains spent and owned for round cleanup")) return false;
|
|
m_ERRIntelRerollStage = 3;
|
|
return true;
|
|
}
|
|
|
|
protected bool ERR_Check(bool condition, string name)
|
|
{
|
|
if (condition) { Print("ERR SMOKE PASS: " + name); return true; }
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE FAIL: " + name, LogLevel.ERROR);
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
return false;
|
|
}
|
|
protected void ERR_StopHQPatrol()
|
|
{
|
|
if (m_ERRMapOwnership) m_ERRMapOwnership.Stop();
|
|
if (m_ERRAircraftControl) m_ERRAircraftControl.Stop();
|
|
if (m_ERRAircraftRoute) m_ERRAircraftRoute.Stop();
|
|
if (m_ERRAircraftField) m_ERRAircraftField.Stop();
|
|
if (m_ERRHQResponseProof) m_ERRHQResponseProof.Stop();
|
|
if (m_ERRMortarSalvo) m_ERRMortarSalvo.Stop();
|
|
if (m_ERRMortarCourier) m_ERRMortarCourier.Stop();
|
|
if (m_ERRMortarTransfer) m_ERRMortarTransfer.Stop();
|
|
if (m_ERRMortarFire) m_ERRMortarFire.Stop();
|
|
if (m_ERRHQPatrolMovement) m_ERRHQPatrolMovement.Stop();
|
|
if (m_ERRHQPatrolOrder) m_ERRHQPatrolOrder.Stop();
|
|
}
|
|
protected void ERR_MovePlayer(vector position)
|
|
{
|
|
vector transform[4];
|
|
m_ERRSmokePlayer.GetWorldTransform(transform);
|
|
transform[3] = ERR_Actors.Ground(position);
|
|
ChimeraCharacter.Cast(m_ERRSmokePlayer).Teleport(transform);
|
|
}
|
|
protected ERR_UnlockPrisonAction ERR_FindLock(IEntity entity)
|
|
{
|
|
BaseActionsManagerComponent manager = BaseActionsManagerComponent.Cast(entity.FindComponent(BaseActionsManagerComponent));
|
|
if (manager)
|
|
{
|
|
array<BaseUserAction> actions = {};
|
|
manager.GetActionsList(actions);
|
|
foreach (BaseUserAction action : actions)
|
|
{
|
|
ERR_UnlockPrisonAction lock = ERR_UnlockPrisonAction.Cast(action);
|
|
if (lock) return lock;
|
|
}
|
|
}
|
|
IEntity child = entity.GetChildren();
|
|
while (child)
|
|
{
|
|
ERR_UnlockPrisonAction lock = ERR_FindLock(child);
|
|
if (lock) return lock;
|
|
child = child.GetSibling();
|
|
}
|
|
return null;
|
|
}
|
|
protected void ERR_SmokePhase(ERR_Phase previous, ERR_Phase current)
|
|
{
|
|
if (m_ERRSmokeDone || !ERR_MissionRules.Terminal(current)) return;
|
|
if (m_ERRTestAircraftField)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 241, "actual aircraft field fixture completed before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
string fieldCancel;
|
|
string fieldResult = "actual immutable100m field route, measured cruise/braking, destination hold and contact landing; bounded prototype, not HQ support";
|
|
if (System.GetCLIParam("errSmokeFieldServiceCancel", fieldCancel) && fieldCancel == "1")
|
|
fieldResult = "actual moving field service Stop retains control through braking, frozen-point contact landing and engine shutdown; no destination arrival or HQ support claim";
|
|
Print("ERR SMOKE COMPLETE: " + fieldResult);
|
|
return;
|
|
}
|
|
if (m_ERRTestAircraftRoute)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 231, "actual aircraft route completed before explicit fixture reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
string serviceCancel;
|
|
string routeResult = "actual immutable short aircraft route, destination hold and contact landing; experimental mechanism, not HQ transport";
|
|
if (System.GetCLIParam("errSmokeRouteServiceCancel", serviceCancel) && serviceCancel == "1")
|
|
routeResult = "actual airborne service Stop retains original control through frozen-point contact landing and engine shutdown; no destination arrival or HQ transport claim";
|
|
Print("ERR SMOKE COMPLETE: " + routeResult);
|
|
return;
|
|
}
|
|
if (m_ERRTestAircraftControl)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 221, "actual aircraft controller completed before explicit fixture reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
if (m_ERRTestAircraftCancel)
|
|
Print("ERR SMOKE COMPLETE: actual airborne aircraft cancellation retains inputs through settled contact landing and engine-off; isolated mechanism, not HQ dispatch");
|
|
else if (m_ERRTestAircraftTranslate)
|
|
Print("ERR SMOKE COMPLETE: actual aircraft controller immutable target translation, observed target hold and contact landing; bounded mechanism, not route support or HQ dispatch");
|
|
else Print("ERR SMOKE COMPLETE: actual aircraft controller and post-spawn qualification, powered hover and contact landing; isolated physical mechanism, not HQ dispatch");
|
|
return;
|
|
}
|
|
if (m_ERRTestMapOwnership)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 201, "native map ownership probe cleaned up before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE COMPLETE: native replacement map/config/root ownership and spare deletion; camera authorization observed, render flag not measured");
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarSalvo)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 191, "native configured salvo and unchanged-report replay observation complete before reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE COMPLETE: native full configured mortar salvo, exact original ammunition and unchanged-report replay refusal; fixture reset requested");
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarCourier)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 181, "native finite courier reached an exhausted battery before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE COMPLETE: native finite courier, real supply uplink/downlink, original patrol walking and exact physical shell delivery; original 27 shells sequestered in fixture boxes; reset requested");
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarTransfer)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 161, "native exact shell transfers and original-owner restoration complete before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE COMPLETE: native exact mortar inventory transfer, accepted-operation cancellation and original-owner restoration; radio bypassed; fixture reset requested");
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarFire)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 151, "native mortar first shot and stop complete before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
ERR_StopHQPatrol();
|
|
Print("ERR SMOKE COMPLETE: native HQ radio mortar projectile and configured cancellation/expiry; fixture reset requested");
|
|
return;
|
|
}
|
|
if (m_ERRTestPatrolHQ)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 133, "HQ patrol preemption and physical resumption before explicit reset")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
Print("ERR SMOKE COMPLETE: native HQ patrol preemption and physical resumption");
|
|
return;
|
|
}
|
|
if (m_ERRTestPlayerDeath)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 91, "death exhausts round life and reaches defeat after spectator grace")) return;
|
|
}
|
|
else if (m_ERRTestLostHelicopter)
|
|
{
|
|
if (!ERR_Check(current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 8, "lost extraction helicopter causes defeat")) return;
|
|
}
|
|
else if (!ERR_Check(current == ERR_Phase.VICTORY && m_ERRSmokeStep == 7, "scripted escape reaches victory after physical vehicle occupancy")) return;
|
|
m_ERRSmokeDone = true;
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
PrintFormat("ERR SMOKE COMPLETE: terminal=%1 lostHelicopterCase=%2 playerDeathCase=%3", current, m_ERRTestLostHelicopter, m_ERRTestPlayerDeath);
|
|
GetGame().GetCallqueue().CallLater(ERR_EndScreenshot, 4000);
|
|
}
|
|
protected bool ERR_CheckRoster()
|
|
{
|
|
ERR_PlayerRoster roster = new ERR_PlayerRoster();
|
|
UUID first = "00e77100-0000-0000-0000-000000000101";
|
|
UUID second = "00e77100-0000-0000-0000-000000000202";
|
|
if (!ERR_Check(roster.Bind(101, first) && roster.CanEnter(101, ERR_Phase.PRISON), "new identity may enter prison")) return false;
|
|
roster.Spawned(101, m_ERRSmokePlayer);
|
|
if (!ERR_Check(!roster.CanEnter(101, ERR_Phase.PRISON), "spawn consumes identity life")) return false;
|
|
roster.Disconnect(101);
|
|
if (!ERR_Check(roster.HasSurvivor() && !roster.Get(101), "disconnect retains living actor and clears connection")) return false;
|
|
if (!ERR_Check(roster.Bind(202, first) && !roster.CanEnter(202, ERR_Phase.PRISON), "reconnected identity retains spent life")) return false;
|
|
if (!ERR_Check(!roster.Bind(303, first), "duplicate active identity rejected")) return false;
|
|
if (!ERR_Check(roster.Bind(101, second) && roster.CanEnter(101, ERR_Phase.RADIO_DEFENCE) && !roster.CanEnter(101, ERR_Phase.EXTRACTION), "recycled connection receives independent life and extraction admission policy")) return false;
|
|
return true;
|
|
}
|
|
protected bool ERR_CheckCompanionCommands()
|
|
{
|
|
array<string> replies = {};
|
|
array<string> commands = {"hq interval show", "hq squads show", "hq radius show", "intel carrying show", "intel reveal show"};
|
|
array<string> keys = {"reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent"};
|
|
for (int i = 0; i < commands.Count(); i++)
|
|
{
|
|
Admin().Execute(this, true, commands[i], replies);
|
|
if (!ERR_Check(replies.Count() == 1 && replies[0] == keys[i] + "=" + Settings().Get(keys[i]).ToString(), "companion command retained: " + commands[i])) return false;
|
|
}
|
|
Admin().Execute(this, false, "hq log", replies);
|
|
if (!ERR_Check(replies.Count() == 1 && replies[0] == "Administrator access required.", "companion journal is behind Core admin authorization")) return false;
|
|
Admin().Execute(this, true, "hq show", replies);
|
|
ERR_HQ hq = ERR_HQ.Cast(Command());
|
|
if (!ERR_Check(hq && replies.Count() == 1 && replies[0] == hq.Status(), "companion HQ status uses the shared command service")) return false;
|
|
return true;
|
|
}
|
|
protected void ERR_PrisonScreenshot() { System.MakeScreenshot("$logs:err-prison"); }
|
|
protected void ERR_EndScreenshot() { System.MakeScreenshot("$logs:err-result"); }
|
|
protected void ERR_SmokeTick()
|
|
{
|
|
float now = GetWorld().GetWorldTime();
|
|
if (m_ERRSmokeDone) return;
|
|
if (now > m_ERRSmokeDeadline) { ERR_Check(false, "timeout at step " + m_ERRSmokeStep.ToString()); return; }
|
|
if (now < m_ERRSmokeWait) return;
|
|
if (m_ERRSmokeStep == 240)
|
|
{
|
|
int result = m_ERRAircraftField.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native actual aircraft field fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 241; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 230)
|
|
{
|
|
int result = m_ERRAircraftRoute.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native actual aircraft route fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 231; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 220)
|
|
{
|
|
int result = m_ERRAircraftControl.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native actual aircraft controller fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 221; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 200)
|
|
{
|
|
int result = m_ERRMapOwnership.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native map ownership fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 201; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 190)
|
|
{
|
|
int result = m_ERRMortarSalvo.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native complete mortar salvo fixture; inspect source diagnostics"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 191; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 180)
|
|
{
|
|
int result = m_ERRMortarCourier.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native finite courier fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 181; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 160)
|
|
{
|
|
int result = m_ERRMortarTransfer.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native mortar exact-shell transfer fixture"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 161; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 150)
|
|
{
|
|
int result = m_ERRMortarFire.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native mortar firing probe failed; see diagnostics"); return; }
|
|
if (result == 1) { m_ERRSmokeStep = 151; Transition(ERR_Phase.DEFEAT); }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 130 || m_ERRSmokeStep == 132)
|
|
{
|
|
int result = m_ERRHQPatrolMovement.Tick(now);
|
|
if (result < 0) { ERR_StopHQPatrol(); GetGame().GetCallqueue().Remove(ERR_SmokeTick); return; }
|
|
if (result == 1)
|
|
{
|
|
if (m_ERRSmokeStep == 130) { m_ERRHQPatrolOrder = new ERR_NativePatrolHQProbe(this); m_ERRSmokeStep = 131; }
|
|
else { m_ERRSmokeStep = 133; Transition(ERR_Phase.DEFEAT); }
|
|
}
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 131)
|
|
{
|
|
int result = m_ERRHQPatrolOrder.Tick(now);
|
|
if (result < 0) { ERR_StopHQPatrol(); GetGame().GetCallqueue().Remove(ERR_SmokeTick); return; }
|
|
if (result == 1) { m_ERRHQPatrolMovement = new ERR_NativePatrolProbe(this); m_ERRSmokeStep = 132; }
|
|
return;
|
|
}
|
|
if (m_ERRSmokeStep == 0)
|
|
{
|
|
array<IEntity> players = {};
|
|
ERR_Actors.Players(players, true);
|
|
if (players.Count() != 1 || Phase() != ERR_Phase.PRISON) return;
|
|
m_ERRSmokePlayer = players[0];
|
|
if (!ERR_CheckRoster() || !ERR_NativeContractsProbe.Run(this, m_ERRSmokePlayer) || !ERR_CheckCompanionCommands()) return;
|
|
if (m_ERRTestAircraftField)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRAircraftField = new ERR_NativeAircraftFieldProbe(this);
|
|
m_ERRSmokeStep = 240;
|
|
return;
|
|
}
|
|
if (m_ERRTestAircraftRoute)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRAircraftRoute = new ERR_NativeAircraftRouteProbe(this);
|
|
m_ERRSmokeStep = 230;
|
|
return;
|
|
}
|
|
if (m_ERRTestAircraftControl)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRAircraftControl = new ERR_NativeAircraftControlProbe(this);
|
|
m_ERRSmokeStep = 220;
|
|
return;
|
|
}
|
|
if (m_ERRTestPatrolHQ)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRHQPatrolMovement = new ERR_NativePatrolProbe(this);
|
|
m_ERRSmokeStep = 130;
|
|
return;
|
|
}
|
|
if (m_ERRTestMapOwnership)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRMapOwnership = new ERRM_NativeMapOwnershipProbe();
|
|
m_ERRSmokeStep = 200;
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarSalvo)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRMortarSalvo = new ERR_NativeMortarSalvoProbe(this);
|
|
m_ERRSmokeStep = 190;
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarCourier)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRMortarCourier = new ERR_NativeMortarCourierProbe(this);
|
|
m_ERRSmokeStep = 180;
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarTransfer)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRMortarTransfer = new ERR_NativeMortarTransferProbe(this);
|
|
m_ERRSmokeStep = 160;
|
|
return;
|
|
}
|
|
if (m_ERRTestMortarFire)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRMortarFire = new ERR_NativeMortarFiringProbe(this);
|
|
m_ERRSmokeStep = 150;
|
|
return;
|
|
}
|
|
if (m_ERRTestMortar)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRSmokeStep = 120;
|
|
return;
|
|
}
|
|
if (m_ERRTestHQResponse)
|
|
{
|
|
m_ERRHQResponseProof = new ERR_NativeHQResponseProof(this, m_ERRSmokePlayer);
|
|
m_ERRSmokeStep = 110;
|
|
return;
|
|
}
|
|
if (m_ERRTestConvoy)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRConvoyProbe = new ERR_NativeConvoyProbe(this);
|
|
m_ERRSmokeStep = 106;
|
|
return;
|
|
}
|
|
if (m_ERRTestHQMemory)
|
|
{
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRHQMemoryProbe = new ERR_NativeHQMemoryProbe(this);
|
|
m_ERRSmokeStep = 105;
|
|
return;
|
|
}
|
|
if (m_ERRTestRadioNetwork)
|
|
{
|
|
m_ERRRadioProbe = new ERR_NativeRadioProbe();
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
m_ERRSmokeStep = 100;
|
|
return;
|
|
}
|
|
if (m_ERRTestPlayerDeath)
|
|
{
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent)).Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
return;
|
|
}
|
|
DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
if (!ERR_Check(vector.DistanceXZ(m_ERRSmokePlayer.GetOrigin(), PrisonPosition()) < 5, "initial prisoner position")) return;
|
|
foreach (ERR_LocationComponent location : m_Locations)
|
|
if (location.Kind() == ERR_LocationKind.RADIO) m_ERRSmokeRadio = location;
|
|
if (!ERR_Check(m_ERRSmokeRadio && ERR_Actors.Alive(ExtractionVehicle()), "radio and extraction vehicle exist")) return;
|
|
ERR_UnlockPrisonAction lock;
|
|
foreach (IEntity tracked : m_OwnedEntities)
|
|
{
|
|
if (tracked) lock = ERR_FindLock(tracked);
|
|
if (lock) break;
|
|
}
|
|
if (!ERR_Check(lock != null, "authored gate exposes lock action")) return;
|
|
ERR_MovePlayer(lock.GetOwner().GetOrigin() + "1 0 1");
|
|
vector gateLow, gateHigh;
|
|
lock.GetOwner().GetWorldBounds(gateLow, gateHigh);
|
|
PrintFormat("ERR GEOMETRY: gate low=%1 high=%2", gateLow, gateHigh);
|
|
IEntity wall = lock.GetOwner().GetParent().GetChildren();
|
|
while (wall)
|
|
{
|
|
vector wallLow, wallHigh;
|
|
wall.GetWorldBounds(wallLow, wallHigh);
|
|
PrintFormat("ERR GEOMETRY: child origin=%1 low=%2 high=%3", wall.GetOrigin(), wallLow, wallHigh);
|
|
wall = wall.GetSibling();
|
|
}
|
|
GetGame().GetCallqueue().CallLater(ERR_PrisonScreenshot, 2000);
|
|
if (!ERR_Check(lock.CanBePerformedScript(m_ERRSmokePlayer), "lock permits prisoner")) return;
|
|
m_ERRGate = BaseDoorComponent.Cast(lock.GetOwner().FindComponent(BaseDoorComponent));
|
|
lock.PerformAction(lock.GetOwner(), m_ERRSmokePlayer);
|
|
if (!ERR_Check(Phase() == ERR_Phase.RADIO_SEARCH, "lock action advances mission")) return;
|
|
array<IEntity> cacheStock = {};
|
|
ERR_Inventory.Items(InventoryStorageManagerComponent.Cast(Sites().Find("everon-supply-1").Container.FindComponent(InventoryStorageManagerComponent)), cacheStock);
|
|
foreach (IEntity tracked : cacheStock)
|
|
{
|
|
if (tracked && tracked.GetPrefabData() && tracked.GetPrefabData().GetPrefabName() == ERR_Resources.ObjectiveDocument()) m_ERRCacheFolders.Insert(tracked);
|
|
}
|
|
if (!ERR_Check(m_ERRCacheFolders.Count() == 2, "two guaranteed physical cache documents")) return;
|
|
if (!ERR_Check(m_Settings.Set("aiIntelChancePercent", 0), "test does not rely on random intel")) return;
|
|
if (!ERR_Check(m_Settings.Set("radioDefenceSeconds", 30), "isolated test profile defence duration")) return;
|
|
m_ERRSmokeStep = 1;
|
|
}
|
|
else if (m_ERRSmokeStep == 1 || m_ERRSmokeStep == 2)
|
|
{
|
|
if (m_ERRSmokeStep == 2 && !ERR_Check(m_ERRGate && m_ERRGate.IsOpen() && m_ERRGate.CanCharacterPass(0.6), "unlocked gate physically permits passage")) return;
|
|
ERR_MovePlayer(Sites().Find("everon-supply-1").Container.GetOrigin() + "0 0 2");
|
|
SCR_InventoryStorageManagerComponent inventory = SCR_InventoryStorageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_InventoryStorageManagerComponent));
|
|
InventoryStorageManagerComponent source = InventoryStorageManagerComponent.Cast(Sites().Find("everon-supply-1").Container.FindComponent(InventoryStorageManagerComponent));
|
|
BaseInventoryStorageComponent destination = inventory.FindStorageForItem(m_ERRCacheFolders[m_ERRSmokeStep - 1]);
|
|
if (!ERR_Check(destination && source.TryMoveItemToStorage(m_ERRCacheFolders[m_ERRSmokeStep - 1], destination), "authored cache document recovered into inventory")) return;
|
|
m_ERRSmokeStep++;
|
|
m_ERRSmokeWait = now + 2500;
|
|
}
|
|
else if (m_ERRSmokeStep == 3)
|
|
{
|
|
int adminMenu = ERR_ProbeAdminMenu(now);
|
|
if (adminMenu < 0) { ERR_Check(false, "integrated admin menu"); return; }
|
|
if (adminMenu == 0) return;
|
|
if (!ERR_CheckIntelReroll(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;
|
|
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "2 0 0");
|
|
m_ERRSmokeStep = 30;
|
|
m_ERRSmokeWait = now + 1000;
|
|
}
|
|
else if (m_ERRSmokeStep == 30)
|
|
{
|
|
PrintFormat("ERR SMOKE: radio=%1 player=%2", m_ERRSmokeRadio.Position(), m_ERRSmokePlayer.GetOrigin());
|
|
if (!ERR_Check(TuneRadio(m_ERRSmokePlayer, m_ERRSmokeRadio), "identified radio starts defence")) return;
|
|
m_ERRSmokeStep = 4;
|
|
m_ERRSmokeWait = now + 3000;
|
|
}
|
|
else if (m_ERRSmokeStep == 4)
|
|
{
|
|
if (!ERR_Check(DefenceOccupied(), "occupied defence timer running")) return;
|
|
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "90 0 0");
|
|
m_ERRSmokeStep = 5;
|
|
m_ERRSmokeWait = now + 2500;
|
|
}
|
|
else if (m_ERRSmokeStep == 5)
|
|
{
|
|
if (!ERR_Check(!DefenceOccupied() && Phase() == ERR_Phase.RADIO_DEFENCE, "leaving area resets defence")) return;
|
|
ERR_MovePlayer(m_ERRSmokeRadio.Position() + "2 0 0");
|
|
m_ERRSmokeStep = 6;
|
|
m_ERRSmokeWait = now + 33000;
|
|
}
|
|
else if (m_ERRSmokeStep == 6)
|
|
{
|
|
if (!ERR_Check(Phase() == ERR_Phase.EXTRACTION, "continuous occupied defence completes")) return;
|
|
if (m_ERRTestLostHelicopter)
|
|
{
|
|
m_ERRSmokeStep = 8;
|
|
ERR_Actors.DeleteSpawned(ExtractionVehicle());
|
|
return;
|
|
}
|
|
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(m_ERRSmokePlayer.FindComponent(CompartmentAccessComponent));
|
|
BaseCompartmentSlot slot = access.FindFreeCompartment(ExtractionVehicle(), ECompartmentType.PILOT);
|
|
if (!ERR_Check(slot && access.GetInVehicle(ExtractionVehicle(), slot, true, -1, ECloseDoorAfterActions.INVALID, false), "boarding extraction vehicle")) return;
|
|
m_ERRSmokeStep = 7;
|
|
m_ERRSmokeWait = now + 3000;
|
|
}
|
|
else if (m_ERRSmokeStep == 120)
|
|
{
|
|
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
|
if (!battery || !battery.StartupActivated) return;
|
|
if (!ERR_Check(battery.Operational() && battery.Group.GetAgentsCount() == 4, "authored physical mortar and four-person crew initialize")) return;
|
|
if (!ERR_Check(ERR_HQRadio.Capability(battery.RadioOperator) == 2, "mortar has a dedicated physical manpack operator")) return;
|
|
if (!ERR_Check(battery.CountShells() == 27, "mortar starts with exactly 27 physical HE shells")) return;
|
|
array<AIAgent> agents = {};
|
|
battery.Group.GetAgents(agents);
|
|
array<IEntity> intelCrew = {};
|
|
foreach (AIAgent intelAgent : agents)
|
|
{
|
|
IEntity intelActor;
|
|
if (intelAgent) intelActor = intelAgent.GetControlledEntity();
|
|
if (!ERR_Check(intelActor && !intelCrew.Contains(intelActor), "mortar Intel observation uses distinct actual crew actors")) return;
|
|
if (!ERR_Check(m_Intel && m_Intel.ERR_ProbeWasRolled(intelActor), "ready mortar crew actor reached its one optional Intel carrying roll")) return;
|
|
intelCrew.Insert(intelActor);
|
|
}
|
|
if (!ERR_Check(intelCrew.Count() == 4, "all four original ready physical crew reached Intel without forcing documents or changing carrying chance")) return;
|
|
foreach (AIAgent agent : agents)
|
|
{
|
|
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(agent.GetControlledEntity().FindComponent(InventoryStorageManagerComponent));
|
|
array<IEntity> items = {};
|
|
ERR_Inventory.Items(inventory, items);
|
|
foreach (IEntity item : items)
|
|
if (item && item.GetPrefabData() && item.GetPrefabData().GetPrefabName() == ERR_MortarBattery.SHELL) SCR_EntityHelper.DeleteEntityAndChildren(item);
|
|
}
|
|
m_ERRSmokeStep = 121;
|
|
m_ERRSmokeWait = now + 15000;
|
|
}
|
|
else if (m_ERRSmokeStep == 121)
|
|
{
|
|
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
|
if (!ERR_Check(battery.CountShells() == 0 && battery.TotalShots == 0, "depleted mortar inventory stays empty without fabricated ammunition")) return;
|
|
ERR_Actors.DeleteSpawned(battery.Gun);
|
|
ERR_Actors.DeleteSpawned(battery.Group);
|
|
m_ERRSmokeStep = 122;
|
|
m_ERRSmokeWait = now + 12000;
|
|
}
|
|
else if (m_ERRSmokeStep == 122)
|
|
{
|
|
ERR_MortarBattery battery = ERR_MortarSystem.Get().ERR_ProbeBattery();
|
|
if (!ERR_Check(battery && !battery.Gun && !battery.Group && !battery.Operational(), "lost mortar and crew remain lost without replacement spawning")) return;
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
damage.EnableDamageHandling(true);
|
|
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
}
|
|
else if (m_ERRSmokeStep == 110)
|
|
{
|
|
int result = m_ERRHQResponseProof.Tick(now);
|
|
if (result < 0) { ERR_Check(false, "native HQ response evidence failed; inspect report lineage diagnostics"); return; }
|
|
if (result == 0) return;
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
damage.EnableDamageHandling(true);
|
|
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
}
|
|
else if (m_ERRSmokeStep == 106)
|
|
{
|
|
int result = m_ERRConvoyProbe.Tick(ERR_HQ.Cast(Command()), now);
|
|
if (result == 0) return;
|
|
if (!ERR_Check(result == 1, "native six-vehicle convoy readiness, cargo and road movement")) return;
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent damage;
|
|
damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
damage.EnableDamageHandling(true);
|
|
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
}
|
|
else if (m_ERRSmokeStep == 105)
|
|
{
|
|
int result = m_ERRHQMemoryProbe.Tick(ERR_HQ.Cast(Command()), now);
|
|
if (result == 0) return;
|
|
if (!ERR_Check(result == 1, "native HQ memory delivery and search lineage")) return;
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent damage;
|
|
damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
damage.EnableDamageHandling(true);
|
|
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
}
|
|
else if (m_ERRSmokeStep == 100)
|
|
{
|
|
int result = m_ERRRadioProbe.Tick(ERR_HQ.Cast(Command()), now);
|
|
if (result == 0) return;
|
|
if (!ERR_Check(result == 1, "native radio delivery and interruption sequence")) return;
|
|
m_ERRSmokeStep = 9;
|
|
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
damage.EnableDamageHandling(true);
|
|
damage.Kill(Instigator.CreateInstigator(m_ERRSmokePlayer));
|
|
m_ERRSmokeWait = now + 4000;
|
|
}
|
|
else if (m_ERRSmokeStep == 9)
|
|
{
|
|
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
|
|
if (!ERR_Check(!ERR_Actors.Alive(m_ERRSmokePlayer), "native damage kills prisoner")) return;
|
|
if (!ERR_Check(controller && controller.ERR_ObserverState() == ERR_ObserverReason.SPENT_LIFE, "death selects spectator without another life")) return;
|
|
if (!ERR_Check(controller.ERR_HasObserverCamera() && SCR_ManualCamera.Cast(GetGame().GetCameraManager().CurrentCamera()), "spectator camera activates after native death")) return;
|
|
m_ERRSmokeStep = 91;
|
|
}
|
|
else if (m_ERRSmokeStep == 7)
|
|
{
|
|
if (!ERR_Check(CompartmentAccessComponent.GetVehicleIn(m_ERRSmokePlayer) == ExtractionVehicle(), "physical extraction occupancy")) return;
|
|
// Controlled transport tests the boundary detector, not piloting skill.
|
|
ExtractionVehicle().SetOrigin(m_EscapeCenter + Vector(m_EscapeRadius + 100, 300, 0));
|
|
m_ERRSmokeWait = now + 5000;
|
|
}
|
|
}
|
|
void ~ERR_GameMode()
|
|
{
|
|
if (GetGame())
|
|
{
|
|
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
|
|
GetGame().GetCallqueue().Remove(ERR_PrisonScreenshot);
|
|
GetGame().GetCallqueue().Remove(ERR_EndScreenshot);
|
|
}
|
|
}
|
|
}
|
|
#endif
|