Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
71 lines
4.8 KiB
Python
71 lines
4.8 KiB
Python
"""Run the production prison hold/release callbacks with engine boundaries stubbed."""
|
|
from policy_harness import ROOT, body, run
|
|
from pathlib import Path
|
|
import os
|
|
source = Path(os.environ.get('ERR_PRISON_AI_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_PrisonAI.c'))
|
|
def cpp(signature):
|
|
s=body(source,signature).replace('protected ','')
|
|
for ns in ('Replication','SCR_Global','ERR_GameMode','ERR_Phase','ERR_Actors','SCR_FactionManager'):
|
|
s=s.replace(ns+'.',ns+'::')
|
|
s=s.replace('ERR_GameMode mode =','ERR_GameMode* mode =').replace('IEntity actor =','Actor* actor =')
|
|
for var in ('mode','actor','m_ERRPrisonMode'):
|
|
s=s.replace(var+'.',var+'->')
|
|
s=s.replace('ERR_Phase previous, ERR_Phase current','int previous, int current')
|
|
for callback in ('ERR_PrisonPhaseChanged','ERR_PrisonStopped'):
|
|
s=s.replace('Insert('+callback+')','Insert("'+callback+'")').replace('Remove('+callback+')','Remove("'+callback+'")')
|
|
s=s.replace('OnPhaseChanged().','OnPhaseChanged()->').replace('OnStop().','OnStop()->')
|
|
return s
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <set>
|
|
#include <string>
|
|
#define null nullptr
|
|
namespace Replication {bool server=true;bool IsServer(){return server;}}
|
|
namespace SCR_Global {bool edit=false;bool IsEditMode(){return edit;}}
|
|
namespace ERR_Phase {constexpr int PRISON=1,RADIO_SEARCH=2;}
|
|
struct Actor {bool player=false, fighting=true;int faction=2;};
|
|
namespace ERR_Actors {bool Player(Actor* a){return a&&a->player;}bool CanFight(Actor* a){return a&&a->fighting;}}
|
|
namespace SCR_FactionManager {int SGetFaction(Actor* a){return a->faction;}}
|
|
struct Hook {std::set<std::string> callbacks;int inserted=0;void Insert(std::string c){callbacks.insert(c);inserted++;}void Remove(std::string c){callbacks.erase(c);}};
|
|
struct ERR_GameMode {static ERR_GameMode* current;int phase=0,enemy=2;Hook phases,stops;
|
|
static ERR_GameMode* Get(){return current;}int Phase(){return phase;}int EnemyFaction(){return enemy;}bool IsMissionEnemy(int f){return enemy&&f>=enemy;}
|
|
Hook* OnPhaseChanged(){return &phases;}Hook* OnStop(){return &stops;}};
|
|
ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
struct Agent {ERR_GameMode* m_ERRPrisonMode=nullptr;Actor* actor=nullptr;bool pending=false,active=true;int starts=0;
|
|
Actor* GetControlledEntity(){return actor;}bool IsPlayerPending_S(){return pending;}
|
|
void DeactivateAI(){active=false;}void ActivateAI(){active=true;starts++;}
|
|
'''+cpp('bool ERR_PrisonHeldForTest(')+cpp('void ERR_HoldForPrison(')+cpp('protected void ERR_PrisonUnsubscribe(')+cpp('protected void ERR_PrisonPhaseChanged(')+cpp('protected void ERR_PrisonStopped(')+r'''
|
|
};
|
|
int main(){
|
|
for(int phase=0;phase<=1;phase++){
|
|
ERR_GameMode mode;mode.phase=phase;ERR_GameMode::current=&mode;Actor actor;Agent a;a.actor=&actor;
|
|
a.ERR_HoldForPrison();assert(a.ERR_PrisonHeldForTest());assert(!a.active&&mode.phases.callbacks.size()==1&&mode.stops.callbacks.size()==1);
|
|
a.active=true;a.ERR_HoldForPrison();assert(!a.active&&mode.phases.inserted==1);
|
|
a.ERR_PrisonPhaseChanged(0,1);assert(!a.active&&a.m_ERRPrisonMode==&mode);
|
|
mode.phase=2;a.ERR_PrisonPhaseChanged(phase,2);assert(a.active&&a.starts==1&&!a.m_ERRPrisonMode&&mode.phases.callbacks.empty()&&mode.stops.callbacks.empty());
|
|
a.ERR_HoldForPrison();assert(a.active&&mode.phases.inserted==1);
|
|
assert(!a.ERR_PrisonHeldForTest());a.DeactivateAI();assert(!a.active&&!a.ERR_PrisonHeldForTest());
|
|
}
|
|
for(int fault=0;fault<8;fault++){
|
|
ERR_GameMode mode;ERR_GameMode::current=&mode;Actor actor;Agent a;a.actor=&actor;
|
|
if(fault==0)Replication::server=false;if(fault==1)SCR_Global::edit=true;
|
|
if(fault==2)ERR_GameMode::current=nullptr;if(fault==3)a.actor=nullptr;
|
|
if(fault==4)actor.player=true;if(fault==5)a.pending=true;
|
|
if(fault==6)actor.faction=1;if(fault==7)mode.enemy=0;
|
|
a.ERR_HoldForPrison();assert(a.active&&!a.m_ERRPrisonMode&&mode.phases.callbacks.empty());
|
|
Replication::server=true;SCR_Global::edit=false;
|
|
}
|
|
// Dead/unconscious, newly possessed, pending-player and removed actors cannot
|
|
// be activated by the release callback; all subscriptions are still removed.
|
|
for(int fault=0;fault<4;fault++){
|
|
ERR_GameMode mode;ERR_GameMode::current=&mode;Actor actor;Agent a;a.actor=&actor;a.ERR_HoldForPrison();
|
|
if(fault==0)actor.fighting=false;if(fault==1)actor.player=true;
|
|
if(fault==2)a.pending=true;if(fault==3)a.actor=nullptr;
|
|
mode.phase=2;a.ERR_PrisonPhaseChanged(1,2);assert(!a.active&&a.starts==0&&mode.phases.callbacks.empty());
|
|
}
|
|
{ERR_GameMode mode;ERR_GameMode::current=&mode;Actor actor;Agent a;a.actor=&actor;a.ERR_HoldForPrison();
|
|
a.ERR_PrisonStopped();assert(!a.active&&a.starts==0&&!a.m_ERRPrisonMode&&mode.phases.callbacks.empty()&&mode.stops.callbacks.empty());}
|
|
}
|
|
'''.replace(';if(', ';\n if('))
|
|
print('PASS: production prison AI hold/release, repeat activation, authority/faction/player exclusion, dead actor and stop cleanup')
|