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.
47 lines
3.1 KiB
Python
47 lines
3.1 KiB
Python
"""Execute the actual spectator message/refresh policy; no native UI claim."""
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c'
|
|
source=body(p,'static string Message(')+'\n'+body(p,'static bool Refresh(')
|
|
source=source.replace('ERR_ObserverReason.','ERR_ObserverReason::')
|
|
run('''#include <cassert>
|
|
#include <string>
|
|
using string=std::string;
|
|
namespace ERR_ObserverReason {enum {NONE,SPENT_LIFE,LATE_EXTRACTION,SPAWN_UNAVAILABLE};}
|
|
'''+source+'''
|
|
int main(){
|
|
for(int reason=0;reason<5;reason++)for(bool available:{false,true}){
|
|
auto message=Message(reason,available);
|
|
if(reason==0){assert(message.empty());continue;}
|
|
assert(message.find("next round")!=string::npos);
|
|
assert((message.find("/esc spectate next")!=string::npos)==available);
|
|
assert((message.find("/esc spectate previous")!=string::npos)==available);
|
|
assert((message.find("No survivor is currently available")!=string::npos)==!available);
|
|
assert((message.find("when one becomes available")!=string::npos)==!available);
|
|
}
|
|
assert(Message(1,true).find("life for this round has been used")!=string::npos);
|
|
assert(Message(2,true).find("Extraction has started")!=string::npos);
|
|
assert(Message(3,true).find("could not be spawned")!=string::npos);
|
|
for(bool shown:{false,true})for(int previous=0;previous<5;previous++)for(int reason=0;reason<5;reason++)for(bool oldAvailable:{false,true})for(bool available:{false,true}){
|
|
bool expected=reason!=0&&(!shown||previous!=reason||oldAvailable!=available);
|
|
assert(Refresh(shown,previous,reason,oldAvailable,available)==expected);
|
|
}
|
|
// Stable monitoring must not repeat. Availability and reason changes do refresh.
|
|
assert(!Refresh(true,1,1,true,true));assert(Refresh(true,1,1,true,false));
|
|
assert(Refresh(true,1,1,false,true));assert(Refresh(true,1,2,true,true));
|
|
// Camera close/reopen resets shown; missing hint manager must not consume it.
|
|
assert(Refresh(false,1,1,true,true));assert(Refresh(false,1,1,false,false));
|
|
}
|
|
''')
|
|
monitor=body(p,'protected void ERR_UpdateObserverClient(')
|
|
assert 'bool survivorAvailable = m_ERRObservedId != RplId.Invalid();' in monitor
|
|
assert 'if (!hints) return;' in monitor
|
|
assert monitor.index('if (!hints) return;')<monitor.index('hints.ShowCustom(')<monitor.index('m_ERRObserverReady = true;')
|
|
assert 'm_ERRShownObserverReason = m_ERRObserverReason;' in monitor and 'm_ERRShownObserverAvailable = survivorAvailable;' in monitor
|
|
assert 'm_ERRObserverReady = false;' in body(p,'protected void ERR_CloseObserverCamera(')
|
|
assert 'CallLater' not in monitor, 'No additional timer belongs to guidance'
|
|
commands=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerAdminCommands.c','void ERR_AdminSubmit(')
|
|
assert commands.index('ERR_CycleObserver(direction);')<commands.index('Rpc(ERR_AdminRequest,')
|
|
admin=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminService.c','void Execute(')
|
|
assert 'if (!authorized) { replies.Insert("Administrator access required."); return; }' in admin
|
|
print('PASS: actual reason/availability guidance and refresh matrix; no-target copy omits cycling, hint retry retained, existing command/admin paths unchanged; native UI pending')
|