Embed RHS 1.9 compatibility; record Workshop 0.0.141
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -7,13 +7,13 @@ ROAD=Path(os.environ.get('ERR_HQ_ROAD_SOURCE',ROOT/'addons/core/Scripts/Game/ERR
|
||||
def cpp(s):
|
||||
s=s.replace('EngineerMine.', 'EngineerMine->')
|
||||
s=s.replace('ref ', '')
|
||||
s=s.replace('protected ','').replace('null','nullptr').replace('string.Format','Format')
|
||||
for name in ('ERR_SupplyConvoyTactics','Math','ERR_GameMode','SCR_EntityHelper','ERR_HQPursuitRules','AIWaypoint','ERR_Actors','ERR_Resources','ERR_ForceDuty','ERR_HQRoadIntercept','ERR_ForceRules','ERR_HQPursuitRole','ERR_HQMemoryKind','ERR_HQOperationRole'):
|
||||
s=s.replace('override ', '').replace('protected ','').replace('null','nullptr').replace('string.Format','Format')
|
||||
for name in ('ERR_SupplyConvoyTactics','Math','ERR_GameMode','SCR_EntityHelper','ERR_HQPursuitRules','AIWaypoint','ERR_Actors','ERR_Resources','ERR_ForceDuty','ERR_HQRoadIntercept','ERR_ForceRules','ERR_HQPursuitRole','ERR_HQMemoryKind','ERR_HQOperationRole','ERR_HQExecutionRules'):
|
||||
s=s.replace(name+'.',name+'::')
|
||||
for typ in ('ERR_HQUnit','ERR_HQTask','ERR_GameMode','ERR_HQReport','ERR_HQBelief','ERR_ForceRecord','ERR_HQHuntWindow','AIWaypoint'):
|
||||
for typ in ('ERR_HQUnit','ERR_HQTask','ERR_GameMode','ERR_HQReport','ERR_HQBelief','ERR_ForceRecord','ERR_HQHuntWindow','AIWaypoint','ERR_HQSquadJudgment','ERR_HQOperationSlot'):
|
||||
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
|
||||
s=re.sub(r'foreach \((ERR_HQUnit\*) (\w+) : ([^)]+)\)',r'for (\1 \2 : \3)',s)
|
||||
for name in ('unit','other','task','planned','order','belief','record','mode','Group','hunt','waypoint','m_Mode','lostActive','lostPending'):
|
||||
for name in ('unit','other','task','planned','order','belief','record','mode','Group','hunt','waypoint','m_Mode','lostActive','lostPending','squad','slot'):
|
||||
s=s.replace(name+'.',name+'->')
|
||||
s=s.replace('ResourceName ', 'int ').replace('.IsEmpty()', '.empty()')
|
||||
s=s.replace('PendingOrder.', 'PendingOrder->').replace('Assignment.', 'Assignment->')
|
||||
@@ -28,6 +28,8 @@ if 'protected void RememberCompletedHunt(' in P.read_text(): methods=cpp(body(P,
|
||||
if 'protected void ReleaseLostForce(' in P.read_text(): methods=cpp(body(P,'protected void ReleaseLostForce('))+'\n'+methods
|
||||
missing=cpp(body(P,'if (!unit.Group && !unit.Uplink)')).replace('m_Units.Remove(i)', 'm_Units.erase(m_Units.begin()+i)')
|
||||
cleanup=cpp(body(P,' foreach (ERR_HQUnit unit : m_Units)\n {\n if (!unit.Assignment && !unit.PendingOrder) continue;\n bool security = SecurityAssigned(unit);'))
|
||||
C=Path(os.environ.get('ERR_HQ_COORDINATED_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQCoordinated.c'))
|
||||
methods=cpp(body(C,'override protected bool CoordinatedDelivery('))+'\n'+methods
|
||||
R=Path(os.environ.get('ERR_HQ_PURSUIT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'))
|
||||
rules='\n'.join(cpp(body(R,x)) for x in ('static bool Finite(', 'static bool PositionValid(', 'static bool Actionable(', 'static bool Heading(', 'static ERR_HQPursuitRole Role('))
|
||||
eligible=cpp(body(ROAD,'static bool Eligible('))
|
||||
@@ -49,7 +51,7 @@ struct ERR_HQPursuitRules{
|
||||
'''+rules+r'''
|
||||
};
|
||||
struct Math{static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}};
|
||||
struct ERR_HQTask{ERR_HQOperationRole OperationRole=ERR_HQOperationRole::SEARCH;int OperationId=0,ExecutionId=0;string SecuritySite;bool Maneuver=false,RoadIntercept=false,ReleaseOnly=false;ERR_HQTask* ReplacesTask=nullptr;string WatchSourceId;int WatchMessageId=0,WatchRequestId=0;
|
||||
struct ERR_HQTask{ERR_HQOperationRole OperationRole=ERR_HQOperationRole::SEARCH;int OperationId=0,ExecutionId=0,OperationRevision=2,SupportSourceId=0;float SupportUntil=0;string SecuritySite;bool Maneuver=false,RoadIntercept=false,ReleaseOnly=false;ERR_HQTask* ReplacesTask=nullptr;string WatchSourceId;int WatchMessageId=0,WatchRequestId=0;
|
||||
int BeliefId=1,BeliefRevision=2;ERR_HQPursuitRole Role=ERR_HQPursuitRole::SEARCH;vector Destination{40,0,50};float SearchRadiusMetres=60,QueuedAt=0,ExpiresAt=60000;bool Investigation(){return !WatchSourceId.empty();}string Name(){return "search";}};
|
||||
struct ERR_HQReport{string TravelMode="on foot";int MessageId=10;float ObservedAt=0;};
|
||||
struct ERR_HQRoute{int MessageId=20;};
|
||||
@@ -67,7 +69,7 @@ struct MineJob {int stops=0;void Stop(){stops++;}};
|
||||
struct TestWorld{operator bool(){return true;}float GetWorldTime(){return 0;}};struct TestGame{operator bool(){return true;}TestWorld GetWorld(){return {};}};TestGame GetGame(){return {};}struct ExecutionDouble{void Update(void*,float){}operator bool(){return false;}void Stop(float,bool){}void Receive(ERR_HQTask*,float){}};
|
||||
struct ERR_HQUnit{ExecutionDouble Execution;MineJob* EngineerMine=nullptr;int Id=1;string ForceSourceId="original";SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
|
||||
ERR_HQTask* Task=nullptr;ERR_HQTask* PendingTask=nullptr;ERR_HQReport* Assignment=nullptr;ERR_HQReport* PendingOrder=nullptr;
|
||||
ERR_HQRoute* Downlink=nullptr;ERR_HQRoute* Uplink=nullptr;void* PendingConvoyOrder=nullptr;bool HomeGuard=false,PendingCheckin=false,eligible=true,fighting=false,PlanEligible=true;
|
||||
ERR_HQRoute* Downlink=nullptr;ERR_HQRoute* Uplink=nullptr;void* PendingConvoyOrder=nullptr;int LogisticsReservation=0;bool ReservedForSupport=false,HomeGuard=false,PendingCheckin=false,eligible=true,fighting=false,PlanEligible=true;
|
||||
bool Eligible(ERR_GameMode*){return eligible&&Group;}
|
||||
'''+units+r'''
|
||||
};
|
||||
@@ -79,7 +81,11 @@ struct Memory{ERR_HQBelief* belief=nullptr;ERR_HQBelief* GetById(int id){return
|
||||
struct Watch{bool empty=false;int finished=0;bool ArmedSearchBlocked(string,float){return empty;}bool FinishInvestigation(string,int,int,float){finished++;return true;}};
|
||||
struct Journal{int writes=0;void Write(string,string,int=0){writes++;}};
|
||||
struct ERR_HQHuntWindow{float CompletedUntil=0;float NextHunt=200000;};
|
||||
struct HQ{bool CoordinatedDelivery(ERR_HQUnit*,ERR_HQTask*,float){return false;}struct Operations{void Release(int,int,float){}}m_Operations;ERR_GameMode* m_Mode;Memory m_Memory;Watch m_Watch;Journal m_Journal;std::vector<ERR_HQUnit*>m_Units;ERR_HQHuntWindow hunt,otherHunt;bool watchValid=true;
|
||||
struct ERR_HQSquadJudgment{bool Recovering=false;};
|
||||
struct ERR_HQOperationSlot{ERR_HQTask* Task=nullptr;};
|
||||
struct ERR_HQExecutionRules{static bool SupportFresh(float until,float now){return std::isfinite(until)&&until>now&&until<=now+20000;}};
|
||||
struct Operations{ERR_HQSquadJudgment* squad=nullptr;ERR_HQOperationSlot* slot=nullptr;ERR_HQSquadJudgment* Judgment(int id){assert(id==1);return squad;}ERR_HQOperationSlot* Find(int id){assert(id==1);return slot;}void Release(int,int,float){}};
|
||||
struct HQ{bool m_Coordinated=false;Operations m_Operations;ERR_GameMode* m_Mode;Memory m_Memory;Watch m_Watch;Journal m_Journal;std::vector<ERR_HQUnit*>m_Units;ERR_HQHuntWindow hunt,otherHunt;bool watchValid=true;
|
||||
bool Fighting(ERR_HQUnit* u){return u->fighting;}bool WatchTaskValid(ERR_HQTask*){return watchValid;}ERR_HQHuntWindow* Hunt(int id){return id==2?&otherHunt:&hunt;}
|
||||
'''+methods+'\nvoid Cleanup(float now,int assigned,int globalLimit){'+cleanup+'}\nvoid RemoveMissing(float now){for(int i=int(m_Units.size())-1;i>=0;i--){ERR_HQUnit* unit=m_Units[i];'+missing+'}}'+r'''
|
||||
};
|
||||
@@ -89,6 +95,50 @@ struct Fixture{ERR_GameMode mode;HQ hq;ERR_HQUnit unit;SCR_AIGroup group;AIWaypo
|
||||
void Retained(){assert(unit.Task==&active&&unit.Assignment==&oldReport&&unit.Waypoint==&previous&&previous.deletes==0&&group.removals==0&&!unit.PendingTask&&!unit.PendingOrder&&!unit.Downlink);}
|
||||
};
|
||||
int main(){
|
||||
// Real delivery and actual coordinated identity admission must permit rest for
|
||||
// a known-empty recovering squad, without admitting ordinary reserve/attacks.
|
||||
for(bool withdrawing:{false,true}){
|
||||
Fixture f;ERR_HQSquadJudgment mood;ERR_HQOperationSlot slot;
|
||||
mood.Recovering=true;slot.Task=&f.next;f.hq.m_Operations.squad=&mood;f.hq.m_Operations.slot=&slot;
|
||||
f.hq.m_Coordinated=true;f.hq.m_Watch.empty=true;f.next.OperationId=1;f.next.ExecutionId=12;
|
||||
f.next.OperationRole=withdrawing?ERR_HQOperationRole::WITHDRAW:ERR_HQOperationRole::RESERVE;
|
||||
// WITHDRAW keeps its original exemption without a recovery judgment.
|
||||
if(withdrawing)f.hq.m_Operations.squad=nullptr;
|
||||
f.hq.DeliverOrder(&f.unit,1000);
|
||||
assert(f.unit.Task==&f.next&&f.unit.Assignment==&f.newReport&&f.unit.Waypoint&&f.unit.Waypoint->resource==2);
|
||||
assert(ERR_Actors::spawns==1&&f.group.additions==1&&f.previous.deletes==1&&!f.unit.PendingTask);
|
||||
}
|
||||
for(int invalid=0;invalid<15;invalid++){
|
||||
Fixture f;ERR_HQSquadJudgment mood;ERR_HQOperationSlot slot;ERR_HQTask foreign;
|
||||
mood.Recovering=true;slot.Task=&f.next;f.hq.m_Operations.squad=&mood;f.hq.m_Operations.slot=&slot;
|
||||
f.hq.m_Coordinated=true;f.hq.m_Watch.empty=true;f.next.OperationId=1;f.next.ExecutionId=12;f.next.OperationRole=ERR_HQOperationRole::RESERVE;
|
||||
if(invalid==0)mood.Recovering=false;
|
||||
if(invalid==1)f.hq.m_Operations.squad=nullptr;
|
||||
if(invalid==2)f.next.OperationId=0;
|
||||
if(invalid==3)f.hq.m_Coordinated=false;
|
||||
if(invalid==4)slot.Task=&foreign;
|
||||
if(invalid==5)f.hq.m_Operations.slot=nullptr;
|
||||
if(invalid==6)f.next.ExpiresAt=1000;
|
||||
if(invalid==7)f.unit.eligible=false;
|
||||
if(invalid==8)f.next.BeliefRevision=3;
|
||||
if(invalid==9)f.next.OperationRevision=3;
|
||||
if(invalid==10)f.unit.HomeGuard=true;
|
||||
if(invalid==11)f.unit.ReservedForSupport=true;
|
||||
if(invalid==12)f.unit.LogisticsReservation=1;
|
||||
if(invalid==13)f.hq.m_Memory.belief=nullptr;
|
||||
if(invalid==14)f.belief.Confidence=0;
|
||||
f.hq.DeliverOrder(&f.unit,1000);f.Retained();assert(ERR_Actors::spawns==0);
|
||||
}
|
||||
for(auto role:{ERR_HQOperationRole::SEARCH,ERR_HQOperationRole::OBSERVE,ERR_HQOperationRole::FIX,ERR_HQOperationRole::FLANK,ERR_HQOperationRole::BLOCK}){
|
||||
Fixture f;ERR_HQSquadJudgment mood;ERR_HQOperationSlot slot;mood.Recovering=true;slot.Task=&f.next;
|
||||
f.hq.m_Coordinated=true;f.hq.m_Watch.empty=true;f.hq.m_Operations.squad=&mood;f.hq.m_Operations.slot=&slot;
|
||||
f.next.OperationId=1;f.next.OperationRole=role;f.hq.DeliverOrder(&f.unit,1000);f.Retained();assert(ERR_Actors::spawns==0);
|
||||
}
|
||||
// A tactical reserve still uses the ordinary ammo gate: with ammo present it
|
||||
// is valid even without a recovery judgment; empty it is rejected above.
|
||||
{Fixture f;ERR_HQOperationSlot slot;slot.Task=&f.next;f.hq.m_Coordinated=true;f.hq.m_Operations.slot=&slot;
|
||||
f.next.OperationId=1;f.next.OperationRole=ERR_HQOperationRole::RESERVE;f.hq.DeliverOrder(&f.unit,1000);assert(f.unit.Task==&f.next&&ERR_Actors::spawns==1);}
|
||||
|
||||
// The copied radius is used before installation even if current uncertainty grows.
|
||||
for(float radius:{45.f,60.f,110.f}){Fixture f;f.next.SearchRadiusMetres=radius;f.belief.Radius=500;
|
||||
float deadline=f.next.ExpiresAt;f.hq.DeliverOrder(&f.unit,1000);
|
||||
|
||||
Reference in New Issue
Block a user