Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
"""Execute Cessna boarding policy; engine transitions/components are explicit doubles."""
|
||||
from policy_harness import ROOT, body, run
|
||||
|
||||
SOURCE = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_PlaneFlight.c'
|
||||
|
||||
|
||||
def cpp(method):
|
||||
# Translate syntax only; the three production decision bodies stay intact.
|
||||
method = method.replace('protected ', '')
|
||||
method = method.replace(
|
||||
'AIControlComponent.Cast(entry.Actor.FindComponent(AIControlComponent))',
|
||||
'entry.Actor.Control',
|
||||
).replace(
|
||||
'CompartmentAccessComponent.Cast(entry.Actor.FindComponent(CompartmentAccessComponent))',
|
||||
'entry.Actor.Access',
|
||||
)
|
||||
for before, after in (
|
||||
('ERR_PlaneCrewSeat entry', 'Entry* entry'),
|
||||
('AIControlComponent control', 'Control* control'),
|
||||
('CompartmentAccessComponent access', 'Access* access'),
|
||||
('CompartmentAccessComponent.', 'Access::'),
|
||||
('SCR_ChimeraAIAgent.Cast(', 'CharacterAgent::Cast('),
|
||||
('SCR_ChimeraAIAgent characterAgent', 'Agent* characterAgent'),
|
||||
('SCR_FactionManager.', 'Factions::'),
|
||||
('ERR_Actors.', 'Actors::'),
|
||||
('ECloseDoorAfterActions.', 'DoorAction::'),
|
||||
('string action', 'std::string action'),
|
||||
):
|
||||
method = method.replace(before, after)
|
||||
for name in ('entry', 'Actor', 'Seat', 'Agent', 'access', 'control', 'm_Mode'):
|
||||
method = method.replace(name + '.', name + '->')
|
||||
return method
|
||||
|
||||
|
||||
methods = '\n'.join(cpp(body(SOURCE, signature)) for signature in (
|
||||
'protected bool OwnsCrewSeat(',
|
||||
'protected bool CrewSeated(',
|
||||
'protected bool PrepareCrewSeat(',
|
||||
))
|
||||
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Entity;
|
||||
struct Seat;
|
||||
struct Access;
|
||||
struct Agent {
|
||||
int lod=2, activations=0, lodWrites=0;
|
||||
bool active=false,playerPending=false,character=true;
|
||||
std::vector<std::string> calls;
|
||||
void SetPermanentLOD(int value) {lod=value;lodWrites++;calls.push_back("LOD");}
|
||||
void ActivateAI() {active=true;activations++;calls.push_back("activate");}
|
||||
bool IsPlayerPending_S(){return playerPending;}
|
||||
};
|
||||
struct CharacterAgent {static Agent* Cast(Agent* agent){return agent->character?agent:nullptr;}};
|
||||
struct Control {Agent* agent=nullptr;Agent* GetControlAIAgent(){return agent;}};
|
||||
struct Entity {
|
||||
bool player=false,fit=true;
|
||||
int faction=1;
|
||||
Entity* group=nullptr;
|
||||
struct Control* Control=nullptr;
|
||||
struct Access* Access=nullptr;
|
||||
};
|
||||
struct Seat {
|
||||
Entity* owner=nullptr;
|
||||
Entity* occupant=nullptr;
|
||||
Entity* reservation=nullptr;
|
||||
bool occupied=false,accessible=true,locked=false,reserved=false;
|
||||
Entity* GetOwner(){return owner;}
|
||||
Entity* GetOccupant(){return occupant;}
|
||||
bool IsOccupied(){return occupied||occupant;}
|
||||
bool IsCompartmentAccessible(){return accessible;}
|
||||
bool IsGetInLockedFor(Entity*){return locked;}
|
||||
bool IsReserved(){return reserved;}
|
||||
bool IsReservedBy(Entity* actor){return reservation==actor;}
|
||||
};
|
||||
struct DoorAction {enum {INVALID=-1};};
|
||||
struct Access {
|
||||
Entity* actor=nullptr;
|
||||
Entity* vehicle=nullptr;
|
||||
Seat* compartment=nullptr;
|
||||
Seat* expectedSeat=nullptr;
|
||||
bool entering=false,exiting=false,inside=false,accepted=true,synchronousSeat=false;
|
||||
int requests=0;
|
||||
bool IsGettingIn(){return entering;}
|
||||
bool IsGettingOut(){return exiting;}
|
||||
bool IsInCompartment(){return inside;}
|
||||
Seat* GetCompartment(){return compartment;}
|
||||
static Entity* GetVehicleIn(Entity* actor){return actor&&actor->Access?actor->Access->vehicle:nullptr;}
|
||||
bool GetInVehicle(Entity* owner,Seat* seat,bool teleport,int door,int close,bool unknown) {
|
||||
assert(seat==expectedSeat&&owner==expectedSeat->owner);
|
||||
assert(teleport&&door==-1&&close==DoorAction::INVALID&&!unknown);
|
||||
Agent* agent=actor->Control->agent;
|
||||
assert(agent->active&&agent->lod==0);
|
||||
assert(agent->calls.size()>=2&&agent->calls[agent->calls.size()-2]=="LOD"&&agent->calls.back()=="activate");
|
||||
agent->calls.push_back("board");requests++;
|
||||
if(synchronousSeat) {inside=true;compartment=seat;vehicle=owner;seat->occupant=actor;}
|
||||
return accepted;
|
||||
}
|
||||
};
|
||||
struct Actors {
|
||||
static bool Player(Entity* actor){return actor->player;}
|
||||
static Entity* Group(Entity* actor){return actor->group;}
|
||||
static bool CanFight(Entity* actor){return actor&&actor->fit;}
|
||||
};
|
||||
struct Factions {static int SGetFaction(Entity* actor){return actor->faction;}};
|
||||
struct Mode {int EnemyFaction(){return 1;}};
|
||||
struct Entry {
|
||||
Entity* Actor=nullptr;
|
||||
struct Seat* Seat=nullptr;
|
||||
struct Agent* Agent=nullptr;
|
||||
int Attempts=0;
|
||||
float NextAttempt=0;
|
||||
};
|
||||
struct Flight {
|
||||
Mode* m_Mode=nullptr;
|
||||
Entity* Crew=nullptr;
|
||||
Entity* Aircraft=nullptr;
|
||||
std::vector<std::string> actions;
|
||||
void LogCrewSeat(Entry*,int,float,std::string action="",bool force=false) {
|
||||
if(force)actions.push_back(action);
|
||||
}
|
||||
''' + methods + r'''
|
||||
};
|
||||
struct Setup {
|
||||
Mode mode;
|
||||
Entity aircraft,group,actor,other;
|
||||
Agent agent,otherAgent;
|
||||
Control control;
|
||||
Access access;
|
||||
Seat seat,otherSeat;
|
||||
Entry entry;
|
||||
Flight flight;
|
||||
Setup() {
|
||||
flight.m_Mode=&mode;flight.Aircraft=&aircraft;flight.Crew=&group;
|
||||
actor.group=&group;actor.Control=&control;actor.Access=&access;control.agent=&agent;
|
||||
access.actor=&actor;access.expectedSeat=&seat;seat.owner=&aircraft;
|
||||
entry.Actor=&actor;entry.Seat=&seat;entry.Agent=&agent;
|
||||
}
|
||||
bool Tick(float now){return flight.PrepareCrewSeat(&entry,0,now);}
|
||||
void Seated(){access.inside=true;access.compartment=&seat;access.vehicle=&aircraft;seat.occupant=&actor;}
|
||||
};
|
||||
int main() {
|
||||
// Native acceptance can leave the actor on foot. All requests, including
|
||||
// refused requests, consume the same finite budget with 5s between requests.
|
||||
for(bool accepted:{false,true}) {
|
||||
Setup s;s.access.accepted=accepted;
|
||||
assert(!s.Tick(1000)&&s.entry.Attempts==1&&s.access.requests==1);
|
||||
assert(s.entry.NextAttempt==6000&&s.agent.active&&s.agent.lod==0);
|
||||
assert(!s.Tick(1001)&&!s.Tick(5999)&&s.access.requests==1);
|
||||
assert(!s.Tick(6000)&&s.entry.Attempts==2&&s.entry.NextAttempt==11000);
|
||||
assert(!s.Tick(10999)&&s.access.requests==2);
|
||||
assert(!s.Tick(11000)&&s.entry.Attempts==3&&s.entry.NextAttempt==16000);
|
||||
for(float now:{16000.f,45000.f,1000000.f})assert(!s.Tick(now));
|
||||
assert(s.access.requests==3&&s.agent.activations==3&&s.agent.lodWrites==3);
|
||||
assert(s.flight.actions==std::vector<std::string>(3,accepted?"boarding accepted":"boarding refused"));
|
||||
// Delayed native completion is still accepted after the retry cap.
|
||||
s.Seated();assert(s.Tick(1000001)&&s.access.requests==3);
|
||||
}
|
||||
// Even if a fake native request completes synchronously, that request is not
|
||||
// the readiness proof. The next observation must confirm the exact seat.
|
||||
{Setup s;s.access.synchronousSeat=true;assert(!s.Tick(1000));assert(s.Tick(1001)&&s.access.requests==1);}
|
||||
{Setup s;s.Seated();assert(s.Tick(1000)&&s.access.requests==0&&s.agent.activations==0);}
|
||||
|
||||
// Each independent native reading must agree before readiness is reported;
|
||||
// disagreement must not eject/reboard someone already transitioning/inside.
|
||||
for(const auto& fault:std::vector<std::function<void(Setup&)>>{
|
||||
[](Setup& s){s.seat.occupant=nullptr;},
|
||||
[](Setup& s){s.seat.occupant=&s.other;},
|
||||
[](Setup& s){s.access.inside=false;},
|
||||
[](Setup& s){s.access.compartment=&s.otherSeat;},
|
||||
[](Setup& s){s.access.vehicle=&s.other;},
|
||||
[](Setup& s){s.access.vehicle=nullptr;},
|
||||
[](Setup& s){s.access.entering=true;},
|
||||
[](Setup& s){s.access.exiting=true;},
|
||||
[](Setup& s){s.actor.Access=nullptr;},
|
||||
}) {
|
||||
Setup s;s.Seated();fault(s);
|
||||
assert(!s.flight.CrewSeated(&s.entry)&&!s.Tick(10000));
|
||||
assert(s.access.requests==0&&s.entry.Attempts==0&&s.agent.activations==0);
|
||||
}
|
||||
|
||||
// Unfit, player-controlled, foreign, replaced or missing actors/components
|
||||
// cannot receive another request, regardless of the elapsed retry interval.
|
||||
for(const auto& fault:std::vector<std::function<void(Setup&)>>{
|
||||
[](Setup& s){s.entry.Actor=nullptr;},
|
||||
[](Setup& s){s.actor.player=true;},
|
||||
[](Setup& s){s.actor.group=nullptr;},
|
||||
[](Setup& s){s.actor.group=&s.other;},
|
||||
[](Setup& s){s.actor.faction=2;},
|
||||
[](Setup& s){s.actor.Control=nullptr;},
|
||||
[](Setup& s){s.control.agent=&s.otherAgent;},
|
||||
[](Setup& s){s.control.agent=nullptr;},
|
||||
[](Setup& s){s.entry.Agent=nullptr;},
|
||||
[](Setup& s){s.agent.playerPending=true;},
|
||||
[](Setup& s){s.actor.fit=false;},
|
||||
[](Setup& s){s.entry.Seat=nullptr;},
|
||||
[](Setup& s){s.actor.Access=nullptr;},
|
||||
[](Setup& s){s.access.entering=true;},
|
||||
[](Setup& s){s.access.exiting=true;},
|
||||
[](Setup& s){s.access.inside=true;s.access.compartment=&s.otherSeat;},
|
||||
[](Setup& s){s.access.vehicle=&s.other;},
|
||||
[](Setup& s){s.seat.occupied=true;},
|
||||
[](Setup& s){s.seat.occupant=&s.other;},
|
||||
[](Setup& s){s.seat.accessible=false;},
|
||||
[](Setup& s){s.seat.locked=true;},
|
||||
[](Setup& s){s.seat.reserved=true;s.seat.reservation=&s.other;},
|
||||
}) {
|
||||
Setup s;assert(!s.Tick(1000));fault(s);
|
||||
assert(!s.Tick(10000)&&!s.Tick(20000));
|
||||
assert(s.access.requests==1&&s.entry.Attempts==1&&s.entry.NextAttempt==6000);
|
||||
assert(s.agent.activations==1&&s.agent.lodWrites==1&&s.otherAgent.activations==0);
|
||||
}
|
||||
// Capture/group/control/health changes also prevent already seated readiness.
|
||||
for(const auto& fault:std::vector<std::function<void(Setup&)>>{
|
||||
[](Setup& s){s.actor.player=true;},
|
||||
[](Setup& s){s.actor.group=&s.other;},
|
||||
[](Setup& s){s.actor.faction=2;},
|
||||
[](Setup& s){s.control.agent=&s.otherAgent;},
|
||||
[](Setup& s){s.agent.playerPending=true;},
|
||||
[](Setup& s){s.actor.fit=false;},
|
||||
}) {
|
||||
Setup s;s.Seated();assert(s.Tick(1000));fault(s);assert(!s.Tick(10000)&&s.access.requests==0);
|
||||
}
|
||||
// Our own reservation is permitted. Native transitions can finish/fail on
|
||||
// their own schedule; skipped ticks do not burn attempts or push the deadline.
|
||||
{Setup s;s.seat.reserved=true;s.seat.reservation=&s.actor;assert(!s.Tick(1000)&&s.access.requests==1);}
|
||||
{Setup s;assert(!s.Tick(1000));s.access.entering=true;assert(!s.Tick(6000)&&!s.Tick(10000));
|
||||
assert(s.entry.Attempts==1&&s.entry.NextAttempt==6000);s.access.entering=false;
|
||||
assert(!s.Tick(10250)&&s.entry.Attempts==2&&s.entry.NextAttempt==15250);}
|
||||
// Pilot and observer have independent request budgets.
|
||||
{Setup pilot,observer;for(float now:{1000.f,6000.f,11000.f})assert(!pilot.Tick(now));
|
||||
assert(!pilot.Tick(16000)&&!observer.Tick(16000));assert(pilot.access.requests==3&&observer.access.requests==1);}
|
||||
}
|
||||
''')
|
||||
|
||||
print('PASS: production Cessna boarding requires exact observed occupancy, activates before requesting, caps retries at three with 5s spacing, and preserves transition, ownership, health and seat restrictions; engine behavior is faked')
|
||||
Reference in New Issue
Block a user