Files
EscapistsReintroduced/tests/test_action_feedback.py
T
jakub cab998fb64 Combine Workshop addon and fix prison startup
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.
2026-09-20 16:42:52 +02:00

63 lines
4.9 KiB
Python

"""Execute actual action availability methods; native UI rendering is separate."""
from policy_harness import ROOT, body, run
source=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Actions.c').read_text()
class Source:
def __init__(self,name): self.text=source.split('class '+name+' :',1)[1].split('\nclass ',1)[0]
def read_text(self): return self.text
def method(name):
s=body(Source(name),'override bool CanBePerformedScript(').replace('override ','')
s=s.replace('ERR_GameMode mode = ERR_GameMode.Get();','ERR_GameMode* mode = ERR_GameMode::Get();').replace('mode.Phase()','mode->Phase()')
s=s.replace('ERR_LocationComponent location = ERR_LocationComponent.Cast(GetOwner().FindComponent(ERR_LocationComponent));','Location* location = GetOwner()->location;')
s=s.replace('location.Knowledge()','location->Knowledge()').replace('ERR_Phase.','ERR_Phase::').replace('ERR_Actors.','ERR_Actors::')
return s.replace('string.Empty','string{}').replace('super.CanBePerformedScript','NativeDoor::CanBePerformedScript')
run(r'''
#include <cassert>
#include <string>
using string=std::string;
struct Actor{bool conscious=true;};using IEntity=Actor*;
namespace ERR_Actors{bool CanFight(IEntity a){return a&&a->conscious;}}
namespace ERR_Phase{enum Value{PREPARING,PRISON,RADIO_SEARCH,RADIO_DEFENCE,EXTRACTION,VICTORY,DEFEAT};}
struct ERR_GameMode{int phase=0;static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}int Phase(){return phase;}};ERR_GameMode* ERR_GameMode::current=nullptr;
struct Location{int knowledge=0;int Knowledge(){return knowledge;}};
struct Owner{Location* location=nullptr;};
struct ActionBase{string reason="stale";Owner owner;void SetCannotPerformReason(string s){reason=s;}Owner* GetOwner(){return &owner;}};
struct NativeDoor:ActionBase{bool allowed=true,explain=true;int queries=0;bool CanBePerformedScript(IEntity){queries++;if(!allowed&&explain)SetCannotPerformReason("Native door obstructed");return allowed;}};
struct Unlock:ActionBase{
'''+method('ERR_UnlockPrisonAction')+r'''
};struct Door:NativeDoor{
'''+method('ERR_PrisonDoorAction')+r'''
};struct Radio:ActionBase{
'''+method('ERR_TuneRadioAction')+r'''
};
int main(){
Actor actor;ERR_GameMode mode;Location location;
for(int present=0;present<2;present++)for(int phase=0;phase<7;phase++)for(int conscious=0;conscious<2;conscious++)for(int known=-1;known<=3;known++)for(int presentLocation=0;presentLocation<2;presentLocation++){
ERR_GameMode::current=present?&mode:nullptr;mode.phase=phase;actor.conscious=conscious;location.knowledge=known;
Unlock unlock;Radio radio;radio.owner.location=presentLocation?&location:nullptr;
bool lockAllowed=present&&phase==ERR_Phase::PRISON&&conscious;
bool radioAllowed=present&&phase==ERR_Phase::RADIO_SEARCH&&presentLocation&&known==2&&conscious;
assert(unlock.CanBePerformedScript(&actor)==lockAllowed);assert(unlock.reason.empty()==lockAllowed);
assert(radio.CanBePerformedScript(&actor)==radioAllowed);assert(radio.reason.empty()==radioAllowed);
for(int native=0;native<2;native++){
Door door;door.allowed=native;bool doorAllowed=present&&phase>ERR_Phase::PRISON&&native;
assert(door.CanBePerformedScript(&actor)==doorAllowed);assert(door.reason.empty()==doorAllowed);
bool delegated=present&&phase>ERR_Phase::PRISON;assert(door.queries==(delegated?1:0));
if(delegated&&!native)assert(door.reason=="Native door obstructed");
}
}
ERR_GameMode::current=&mode;actor.conscious=true;Radio radio;radio.owner.location=&location;mode.phase=ERR_Phase::RADIO_SEARCH;location.knowledge=1;
assert(!radio.CanBePerformedScript(&actor)&&radio.reason.find("Identify")!=string::npos);
location.knowledge=2;assert(radio.CanBePerformedScript(&actor)&&radio.reason.empty());
actor.conscious=false;assert(!radio.CanBePerformedScript(&actor)&&radio.reason=="You must be alive and conscious");
actor.conscious=true;assert(radio.CanBePerformedScript(&actor)&&radio.reason.empty());
mode.phase=ERR_Phase::RADIO_DEFENCE;assert(!radio.CanBePerformedScript(&actor)&&radio.reason.find("already in progress")!=string::npos);
mode.phase=ERR_Phase::EXTRACTION;assert(!radio.CanBePerformedScript(&actor)&&radio.reason.find("complete")!=string::npos);
mode.phase=ERR_Phase::PRISON;Door door;assert(!door.CanBePerformedScript(&actor)&&door.reason.find("Pick the prison lock")!=string::npos);
mode.phase=ERR_Phase::RADIO_SEARCH;door.allowed=false;assert(!door.CanBePerformedScript(&actor)&&door.reason=="Native door obstructed");
door.allowed=true;assert(door.CanBePerformedScript(&actor)&&door.reason.empty());
door.allowed=false;door.explain=false;assert(!door.CanBePerformedScript(&actor)&&door.reason.empty()); // No stale native denial either.
mode.phase=ERR_Phase::PRISON;Unlock unlock;assert(!unlock.CanBePerformedScript(nullptr));assert(unlock.CanBePerformedScript(&actor)&&unlock.reason.empty());
}
''')
print('PASS: actual action permission matrix unchanged; useful phase/intel/consciousness denials, stale reason cleared, native door denial preserved; no native UI claim')