Files
EscapistsReintroduced/docs/evidence/playtest-traffic-editor-2026-10-04/native-fixture.c.txt
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

147 lines
7.5 KiB
Plaintext

// Private observer fixture; all traffic placement, driving and retirement are production.
modded class ERR_CivilianTraffic
{
protected IEntity m_ERRObserver;
protected bool m_ERRLeave, m_ERRReady, m_ERRMoved, m_ERRFault;
protected float m_ERRStarted=-1, m_ERRNext, m_ERRLog;
protected override bool Observers(array<vector> positions, array<IEntity> actors)
{
positions.Clear();actors.Clear();if(m_ERRLeave)return true;
if(!m_ERRObserver)m_ERRObserver=ERR_Actors.Spawn(DriverPrefab(),ERR_Actors.Ground("9322.19 0 4823.22"));
if(!m_ERRObserver)return false;
positions.Insert(m_ERRObserver.GetOrigin());actors.Insert(m_ERRObserver);return true;
}
bool ERR_Probe(float now)
{
if(m_ERRStarted<0)m_ERRStarted=now;
if(now<m_ERRNext)return false;m_ERRNext=now+1000;
if(now>=m_ERRLog){m_ERRLog=now+15000;PrintFormat("ERR TRAFFIC PROGRESS: cars=%1 spawned=%2",m_Cars.Count(),m_Serial);
foreach(ERR_CivilianCar record:m_Cars)if(record.Car)PrintFormat("ERR TRAFFIC STATE: prefab=%1 ready=%2 finished=%3 agents=%4 orders=%5 distance=%6", record.Car.GetPrefabData().GetPrefabName(),record.Ready,record.Finished,record.Group.GetAgentsCount(),record.Orders.Count(),vector.DistanceXZ(record.Route[0],record.Car.GetOrigin()));}
foreach(ERR_CivilianCar traffic:m_Cars)
{
if(!traffic.Ready || traffic.Released || !traffic.Car || !traffic.Driver)continue;
if(traffic.Group.GetFaction().GetFactionKey()!="CIV" || traffic.Seat.GetOccupant()!=traffic.Driver)continue;
if(m_Mode.EnemyFaction().IsFactionEnemy(traffic.Group.GetFaction()) || traffic.Group.GetFaction().IsFactionEnemy(m_Mode.EnemyFaction())){Print("ERR TRAFFIC FAIL: hostile civilian relation",LogLevel.ERROR);return true;}
BaseWeaponManagerComponent inventory=BaseWeaponManagerComponent.Cast(traffic.Driver.FindComponent(BaseWeaponManagerComponent));
array<IEntity> weapons={};if(inventory)inventory.GetWeaponsList(weapons);
if(!inventory || !weapons.IsEmpty()){Print("ERR TRAFFIC FAIL: unarmed driver check",LogLevel.ERROR);return true;}
if(!m_ERRFault)
{
m_ERRFault=true;traffic.ClearOrders();traffic.ProgressPosition=traffic.Car.GetOrigin();traffic.ProgressAt=now-61000;
Drive(traffic,now);
if(traffic.Retries!=1 || traffic.Orders.Count()!=2){Print("ERR TRAFFIC FAIL: lost-order recovery",LogLevel.ERROR);return true;}
Print("ERR TRAFFIC PASS: production retry restored both native S1203 route orders after injected order loss");
}
if(!m_ERRReady)
{
if(vector.DistanceXZ(traffic.Route[0],m_ERRObserver.GetOrigin())<1000){Print("ERR TRAFFIC FAIL: close spawn",LogLevel.ERROR);return true;}
SCR_GarbageSystem garbage=SCR_GarbageSystem.GetByEntityWorld(traffic.Car);
if(!garbage || garbage.Insert(traffic.Car,1) || garbage.Insert(traffic.Driver,1)){Print("ERR TRAFFIC FAIL: managed entities not protected from native garbage",LogLevel.ERROR);return true;}
Print("ERR TRAFFIC PASS: native garbage collector refuses managed car and driver");
m_ERRReady=true;Print("ERR TRAFFIC PASS: native civilian car/driver, neutral and unarmed; hidden start beyond 1 km");
}
if(!m_ERRMoved && vector.DistanceXZ(traffic.Route[0],traffic.Car.GetOrigin())>25)
{
m_ERRMoved=true;m_ERRLeave=true;
Print("ERR TRAFFIC PASS: civilian drives more than 25 m natively along fixed road route");
Print("ERR TRAFFIC FIXTURE: observer leaves; wait for production cleanup grace");
}
}
if(m_ERRMoved && m_Cars.IsEmpty())
{
Stop();Print("ERR TRAFFIC PASS: all unclaimed traffic retired after observer departure and production grace");return true;
}
if(now-m_ERRStarted>240000){PrintFormat("ERR TRAFFIC FAIL: deadline cars=%1 spawned=%2",m_Cars.Count(),m_Serial);return true;}
return false;
}
}
modded class ArmaReforgerScripted
{
protected bool m_ERRTrafficDone;
override void OnUpdate(BaseWorld world,float timeslice)
{
super.OnUpdate(world,timeslice);ERR_GameMode mode=ERR_GameMode.Get();
if(m_ERRTrafficDone || !world || !Replication.IsServer() || !mode || !mode.Active())return;
if(mode.Phase()==ERR_Phase.PRISON)mode.Transition(ERR_Phase.RADIO_SEARCH);
if(mode.CivilianTraffic() && mode.CivilianTraffic().ERR_Probe(world.GetWorldTime())){m_ERRTrafficDone=true;Print("ERR TRAFFIC COMPLETE");}
}
}
// Private native fixture: sentinel events exercise real invokers without fake players.
[BaseContainerProps(configRoot: true)]
modded class SCR_EditorManagerCore
{
protected int m_ERRProbeEvents;
protected bool m_ERRProbeComplete;
protected override void OnPlayerRegistered(int playerID)
{
if (playerID == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerRegistered(playerID);
}
protected override void OnPlayerDisconnected(int playerID)
{
if (playerID == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerDisconnected(playerID);
}
[Friend(SCR_ReconnectComponent)]
protected override void OnPlayerSpawn(int playerID, IEntity controlledEntity)
{
if (playerID == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerSpawn(playerID, controlledEntity);
}
protected override void OnPlayerKilled(notnull SCR_InstigatorContextData instigatorContextData)
{
if (instigatorContextData.GetVictimPlayerID() == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerKilled(instigatorContextData);
}
protected override void OnPlayerDeleted(int playerID, IEntity player)
{
if (playerID == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerDeleted(playerID, player);
}
protected override void OnPlayerRoleChange(int playerId, EPlayerRole roleFlags)
{
if (playerId == -919) { m_ERRProbeEvents++; return; }
super.OnPlayerRoleChange(playerId, roleFlags);
}
protected void ERR_ProbeEvents(SCR_BaseGameMode mode, int expected, string label)
{
m_ERRProbeEvents = 0;
mode.GetOnPlayerRegistered().Invoke(-919);
mode.GetOnPostCompPlayerDisconnected().Invoke(-919, 0, 0);
mode.GetOnPlayerSpawned().Invoke(-919, null);
mode.GetOnPlayerKilled().Invoke(new SCR_InstigatorContextData(-919, null, null, Instigator.CreateInstigatorGM(), true));
mode.GetOnPlayerDeleted().Invoke(-919, null);
mode.GetOnPlayerRoleChange().Invoke(-919, 0);
if (m_ERRProbeEvents == expected) Print("ERR EDITOR PASS: " + label);
else PrintFormat("ERR EDITOR FAIL: %1 expected=%2 actual=%3", label, expected, m_ERRProbeEvents);
}
override void ERR_SetTerrainModeEvents(SCR_BaseGameMode mode, bool attach)
{
super.ERR_SetTerrainModeEvents(mode, attach);
if (attach) GetGame().GetCallqueue().CallLater(ERR_ProbeAttached, 1000, false, mode);
}
protected void ERR_ProbeAttached(SCR_BaseGameMode mode)
{
if (m_ERRProbeComplete) return;
m_ERRProbeComplete = true;
Print("ERR EDITOR PASS: automatic Nogova takeover calls editor handover");
// Keep fake player events away from unrelated game systems. Only this private
// playerless fixture clears listeners, then tests the actual production bind.
mode.GetOnPlayerRegistered().Clear();
mode.GetOnPostCompPlayerDisconnected().Clear();
mode.GetOnPlayerSpawned().Clear();
mode.GetOnPlayerKilled().Clear();
mode.GetOnPlayerDeleted().Clear();
mode.GetOnPlayerRoleChange().Clear();
super.ERR_SetTerrainModeEvents(mode, true);
ERR_ProbeEvents(mode, 6, "all six native editor event subscriptions deliver");
super.ERR_SetTerrainModeEvents(mode, true);
ERR_ProbeEvents(mode, 6, "repeat attach does not duplicate editor listeners");
super.ERR_SetTerrainModeEvents(mode, false);
ERR_ProbeEvents(mode, 0, "replacement can detach for lifecycle cleanup");
super.ERR_SetTerrainModeEvents(mode, true);
ERR_ProbeEvents(mode, 6, "reattach restores normal editor lifecycle");
Print("ERR EDITOR COMPLETE");
}
}