Integrate delivered HQ memory, finite physical convoy and RHS WCS dependencies
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
"""Execute actual own-roster status sampler against explicit native-state stand-ins."""
|
||||
from policy_harness import ROOT,body,run
|
||||
from pathlib import Path
|
||||
import os,re
|
||||
P=Path(os.environ.get('ERR_SAMPLER_SOURCE',ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQStatusSampler.c'))
|
||||
U=ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQPursuit.c'
|
||||
def cpp(s):
|
||||
s=s.replace('DamageManagerComponent.Cast(actor.FindComponent(DamageManagerComponent))',
|
||||
'(actor.hasDamage ? &actor.damage : null)')
|
||||
for old,new in [('protected ',''),('null','nullptr'),('Replication.','Replication::'),
|
||||
('ERR_Actors.','ERR_Actors::'),('ERR_HQPursuitRules.','ERR_HQPursuitRules::'),
|
||||
('SCR_FactionManager.','SCR_FactionManager::'),('ERR_HQRadio.','ERR_HQRadio::'),('Math.','Math::'),
|
||||
('sourceId.IsEmpty()','sourceId.empty()'),('sourceId.Length()','sourceId.size()')]:s=s.replace(old,new)
|
||||
for typ in ('IEntity','AIAgent','SCR_AIGroup','Faction','ERR_HQStatusReport','DamageManagerComponent'):
|
||||
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
|
||||
for var in ('actor','agent','group','report','m_Group','damage'):s=s.replace(var+'.',var+'->')
|
||||
s=s.replace('foreach (AIAgent* agent : m_Agents)','for (AIAgent* agent : m_Agents)')
|
||||
return s
|
||||
methods='\n'.join(cpp(body(P,s)) for s in ('bool Bound()', 'protected bool Eligible(', 'bool Bind(',
|
||||
'bool ObserveDeaths(', 'ERR_HQStatusReport Sample(', 'void Clear('))
|
||||
common='\n'.join(cpp(body(U,s)) for s in ('static bool Finite(', 'static bool PositionValid('))
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
using string=std::string;
|
||||
struct vector{float x=0,y=0,z=0;float operator[](int i)const{return i==0?x:i==1?y:z;}};
|
||||
struct Math{static float AbsFloat(float n){return std::abs(n);}};
|
||||
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
|
||||
template<class T>struct Array:std::vector<T>{
|
||||
int Count(){return this->size();}void Insert(T n){this->push_back(n);}void Clear(){this->clear();}
|
||||
bool Contains(T n){return std::find(this->begin(),this->end(),n)!=this->end();}
|
||||
int Find(T n){auto i=std::find(this->begin(),this->end(),n);return i==this->end()?-1:i-this->begin();}
|
||||
};
|
||||
struct Faction{};struct SCR_AIGroup;
|
||||
struct DamageManagerComponent{bool dead=false;bool IsDestroyed(){return dead;}};
|
||||
struct IEntity{
|
||||
SCR_AIGroup* group=nullptr;Faction* faction=nullptr;bool player=false,unconscious=false,radio=true,hasDamage=true;
|
||||
DamageManagerComponent damage;
|
||||
};
|
||||
struct AIAgent{IEntity* actor=nullptr;IEntity* GetControlledEntity(){return actor;}};
|
||||
struct SCR_AIGroup{
|
||||
Array<AIAgent*>agents;Faction* faction=nullptr;int players=0;bool slave=false;vector position{100,5,300};
|
||||
void GetAgents(Array<AIAgent*>& out){out=agents;}int GetPlayerCount(bool){return players;}
|
||||
bool IsSlave(){return slave;}Faction* GetFaction(){return faction;}vector GetOrigin(){return position;}
|
||||
};
|
||||
struct ERR_Actors{
|
||||
static bool Alive(IEntity* a){return a&&a->hasDamage&&!a->damage.dead;}
|
||||
static bool CanFight(IEntity* a){return Alive(a)&&!a->unconscious;}
|
||||
static bool Player(IEntity* a){return a&&a->player;}
|
||||
static SCR_AIGroup* Group(IEntity* a){return a?a->group:nullptr;}
|
||||
};
|
||||
struct SCR_FactionManager{static Faction* SGetFaction(IEntity* a){return a?a->faction:nullptr;}};
|
||||
struct ERR_HQRadio{static int Capability(IEntity* a){return ERR_Actors::CanFight(a)&&a->radio?1:0;}};
|
||||
struct ERR_HQStatusReport{
|
||||
string SourceId;int MessageId=0;vector Position;int Strength=0,Count=0,Fit=0,ConfirmedLosses=0;
|
||||
float ObservedAt=-1;
|
||||
};
|
||||
struct ERR_HQPursuitRules{
|
||||
'''+common+r'''
|
||||
};
|
||||
struct Sampler{
|
||||
SCR_AIGroup* m_Group=nullptr;Faction* m_Faction=nullptr;string m_SourceId;
|
||||
int m_Strength=0,m_Deaths=0;bool m_Bound=false;float m_LastSampleAt=-1;
|
||||
Array<IEntity*>m_Members;Array<bool>m_Dead,m_Released;Array<AIAgent*>m_Agents;
|
||||
'''+methods+r'''
|
||||
};
|
||||
int main(){
|
||||
Faction enemy,other;SCR_AIGroup group;group.faction=&enemy;
|
||||
IEntity members[4];AIAgent agents[4];
|
||||
for(int i=0;i<4;i++){members[i].group=&group;members[i].faction=&enemy;agents[i].actor=&members[i];group.agents.Insert(&agents[i]);}
|
||||
Sampler sample;assert(!sample.Bound());
|
||||
Replication::server=false;assert(!sample.Bind(&group,"site",4,&enemy));Replication::server=true;
|
||||
assert(!sample.Bind(&group,"site",33,&enemy)&&!sample.Bind(&group,"site",3,&enemy));
|
||||
assert(!sample.Bind(&group,"site",4,&other));
|
||||
group.players=1;assert(!sample.Bind(&group,"site",4,&enemy));group.players=0;
|
||||
members[2].player=true;assert(!sample.Bind(&group,"site",4,&enemy));members[2].player=false;
|
||||
members[2].damage.dead=true;assert(!sample.Bind(&group,"site",4,&enemy));members[2].damage.dead=false;
|
||||
group.agents[3]=&agents[2];assert(!sample.Bind(&group,"site",4,&enemy));group.agents[3]=&agents[3];
|
||||
assert(sample.Bind(&group,"site",4,&enemy)&&sample.Bound());
|
||||
assert(sample.Bind(&group,"site",4,&enemy)&&!sample.Bind(&group,"other-source",4,&enemy));
|
||||
auto first=sample.Sample(1000,1,&members[0]);assert(first&&first->Count==4&&first->Fit==4&&first->Strength==4);
|
||||
assert(first->Position.x==100&&first->SourceId=="site");
|
||||
group.position.x=200;assert(first->Position.x==100); // detached snapshot
|
||||
members[1].unconscious=true;auto injured=sample.Sample(2000,2,&members[0]);
|
||||
assert(injured&&injured->Count==4&&injured->Fit==3&&injured->ConfirmedLosses==0);
|
||||
// A retained explicit destroyed body is evidence even after its agent detaches.
|
||||
members[1].damage.dead=true;members[1].group=nullptr;group.agents.erase(group.agents.begin()+1);
|
||||
assert(sample.ObserveDeaths()&&sample.m_Deaths==1);
|
||||
assert(sample.ObserveDeaths()&&sample.m_Deaths==1); // counted once
|
||||
sample.m_Members[1]=nullptr; // simulate native weak-reference invalidation after corpse cleanup
|
||||
auto losses=sample.Sample(3000,3,&members[0]);assert(losses&&losses->Count==3&&losses->ConfirmedLosses==1);
|
||||
// Deleted unknown actor and missing damage component cannot be manufactured into deaths.
|
||||
sample.m_Members[2]=nullptr;members[3].hasDamage=false;
|
||||
auto absent=sample.Sample(4000,4,&members[0]);
|
||||
assert(absent&&absent->Count==1&&absent->ConfirmedLosses==1);
|
||||
members[3].hasDamage=true;members[3].player=true;assert(sample.ObserveDeaths());
|
||||
members[3].player=false;members[3].damage.dead=true;assert(sample.ObserveDeaths()&&sample.m_Deaths==1);
|
||||
// Never adopt an externally attached replacement as one of the authored original members.
|
||||
IEntity replacement;replacement.group=&group;replacement.faction=&enemy;AIAgent replacementAgent;replacementAgent.actor=&replacement;
|
||||
group.agents.Insert(&replacementAgent);assert(!sample.Sample(5000,5,&replacement));
|
||||
auto original=sample.Sample(5000,5,&members[0]);assert(original&&original->Strength==4&&original->Count==1);
|
||||
members[0].radio=false;assert(!sample.Sample(6000,6,&members[0]));members[0].radio=true;
|
||||
members[0].unconscious=true;assert(!sample.Sample(6000,6,&members[0]));members[0].unconscious=false;
|
||||
group.players=1;assert(!sample.ObserveDeaths()&&!sample.Sample(6000,6,&members[0]));group.players=0;
|
||||
group.faction=&other;assert(!sample.Bind(&group,"site",4,&enemy)&&!sample.Sample(6000,6,&members[0]));group.faction=&enemy;
|
||||
group.slave=true;assert(!sample.ObserveDeaths());group.slave=false;
|
||||
Replication::server=false;assert(!sample.ObserveDeaths()&&!sample.Sample(6000,6,&members[0]));Replication::server=true;
|
||||
assert(!sample.Sample(4999,6,&members[0])&&!sample.Sample(6000,0,&members[0]));
|
||||
assert(!sample.Sample(std::numeric_limits<float>::quiet_NaN(),6,&members[0]));
|
||||
// No sender survives: local evidence may update, but a radio report cannot be fabricated.
|
||||
members[0].damage.dead=true;assert(sample.ObserveDeaths()&&sample.m_Deaths==2);
|
||||
assert(!sample.Sample(7000,7,&members[0])&&!sample.Sample(7000,7,nullptr));
|
||||
sample.Clear();assert(!sample.Bound()&&sample.m_Members.Count()==0&&sample.m_Deaths==0);
|
||||
}
|
||||
''')
|
||||
source=P.read_text()
|
||||
for forbidden in ('ERR_HQWatch ', 'ERR_HQMemory', 'GetPlayers(', 'GetWorld(', 'GetControlledEntity() == target',
|
||||
'Spawn(', 'CallLater(', 'Ground('):assert forbidden not in source,forbidden
|
||||
assert 'new ' not in body(P,'bool ObserveDeaths(')
|
||||
assert 'GetAgents(' not in body(P,'bool ObserveDeaths(')
|
||||
assert all(len(line)<120 for line in source.splitlines())
|
||||
print('PASS: own initial roster, explicit retained corpse evidence, unconscious/missing/ref transfer guards, fixed strength, no replacement adoption, detached samples and surviving radio sender requirement')
|
||||
Reference in New Issue
Block a user