Stop successor services on native game-over and preserve escape results
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""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/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')
|
||||
Reference in New Issue
Block a user