"""Actual scoped binding and original turret identity predicate, with native doubles.""" import os,re from pathlib import Path from policy_harness import ROOT,run helper=Path(os.environ.get('ERR_GUNNER_SOURCE', str(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyGunnerSkill.c'))).read_text() equipment=Path(os.environ.get('ERR_EQUIPMENT_SOURCE', str(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_EnemyEquipment.c'))).read_text() def convert(s): s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('override ','') s=s.replace('ERR_HQ.Cast(mode.Command())','mode->Command()').replace('TurretCompartmentSlot.Cast(slot)','dynamic_cast(slot)') for cls in ('ERR_GameMode','SCR_AIGroupUtilityComponent','SCR_AIGroup','IEntity','ERR_HQ','ERR_ConvoyTask','ERR_ConvoyVehicle','ERR_ForceRecord','BaseCompartmentSlot'): s=re.sub(r'\b'+cls+r' ([a-zA-Z_]\w*)',cls+r'* \1',s) for obj in ('mode','group','actor','utility','hq','task','unit','record','slot'): s=s.replace(obj+'.',obj+'->') for field in ('Receipt','Entity'): s=s.replace(field+'.',field+'->') s=s.replace('Forces().','Forces()->') for cls in ('ERR_EnemyEquipmentRules','ERR_EnemyGunnerSkill','ERR_ConvoyRules','ERR_Actors','Replication','SCR_Global','ERR_GameMode','EAISkill','ERR_ForceKind','ECompartmentType','CompartmentAccessComponent'): s=s.replace(cls+'.',cls+'::') s=s.replace('super.','NativeCombat::').replace('null','nullptr').replace('foreach (','for (') s=re.sub(r'\b(bool|int|EAISkill|[A-Za-z_]\w*\*) ([A-Za-z_]\w*);',r'\1 \2 {};',s) return s rules=equipment[equipment.index('class ERR_EnemyEquipmentRules'):equipment.index('class ERR_EnemyEquipmentCallback')] rules=convert(rules.replace('class ERR_EnemyEquipmentRules','struct ERR_EnemyEquipmentRules').replace('modded class SCR_AICombatComponent','struct SCR_AICombatComponent : NativeCombat')).replace('\n}\n','\n};\n') # Declare helper before rules, then define its real method bodies after rules. helper=helper[helper.index(' static bool CapturedGroup'):helper.rfind('}')] helper=convert(helper).replace('static bool CapturedGroup','bool ERR_EnemyGunnerSkill::CapturedGroup').replace('static bool OriginalGunner','bool ERR_EnemyGunnerSkill::OriginalGunner').replace('static EAISkill Profile','EAISkill ERR_EnemyGunnerSkill::Profile') run(r''' #include #include enum class EAISkill{ROOKIE=20,REGULAR=50,EXPERT=80,CYLON=100}; enum class ERR_ForceKind{CONVOY,GUARD}; enum class ECompartmentType{TURRET,CARGO}; templatestruct Array:std::vector{using std::vector::vector;using std::vector::operator=;int Count(){return this->size();}}; namespace Replication{bool server=true;bool IsServer(){return server;}} namespace SCR_Global{bool edit=false;bool IsEditMode(){return edit;}} struct SCR_AIGroupUtilityComponent{bool military=true;bool IsMilitary(){return military;}}; struct SCR_AIGroup{SCR_AIGroupUtilityComponent utility;int players=0,faction=1;auto GetGroupUtilityComponent(){return &utility;}int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}}; struct IEntity{virtual ~IEntity(){}bool alive=true,player=false;SCR_AIGroup* group=nullptr;IEntity* vehicle=nullptr;}; using Vehicle=IEntity; struct BaseCompartmentSlot{virtual ~BaseCompartmentSlot(){}IEntity* occupant=nullptr;IEntity* vehicle=nullptr;ECompartmentType type=ECompartmentType::TURRET;auto GetType(){return type;}auto GetOccupant(){return occupant;}auto GetVehicle(){return vehicle;}}; struct TurretCompartmentSlot:BaseCompartmentSlot{}; struct ERR_ForceRecord{int Id=8;bool Physical=true,Eliminated=false;ERR_ForceKind Kind=ERR_ForceKind::CONVOY;}; struct Ledger{ERR_ForceRecord* record=nullptr;auto FindGroup(SCR_AIGroup*){return record;}}; struct ERR_ConvoyReceipt{bool Consumed=true;Array Records={8};}; struct ERR_ConvoyVehicle{SCR_AIGroup* Group=nullptr;Vehicle* Entity=nullptr;bool Registered=true,Released=false;int Index=0;Array Members;Array Seats;}; struct ERR_ConvoyTask{bool Ready=true,Stopped=false;ERR_ConvoyReceipt* Receipt=nullptr;Array Vehicles;}; struct ERR_HQ{ERR_ConvoyTask* task=nullptr;auto ConvoyTask(){return task;}}; struct ERR_GameMode{static inline ERR_GameMode* current=nullptr;bool active=true;ERR_HQ* hq=nullptr;Ledger* ledger=nullptr;static auto Get(){return current;}bool Active(){return active;}int EnemyFaction(){return 1;}auto Command(){return hq;}auto Forces(){return ledger;}}; namespace ERR_Actors{bool CanFight(IEntity* a){return a->alive;}bool Alive(IEntity* a){return a->alive;}bool Player(IEntity* a){return a->player;}auto Group(IEntity* a){return a->group;}} namespace ERR_ConvoyRules{bool capture=false;bool Captured(Vehicle*,SCR_AIGroup* group){return capture||group->players>0;}bool PartOf(IEntity* a,IEntity* b){return a==b;}} namespace CompartmentAccessComponent{auto GetVehicleIn(IEntity* actor){return actor->vehicle;}} struct ERR_EnemyGunnerSkill{static bool CapturedGroup(ERR_GameMode*,SCR_AIGroup*);static bool OriginalGunner(ERR_GameMode*,SCR_AIGroup*,IEntity*);static EAISkill Profile(EAISkill,bool);}; struct NativeCombat{IEntity* owner=nullptr;EAISkill skill=EAISkill::REGULAR;int writes=0;auto GetOwner(){return owner;}auto GetAISkill(){return skill;}virtual void SetAISkill(EAISkill value){writes++;skill=value;}virtual void ResetAISkill(){skill=EAISkill::ROOKIE;}}; ''' + rules + helper + r''' int main(){ ERR_GameMode mode;ERR_GameMode::current=&mode;SCR_AIGroup group;IEntity actor,vehicle,other;actor.group=&group;actor.vehicle=&vehicle; TurretCompartmentSlot slot;slot.occupant=&actor;slot.vehicle=&vehicle;BaseCompartmentSlot falseTurret;falseTurret.occupant=&actor;falseTurret.vehicle=&vehicle; ERR_ConvoyVehicle unit;unit.Group=&group;unit.Entity=&vehicle;unit.Members={&actor};unit.Seats={&slot}; ERR_ConvoyReceipt receipt;ERR_ConvoyTask task;task.Receipt=&receipt;task.Vehicles={&unit};ERR_HQ hq;hq.task=&task;mode.hq=&hq; ERR_ForceRecord record;Ledger ledger;ledger.record=&record;mode.ledger=&ledger; assert(ERR_EnemyGunnerSkill::OriginalGunner(&mode,&group,&actor)); // Every native/canonical ownership fact independently matters. for(int i=0;i<25;i++){ auto savedActor=actor;auto savedVehicle=vehicle;auto savedGroup=group;auto savedUnit=unit;auto savedTask=task;auto savedReceipt=receipt;auto savedRecord=record;auto savedSlot=slot; if(i==0)Replication::server=false; if(i==1)SCR_Global::edit=true; if(i==2)mode.active=false; if(i==3)actor.player=true; if(i==4)actor.alive=false; if(i==5)actor.group=nullptr; if(i==6)group.players=1; if(i==7)group.faction=2; if(i==8)group.utility.military=false; if(i==9)task.Stopped=true; if(i==10)task.Ready=false; if(i==11)receipt.Consumed=false; if(i==12)unit.Registered=false; if(i==13)unit.Released=true; if(i==14)vehicle.alive=false; if(i==15)record.Physical=false; if(i==16)record.Eliminated=true; if(i==17)record.Kind=ERR_ForceKind::GUARD; if(i==18)record.Id=9; if(i==19)unit.Members[0]=&other; if(i==20)slot.occupant=&other; if(i==21)unit.Seats[0]=&falseTurret; if(i==22)slot.type=ECompartmentType::CARGO; if(i==23)slot.vehicle=&other; if(i==24)actor.vehicle=&other; assert(!ERR_EnemyGunnerSkill::OriginalGunner(&mode,&group,&actor)); actor=savedActor;vehicle=savedVehicle;group=savedGroup;unit=savedUnit;task=savedTask;receipt=savedReceipt;record=savedRecord;slot=savedSlot; Replication::server=true;SCR_Global::edit=false;mode.active=true; } ERR_ConvoyRules::capture=true;assert(!ERR_EnemyGunnerSkill::OriginalGunner(&mode,&group,&actor));ERR_ConvoyRules::capture=false; // Current turret specialization must not overwrite the underlying requested skill. SCR_AICombatComponent combat;combat.owner=&actor;combat.ERR_BindEnemySkill(&mode,&group);assert(combat.skill==EAISkill::CYLON); combat.ResetAISkill();assert(combat.skill==EAISkill::CYLON); combat.SetAISkill(static_cast(90));assert(combat.skill==EAISkill::CYLON); actor.vehicle=nullptr;slot.occupant=nullptr;combat.ERR_RefreshEnemySkill();assert(static_cast(combat.skill)==90); actor.vehicle=&vehicle;slot.occupant=&actor;combat.ERR_RefreshEnemySkill();assert(combat.skill==EAISkill::CYLON); combat.SetAISkill(EAISkill::ROOKIE);slot.occupant=nullptr;combat.ERR_RefreshEnemySkill();assert(combat.skill==EAISkill::EXPERT); slot.occupant=&actor;combat.ERR_RefreshEnemySkill();assert(combat.skill==EAISkill::CYLON); // Captured group refresh cannot mutate any soldier. ERR_ConvoyRules::capture=true;int writes=combat.writes;combat.ERR_RefreshEnemySkill();assert(combat.writes==writes);ERR_ConvoyRules::capture=false; combat.ERR_ReleaseEnemySkill(&mode);assert(combat.skill==EAISkill::REGULAR); // A legitimately high underlying setting stays high after leaving a gunner seat. combat.SetAISkill(EAISkill::CYLON);combat.ERR_BindEnemySkill(&mode,&group);slot.occupant=nullptr;combat.ERR_RefreshEnemySkill();assert(combat.skill==EAISkill::CYLON); combat.ERR_ReleaseEnemySkill(&mode);assert(combat.skill==EAISkill::CYLON); // Ordinary original infantry retain the existing floor without convoy identity. hq.task=nullptr;combat.SetAISkill(EAISkill::REGULAR);combat.ERR_BindEnemySkill(&mode,&group);assert(combat.skill==EAISkill::EXPERT); combat.SetAISkill(static_cast(90));assert(static_cast(combat.skill)==90); combat.ERR_ReleaseEnemySkill(&mode);assert(combat.skill==EAISkill::REGULAR); } ''') print('PASS: actual original turret identity, native/canonical guards, scoped >=80/>=100 profile, reset, dismount/reboard, requested-skill preservation and restoration')