Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,273 @@
// Decisions consumed by the finite original-item controller below.
class ERR_EngineerMineRules
{
static bool Within(float started, float previous, float now)
{
return ERR_ForceRules.Finite(started) && ERR_ForceRules.Finite(previous) && ERR_ForceRules.Finite(now)
&& started >= 0 && previous >= started && now >= previous && now - started < 180000;
}
static float PrepareSince(float since, float now, bool near, bool engaged)
{
if (!ERR_ForceRules.Finite(now) || now < 0 || !near || engaged) return -1;
if (!ERR_ForceRules.Finite(since) || since < 0 || since > now) return now;
return since;
}
static bool Prepared(float since, float now)
{
return ERR_ForceRules.Finite(since) && ERR_ForceRules.Finite(now) && since >= 0 && now - since >= 10000;
}
static bool MayArm(bool grounded, bool ownerValid, float distance, bool friendlyNear)
{
return grounded && ownerValid && ERR_ForceRules.Finite(distance) && distance >= 20 && !friendlyNear;
}
}
// Callback records only terminal evidence. It never moves, arms or retries items.
class ERR_EngineerMineRemoval : ScriptedInventoryOperationCallback
{
int Result;
protected void Finish(int result) { if (!Result) Result = result; }
protected override void OnComplete() { Finish(1); }
protected override void OnAllComplete() { Finish(1); }
protected override void OnFailed() { Finish(-1); }
protected override void OnAllFailed() { Finish(-1); }
}
// One original soldier, one carried PMN4, one accepted HQ road-interception post.
// Loose cancelled items remain ordinary unarmed loot. Armed mines retain native
// lifetime; a round's world reset owns their destruction, not this controller.
class ERR_EngineerMine
{
protected ERR_GameMode m_Mode;
protected ERR_HQUnit m_Unit;
protected SCR_AIGroup m_Group;
protected ref ERR_HQTask m_Task;
protected ref ERR_HQReport m_Assignment;
protected ref ERR_ForceRecord m_Record;
protected string m_SourceId;
protected int m_Strength;
protected IEntity m_Actor;
protected int m_Slot;
protected IEntity m_Mine;
protected SCR_PressureTriggerComponent m_Trigger;
protected SCR_InventoryStorageManagerComponent m_Inventory;
protected SCR_AIUtilityComponent m_Utility;
protected ref SCR_AIBehaviorBase m_Action;
protected ref ERR_EngineerMineRemoval m_Removal;
protected ref array<IEntity> m_Attempted;
protected ref array<IEntity> m_IntelFolders = {};
protected vector m_Position;
protected vector m_Rally;
protected float m_Started = -1;
protected float m_LastTick = -1;
protected float m_PreparedAt = -1;
protected float m_NextClearance;
protected int m_State; // 0 approach,1 continuous preparation,2 removal pending,3 withdraw,4 finished
protected int m_Checked;
protected bool m_FriendlyNear;
protected bool m_Begun;
protected bool m_Stopped;
void ERR_EngineerMine(array<IEntity> attempted) { m_Attempted = attempted; }
bool Finished() { return m_Stopped || m_State == 4; }
protected bool Refuse() { Stop(); return false; }
protected bool Quiet()
{
return m_Unit && !m_Unit.InfantryCombat.Held && !ERR_HunterCombat.Active(m_Group);
}
protected bool Owned()
{
if (m_Stopped || !Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active()) return false;
if (!m_Unit || !m_Group || m_Unit.Group != m_Group || !m_Task || m_Unit.Task != m_Task) return false;
if (!m_Assignment || m_Unit.Assignment != m_Assignment || !m_Task.RoadIntercept || m_Task.ReleaseOnly) return false;
if (m_Task.Investigation() || !m_Task.SecuritySite.IsEmpty() || m_Unit.PendingTask || m_Unit.PendingOrder) return false;
if (m_Unit.ReservedForSupport || m_Unit.LogisticsReservation > 0 || m_Group.IsSlave() || m_Group.GetPlayerCount(true) > 0) return false;
if (!m_Mode.EnemyFaction() || m_Group.GetFaction() != m_Mode.EnemyFaction() || !m_Unit.StatusSampler) return false;
if (vector.Distance(m_Task.Destination, m_Rally) > 0.01 || !m_Mode.Forces() || !m_Record) return false;
if (m_Mode.Forces().FindGroup(m_Group) != m_Record || m_Record.Group != m_Group || !m_Record.Physical || m_Record.Eliminated) return false;
if (m_Record.SourceId != m_SourceId || m_Unit.ForceSourceId != m_SourceId || m_Record.InitialStrength != m_Strength) return false;
if (!m_Unit.StatusSampler.OwnsRoster(m_Group) || m_Unit.StatusSampler.InitialStrength() != m_Strength) return false;
if (!m_Actor || !ERR_Actors.CanFight(m_Actor) || ERR_Actors.Player(m_Actor) || CompartmentAccessComponent.GetVehicleIn(m_Actor)) return false;
if (!m_Unit.StatusSampler.CanAttribute(m_Slot, m_Actor, false) || ERR_Actors.Group(m_Actor) != m_Group) return false;
if (SCR_FactionManager.SGetFaction(m_Actor) != m_Mode.EnemyFaction()) return false;
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(m_Actor));
if (!agent || !m_Utility || agent.m_UtilityComponent != m_Utility || !m_Utility.m_AIInfo) return false;
if (!m_Inventory || m_Actor.FindComponent(SCR_InventoryStorageManagerComponent) != m_Inventory) return false;
return true;
}
protected bool MineValid()
{
if (!ERR_Actors.Alive(m_Mine) || !m_Mine.GetPrefabData() || m_Mine.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) return false;
if (!m_Trigger || m_Mine.FindComponent(SCR_PressureTriggerComponent) != m_Trigger || m_Trigger.IsActivated()) return false;
return m_Attempted && m_Attempted.Contains(m_Mine);
}
protected bool Carried()
{
if (!MineValid() || !m_Inventory) return false;
array<IEntity> items = {}; ERR_Inventory.Items(m_Inventory, items);
return items.Count() <= 128 && items.Contains(m_Mine) && ERR_InfantryAmmoTransfer.StoredUnder(m_Mine, m_Actor);
}
protected bool Grounded()
{
if (!MineValid() || !m_Inventory) return false;
InventoryItemComponent item = InventoryItemComponent.Cast(m_Mine.FindComponent(InventoryItemComponent));
if (!item || item.GetParentSlot() || m_Mine.GetParent()) return false;
array<IEntity> items = {}; ERR_Inventory.Items(m_Inventory, items);
return items.Count() <= 128 && !items.Contains(m_Mine);
}
protected bool Placed()
{
if (!Grounded() || vector.Distance(m_Mine.GetOrigin(), m_Position) > 1) return false;
Physics physics = m_Mine.GetPhysics();
return physics && physics.GetVelocity().LengthSq() <= 0.25;
}
protected void ClearAction()
{
if (m_Action && m_Actor && m_Utility && m_Utility.m_AIInfo)
{
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(m_Actor));
if (agent && agent.m_UtilityComponent == m_Utility)
{
array<ref AIActionBase> actions = {}; m_Utility.GetActions(actions);
if (actions.Count() <= 128 && actions.Contains(m_Action)) m_Action.Complete();
}
}
m_Action = null;
}
protected void Move(vector destination)
{
ClearAction();
m_Action = new SCR_AIMoveIndividuallyBehavior(m_Utility, null, destination, 1000, 0, null, 1);
m_Utility.AddAction(m_Action);
}
protected void WaitAtMine()
{
ClearAction();
m_Action = new SCR_AIWaitBehavior(m_Utility, null);
m_Action.SetPriority(1000);
m_Utility.AddAction(m_Action);
}
protected bool CheckFriendly(IEntity entity)
{
if (++m_Checked > 256) { m_FriendlyNear = true; return false; }
if (!entity || (!ChimeraCharacter.Cast(entity) && !Vehicle.Cast(entity))) return true;
DamageManagerComponent damage = DamageManagerComponent.Cast(entity.FindComponent(DamageManagerComponent));
if (damage && damage.IsDestroyed()) return true;
FactionAffiliationComponent affiliation = FactionAffiliationComponent.Cast(entity.FindComponent(FactionAffiliationComponent));
Faction faction;
if (affiliation) faction = affiliation.GetAffiliatedFaction();
if (faction && m_Mode.EnemyFaction().IsFactionEnemy(faction)) return true;
// Unconscious allies and unknown affiliations both prevent arming.
m_FriendlyNear = true;
return false;
}
protected bool SelectActor()
{
if (!m_Unit.StatusSampler.OwnsRoster(m_Group) || m_Strength < 1 || m_Strength > 32) return false;
for (int slot = 0; slot < m_Strength; slot++)
{
IEntity actor = m_Unit.StatusSampler.InitialMember(slot);
if (!actor || !ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor) || CompartmentAccessComponent.GetVehicleIn(actor)) continue;
if (!m_Unit.StatusSampler.CanAttribute(slot, actor, false) || vector.DistanceXZ(actor.GetOrigin(), m_Rally) > 20) continue;
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(actor));
if (!agent || !agent.m_UtilityComponent || !agent.m_UtilityComponent.m_AIInfo) continue;
SCR_InventoryStorageManagerComponent inventory = SCR_InventoryStorageManagerComponent.Cast(actor.FindComponent(SCR_InventoryStorageManagerComponent));
if (!inventory) continue;
array<IEntity> items = {}; ERR_Inventory.Items(inventory, items);
if (items.Count() > 128) continue;
foreach (IEntity mine : items)
{
if (!ERR_Actors.Alive(mine) || m_Attempted.Contains(mine) || !mine.GetPrefabData() || mine.GetPrefabData().GetPrefabName() != ERR_EnemyLoadoutCatalog.MINE) continue;
if (!ERR_InfantryAmmoTransfer.StoredUnder(mine, actor)) continue;
SCR_PressureTriggerComponent trigger = SCR_PressureTriggerComponent.Cast(mine.FindComponent(SCR_PressureTriggerComponent));
if (!trigger || trigger.IsActivated() || trigger.IsActivatedByDefault()) continue;
m_Actor = actor; m_Slot = slot; m_Mine = mine; m_Trigger = trigger;
m_Inventory = inventory; m_Utility = agent.m_UtilityComponent;
return true;
}
}
return false;
}
bool Begin(ERR_GameMode mode, ERR_HQUnit unit, ERR_HQTask task, vector minePosition, float now)
{
if (m_Begun || m_Stopped) return false;
m_Begun = true;
if (!Replication.IsServer() || !mode || mode != ERR_GameMode.Get() || !mode.Active()) return Refuse();
if (!unit || !unit.Group || !unit.StatusSampler || !task || unit.Task != task || !unit.Assignment) return Refuse();
if (!ERR_ForceRules.Finite(now) || now < 0 || !ERR_ForceRules.FiniteVector(minePosition)) return Refuse();
if (!m_Attempted || m_Attempted.Count() >= 256 || !mode.Forces()) return Refuse();
m_Mode = mode; m_Unit = unit; m_Task = task; m_Assignment = unit.Assignment; m_Group = unit.Group;
m_SourceId = unit.ForceSourceId; m_Strength = unit.StatusSampler.InitialStrength(); m_Record = mode.Forces().FindGroup(m_Group);
m_Position = minePosition; m_Rally = task.Destination;
float distance = vector.DistanceXZ(m_Position, m_Rally);
if (!ERR_ForceRules.FiniteVector(m_Rally) || distance < 25 || distance > 60 || now >= task.ExpiresAt) return Refuse();
if (!SelectActor() || !Owned() || !Quiet()) return Refuse();
// The shared exact-item tombstone survives pickup, transfer and late callbacks.
m_Attempted.Insert(m_Mine);
if (!Carried()) return Refuse();
if (m_Mode.MineIntel()) m_Mode.MineIntel().Candidates(m_Actor, m_IntelFolders);
m_Started = now; m_LastTick = now; m_State = 0;
Move(m_Position);
ERR_HQ.Event("ENGINEER MINE", "original carried PMN4 assigned; one approach and finite 180s deadline");
return true;
}
void Stop()
{
if (m_Stopped) return;
m_Stopped = true; m_State = 4;
ClearAction();
// Retain removal evidence and shared item quarantine. No late completion can arm.
}
void Tick(float now)
{
if (!m_Begun || Finished()) return;
if (!ERR_EngineerMineRules.Within(m_Started, m_LastTick, now)) { Stop(); return; }
m_LastTick = now;
if (!Owned() || !Quiet() || !MineValid() || now >= m_Task.ExpiresAt) { Stop(); return; }
if (m_State <= 1)
{
if (!Carried()) { Stop(); return; }
bool near = vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) <= 3;
m_PreparedAt = ERR_EngineerMineRules.PrepareSince(m_PreparedAt, now, near, false);
if (m_State == 0 && near) { m_State = 1; WaitAtMine(); }
if (!ERR_EngineerMineRules.Prepared(m_PreparedAt, now)) return;
// Set state and callback BEFORE native submission; never repeat the removal.
m_Removal = new ERR_EngineerMineRemoval(); m_State = 2;
bool accepted = m_Inventory.TryRemoveItemFromInventory(m_Mine, null, m_Removal);
if (!accepted && !m_Removal.Result) { Stop(); return; }
return;
}
if (m_State == 2)
{
if (!m_Removal || m_Removal.Result < 0) { Stop(); return; }
// Observed transfer ends our custody; a later nearby drop cannot restore it.
// Original inventory ownership may still settle after the native callback.
InventoryItemComponent item = InventoryItemComponent.Cast(m_Mine.FindComponent(InventoryItemComponent));
bool attached = m_Mine.GetParent() || (item && item.GetParentSlot());
if (attached && !ERR_InfantryAmmoTransfer.StoredUnder(m_Mine, m_Actor)) { Stop(); return; }
if (!m_Removal.Result || !Grounded()) return;
if (vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) > 3) { Stop(); return; }
// A callback proves removal, not continued custody of a distant loose item.
if (vector.Distance(m_Mine.GetOrigin(), m_Actor.GetOrigin()) > 3
|| vector.Distance(m_Mine.GetOrigin(), m_Position) > 3) { Stop(); return; }
m_Mine.SetOrigin(m_Position);
m_State = 3; Move(m_Rally);
return;
}
if (m_State != 3 || !Grounded() || vector.Distance(m_Mine.GetOrigin(), m_Position) > 1) { Stop(); return; }
if (now < m_NextClearance || vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) < 20) return;
m_NextClearance = now + 1000;
m_Checked = 0; m_FriendlyNear = false;
if (!GetGame() || !GetGame().GetWorld()) { Stop(); return; }
GetGame().GetWorld().QueryEntitiesBySphere(m_Position, 20, CheckFriendly, queryFlags: EQueryEntitiesFlags.ALL);
if (!ERR_EngineerMineRules.MayArm(Placed(), Owned() && Quiet(), vector.DistanceXZ(m_Actor.GetOrigin(), m_Position), m_FriendlyNear)) return;
m_Trigger.SetUser(m_Actor);
m_Trigger.ActivateTrigger();
if (m_Trigger.IsActivated())
{
if (m_Mode.MineIntel()) m_Mode.MineIntel().Seal(m_Actor, m_Mine, m_Position, m_IntelFolders);
ERR_HQ.Event("ENGINEER MINE", "original PMN4 armed after engineer withdrawal and physical friendly clearance");
}
Stop();
}
}
@@ -0,0 +1,6 @@
{
"path": "addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMine.c",
"working_sha256": "fe05cb6e813155d76fd497788aa92c9dd8c7cf4ec43e017172a8a70e16c00b80",
"index_present": true,
"index_sha256": "e0098577626e719283cf4b40936e7ec038076c4bab9269fbad0b5ad3292e01e3"
}
@@ -0,0 +1,22 @@
{
"scope": "One original engineer controller supports finite new delivered posts; no HQ ledger or selector changes here.",
"baseline": "baseline.json preserves exact working source and separate index bytes.",
"interfaces": [
"Begin(existing arguments, array<vector> priorSites = null)",
"BlocksNextPost(ERR_ForceRecord record)",
"ERR_EngineerMineRules.CorridorClear(vector start, vector end, array<vector> priorSites)",
"ERR_EngineerMineRules.WithinWorkArea(vector post, vector actor)"
],
"semantics": [
"Copy max32 finite historical sites before actor selection; refuse malformed history without movement or item tombstone.",
"At admission both straight XZ approach/withdrawal corridors must remain strictly more than20m from retained sites.",
"Owned checks finite original actor/site inside60m XZ fixed rally and actor/site/rally more than20m from retained sites before subsequent action/removal/placement/arming. Native IsPositionAllowed remains unchanged.",
"Exact original record active job blocks new post; stopped pending callback on surviving item blocks, including destroyed but existing item and uncertain nativefalse. Terminal callback or exact deletion settles stopped job only.",
"No callback actions, retries, stock creation, tombstone reset, deadline extension or new queries."
],
"limits": [
"Sampled points and straight corridors do not establish native route safety.",
"Repeated physical placement at two delivered posts remains unqualified until native followup."
],
"tests": "Actual-method harness owned separately by player_admission_review under /tmp/erri-engineer-posts-tests-0725; pending final result."
}
@@ -0,0 +1,105 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMine.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMine.c
@@ -1,6 +1,39 @@
// Decisions consumed by the finite original-item controller below.
class ERR_EngineerMineRules
{
+ // Straight XZ corridors are conservative local checks, not native route proofs.
+ static bool CorridorClear(vector start, vector end, array<vector> priorSites)
+ {
+ if (!ERR_ForceRules.FiniteVector(start) || !ERR_ForceRules.FiniteVector(end)) return false;
+ if (!priorSites) return true;
+ if (priorSites.Count() > 32) return false;
+ float dx = end[0] - start[0], dz = end[2] - start[2];
+ float squared = dx * dx + dz * dz;
+ if (!ERR_ForceRules.Finite(squared)) return false;
+ foreach (vector site : priorSites)
+ {
+ if (!ERR_ForceRules.FiniteVector(site)) return false;
+ float along = 0;
+ if (squared > 0)
+ {
+ float projection = (site[0] - start[0]) * dx + (site[2] - start[2]) * dz;
+ if (!ERR_ForceRules.Finite(projection)) return false;
+ along = projection / squared;
+ if (!ERR_ForceRules.Finite(along)) return false;
+ along = Math.Clamp(along, 0, 1);
+ }
+ float x = start[0] + along * dx - site[0], z = start[2] + along * dz - site[2];
+ float clearance = x * x + z * z;
+ if (!ERR_ForceRules.Finite(clearance) || clearance <= 400) return false;
+ }
+ return true;
+ }
+ static bool WithinWorkArea(vector post, vector actor)
+ {
+ if (!ERR_ForceRules.FiniteVector(post) || !ERR_ForceRules.FiniteVector(actor)) return false;
+ float distance = vector.DistanceXZ(post, actor);
+ return ERR_ForceRules.Finite(distance) && distance <= 60;
+ }
static bool Within(float started, float previous, float now)
{
return ERR_ForceRules.Finite(started) && ERR_ForceRules.Finite(previous) && ERR_ForceRules.Finite(now)
@@ -56,6 +89,7 @@
protected ref ERR_EngineerMineRemoval m_Removal;
protected ref array<IEntity> m_Attempted;
protected ref array<IEntity> m_IntelFolders = {};
+ protected ref array<vector> m_PriorSites = {};
protected vector m_Position;
protected vector m_Rally;
protected float m_Started = -1;
@@ -70,6 +104,14 @@
void ERR_EngineerMine(array<IEntity> attempted) { m_Attempted = attempted; }
bool Finished() { return m_Stopped || m_State == 4; }
+ bool BlocksNextPost(ERR_ForceRecord record)
+ {
+ if (!record || record != m_Record) return false;
+ if (m_Begun && !Finished()) return true;
+ // A late terminal callback or exact deletion settles only the old job.
+ // Destroyed but surviving items and uncertain native refusals remain pinned.
+ return m_Removal && !m_Removal.Result && m_Mine;
+ }
protected bool Refuse() { Stop(); return false; }
protected bool Quiet()
{
@@ -90,6 +132,11 @@
if (m_Record.SourceId != m_SourceId || m_Unit.ForceSourceId != m_SourceId || m_Record.InitialStrength != m_Strength) return false;
if (!m_Unit.StatusSampler.OwnsRoster(m_Group) || m_Unit.StatusSampler.InitialStrength() != m_Strength) return false;
if (!m_Actor || !ERR_Actors.CanFight(m_Actor) || ERR_Actors.Player(m_Actor) || CompartmentAccessComponent.GetVehicleIn(m_Actor)) return false;
+ vector actorPosition = m_Actor.GetOrigin();
+ if (!ERR_EngineerMineRules.WithinWorkArea(m_Rally, actorPosition) || !ERR_EngineerMineRules.WithinWorkArea(m_Rally, m_Position)) return false;
+ if (!ERR_EngineerMineRules.CorridorClear(actorPosition, actorPosition, m_PriorSites)
+ || !ERR_EngineerMineRules.CorridorClear(m_Position, m_Position, m_PriorSites)
+ || !ERR_EngineerMineRules.CorridorClear(m_Rally, m_Rally, m_PriorSites)) return false;
if (!m_Unit.StatusSampler.CanAttribute(m_Slot, m_Actor, false) || ERR_Actors.Group(m_Actor) != m_Group) return false;
if (SCR_FactionManager.SGetFaction(m_Actor) != m_Mode.EnemyFaction()) return false;
SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(SCR_AIUtils.GetAIAgent(m_Actor));
@@ -190,7 +237,7 @@
}
return false;
}
- bool Begin(ERR_GameMode mode, ERR_HQUnit unit, ERR_HQTask task, vector minePosition, float now)
+ bool Begin(ERR_GameMode mode, ERR_HQUnit unit, ERR_HQTask task, vector minePosition, float now, array<vector> priorSites = null)
{
if (m_Begun || m_Stopped) return false;
m_Begun = true;
@@ -203,7 +250,18 @@
m_Position = minePosition; m_Rally = task.Destination;
float distance = vector.DistanceXZ(m_Position, m_Rally);
if (!ERR_ForceRules.FiniteVector(m_Rally) || distance < 25 || distance > 60 || now >= task.ExpiresAt) return Refuse();
+ if (priorSites)
+ {
+ if (priorSites.Count() > 32) return Refuse();
+ foreach (vector prior : priorSites)
+ {
+ if (!ERR_ForceRules.FiniteVector(prior)) return Refuse();
+ m_PriorSites.Insert(prior);
+ }
+ }
if (!SelectActor() || !Owned() || !Quiet()) return Refuse();
+ if (!ERR_EngineerMineRules.CorridorClear(m_Actor.GetOrigin(), m_Position, m_PriorSites)
+ || !ERR_EngineerMineRules.CorridorClear(m_Position, m_Rally, m_PriorSites)) return Refuse();
// The shared exact-item tombstone survives pickup, transfer and late callbacks.
m_Attempted.Insert(m_Mine);
if (!Carried()) return Refuse();
@@ -0,0 +1,6 @@
{
"files": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMine.c": "defb12924ea5dfe68f86c2bbb4b07362f016ffee810f720d13f01d81881b3cfc"
},
"patch_sha256": "9d324e4580e1cedeb155badb338439cdf0112d6eb0345a847da0b9eadb9b67b9"
}