Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
118 lines
5.9 KiB
Diff
118 lines
5.9 KiB
Diff
--- /tmp/erri-hunt-before/ERR_SupplyConvoyTactics.c 2026-10-04 12:52:07.062390760 +0200
|
|
+++ addons/core/Scripts/Game/ERR/10_AI/ERR_SupplyConvoyTactics.c 2026-10-04 12:57:40.250476502 +0200
|
|
@@ -8,6 +8,9 @@
|
|
bool Registered;
|
|
bool Released;
|
|
vector OrderedContact;
|
|
+ vector SearchPosition;
|
|
+ int SearchStep;
|
|
+ bool SearchPlanned;
|
|
ref ERR_ConvoyBoardingProgress BoardingProgress;
|
|
float BoardingRetryAt;
|
|
void Clear()
|
|
@@ -202,6 +205,7 @@
|
|
protected vector m_Left;
|
|
protected float m_LastContact = -1;
|
|
protected float m_NextOrders;
|
|
+ protected float m_NextSearch;
|
|
protected vector m_LastOrderContact;
|
|
protected bool m_OrdersIssued;
|
|
protected float m_BoardDeadline;
|
|
@@ -280,7 +284,7 @@
|
|
// Only production native observations or a delivered HQ snapshot may call this.
|
|
void Report(vector position, float observed, float now)
|
|
{
|
|
- if (m_Stopped || !ERR_ForceRules.Finite(now) || !ERR_ForceRules.Finite(observed) || !ERR_ForceRules.FiniteVector(position) || observed < 0 || observed > now || now - observed > 60000 || observed < m_LastContact) return;
|
|
+ if (m_Stopped || !ERR_ForceRules.Finite(now) || !ERR_ForceRules.Finite(observed) || !ERR_ForceRules.FiniteVector(position) || observed < 0 || observed > now || now - observed > 60000 || observed <= m_LastContact) return;
|
|
m_LastContact = observed;
|
|
if (m_State != 1) Start(position, now);
|
|
else if (vector.DistanceXZ(position, m_Contact) > 40 && now >= m_NextOrders) { m_Contact = position; Orders(false, now); }
|
|
@@ -299,19 +303,58 @@
|
|
}
|
|
protected void Flank(int index, bool dismount)
|
|
{
|
|
+ PlanFlank(index, dismount, 0);
|
|
+ }
|
|
+ protected bool PlanFlank(int index, bool dismount, int step)
|
|
+ {
|
|
ERR_SupplyTeam team = m_Task.Teams[index];
|
|
- if (team.Released || !team.Living()) return;
|
|
+ if (team.Released || !team.Living() || step < 0 || step > 2) return false;
|
|
// Preserve an in-progress native all-member dismount across contact refreshes.
|
|
- if (!team.Aboard(m_Task.Vehicles[2].Entity, false) && SCR_BoardingWaypoint.Cast(team.Group.GetCurrentWaypoint())) return;
|
|
- team.Clear(); team.OrderedContact = m_Contact;
|
|
- if (dismount && !team.Aboard(m_Task.Vehicles[2].Entity, false)) team.Add(Board(m_Task.Vehicles[2].Entity, false));
|
|
+ if (!team.Aboard(m_Task.Vehicles[2].Entity, false) && SCR_BoardingWaypoint.Cast(team.Group.GetCurrentWaypoint())) return false;
|
|
float side = 1; if (index == 1) side = -1;
|
|
+ vector desired = m_Contact + m_Left * (70 * side);
|
|
+ if (step == 1) desired = m_Contact + m_Forward * 100 + m_Left * (140 * side);
|
|
+ if (step == 2) desired = m_Contact + m_Forward * 160 + m_Left * (50 * side);
|
|
vector approach, sector;
|
|
- if (!FootPosition(team, m_Contact - m_Forward * 80 + m_Left * (110 * side), approach) || !FootPosition(team, m_Contact + m_Left * (70 * side), sector)) return;
|
|
- team.Add(Move(approach, 15));
|
|
+ if (!FootPosition(team, desired, sector)) return false;
|
|
+ approach = sector;
|
|
+ if (step == 0 && !FootPosition(team, m_Contact - m_Forward * 80 + m_Left * (110 * side), approach)) return false;
|
|
+ AIWaypoint move = Move(approach, 15);
|
|
AIWaypoint search = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.SearchWaypoint(), sector));
|
|
- if (search) search.SetCompletionRadius(30);
|
|
+ AIWaypoint exit;
|
|
+ bool needsExit = dismount && !team.Aboard(m_Task.Vehicles[2].Entity, false);
|
|
+ if (needsExit) exit = Board(m_Task.Vehicles[2].Entity, false);
|
|
+ // Failed placement/allocation must not erase the team's working orders.
|
|
+ if (!move || !search || (needsExit && !exit))
|
|
+ {
|
|
+ if (move) SCR_EntityHelper.DeleteEntityAndChildren(move);
|
|
+ if (search) SCR_EntityHelper.DeleteEntityAndChildren(search);
|
|
+ if (exit) SCR_EntityHelper.DeleteEntityAndChildren(exit);
|
|
+ return false;
|
|
+ }
|
|
+ team.Clear(); team.OrderedContact = m_Contact;
|
|
+ team.SearchPosition = sector; team.SearchStep = step; team.SearchPlanned = true;
|
|
+ team.Add(exit); team.Add(move);
|
|
+ search.SetCompletionRadius(30);
|
|
team.Add(search);
|
|
+ ERR_HQ.Event("CONVOY HUNT", string.Format("%1 team %2: search step %3/3 at %4 from last observed contact; formation suspended", m_Task.Receipt.SourceId, index + 1, step + 1, sector));
|
|
+ return true;
|
|
+ }
|
|
+ protected void ContinueSearch(float now)
|
|
+ {
|
|
+ if (now < m_NextSearch) return;
|
|
+ m_NextSearch = now + 45000;
|
|
+ for (int i = 0; i < m_Task.Teams.Count(); i++)
|
|
+ {
|
|
+ ERR_SupplyTeam team = m_Task.Teams[i];
|
|
+ if (team.Released || !team.Living() || !team.Aboard(m_Task.Vehicles[2].Entity, false)) continue;
|
|
+ // Retry refused initial orders, but never replace a team still travelling.
|
|
+ if (!team.SearchPlanned || vector.DistanceXZ(team.OrderedContact, m_Contact) > 40) { PlanFlank(i, false, 0); continue; }
|
|
+ if (team.SearchStep >= 2) continue;
|
|
+ IEntity leader = team.Group.GetLeaderEntity();
|
|
+ if (!leader || vector.DistanceXZ(leader.GetOrigin(), team.SearchPosition) > 45) continue;
|
|
+ PlanFlank(i, false, team.SearchStep + 1);
|
|
+ }
|
|
}
|
|
protected bool SupportPosition(int index, vector desired, out vector position)
|
|
{
|
|
@@ -381,6 +424,8 @@
|
|
if (intercept.InterceptToken != 0) return;
|
|
}
|
|
Clear(); m_State = 1; m_Contact = contact; m_TruckWithdrawn = false; m_OrdersIssued = false;
|
|
+ m_NextSearch = now + 45000;
|
|
+ foreach (ERR_SupplyTeam team : m_Task.Teams) { team.SearchPlanned = false; team.SearchStep = 0; }
|
|
if (m_Task.Column) m_Task.Column.Reset(now);
|
|
m_Task.Waiting = false;
|
|
vector origin;
|
|
@@ -493,7 +538,11 @@
|
|
}
|
|
}
|
|
if (found && now >= m_NextOrders) Orders(!InfantryAboard(false), now);
|
|
- if (now - m_LastContact >= 60000)
|
|
+ // Search only remembered positions. Real new sightings can redirect the
|
|
+ // teams; silence gives them time to reach and sweep both flanks, not a
|
|
+ // one-minute dash followed by forced reboarding.
|
|
+ if (!found && now - m_LastContact < 180000) ContinueSearch(now);
|
|
+ if (now - m_LastContact >= 180000)
|
|
{
|
|
Clear();
|
|
if (!Usable(2)) { foreach (ERR_SupplyTeam lost : m_Task.Teams) lost.Release(m_Mode); Finish(now); return false; }
|