"""Actual objective overlay lifecycle with explicit native widget/map doubles. No claim of native rendering, clipping, layout, remote or platform validation. """ import os,re from pathlib import Path from policy_harness import ROOT,body,run root=Path(os.environ.get('ERR_OBJECTIVE_MAP_ROOT',ROOT)) p=root/'addons/core/Scripts/Game/ERR/00_Core/ERR_ObjectiveMapPresentation.c' def cpp(s): s=s.replace('protected ','').replace('null','nullptr') s=s.replace('SCR_MapEntity.','SCR_MapEntity::').replace('ERR_ObjectiveMapElement.Cast','ERR_ObjectiveMapElement::Cast').replace('FrameSlot.','FrameSlot::') for t in ['SCR_MapEntity','MapConfiguration','Widget']: s=re.sub(r'\b'+t+r' (\w+)',t+r'* \1',s) for n in ['mapEntity','canvas','container','mapRoot','authored','icons','m_Root','m_Element']: s=re.sub(r'\b'+n+r'\.',n+'->',s) for n in ['GetGame','GetWorkspace','GetOnMapOpenComplete','GetOnMapClose','GetOnMapPan','GetOnMapZoom']: s=s.replace(n+'().',n+'()->') s=s.replace('FindHandler(ERR_ObjectiveMapElement)','FindHandler()') for n in ['Opened','Closed','Panned','Zoomed']: s=s.replace('Insert('+n+')','Insert(this)').replace('Remove('+n+')','Remove(this)') return s methods='\n'.join(cpp(body(p,s)) for s in ['void SetData(', 'void Start(', 'protected void Opened(', 'protected void Panned(', 'protected void Zoomed(', 'protected void Closed(', 'protected void ClearWidget(', 'static Widget Container(', 'protected bool OwnsSurface(', 'void Refresh(', 'void Stop(']) run(r''' #include #include #include #include using string=std::string; struct vector{float v[3]={0,0,0};float&operator[](int i){return v[i];}}; struct MapConfiguration{}; struct ERR_ObjectiveMapElement{vector pos;string text,icon;int sets=0;static ERR_ObjectiveMapElement*Cast(ERR_ObjectiveMapElement*p){return p;}void SetObjective(vector p,string t,string i){pos=p;text=t;icon=i;sets++;}}; struct Widget{bool visible=false,removed=false,handler=true;int removals=0;float x=0,y=0,sx=0,sy=0;bool isCanvas=false;Widget*parent=nullptr,*authored=nullptr,*icons=nullptr;std::vectorchildren;Widget*GetParent(){return parent;}Widget*FindAnyWidget(string n){return n=="ERR_ObjectiveOverlay"?authored:icons;}void GetScreenPos(float&a,float&b){a=sx;b=sy;}void ClearChildren(){for(auto*w:children)w->RemoveFromHierarchy();children.clear();}void GetScreenSize(float&w,float&h){w=4000;h=4000;}ERR_ObjectiveMapElement element;ERR_ObjectiveMapElement*FindHandler(){return handler?&element:nullptr;}void RemoveFromHierarchy(){removed=true;removals++;}void SetVisible(bool v){visible=v;}}; struct Event{std::setsubscribers;int inserts=0;void Insert(void*p){subscribers.insert(p);inserts++;}void Remove(void*p){subscribers.erase(p);}}; struct SCR_MapEntity{ static inline SCR_MapEntity*current=nullptr;static inline Event opened,closed,panned,zoomed; bool open=true;MapConfiguration config;Widget canvas,mapRoot,icons,frame;Widget*activeCanvas=&canvas; SCR_MapEntity(){canvas.isCanvas=true;mapRoot.icons=&icons;icons.parent=&frame;}Widget*GetMapMenuRoot(){return &mapRoot;} vector minimum{{0,0,0}},maximum{{1000,0,1000}};int transforms=0;float zoom=2,pan=0; static SCR_MapEntity*GetMapInstance(){return current;}static Event*GetOnMapOpenComplete(){return &opened;}static Event*GetOnMapClose(){return &closed;}static Event*GetOnMapPan(){return &panned;}static Event*GetOnMapZoom(){return &zoomed;} bool IsOpen(){return open;}MapConfiguration*GetMapConfig(){return &config;}Widget*GetMapWidget(){return activeCanvas;} void GetMapVisibleFrame(vector&a,vector&b){a=minimum;b=maximum;} void WorldToScreen(float x,float y,int&sx,int&sy,bool){transforms++;sx=x*zoom+pan;sy=y*zoom+pan;} }; struct Workspace{bool fail=false,missingHandler=false;int creates=0;std::vectorwidgets;Widget*CreateWidgets(const char*,Widget*parent){creates++;if(fail||parent->isCanvas)return nullptr;auto*w=new Widget();w->parent=parent;parent->children.push_back(w);w->handler=!missingHandler;widgets.push_back(w);return w;}float DPIUnscale(float x){return x/2;}~Workspace(){for(auto*w:widgets)delete w;}}; struct Game{bool controller=true,workspaceReady=true;Workspace workspace;bool GetPlayerController(){return controller;}Workspace*GetWorkspace(){return workspaceReady?&workspace:nullptr;}}game;Game*GetGame(){return &game;} namespace FrameSlot{void SetPos(Widget*w,float x,float y){w->x=x;w->y=y;}} struct Presentation{ static constexpr auto LAYOUT="authored-layout";bool m_Started=false;vector m_Position;string m_Text,m_Icon;SCR_MapEntity*m_Map=nullptr;MapConfiguration*m_Config=nullptr;Widget*m_Canvas=nullptr,*m_Container=nullptr,*m_Root=nullptr;ERR_ObjectiveMapElement*m_Element=nullptr; '''+methods+r''' }; int main(){ SCR_MapEntity map;SCR_MapEntity::current=↦map.canvas.sx=120;map.canvas.sy=60;map.frame.sx=20;map.frame.sy=10; Presentation a,b;a.SetData(vector{{250,0,750}},"Unknown location (500 m area)","mark-question"); game.controller=false;a.Start();assert(!a.m_Root&&SCR_MapEntity::opened.subscribers.count(&a)); game.controller=true;a.Refresh();Widget*first=a.m_Root;assert(first&&first->visible&&first->x==300&&first->y==775&&first->parent==&map.frame); assert(first->element.pos[0]==250&&first->element.text=="Unknown location (500 m area)"); int created=game.workspace.creates;a.Start();a.Opened(&map.config);assert(game.workspace.creates==created&&SCR_MapEntity::opened.inserts==1); // Native user-annotation privacy has no state/input in this objective registry. // Ordinary annotations remain outside these methods and are not mutated. bool userAnnotationBlocked=true; a.SetData(vector{{612,0,734}},"Radio station","radio");assert(a.m_Root==first&&first->element.icon=="radio"&&userAnnotationBlocked); map.zoom=4;map.pan=10;a.Zoomed(4);assert(first->x==1279&&first->y==1498); map.pan=-3000;a.Panned(0,0,false);assert(!first->visible); map.pan=10;a.Panned(0,0,false);assert(first->visible); MapConfiguration unrelated;a.Closed(&unrelated);assert(a.m_Root==first); a.Closed(&map.config);assert(first->removed&&!a.m_Root&&SCR_MapEntity::opened.subscribers.count(&a)); a.Opened(&map.config);assert(a.m_Root&&a.m_Root!=first&&a.m_Root->element.text=="Radio station"); // Same native config but another canvas; only our old widget is removed. Widget otherCanvas;otherCanvas.isCanvas=true;Widget*second=a.m_Root;map.activeCanvas=&otherCanvas;a.Refresh();assert(second->removed&&a.m_Canvas==&otherCanvas&&!otherCanvas.removed); // Native task refresh deletes only its own icon children, not our sibling. Widget nativeTask;map.icons.children.push_back(&nativeTask);map.icons.ClearChildren();assert(nativeTask.removed&&!a.m_Root->removed&&a.m_Root->parent==&map.frame); // Reusing canvas/config but changing the supported parent must rebuild only ours. Widget replacementFrame;Widget*oldParentWidget=a.m_Root;map.icons.parent=&replacementFrame;a.Refresh();assert(oldParentWidget->removed&&a.m_Container==&replacementFrame&&a.m_Root->parent==&replacementFrame); // Dedicated authored shared-map frame has priority over native task layout. Widget sharedFrame;sharedFrame.sx=70;sharedFrame.sy=90;map.mapRoot.authored=&sharedFrame;oldParentWidget=a.m_Root;a.Refresh();assert(oldParentWidget->removed&&a.m_Container==&sharedFrame&&a.m_Root->parent==&sharedFrame); // Parent absence or a malformed canvas parent cannot reach CreateWidgets. map.mapRoot.authored=nullptr;map.icons.parent=map.activeCanvas;int beforeInvalid=game.workspace.creates;a.Refresh();assert(!a.m_Root&&game.workspace.creates==beforeInvalid); map.mapRoot.icons=nullptr;a.Refresh();assert(!a.m_Root&&game.workspace.creates==beforeInvalid); map.mapRoot.authored=&sharedFrame;a.Refresh();assert(a.m_Root&&a.m_Container==&sharedFrame); b.Start();Widget*bRoot=b.m_Root;assert(bRoot&&bRoot!=a.m_Root);a.Stop();assert(!a.m_Root&&!bRoot->removed&&!SCR_MapEntity::opened.subscribers.count(&a)&&SCR_MapEntity::opened.subscribers.count(&b)); a.Opened(&map.config);assert(!a.m_Root);a.Stop();assert(!bRoot->removed); // Missing creation/configuration retries without registering duplicate listeners. b.Closed(&map.config);game.workspace.fail=true;b.Refresh();assert(!b.m_Root);game.workspace.fail=false;game.workspace.missingHandler=true;b.Refresh();assert(!b.m_Root);game.workspace.missingHandler=false;b.Refresh();assert(b.m_Root); map.open=false;b.Refresh();assert(!b.m_Root);map.open=true;b.Opened(&map.config);assert(b.m_Root); SCR_MapEntity replacement;SCR_MapEntity::current=&replacement;Widget*old=b.m_Root;b.Refresh();assert(old->removed&&b.m_Map==&replacement); b.Stop();assert(SCR_MapEntity::opened.subscribers.empty()&&SCR_MapEntity::closed.subscribers.empty()&&SCR_MapEntity::panned.subscribers.empty()&&SCR_MapEntity::zoomed.subscribers.empty()); } ''') # Production callers must never insert authored objective data into the annotation lists. discovery=(root/'addons/core/Scripts/Game/ERR/00_Core/ERR_DiscoveryMap.c').read_text() mine=(root/'addons/core/Scripts/Game/ERR/20_Intel/ERR_MineIntel.c').read_text() assert 'manager.ERR_InsertDiscoveryMarker(m_ERRMarker);' in discovery assert 'manager.ERR_InsertDiscoveryMarker(marker);' in mine assert 'manager.InsertStaticMarker(m_ERRMarker' not in discovery assert 'manager.InsertStaticMarker(marker' not in mine assert 'override void SetBlocked' not in discovery shared=root/'addons/core/UI/layouts/ERRMap/SharedMapRenderTarget.layout' assert 'Name "ERR_ObjectiveOverlay"' in body(shared,'RTTextureWidgetClass') assert 'Name "ERR_ObjectiveOverlay"' not in body(shared,'MapWidgetClass') print('PASS: actual objective overlay lifecycle preserves separate ownership, late readiness/reopen, data refresh, pan/zoom/culling, supported sibling/shared-frame parents, coordinate offsets, native task-clear isolation and terminal cleanup; native rendering remains unverified')