"""Actual successor adapter methods; native UI/invoker boundaries are doubled. The invoker models the SDK's one supplied argument and rejects a two-argument subscription. It does not claim rendered medical UI or Enforce execution. """ import os from pathlib import Path from policy_harness import ROOT, body, run source=Path(os.environ.get('ERR_HITZONE_COMPAT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_InventoryHitZoneCompat.c')) signatures=['override void InitializeHitZoneUI(', 'protected void ERR_BindHitZoneCallback(', 'protected void ERR_HitZoneStateChanged(', 'protected void ERR_ClearHitZoneCallbacks(', 'override void HandlerDeattached(', 'void ~SCR_InventoryHitZonePointContainerUI('] def cpp(s): for a,b in [('override ',''),('protected ',''),('super.','Native::'),('ERR_GameMode.Get()','Mode::Get()'),('SCR_CharacterHitZone.Cast(hz)','Character::Cast(hz)'),('BaseInventoryStorageComponent storage','int storage'),('SCR_InventoryMenuUI menuManager','int menuManager'),('IEntity player','Player* player'),('Widget w','int w'),('SCR_HitZone hz','Zone* hz'),('HitZone hz','Zone* hz'),('ScriptInvoker changed','Invoker* changed'),('hz.','hz->'),('changed.','changed->'),('foreach (','for ('),('null','nullptr'),('void ~SCR_InventoryHitZonePointContainerUI()','void Destroy()')]:s=s.replace(a,b) s=s.replace('changed->Remove(UpdateHitZoneState)','changed->Remove(this, 1)').replace('changed->Remove(ERR_HitZoneStateChanged)','changed->Remove(this, 2)') s=s.replace('changed->Insert(ERR_HitZoneStateChanged)','changed->Insert(this, 2, [this](Zone* z){ ERR_HitZoneStateChanged(z); })') return s methods='\n'.join(cpp(body(source,sig)) for sig in signatures) if os.environ.get('ERR_HITZONE_COMPAT_BASELINE')=='1': methods='void Destroy(){}' # Installed native initializer/subscription without the new adapter. run(r''' #include #include #include #include struct Zone; struct Invoker { struct Entry{void* owner;int kind;std::function callback;}; std::vector entries;int errors=0; void Remove(void* o,int k){entries.erase(std::remove_if(entries.begin(),entries.end(),[&](Entry e){return e.owner==o&&e.kind==k;}),entries.end());} void Insert(void* o,int k,std::function cb={}){Remove(o,k);entries.push_back({o,k,cb});} void Invoke(Zone*z){auto snapshot=entries;for(auto&e:snapshot){if(e.kind==1)errors++;else e.callback(z);}} int Count(void*o){return std::count_if(entries.begin(),entries.end(),[&](Entry e){return e.owner==o;});} }; struct Zone{Invoker inv;bool character=true,hasInvoker=true;Invoker*GetOnDamageStateChanged(bool create=true){assert(!create);return hasInvoker?&inv:nullptr;}}; struct Character{static Zone*Cast(Zone*z){return z&&z->character?z:nullptr;}}; templatestruct Array:std::vector{using std::vector::vector;bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}void Insert(T t){this->push_back(t);}void Clear(){this->clear();}}; struct Mode{static inline Mode*current=nullptr;static Mode*Get(){return current;}}; struct Player{Zone*blood;Arrayzones;}; struct Native { Zone*m_pBloodHitZone=nullptr;Arraym_aGroupHitZones; int init=0,detach=0,updates=0;bool lastRefresh=false;Zone*last=nullptr;Player*lastPlayer=nullptr; void UpdateHitZoneState(Zone*z,bool refresh=true){updates++;lastRefresh=refresh;last=z;} void InitializeHitZoneUI(int storage,int menuManager,int hitZoneId,Player*player,bool refresh=true){ assert(storage==11&&menuManager==22&&hitZoneId==3);init++;lastPlayer=player; m_pBloodHitZone=player->blood;m_aGroupHitZones=player->zones; if(m_pBloodHitZone){m_pBloodHitZone->inv.Insert(this,1);UpdateHitZoneState(m_pBloodHitZone,refresh);} for(auto*z:m_aGroupHitZones)if(Character::Cast(z)){z->inv.Insert(this,1);UpdateHitZoneState(z,refresh);} } void HandlerDeattached(int w){assert(w==7);detach++;} }; struct Adapter:Native { Mode*m_ERRHitZoneMode=nullptr;Arraym_ERRHitZoneSources; METHODS }; int main(){ Mode mode,next;Mode::current=&mode;Zone blood,limb,ignored;blood.character=false;ignored.character=false; Player player{&blood,{&blood,&limb,&limb,&ignored,nullptr}}; Adapter a;a.InitializeHitZoneUI(11,22,3,&player,false); assert(a.init==1&&a.lastPlayer==&player&&!a.lastRefresh); // Direct native initialization keeps false. int before=a.updates;blood.inv.Invoke(&blood);limb.inv.Invoke(&limb); assert(blood.inv.errors==0&&limb.inv.errors==0); // Baseline fails here after compiling. assert(a.updates==before+2&&a.lastRefresh&&a.last==&limb); assert(blood.inv.Count(&a)==1&&limb.inv.Count(&a)==1&&ignored.inv.Count(&a)==0); a.InitializeHitZoneUI(11,22,3,&player,true);before=a.updates;blood.inv.Invoke(&blood);assert(a.updates==before+1&&blood.inv.Count(&a)==1); // Each widget owns only its callbacks, even on the shared blood invoker. Adapter other;other.InitializeHitZoneUI(11,22,3,&player,false);a.HandlerDeattached(7);before=other.updates; blood.inv.Invoke(&blood);assert(a.detach==1&&blood.inv.Count(&a)==0&&other.updates==before+1); // Current mode loss/replacement makes retained callbacks inert; cleanup still works. before=other.updates;Mode::current=nullptr;blood.inv.Invoke(&blood);Mode::current=&next;limb.inv.Invoke(&limb);assert(other.updates==before); auto late=limb.inv.entries.back().callback;other.Destroy();assert(blood.inv.Count(&other)==0&&limb.inv.Count(&other)==0); Mode::current=&mode;late(&limb);assert(other.updates==before); // Reinitialize in a non-successor mode: remove old wrappers, preserve native setup. Mode::current=&mode;Adapter reuse;reuse.InitializeHitZoneUI(11,22,3,&player,false); Zone b2,l2;b2.character=false;Player p2{&b2,{&l2}};Mode::current=nullptr; reuse.InitializeHitZoneUI(11,22,3,&p2,false);assert(reuse.init==2&&!reuse.lastRefresh); assert(blood.inv.Count(&reuse)==0&&limb.inv.Count(&reuse)==0); b2.inv.Invoke(&b2);assert(b2.inv.errors==1); // Foreign native behavior untouched. // Empty/invalid group entries are not adopted; repeated cleanup is harmless. Mode::current=&mode;Adapter empty;Player missing{nullptr,{nullptr,&ignored}};empty.InitializeHitZoneUI(11,22,3,&missing,false); empty.Destroy();empty.Destroy();assert(ignored.inv.Count(&empty)==0); } '''.replace('METHODS',methods)) print('PASS: actual one-argument adapter, explicit refresh, native initialization flag, duplicate/blood ownership, rebind/detach/destruction and foreign-mode transparency')