--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c +++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c @@ -1408,6 +1408,8 @@ Retask(now); Investigate(now, globalLimit); assigned = HuntingAssigned(); + // Retask/investigation and cleanup above have settled commitments for this pass. + int totalAssigned = Assigned(); array reserved = {}; int first = -1; int second = -1; @@ -1417,7 +1419,7 @@ } // Strongest delivered track first; finite scans (64 beliefs,128 own units). array considered = {}; - for (int pass = 0; pass < m_PlanBeliefs.Count() && assigned < limit && Assigned() < globalLimit; pass++) + for (int pass = 0; pass < m_PlanBeliefs.Count() && assigned < limit && totalAssigned < globalLimit; pass++) { ERR_HQBelief belief; for (int i = 0; i < m_PlanBeliefs.Count(); i++) @@ -1433,7 +1435,7 @@ if (belief.Kind == ERR_HQMemoryKind.ALARM && AlarmCommitted()) continue; ERR_HQHuntWindow hunt = Hunt(belief.BeliefId); if (!hunt || now < hunt.NextHunt) continue; - for (int slot = 0; slot < limit && assigned < limit && Assigned() < globalLimit; slot++) + for (int slot = 0; slot < limit && assigned < limit && totalAssigned < globalLimit; slot++) { // One mobile search squad for uncertain sound; never empty objectives for it. if ((belief.Kind == ERR_HQMemoryKind.GUNFIRE || belief.Kind == ERR_HQMemoryKind.ALARM) && slot > 0) break; @@ -1489,6 +1491,7 @@ if (best.HomeGuard) m_HomeAvailable.Set(best.HomeId, HomeAvailable(best) - 1); hunt.NextHunt = Math.Max(hunt.NextHunt, task.ExpiresAt + 180000); assigned++; + totalAssigned++; } } } --- a/tools/check.py +++ b/tools/check.py @@ -14,6 +14,7 @@ subprocess.run([sys.executable,str(ROOT/'tests/test_spawn_scene_lease.py')],check=True) subprocess.run([sys.executable,str(ROOT/'tests/test_prison_ai.py')],check=True) subprocess.run([sys.executable,str(ROOT/'tests/test_native_player_death_fixture.py')],check=True) + subprocess.run([sys.executable,str(ROOT/'tests/test_hq_planner_budget.py')],check=True) ids={};graph={};resources={};paths={};owners={} for p in sorted((ROOT/'addons').glob('*/addon.gproj')): text=p.read_text();g=re.search(r'GUID "([A-F0-9]{16})"',text)[1]