Files
EscapistsReintroduced/addons/ai/Scripts/Game/ERR/ERR_EnemyRadio.c
T

164 lines
6.5 KiB
C

// Owner-authorized group startup radio fitting; provenance in equipment migration docs.
// Existing HQ registration selects the group. Physical inventory alone enables routes.
class ERR_EnemyRadio
{
static const ResourceName BASIC = "{E1A5D4B878AA8980}Prefabs/Items/Equipment/Radios/Radio_R148.et";
static const ResourceName MANPACK = "{54C68E438DD34265}Prefabs/Items/Equipment/Radios/Radio_R107M.et";
protected SCR_AIGroup m_Group;
protected bool m_RelaySelected;
protected bool m_BasicDone;
protected bool m_RelayDone;
protected bool m_Captured;
protected bool m_Stopped;
protected int m_Checks;
protected int m_Requests;
protected int m_Completed;
protected ref array<IEntity> m_Members = {};
protected ref array<IEntity> m_Allowed = {};
protected ref array<IEntity> m_BasicAttempts = {};
protected ref array<IEntity> m_RelayAttempts = {};
protected IEntity m_PendingActor;
protected int m_PendingType;
protected bool m_Submitted;
protected ref ERR_EnemyEquipmentCallback m_Operation;
void ERR_EnemyRadio(SCR_AIGroup group, int radioId)
{
m_Group = group;
m_RelaySelected = radioId > 0 && radioId % 4 == 1;
m_RelayDone = !m_RelaySelected;
if (radioId <= 0) m_Stopped = true;
}
// Only the equipment coordinator may add newly seen original actors. Its
// retained actor tombstones exclude transferred bodies from another grant.
void Register(IEntity actor)
{
if (m_Stopped || m_Captured || !actor || m_Allowed.Count() >= 32 || m_Allowed.Contains(actor)) return;
m_Allowed.Insert(actor);
}
// Negative means inventory is not yet inspectable, never permission to issue.
static int Capability(IEntity actor)
{
if (!actor) return -1;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
if (!inventory) return -1;
array<IEntity> items = {}; inventory.GetItems(items);
if (items.Count() > 256) return -1;
int capability;
foreach (IEntity item : items)
{
if (!item || !item.FindComponent(BaseRadioComponent)) continue;
capability = 1;
SCR_RadioComponent radio = SCR_RadioComponent.Cast(item.FindComponent(SCR_RadioComponent));
if (radio && radio.ERR_IsHQManpack()) return 2;
}
return capability;
}
protected void Finish(string reason)
{
if (m_Stopped) return;
m_Stopped = true;
ERR_HQ.Event("RADIO EQUIPMENT", reason);
}
void Stop() { Finish("startup fitting stopped; accepted native insert is not cancelled or replaced"); }
protected bool OriginalCrew(ERR_GameMode mode)
{
array<AIAgent> agents = {}; m_Group.GetAgents(agents);
if (agents.IsEmpty() || agents.Count() > 32) return false;
array<IEntity> current = {};
foreach (AIAgent agent : agents)
{
if (!agent) return false;
IEntity actor = agent.GetControlledEntity();
if (!m_Allowed.Contains(actor) || !ERR_EnemyEquipmentRules.Eligible(mode, m_Group, actor) || current.Contains(actor)) return false;
current.Insert(actor);
}
if (!m_Captured)
{
foreach (IEntity original : current) m_Members.Insert(original);
m_Captured = true;
}
if (current.Count() != m_Members.Count()) return false;
foreach (IEntity member : m_Members) if (!current.Contains(member)) return false;
return true;
}
protected bool Submit(ERR_GameMode mode, int capability)
{
if (m_Stopped || m_Submitted) return false;
ResourceName prefab = BASIC;
if (capability == 2) prefab = MANPACK;
foreach (IEntity actor : m_Members)
{
if (!ERR_EnemyEquipmentRules.Eligible(mode, m_Group, actor)) { Stop(); return false; }
if (capability == 1 && m_BasicAttempts.Contains(actor)) continue;
if (capability == 2 && m_RelayAttempts.Contains(actor)) continue;
InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent));
if (!inventory || !inventory.CanInsertResource(prefab, EStoragePurpose.PURPOSE_ANY)) continue;
if (capability == 1) m_BasicAttempts.Insert(actor);
else m_RelayAttempts.Insert(actor);
m_PendingActor = actor; m_PendingType = capability;
m_Operation = new ERR_EnemyEquipmentCallback(); m_Submitted = true; m_Requests++;
bool accepted = inventory.TrySpawnPrefabToStorage(prefab, null, -1, EStoragePurpose.PURPOSE_ANY, m_Operation);
if (!accepted && !(m_Operation.Finished && m_Operation.Succeeded)) m_Submitted = false;
return true; // At most one native request across the entire group per check.
}
return false;
}
bool Check(ERR_GameMode mode)
{
if (m_Stopped) return true;
if (!Replication.IsServer() || SCR_Global.IsEditMode() || !mode || mode != ERR_GameMode.Get() || !mode.Active() || !m_Group || m_Group.IsSlave() || m_Group.GetPlayerCount(true) > 0 || m_Group.GetFaction() != mode.EnemyFaction()) { Stop(); return true; }
m_Checks++;
if (!m_Group.IsExpandComplete())
{
if (m_Checks >= 5) Finish("native startup crew did not complete before radio deadline");
return m_Stopped;
}
if (!OriginalCrew(mode))
{
if (m_Captured || m_Checks >= 5) Finish("original radio crew unavailable or changed; no replacement");
return m_Stopped;
}
bool known = true;
int best;
foreach (IEntity actor : m_Members)
{
int capability = Capability(actor);
if (capability < 0) known = false;
else if (capability > best) best = capability;
}
if (best >= 1) m_BasicDone = true;
if (best >= 2) m_RelayDone = true;
if (m_Submitted)
{
if (!ERR_EnemyEquipmentRules.Eligible(mode, m_Group, m_PendingActor)) { Stop(); return true; }
if (m_Operation && m_Operation.Finished)
{
if (m_Operation.Succeeded)
{
m_Completed++; m_BasicDone = true;
if (m_PendingType == 2) m_RelayDone = true;
}
m_Submitted = false;
}
}
if (m_BasicDone && m_RelayDone)
{
if (m_Requests == 0) Finish("original group already equipped; no native radio request submitted");
else if (m_Completed > 0) Finish("native radio completion recorded; original group allocation terminal, no replenishment");
else Finish("physical group radio observed after request; insertion provenance unconfirmed, no replenishment");
return true;
}
if (m_Checks >= 5)
{
if (m_Submitted) Finish("startup radio deadline; accepted unresolved insertion quarantined");
else Finish("startup radio deadline; required compatible equipment not confirmed");
return true;
}
if (m_Submitted || !known) return false;
// A relay satisfies the basic need too; never submit both in parallel.
if (!m_RelayDone && Submit(mode, 2)) return m_Stopped;
if (!m_BasicDone) Submit(mode, 1);
return m_Stopped;
}
}