"""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(', 'string InitialSourceId(', 'int InitialStrength(', 'bool OwnsRoster(', 'IEntity InitialMember(', 'bool CanAttribute(', '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 #include #include #include #include #include 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;}}; templatestruct Array:std::vector{ 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{ Arrayagents;Faction* faction=nullptr;int players=0;bool slave=false;vector position{100,5,300}; void GetAgents(Array& 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;Array DeathSlots; }; 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; Arraym_Members;Arraym_Dead,m_Released;Arraym_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)); assert(sample.InitialSourceId()=="site"&&sample.InitialStrength()==4&&sample.OwnsRoster(&group)); assert(sample.InitialMember(1)==&members[1]&&!sample.InitialMember(-1)&&!sample.InitialMember(4)); std::reverse(group.agents.begin(),group.agents.end()); assert(sample.InitialMember(1)==&members[1]&&sample.CanAttribute(1,&members[1])); assert(!sample.CanAttribute(1,&members[2]));std::reverse(group.agents.begin(),group.agents.end()); 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 assert(sample.CanAttribute(1,&members[1])); sample.m_Group=nullptr; // native all-dead group handle invalidates while the retained corpse still exists assert(sample.CanAttribute(1,&members[1])); // candidate-held sampler preserves original slot without live group sample.m_Group=&group; 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); assert(losses->DeathSlots.Count()==1&&losses->DeathSlots[0]==1); assert(first->DeathSlots.Count()==0); // earlier report array is detached // 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()); assert(!sample.CanAttribute(3,&members[3])); members[3].player=false;members[3].damage.dead=true;assert(sample.ObserveDeaths()&&sample.m_Deaths==1); assert(!sample.CanAttribute(3,&members[3])); // released slot cannot be rediscovered as our casualty // 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::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)); // Transfer and death between slow ticks: a retained foreign group proves the // old initial slot was released, even without an earlier living observation. SCR_AIGroup source,destination;source.faction=&enemy;destination.faction=&enemy; IEntity moved,stayed,recipient;AIAgent movedAgent,stayedAgent,recipientAgent; moved.faction=&enemy;moved.group=&source;movedAgent.actor=&moved; stayed.faction=&enemy;stayed.group=&source;stayedAgent.actor=&stayed; recipient.faction=&enemy;recipient.group=&destination;recipientAgent.actor=&recipient; source.agents.Insert(&stayedAgent);source.agents.Insert(&movedAgent);destination.agents.Insert(&recipientAgent); // Independent source instances exercise body and status gates without one // gate's release masking a bug in the other gate. Sampler priorStatus,priorBody,later; assert(priorStatus.Bind(&source,"before-transfer",2,&enemy)); assert(priorBody.Bind(&source,"before-transfer",2,&enemy)); moved.group=&destination;source.agents.pop_back();destination.agents.Insert(&movedAgent); assert(later.Bind(&destination,"after-transfer",2,&enemy));moved.damage.dead=true; assert(!priorBody.CanAttribute(1,&moved)&&priorBody.m_Released[1]); assert(priorStatus.ObserveDeaths()&&priorStatus.m_Deaths==0&&priorStatus.m_Released[1]); assert(later.ObserveDeaths()&&later.m_Deaths==1); auto oldFormation=priorStatus.Sample(8000,8,&stayed); auto newFormation=later.Sample(8000,9,&recipient); assert(oldFormation&&oldFormation->Count==1&&oldFormation->ConfirmedLosses==0&&oldFormation->DeathSlots.Count()==0); assert(newFormation&&newFormation->Count==1&&newFormation->ConfirmedLosses==1&&newFormation->DeathSlots[0]==1); // Returning to the original group cannot reactivate a released slot. moved.group=&source;assert(!priorBody.CanAttribute(1,&moved)); assert(priorStatus.ObserveDeaths()&&priorStatus.m_Deaths==0); // Normal native corpse detachment still permits its destination attribution. moved.group=nullptr;assert(later.CanAttribute(1,&moved)); assert(!priorBody.CanAttribute(1,&moved)&&priorStatus.ObserveDeaths()&&priorStatus.m_Deaths==0); 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')