62 lines
6.1 KiB
Python
62 lines
6.1 KiB
Python
"""Actual result callbacks/UI lifecycle with engine boundaries doubled, not native replication."""
|
|
import os,re,sys
|
|
from pathlib import Path
|
|
REPO=Path(os.environ.get('ERR_REPO','/home/jakub/devel/escapistsrereintroduced'))
|
|
sys.path.insert(0,str(REPO/'tests'))
|
|
from policy_harness import body,run
|
|
SOURCE=Path(os.environ.get('ERR_RESULT_SOURCE',REPO/'addons/core/Scripts/Game/ERR/00_Core/ERR_EscapeGameOverScreenInfo.c'))
|
|
MODE=Path(os.environ.get('ERR_MODE_SOURCE',REPO/'addons/core/Scripts/Game/ERR/00_Core/ERR_GameMode.c'))
|
|
def adapt(s):
|
|
s=s.replace('protected ','').replace('override ','').replace('null','nullptr')
|
|
s=s.replace('ERR_GameMode.Get()','Mode::Get()').replace('ERR_GameMode mode','Mode* mode')
|
|
s=s.replace('ERR_FailureGameOverScreenInfo.Cast(', 'FailureInfo::Cast(').replace('ERR_EscapeGameOverScreenInfo.Cast(', 'EscapeInfo::Cast(')
|
|
s=s.replace('ERR_FailureGameOverScreenInfo info = new ERR_FailureGameOverScreenInfo();','FailureInfo info;')
|
|
s=s.replace('TextWidget.Cast(', 'TextWidget::Cast(').replace('TextWidget debrief','TextWidget* debrief').replace('TextWidget text','TextWidget* text')
|
|
s=s.replace('SCR_PlayerController.ERR_OnEscapeResultChanged()','escapeEvent')
|
|
s=s.replace('SCR_GameOverScreenUIContentData endScreenUIContent','Data* endScreenUIContent').replace('Widget w','int w')
|
|
s=s.replace('array<int> otherPlayerIds','void* otherPlayerIds').replace('string ','std::string ')
|
|
s=s.replace('super.InitContent(endScreenUIContent);','nativeInit++;').replace('super.HandlerDeattached(w);','nativeDetach++;')
|
|
for name in ('m_ERRFailureMode','m_ERRStatisticsMode','m_wRoot','debrief','text','mode','endScreenUIContent'):s=s.replace(name+'.',name+'->')
|
|
s=s.replace('OnEndReasonChanged().','OnEndReasonChanged()->').replace('EndReason().IsEmpty()','EndReason().empty()')
|
|
s=s.replace('Insert(ERR_RefreshFailureContent)','Insert(1)').replace('Remove(ERR_RefreshFailureContent)','Remove(1)')
|
|
s=s.replace('Insert(ERR_RefreshEscapeContent)','Insert(2)').replace('Remove(ERR_RefreshEscapeContent)','Remove(2)')
|
|
s=s.replace('ERR_OnStatisticsChanged().','ERR_OnStatisticsChanged()->').replace('summary.IsEmpty()','summary.empty()')
|
|
s=s.replace('Insert(ERR_RefreshStatistics)','Insert(3)').replace('Remove(ERR_RefreshStatistics)','Remove(3)')
|
|
return s
|
|
failure=body(SOURCE,'class ERR_FailureGameOverScreenInfo')
|
|
failure=failure[failure.index(' override string'):failure.rindex('}')]
|
|
callbacks='\n'.join(body(SOURCE,x) for x in ('override void InitContent(','protected void ERR_RefreshFailureContent(','protected void ERR_RefreshStatistics(','override void HandlerDeattached('))
|
|
notify=body(MODE,'protected void ERR_OnEndReasonReplicated(')
|
|
assert '[RplProp(onRplName: "ERR_OnEndReasonReplicated")]' in MODE.read_text()
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <functional>
|
|
struct Event{int count=0;std::function<void()> handler;void Insert(int){count=1;}void Remove(int){count=0;}void Invoke(){if(count&&handler)handler();}};
|
|
struct Mode{static inline Mode* current=nullptr;Event m_OnEndReasonChanged,statsChanged;std::string reason,summary;Event* ERR_OnStatisticsChanged(){return &statsChanged;}std::string ERR_RoundSummary(){return summary;}static Mode* Get(){return current;}std::string EndReason(){return reason;}Event* OnEndReasonChanged(){return &m_OnEndReasonChanged;}
|
|
NOTIFY
|
|
};
|
|
struct FailureInfo{static bool Cast(int kind){return kind==1;}
|
|
FAILURE
|
|
};
|
|
struct EscapeInfo{static EscapeInfo* Cast(int kind){static EscapeInfo value;return kind==2?&value:nullptr;}};
|
|
struct TextWidget{std::string text;int writes=0;static TextWidget* Cast(TextWidget* w){return w;}void SetText(std::string value){text=value;writes++;}};
|
|
struct Root{TextWidget debrief,statistics;bool exists=true;TextWidget* FindAnyWidget(std::string name){return exists?(name=="RoundStatistics"?&statistics:&debrief):nullptr;}};
|
|
struct Data{int m_GameOverInfo;};
|
|
struct Content{Event escapeEvent;bool m_ERREscapeContent=false;Mode* m_ERRFailureMode=nullptr;Mode* m_ERRStatisticsMode=nullptr;Root* m_wRoot=nullptr;std::string m_sDebriefingName="debrief";int nativeInit=0,nativeDetach=0,escapeRefresh=0;void ERR_RefreshEscapeContent(){escapeRefresh++;}
|
|
CALLBACKS
|
|
};
|
|
int main(){Mode mode;Mode::current=&mode;Root root;Content content;content.m_wRoot=&root;mode.m_OnEndReasonChanged.handler=[&](){content.ERR_RefreshFailureContent();};Data failure{1},escape{2},other{3};mode.statsChanged.handler=[&](){content.ERR_RefreshStatistics();};
|
|
content.InitContent(&failure);assert(mode.m_OnEndReasonChanged.count==1&&root.debrief.text.find("No surviving")!=std::string::npos);
|
|
assert(mode.statsChanged.count==1&&root.statistics.text=="Waiting for mission statistics...");
|
|
mode.summary="Player: kills 2, escaped";mode.statsChanged.Invoke();assert(root.statistics.text==mode.summary);
|
|
mode.reason="An administrator abandoned this round.";mode.ERR_OnEndReasonReplicated();assert(root.debrief.text==mode.reason&&root.debrief.writes==2);
|
|
content.HandlerDeattached(0);assert(mode.statsChanged.count==0&&content.m_ERRStatisticsMode==nullptr);assert(mode.m_OnEndReasonChanged.count==0&&content.m_ERRFailureMode==nullptr);mode.reason="late";mode.ERR_OnEndReasonReplicated();assert(root.debrief.writes==2);
|
|
content.InitContent(&failure);assert(root.debrief.writes==3);content.InitContent(&escape);assert(mode.m_OnEndReasonChanged.count==0&&content.escapeEvent.count==1&&content.escapeRefresh==1);mode.ERR_OnEndReasonReplicated();assert(root.debrief.writes==3);
|
|
content.InitContent(&failure);assert(content.escapeEvent.count==0);Mode replacement;Mode::current=&replacement;mode.ERR_OnEndReasonReplicated();assert(root.debrief.writes==4);
|
|
Mode::current=&mode;content.m_wRoot=nullptr;mode.ERR_OnEndReasonReplicated();assert(root.debrief.writes==4);content.m_wRoot=&root;root.exists=false;mode.ERR_OnEndReasonReplicated();assert(root.debrief.writes==4);
|
|
content.InitContent(&other);assert(content.m_ERRFailureMode==nullptr&&mode.m_OnEndReasonChanged.count==0&&content.escapeEvent.count==0);
|
|
}
|
|
'''.replace('NOTIFY',adapt(notify)).replace('FAILURE',adapt(failure)).replace('CALLBACKS',adapt(callbacks)))
|
|
print('PASS: late authoritative failure reason refreshes open result; detach, mode change and success subscriptions preserved')
|