class ERR_ObjectProbeScanner
{
 protected int m_ObjectProbeCount;
 protected int m_ObjectProbeElevated;
 ref array<IEntity> m_ObjectProbeCandidates = {};
 protected bool ObjectProbeFilter(IEntity entity)
 {
  if (!entity || Vehicle.Cast(entity) || ChimeraCharacter.Cast(entity)) return false;
  string prefab;
  if (entity.GetPrefabData()) prefab = entity.GetPrefabData().GetPrefabName();
  MapDescriptorComponent descriptor = MapDescriptorComponent.Cast(entity.FindComponent(MapDescriptorComponent));
  int type = -1; if (descriptor) type = descriptor.GetBaseType();
  return ERR_ObjectGuardRules.Structure(prefab, type);
 }
 protected bool ObjectProbeRemember(IEntity entity)
 {
  m_ObjectProbeCandidates.Insert(entity);
  return m_ObjectProbeCandidates.Count() < 80;
 }
 void Run(BaseWorld world)
 {
  world.QueryEntitiesByAABB("0 -1000 0", "14000 1000 14000", ObjectProbeRemember, ObjectProbeFilter, EQueryEntitiesFlags.ALL);
  PrintFormat("ERR OBJECT PROBE candidates=%1", m_ObjectProbeCandidates.Count());
  foreach (IEntity entity : m_ObjectProbeCandidates)
  {
   ERR_ObjectGuardPlacement posts = new ERR_ObjectGuardPlacement(); posts.Reset();
   array<vector> players = {entity.GetOrigin() + "1200 0 0"};
   vector point; bool found = posts.Find(entity, players, 0, point);
   float height; if (found) height = point[1] - world.GetSurfaceY(point[0], point[2]);
   string prefab; if (entity.GetPrefabData()) prefab = entity.GetPrefabData().GetPrefabName();
   PrintFormat("ERR OBJECT PROBE structure=%1 origin=%2 found=%3 point=%4 aboveGround=%5", prefab, entity.GetOrigin(), found, point, height);
   if (found && height > 2) m_ObjectProbeElevated++;
   m_ObjectProbeCount++;
  }
  PrintFormat("ERR OBJECT GEOMETRY COMPLETE: tested=%1 elevated=%2", m_ObjectProbeCount, m_ObjectProbeElevated);
  ERR_HQ hq = ERR_HQ.Cast(ERR_GameMode.Get().Command());
  foreach (IEntity candidate : m_ObjectProbeCandidates)
  {
   string name; if (candidate.GetPrefabData()) name = candidate.GetPrefabData().GetPrefabName();
   if (!name.Contains("LightTower_01")) continue;
   hq.ObjectProbeStart(candidate, m_ObjectProbeCandidates); break;
  }
 }
}


modded class SCR_ChimeraAIAgent
{
 void ObjectProbeRelease() { ERR_PrisonPhaseChanged(ERR_Phase.PRISON, ERR_Phase.RADIO_SEARCH); SetPermanentLOD(0); ActivateAI(); }
}
modded class ERR_ObjectGarrisons
{
 protected ref array<vector> m_ProbeObservers = {};
 protected ref array<vector> m_ProbeStarts = {};
 override protected void Players(out array<vector> positions)
 {
  if (m_ProbeObservers.IsEmpty()) { super.Players(positions); return; }
  positions.Copy(m_ProbeObservers);
 }
 void ObjectProbeSetup(IEntity first, array<IEntity> nearby)
 {
  ConfigureCount(8); Remember(first);
  foreach (IEntity other : nearby) if (vector.DistanceXZ(first.GetOrigin(), other.GetOrigin()) < 120) Remember(other);
  m_ProbeObservers.Insert(first.GetOrigin() + "1200 0 0");
  PrintFormat("ERR OBJECT SETUP sites=%1 groups=%2 structures=%3", m_Sites.Count(), m_Towns.Count(), m_Sites[0].Structures.Count());
 }
 override void ObjectProbeSpawn(float now)
 {
  foreach (ERR_TownGarrison entry : m_Towns) Attempt(entry, m_ProbeObservers, now);
 }
 override void ObjectProbeInspect(bool release)
 {
  int total, fixed, radios, elevated;
  foreach (ERR_TownGarrison entry : m_Towns)
  {
   if (!entry.Admitted) { PrintFormat("ERR OBJECT WAIT id=%1", entry.Id); continue; }
   array<AIAgent> agents = {}; entry.Group.GetAgents(agents);
   foreach (AIAgent agent : agents)
   {
    IEntity actor = agent.GetControlledEntity(); if (!actor) continue;
    vector point = actor.GetOrigin();
    float height = point[1] - GetGame().GetWorld().GetSurfaceY(point[0], point[2]);
    int radio = ERR_HQRadio.Capability(actor);
    bool alive = ERR_Actors.Alive(actor);
    PrintFormat("ERR OBJECT ACTOR id=%1 fixed=%2 alive=%3 radio=%4 height=%5 point=%6", entry.Id, entry.FixedGuard, alive, radio, height, point);
    total++; if (entry.FixedGuard) fixed++; if (radio > 0) radios++; if (height > 2) elevated++;
    SCR_ChimeraAIAgent soldier = SCR_ChimeraAIAgent.Cast(agent);
    if (release && soldier) soldier.ObjectProbeRelease();
   }
  }
  PrintFormat("ERR OBJECT COUNTS total=%1 fixed=%2 radios=%3 elevated=%4 release=%5", total, fixed, radios, elevated, release);
 }
}
modded class ERR_TownGarrisons
{
 void ObjectProbeStart(IEntity first, array<IEntity> nearby)
 {
  m_Objects = new ERR_ObjectGarrisons(m_Mode); m_Objects.ObjectProbeSetup(first, nearby);
 }
 void ObjectProbeSpawn(float now) { if (m_Objects) m_Objects.ObjectProbeSpawn(now); }
 void ObjectProbeInspect(bool release) { if (m_Objects) m_Objects.ObjectProbeInspect(release); }
}
modded class ERR_HQ
{
 void ObjectProbeStart(IEntity first, array<IEntity> nearby) { m_Towns.ObjectProbeStart(first, nearby); }
 void ObjectProbeSpawn(float now) { m_Towns.ObjectProbeSpawn(now); m_Equipment.Tick(now); }
 void ObjectProbeInspect(bool release)
 {
  m_Towns.ObjectProbeInspect(release);
  foreach (ERR_HQUnit unit : m_Units)
   if (unit.ForceSourceId.StartsWith("object-")) PrintFormat("ERR OBJECT HQ id=%1 fixed=%2 reserved=%3 eligible=%4", unit.ForceSourceId, unit.HomeGuard, unit.ReservedForSupport, unit.Eligible(m_Mode));
 }
}
modded class ArmaReforgerScripted
{
 protected int m_ObjectProbePhase;
 protected float m_ObjectProbeStarted;
 protected float m_ObjectProbeNext;
 override void OnUpdate(BaseWorld world, float timeslice)
 {
  super.OnUpdate(world, timeslice);
  ERR_GameMode mode = ERR_GameMode.Get();
  if (!mode || mode.Phase() < ERR_Phase.PRISON || m_ObjectProbePhase == 3) return;
  float now = world.GetWorldTime(); if (now < m_ObjectProbeNext) return; m_ObjectProbeNext = now + 1000;
  ERR_HQ hq = ERR_HQ.Cast(mode.Command());
  if (m_ObjectProbePhase == 0)
  {
   ERR_ObjectProbeScanner scanner = new ERR_ObjectProbeScanner(); scanner.Run(world);
   m_ObjectProbePhase = 1; m_ObjectProbeStarted = now; return;
  }
  if (m_ObjectProbePhase == 1)
  {
   hq.ObjectProbeSpawn(now);
   if (now - m_ObjectProbeStarted < 12000) return;
   hq.ObjectProbeInspect(true); m_ObjectProbePhase = 2; return;
  }
  hq.ObjectProbeSpawn(now);
  if (now - m_ObjectProbeStarted < 45000) return;
  hq.ObjectProbeInspect(false); m_ObjectProbePhase = 3;
  Print("ERR OBJECT PROBE COMPLETE: native spawn / HQ role / 30-second post observation");
 }
}
