Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
107 lines
7.0 KiB
Python
107 lines
7.0 KiB
Python
"""Execute actual HQ assignment ownership and failed-radio backoff bodies."""
|
|
from policy_harness import ROOT, body, run
|
|
import re
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'
|
|
unit='\n'.join(body(p,s) for s in ('void CancelPending(', 'ERR_HQTask PlannedTask(', 'void ReleaseAssignment(', 'void Release()'))
|
|
lost='\n'.join(body(p,s) for s in ('protected void FinishWatch(', 'protected void LostOrder(')).replace('protected ', '')
|
|
def adapt(code):
|
|
code=code.replace('null','nullptr').replace('Math.','Math::').replace('ERR_GameMode.Get()', 'ERR_GameMode::Get()')
|
|
code=code.replace('SCR_EntityHelper.DeleteEntityAndChildren', 'SCR_EntityHelper::DeleteEntityAndChildren')
|
|
code=code.replace('ERR_HQUnit unit', 'ERR_HQUnit* unit')
|
|
code=code.replace('ERR_HQTask ', 'ERR_HQTask* ').replace('ERR_GameMode ', 'ERR_GameMode* ')
|
|
code=code.replace('ERR_ForceRecord record;', 'ERR_ForceRecord* record=nullptr;')
|
|
code=code.replace('ERR_HQHuntWindow hunt', 'ERR_HQHuntWindow* hunt')
|
|
code=code.replace('foreach (ERR_HQUnit other : m_Units)', 'for (ERR_HQUnit* other : m_Units)')
|
|
code=re.sub(r'\b(mode|record|Group|unit|other|task|planned|hunt)\.', r'\1->', code)
|
|
code=code.replace('mode->Forces().FindGroup', 'mode->Forces()->FindGroup')
|
|
return code
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <string>
|
|
struct Math {static float Min(float a,float b){return std::min(a,b);}};
|
|
struct ERR_HQTask {bool ReleaseOnly=false;int BeliefId=0;std::string WatchSourceId;int WatchMessageId=0,WatchRequestId=0;bool Investigation(){return !WatchSourceId.empty();}};
|
|
struct ERR_HQReport {};
|
|
struct ERR_HQRoute {};
|
|
struct AIWaypoint {int deleted=0;};
|
|
struct SCR_AIGroup {int removals=0; AIWaypoint* last=nullptr;
|
|
void RemoveWaypoint(AIWaypoint* p){removals++;last=p;}};
|
|
struct ERR_ForceRecord {bool Eliminated=false;int Duty=2,HomeDuty=1,DutyTarget=200,HomePosition=100;};
|
|
struct Ledger {ERR_ForceRecord* record=nullptr;ERR_ForceRecord* FindGroup(SCR_AIGroup*){return record;}};
|
|
struct ERR_GameMode {Ledger ledger;static ERR_GameMode* current;
|
|
static ERR_GameMode* Get(){return current;} Ledger* Forces(){return &ledger;}};
|
|
ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
struct SCR_EntityHelper {static void DeleteEntityAndChildren(AIWaypoint* p){p->deleted++;}};
|
|
struct ERR_HQHuntWindow {float NextHunt=0;};
|
|
struct ERR_HQUnit {
|
|
SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
|
|
ERR_HQTask *Task=nullptr,*PendingTask=nullptr;
|
|
ERR_HQReport *Assignment=nullptr,*PendingOrder=nullptr;
|
|
void* PendingConvoyOrder=nullptr;
|
|
ERR_HQRoute* Downlink=nullptr;bool PendingCheckin=false;
|
|
'''+adapt(unit)+r'''};
|
|
struct Watch {int calls=0;std::string source;int message=0,request=0;
|
|
bool FinishInvestigation(std::string s,int m,int r,float){calls++;source=s;message=m;request=r;return true;}};
|
|
struct HQ {Watch m_Watch;std::vector<ERR_HQUnit*> m_Units;ERR_HQHuntWindow window;
|
|
ERR_HQHuntWindow* Hunt(int){return &window;}
|
|
'''+adapt(lost)+r'''};
|
|
int main(){
|
|
ERR_GameMode mode;ERR_GameMode::current=&mode;ERR_ForceRecord record;mode.ledger.record=&record;
|
|
SCR_AIGroup group;ERR_HQReport oldReport,newReport;ERR_HQTask oldTask,newTask;ERR_HQRoute route;
|
|
oldTask.BeliefId=1;newTask.BeliefId=2;AIWaypoint oldWaypoint;
|
|
ERR_HQUnit u;u.Group=&group;u.Waypoint=&oldWaypoint;u.Assignment=&oldReport;u.Task=&oldTask;
|
|
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
|
assert(u.PlannedTask()==&newTask);
|
|
u.CancelPending();
|
|
assert(!u.PendingOrder&&!u.PendingTask&&!u.Downlink);
|
|
assert(u.Task==&oldTask&&u.Assignment==&oldReport&&u.Waypoint==&oldWaypoint);
|
|
assert(oldWaypoint.deleted==0&&group.removals==0&&record.Duty==2);
|
|
assert(u.PlannedTask()==&oldTask);
|
|
// Expiry of the current task must leave its replacement radio transaction intact.
|
|
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
|
u.ReleaseAssignment();
|
|
assert(!u.Assignment&&!u.Task&&!u.Waypoint);
|
|
assert(u.PendingOrder==&newReport&&u.PendingTask==&newTask&&u.Downlink==&route);
|
|
assert(oldWaypoint.deleted==1&&group.removals==1&&group.last==&oldWaypoint);
|
|
assert(record.Duty==record.HomeDuty&&record.DutyTarget==record.HomePosition);
|
|
u.ReleaseAssignment();assert(oldWaypoint.deleted==1&&group.removals==1);
|
|
u.Release();assert(!u.PendingOrder&&!u.PendingTask&&!u.Downlink);
|
|
// Elimination and absent group/mode cannot rewrite casualty state or double-delete.
|
|
AIWaypoint detached;u.Waypoint=&detached;u.Group=nullptr;record.Eliminated=true;record.Duty=9;
|
|
ERR_GameMode::current=nullptr;u.Release();assert(detached.deleted==1&&record.Duty==9);
|
|
ERR_GameMode::current=&mode;u.Group=&group;u.ReleaseAssignment();assert(record.Duty==9);
|
|
// A failed initial dispatch can retry after a short backoff.
|
|
HQ hq;hq.m_Units={&u};hq.window.NextHunt=300000;
|
|
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
|
hq.LostOrder(&u,1000);assert(hq.window.NextHunt==16000&&!u.PendingOrder);
|
|
// Failed retask preserves delivered assignment and its active hunt window.
|
|
u.Task=&newTask;u.Assignment=&oldReport;u.Waypoint=&oldWaypoint;
|
|
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;hq.window.NextHunt=300000;
|
|
hq.LostOrder(&u,1000);assert(hq.window.NextHunt==300000&&u.Task==&newTask&&u.Waypoint==&oldWaypoint);
|
|
// Another group's pending hunt prevents duplicate retry commitments.
|
|
u.Task=nullptr;u.Assignment=nullptr;u.Waypoint=nullptr;
|
|
ERR_HQUnit peer;peer.PendingTask=&newTask;hq.m_Units.push_back(&peer);
|
|
u.PendingTask=&newTask;u.PendingOrder=&newReport;u.Downlink=&route;
|
|
hq.LostOrder(&u,2000);assert(hq.window.NextHunt==300000);
|
|
// A failed pending watch investigation completes only that pending lineage.
|
|
ERR_HQTask activeWatch,pendingWatch;activeWatch.WatchSourceId="patrol-A";activeWatch.WatchMessageId=41;activeWatch.WatchRequestId=1;
|
|
pendingWatch.WatchSourceId="patrol-B";pendingWatch.WatchMessageId=42;pendingWatch.WatchRequestId=2;
|
|
u.Task=&activeWatch;u.Waypoint=&oldWaypoint;u.Assignment=&oldReport;
|
|
u.PendingTask=&pendingWatch;u.PendingOrder=&newReport;u.Downlink=&route;
|
|
hq.LostOrder(&u,3000);assert(hq.m_Watch.calls==1&&hq.m_Watch.source=="patrol-B"&&hq.m_Watch.message==42&&hq.m_Watch.request==2);
|
|
assert(u.Task==&activeWatch&&u.Waypoint==&oldWaypoint&&u.Assignment==&oldReport);
|
|
assert(!u.PendingTask&&!u.PendingOrder&&!u.Downlink&&hq.window.NextHunt==300000);
|
|
// Dropped status-check-in transport cannot finish the delivered investigation.
|
|
u.PendingCheckin=true;u.Downlink=&route;hq.LostOrder(&u,4000);
|
|
assert(!u.PendingCheckin&&!u.Downlink&&u.Task==&activeWatch&&hq.m_Watch.calls==1);
|
|
ERR_HQTask recall;recall.ReleaseOnly=true;u.PendingTask=&recall;u.PendingOrder=&newReport;u.Downlink=&route;
|
|
hq.LostOrder(&u,5000);assert(!u.PendingTask&&u.Task==&activeWatch&&u.Waypoint==&oldWaypoint&&hq.m_Watch.calls==1);
|
|
u.Task=nullptr;u.Waypoint=nullptr;u.Assignment=nullptr;
|
|
// Unrelated belief cannot suppress bounded retry, and existing sooner retry remains sooner.
|
|
peer.PendingTask=&oldTask;u.PendingTask=&newTask;hq.window.NextHunt=5000;
|
|
hq.LostOrder(&u,2000);assert(hq.window.NextHunt==5000);
|
|
}
|
|
''')
|
|
print('PASS: actual assignment cancellation/release preserves radio ownership, native waypoint lifecycle and failed-order retry boundaries')
|