Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
122 lines
8.3 KiB
Python
122 lines
8.3 KiB
Python
"""Execute production input dispatch and recovery with native camera/editor doubles."""
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c'
|
|
s=body(p,'protected void ERR_UpdateRecoveryCamera(').replace('protected ','').replace('ERR_GameMode mode','ERR_GameMode* mode').replace('null','nullptr')
|
|
for name in ('ERR_Actors','SCR_EditorManagerEntity','LogLevel'):
|
|
s=s.replace(name+'.',name+'::')
|
|
for name in ('SCR_EditorManagerEntity','CameraManager','CameraBase'):
|
|
s=s.replace(name+' ',name+'* ')
|
|
for name in ('mode','editor','manager'):
|
|
s=s.replace(name+'.',name+'->')
|
|
s=s.replace('GetGame().','GetGame()->').replace('GetWorld().','GetWorld()->')
|
|
run(r'''#include <cassert>
|
|
struct Actor {bool alive=true,conscious=true;};using IEntity=Actor*;
|
|
struct ERR_Actors {static bool Alive(IEntity a){return a&&a->alive;}static bool CanFight(IEntity a){return Alive(a)&&a->conscious;}};
|
|
struct ERR_GameMode {bool active=true;bool Active(){return active;}};
|
|
struct SCR_EditorManagerEntity {bool opened=false,canClose=true;int closes=0;static SCR_EditorManagerEntity* current;static SCR_EditorManagerEntity* GetInstance(){return current;}bool IsOpened(){return opened;}void Close(bool){closes++;if(canClose)opened=false;}} editor;
|
|
SCR_EditorManagerEntity* SCR_EditorManagerEntity::current=&editor;
|
|
struct CameraBase{} player,other;
|
|
struct CameraManager {CameraBase* current=&other;int sets=0;void SetCamera(CameraBase* c){current=c;sets++;}CameraBase* CurrentCamera(){return current;}} manager;
|
|
struct World {float now=0;float GetWorldTime(){return now;}} world;
|
|
struct Game {World* GetWorld(){return &world;}CameraManager* GetCameraManager(){return &manager;}} game;
|
|
Game* GetGame(){return &game;}namespace LogLevel {enum{WARNING};}int warnings=0;void Print(const char*,int){warnings++;}
|
|
struct Controller {
|
|
IEntity controlled=nullptr,m_ERRRecoveryBody=nullptr;float m_ERRRecoveryDeadline=-1;CameraBase* camera=&player;
|
|
IEntity GetControlledEntity(){return controlled;}CameraBase* GetPlayerCamera(){return camera;}
|
|
'''+s+r'''
|
|
};
|
|
int main(){
|
|
ERR_GameMode mode;Actor body,replacement;Controller c;c.controlled=&body;
|
|
// Healthy admins retain their camera: restoration belongs only to recovery.
|
|
c.ERR_UpdateRecoveryCamera(&mode);assert(manager.sets==0&&editor.closes==0);
|
|
body.conscious=false;c.ERR_UpdateRecoveryCamera(&mode);assert(c.m_ERRRecoveryBody==&body&&manager.sets==0);
|
|
// Recovery works with NO observer-camera instance and a different active camera.
|
|
editor.opened=true;body.conscious=true;c.ERR_UpdateRecoveryCamera(&mode);
|
|
assert(editor.closes==1&&!editor.opened&&manager.current==&player&&!c.m_ERRRecoveryBody);
|
|
manager.current=&other;c.ERR_UpdateRecoveryCamera(&mode);assert(manager.current==&other); // no permanent camera lock
|
|
// An asynchronously closing editor retains the pending recovery; next tick completes.
|
|
body.conscious=false;c.ERR_UpdateRecoveryCamera(&mode);body.conscious=true;editor.opened=true;editor.canClose=false;
|
|
c.ERR_UpdateRecoveryCamera(&mode);assert(c.m_ERRRecoveryBody==&body);
|
|
editor.canClose=true;c.ERR_UpdateRecoveryCamera(&mode);assert(!c.m_ERRRecoveryBody&&manager.current==&player);
|
|
// Wait for late camera creation, then return once.
|
|
body.conscious=false;c.ERR_UpdateRecoveryCamera(&mode);body.conscious=true;c.camera=nullptr;
|
|
c.ERR_UpdateRecoveryCamera(&mode);assert(c.m_ERRRecoveryBody==&body);
|
|
c.camera=&player;c.ERR_UpdateRecoveryCamera(&mode);assert(!c.m_ERRRecoveryBody);
|
|
// Refused editor close is bounded, warning instead of permanently stealing input.
|
|
body.conscious=false;c.ERR_UpdateRecoveryCamera(&mode);body.conscious=true;editor.opened=true;editor.canClose=false;
|
|
c.ERR_UpdateRecoveryCamera(&mode);world.now=10001;c.ERR_UpdateRecoveryCamera(&mode);assert(!c.m_ERRRecoveryBody&&warnings==1);
|
|
editor.opened=false;editor.canClose=true;
|
|
// Changed possession / stopped round must not return a stale casualty's camera.
|
|
body.conscious=false;c.ERR_UpdateRecoveryCamera(&mode);c.controlled=&replacement;int before=manager.sets;
|
|
c.ERR_UpdateRecoveryCamera(&mode);assert(!c.m_ERRRecoveryBody&&manager.sets==before);
|
|
c.controlled=&body;c.ERR_UpdateRecoveryCamera(&mode);mode.active=false;body.conscious=true;
|
|
c.ERR_UpdateRecoveryCamera(&mode);assert(!c.m_ERRRecoveryBody&&manager.sets==before);
|
|
}
|
|
''')
|
|
input_body=body(p,'void Input(').replace('SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());','SCR_PlayerController* controller = &pc;').replace('controller.','controller->').replace('ERR_ObserverRules.','ERR_ObserverRules::')
|
|
run(r'''#include <cassert>
|
|
struct ERR_ObserverRules {
|
|
'''+body(p,'static int Cycle(')+body(p,'static bool Toggle(')+r'''
|
|
};
|
|
struct SCR_PlayerController {int calls=0,direction=0;void ERR_CycleObserver(int d){calls++;direction=d;}} pc;
|
|
struct Follow {bool m_LeftDown=false,m_RightDown=false,m_EnterDown=false,m_PlayerView=false;int m_InputResumeFrames=0;
|
|
'''+input_body+r'''
|
|
};
|
|
int main(){
|
|
Follow f;f.Input(true,true,false,false);assert(pc.calls==1&&pc.direction==-1);
|
|
f.Input(true,true,false,false);assert(pc.calls==1);
|
|
f.Input(true,false,false,false);f.Input(true,false,true,false);assert(pc.calls==2&&pc.direction==1);
|
|
f.Input(true,true,true,false);assert(pc.calls==2);
|
|
f.Input(false,false,false,true);f.Input(true,false,false,true);assert(!f.m_PlayerView); // menu-held Enter
|
|
f.Input(true,false,false,false);f.Input(true,false,false,true);assert(f.m_PlayerView);
|
|
f.Input(true,false,false,true);assert(f.m_PlayerView);
|
|
f.Input(true,false,false,false);f.Input(true,false,false,true);assert(!f.m_PlayerView);
|
|
// Inactive chat context masks held keys as zero; submission must not become
|
|
// a new camera press when the context resumes (including its first zero frame).
|
|
f.Input(false,false,false,false);
|
|
f.Input(true,false,false,false);
|
|
f.Input(true,false,false,true);assert(!f.m_PlayerView);
|
|
f.Input(true,false,false,true);assert(!f.m_PlayerView);
|
|
f.Input(true,false,false,false);
|
|
f.Input(true,false,false,true);assert(f.m_PlayerView);
|
|
int before=pc.calls;
|
|
f.Input(false,false,false,false);
|
|
f.Input(true,false,false,false);
|
|
f.Input(true,true,false,false);f.Input(true,true,false,false);assert(pc.calls==before);
|
|
f.Input(true,false,false,false);f.Input(true,true,false,false);assert(pc.calls==before+1);
|
|
}
|
|
''')
|
|
# Execute the actual pre-activation gate with UI/native state doubled.
|
|
gate=body(p,'override protected bool IsDisabledByMenu(')
|
|
gate=gate.replace('override protected ', '').replace('super.', 'Base::')
|
|
gate=gate.replace('GetGame().', 'GetGame()->').replace('GetInputManager().', 'GetInputManager()->').replace('GetWorkspace().', 'GetWorkspace()->')
|
|
gate=gate.replace('Widget focused', 'Widget* focused').replace('EditBoxWidget.Cast', 'EditBoxWidget::Cast').replace('focused.GetParent()', 'focused->GetParent()')
|
|
run(r'''#include <cassert>
|
|
struct Widget {Widget*parent=nullptr;bool edit=false;Widget*GetParent(){return parent;}};
|
|
struct EditBoxWidget {static Widget*Cast(Widget*w){return w&&w->edit?w:nullptr;}};
|
|
struct Inputs {bool chat=false;bool IsContextActive(const char*){return chat;}} inputs;
|
|
struct Workspace {Widget*focus=nullptr;Widget*GetFocusedWidget(){return focus;}} workspace;
|
|
struct Game {Inputs*GetInputManager(){return &inputs;}Workspace*GetWorkspace(){return &workspace;}} game;
|
|
Game*GetGame(){return &game;}
|
|
struct Base {bool menu=false;bool IsDisabledByMenu(){return menu;}};
|
|
struct Camera:Base {
|
|
'''+gate+r'''
|
|
};
|
|
int main(){
|
|
Camera c;assert(!c.IsDisabledByMenu());
|
|
c.menu=true;assert(c.IsDisabledByMenu());c.menu=false;
|
|
inputs.chat=true;assert(c.IsDisabledByMenu());inputs.chat=false;
|
|
Widget edit,child,ordinary;edit.edit=true;child.parent=&edit;
|
|
workspace.focus=&edit;assert(c.IsDisabledByMenu());
|
|
workspace.focus=&child;assert(c.IsDisabledByMenu());
|
|
workspace.focus=&ordinary;assert(!c.IsDisabledByMenu());
|
|
workspace.focus=nullptr;assert(!c.IsDisabledByMenu());
|
|
}
|
|
''')
|
|
assert (ROOT/'addons/core/Prefabs/ERR/SpectatorCamera.et').read_text().startswith('ERR_ObserverCamera {')
|
|
config=(ROOT/'addons/core/Configs/ERR/ObserverInput.conf').read_text()
|
|
for key in ('KC_LEFT','KC_RIGHT','KC_RETURN','KC_NUMPADENTER'): assert f'keyboard:{key}' in config
|
|
assert 'chimeraInputCommon.conf' in config and 'ActionContext/ManualCameraContext.conf' in config
|
|
assert 'Debug.KeyState' not in p.read_text()
|
|
print('PASS: actual input dispatch, one press per edge, menu-held suppression; actual revival camera handoff with editor close/retry, missing camera, replacement body and stopped round')
|