Files
EscapistsReintroduced/tests/test_ai_driver_horn.py
T
jakub 08e5cbf5ea
continuous-integration/drone/push Build is passing
Limit repeated AI driver honking; record Workshop 0.0.129
2026-10-30 15:38:44 +01:00

42 lines
3.7 KiB
Python

"""Run production horn limits and the control hook with engine boundaries doubled."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AIDriverHorn.c'
policy=body(p,'class ERR_AIDriverHornLimit').replace('protected ','').replace('class ERR_AIDriverHornLimit','struct ERR_AIDriverHornLimit')+';'
hook=body(p,'protected override void OnApplyControls(')
for a,b in [('protected override ','') ,('IEntity owner','Actor* owner'),('super.OnApplyControls(owner, timeSlice);','nativeCalls++;'),('Replication.IsServer()','authority'),('ERR_Actors.Player(owner)','owner->player'),('ERR_GameMode mode = ERR_GameMode.Get();','Mode* mode = currentMode;'),('IEntity vehicle = CompartmentAccessComponent.GetVehicleIn(owner);','Vehicle* vehicle = owner->vehicle;'),('vehicle.FindComponent(AICarMovementComponent)','vehicle->road'),('BaseVehicleControllerComponent controls = BaseVehicleControllerComponent.Cast(vehicle.FindComponent(BaseVehicleControllerComponent));','Controls* controls = vehicle->controls;'),('controls.GetPilotCompartmentSlot()','controls->seat'),('controls->seat.GetOccupant()','controls->seat->occupant'),('CharacterInputContext input = GetInputContext();','Input* input = inputs;'),('mode.Active()','mode->active'),('input.','input->'),('m_ERRHornLimit.Allow','m_ERRHornLimit->Allow'),('System.GetTickCount()','now')]: hook=hook.replace(a,b)
run(r'''
#include <cassert>
'''+policy+r'''
struct Input {int horn=1,writes=0;int GetVehicleHorn(){return horn;}void SetVehicleHorn(int v){writes++;horn=v;}};
struct Actor;struct Seat {Actor* occupant=nullptr;};struct Controls {Seat* seat=nullptr;};
struct Vehicle {bool road=true;Controls* controls=nullptr;};struct Actor {bool player=false;Vehicle* vehicle=nullptr;};
struct Mode {bool active=true;};
struct Controller {bool authority=true;int now=0,nativeCalls=0;Mode mode;Mode* currentMode=&mode;Input input;Input* inputs=&input;ERR_AIDriverHornLimit* m_ERRHornLimit=nullptr;
~Controller(){delete m_ERRHornLimit;}
'''+hook+r'''
};
int main(){
ERR_AIDriverHornLimit continuous;int allowed=0;
for(int t=0;t<90000;t+=10)if(continuous.Allow(true,t))allowed++;
assert(allowed==150); // three half-second beeps over 90 seconds, even held forever
ERR_AIDriverHornLimit tapped;
assert(tapped.Allow(true,0));assert(!tapped.Allow(false,100));
for(int t=200;t<30000;t+=100)assert(!tapped.Allow(t%200==0,t));
assert(tapped.Allow(true,30000));assert(!tapped.Allow(true,30500));
assert(!tapped.Allow(true,100));assert(!tapped.Allow(true,30099));assert(tapped.Allow(true,30100));
Controller c;Actor driver,passenger;Seat seat{&driver};Controls controls{&seat};Vehicle car{true,&controls};driver.vehicle=&car;passenger.vehicle=&car;
c.OnApplyControls(&driver,.02f);assert(c.input.horn==1&&c.nativeCalls==1);
c.now=500;c.OnApplyControls(&driver,.02f);assert(c.input.horn==0);
c.now=1000;c.input.horn=1;c.OnApplyControls(&driver,.02f);assert(c.input.horn==0);
// Player control, passengers, aircraft, replicas and unrelated scenarios are untouched.
driver.player=true;c.input.horn=1;c.OnApplyControls(&driver,.02f);assert(c.input.horn==1);
driver.player=false;c.OnApplyControls(&passenger,.02f);assert(c.input.horn==1);
car.road=false;c.OnApplyControls(&driver,.02f);assert(c.input.horn==1);car.road=true;
c.authority=false;c.OnApplyControls(&driver,.02f);assert(c.input.horn==1);c.authority=true;
c.currentMode=nullptr;c.OnApplyControls(&driver,.02f);assert(c.input.horn==1);c.currentMode=&c.mode;
c.mode.active=false;c.OnApplyControls(&driver,.02f);assert(c.input.horn==0);
c.inputs=nullptr;c.OnApplyControls(&driver,.02f);c.OnApplyControls(nullptr,.02f);
}
''')
print('PASS: held/tapped AI horn limits, half-second pulses, 30-second cooldown, clock reset, player/passenger/aircraft/replica exclusion and ended mission cleanup')