Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
106 lines
5.6 KiB
Python
106 lines
5.6 KiB
Python
"""Run actual personal-result title/debrief and refresh methods with UI adapters.
|
|
No native replication, end-screen rendering or multiplayer claim.
|
|
"""
|
|
import re
|
|
from policy_harness import ROOT, body, run
|
|
|
|
SOURCE=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_EscapeGameOverScreenInfo.c'
|
|
methods='\n'.join(body(SOURCE, signature) for signature in (
|
|
'protected ERR_PersonalEscapeResult PersonalResult(',
|
|
'override string GetTitle(',
|
|
'override string GetDebriefing('))
|
|
refresh=body(SOURCE,'protected void ERR_RefreshEscapeContent(')
|
|
|
|
def adapt(text):
|
|
text=re.sub(r' #ifdef WORKBENCH\n.*? #endif\n','',text,flags=re.S)
|
|
text=text.replace('protected ','').replace('override ','')
|
|
text=text.replace('ERR_PersonalEscapeResult.','Result::').replace('ERR_PersonalEscapeResult','Result::Value')
|
|
text=text.replace('array<int> otherPlayerIds','std::vector<int>* otherPlayerIds')
|
|
text=re.sub(r'\bstring\b','std::string',text)
|
|
text=text.replace('GetGame().GetPlayerController()', 'GetGame()->GetPlayerController()')
|
|
text=text.replace('SCR_PlayerController.Cast(', 'Controller::Cast(').replace('SCR_PlayerController controller', 'Controller* controller')
|
|
text=text.replace('TextWidget.Cast(', 'TextWidget::Cast(')
|
|
text=text.replace('TextWidget title', 'TextWidget* title').replace('TextWidget debrief', 'TextWidget* debrief')
|
|
# Enforce manages this local object; the C++ adapter uses scoped ownership.
|
|
text=text.replace('ERR_EscapeGameOverScreenInfo info = new ERR_EscapeGameOverScreenInfo();', 'auto info = std::make_unique<Info>();')
|
|
for variable in ('controller','info','title','debrief','m_wRoot'):
|
|
text=text.replace(variable+'.',variable+'->')
|
|
text=re.sub(r'\bnull\b','nullptr',text)
|
|
return text
|
|
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
namespace Result {enum Value {UNKNOWN,ESCAPED,MISSED};}
|
|
struct Controller {
|
|
int id=7;Result::Value result=Result::UNKNOWN;
|
|
static Controller* Cast(Controller* value){return value;}
|
|
int GetPlayerId(){return id;}Result::Value ERR_EscapeResult(){return result;}
|
|
};
|
|
struct Game {Controller* local=nullptr;Controller* GetPlayerController(){return local;}} game;
|
|
Game* GetGame(){return &game;}
|
|
struct Info {
|
|
'''+adapt(methods)+r'''
|
|
};
|
|
struct TextWidget {std::string value;int writes=0;
|
|
static TextWidget* Cast(TextWidget* widget){return widget;}
|
|
void SetText(std::string text){value=text;writes++;}
|
|
};
|
|
struct Root {TextWidget title,debrief;bool hasTitle=true,hasDebrief=true;
|
|
TextWidget* FindAnyWidget(std::string name){if(name=="title")return hasTitle?&title:nullptr;return hasDebrief?&debrief:nullptr;}
|
|
};
|
|
struct Content {
|
|
bool m_ERREscapeContent=true;Root* m_wRoot=nullptr;
|
|
std::string m_sTileName="title",m_sDebriefingName="debrief";
|
|
'''+adapt(refresh)+r'''
|
|
};
|
|
int main(){
|
|
Controller controller;game.local=&controller;Info info;
|
|
std::vector<int> staleWinners={7};std::vector<int> noWinners;
|
|
const char* titles[]={"Round ended","You escaped Everon","The helicopter escaped"};
|
|
const char* descriptions[]={"Your personal escape result is not available.","You crossed the extraction boundary aboard the helicopter.","You did not escape with the helicopter."};
|
|
for(int state=0;state<3;state++){
|
|
controller.result=static_cast<Result::Value>(state);
|
|
for(auto* winners:{&staleWinners,&noWinners,static_cast<std::vector<int>*>(nullptr)}){
|
|
assert(info.GetTitle(7,winners)==titles[state]);
|
|
assert(info.GetDebriefing(7,winners)==descriptions[state]);
|
|
// A different connection ID cannot use this local controller's result.
|
|
assert(info.GetTitle(8,winners)==titles[0]);
|
|
assert(info.GetDebriefing(8,winners)==descriptions[0]);
|
|
}
|
|
}
|
|
game.local=nullptr;
|
|
assert(info.GetTitle(7,&staleWinners)==titles[0]);
|
|
assert(info.GetDebriefing(7,&staleWinners)==descriptions[0]);
|
|
// The unchanged actual refresh method pulls only the current personal result;
|
|
// late arrival, account reset, and missing local controller refresh both texts.
|
|
game.local=&controller;Root root;Content content;content.m_wRoot=&root;
|
|
for(int state:{0,1,2,0}){
|
|
controller.result=static_cast<Result::Value>(state);content.ERR_RefreshEscapeContent();
|
|
assert(root.title.value==titles[state]&&root.debrief.value==descriptions[state]);
|
|
}
|
|
game.local=nullptr;content.ERR_RefreshEscapeContent();
|
|
assert(root.title.value==titles[0]&&root.debrief.value==descriptions[0]);
|
|
assert(root.title.writes==5&&root.debrief.writes==5);
|
|
content.m_ERREscapeContent=false;content.ERR_RefreshEscapeContent();
|
|
assert(root.title.writes==5&&root.debrief.writes==5);
|
|
content.m_ERREscapeContent=true;content.m_wRoot=nullptr;content.ERR_RefreshEscapeContent();
|
|
assert(root.title.writes==5&&root.debrief.writes==5);
|
|
content.m_wRoot=&root;root.hasTitle=false;content.ERR_RefreshEscapeContent();
|
|
assert(root.title.writes==5&&root.debrief.writes==6);
|
|
root.hasTitle=true;root.hasDebrief=false;content.ERR_RefreshEscapeContent();
|
|
assert(root.title.writes==6&&root.debrief.writes==6);
|
|
}
|
|
''')
|
|
# Keep native subscription/detach wiring intact; actual text refresh is executed above.
|
|
init=body(SOURCE,'override void InitContent(')
|
|
detach=body(SOURCE,'override void HandlerDeattached(')
|
|
assert 'SCR_PlayerController.ERR_OnEscapeResultChanged().Remove(ERR_RefreshEscapeContent);' in init
|
|
assert 'SCR_PlayerController.ERR_OnEscapeResultChanged().Insert(ERR_RefreshEscapeContent);' in init
|
|
assert 'ERR_RefreshEscapeContent();' in init
|
|
assert 'SCR_PlayerController.ERR_OnEscapeResultChanged().Remove(ERR_RefreshEscapeContent);' in detach
|
|
assert 'super.HandlerDeattached(w);' in detach
|
|
print('PASS: actual three-state result wording and UI refresh; stale winner IDs never manufacture escape')
|