Files
EscapistsReintroduced/tests/test_player_admission_policy.py
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

76 lines
4.1 KiB
Python

"""Execute source-local admission bodies and the reservation rejection predicate.
Only vector arithmetic and opaque UUID equality/nullness are substituted. This is
not a character collision, controller, callback, reconnect or native engine test.
"""
import re
from policy_harness import ROOT, body, run
base = ROOT / 'addons/core/Scripts/Game/ERR/00_Core'
rules = body(base/'ERR_PlayerRoster.c', 'static bool CanEnter(')
rules += '\n' + body(base/'ERR_SpawnFinalization.c', 'static bool Allows(')
rules += '\n' + body(base/'ERR_SpawnFinalization.c', 'static bool PositionClose(')
rules = rules.replace('ERR_Phase.', 'ERR_Phase::').replace('ERR_PlayerLifeRules.', '')
rules = rules.replace('vector.', 'vector::').replace('Math.', 'Math::')
# Extract the actual condition, rather than maintaining a second implementation.
# Native geometry/terrain branches of Clear are deliberately not emulated.
clear = body(base/'ERR_SpawnPlacement.c', 'bool Clear(')
match = re.search(r'if \((id != ownReservation[^\n]+)\) return false;', clear)
assert match, 'Reservation policy moved: update the source-local extraction'
predicate = match[1].replace('vector.', 'vector::').replace('Math.', 'Math::')
rules += '\nstatic bool ReservationAllows(int id, int ownReservation, vector position, vector reserved) { return !(' + predicate + '); }\n'
run('''#include <cassert>
#include <cmath>
#include <initializer_list>
namespace ERR_Phase {enum {PREPARING,PRISON,RADIO_SEARCH,RADIO_DEFENCE,EXTRACTION,VICTORY,DEFEAT};}
struct UUID {
int identity;
bool IsNull() const { return identity==0; }
bool operator==(UUID other) const { return identity==other.identity; }
};
struct vector {
float x,y,z;
float operator[](int axis) const { return axis==0?x:(axis==1?y:z); }
static float DistanceXZ(vector a, vector b) {return std::hypot(a.x-b.x,a.z-b.z);}
};
namespace Math {static float AbsFloat(float value) {return std::abs(value);}}
''' + rules + '''
int main() {
// Complete independent authorization matrix, including invalid phases and null IDs.
for (int phase=-1;phase<=7;++phase)
for (int flags=0;flags<32;++flags)
for (int oldId=0;oldId<=2;++oldId)
for (int currentId=0;currentId<=2;++currentId) {
bool connected=flags&1,usedLife=flags&2,sameController=flags&4;
bool sameRequest=flags&8,noControlledEntity=flags&16;
bool expected=phase>=1&&phase<=3&&connected&&!usedLife&&oldId>0&&oldId==currentId&&sameController&&sameRequest&&noControlledEntity;
assert(Allows(phase,connected,usedLife,UUID{oldId},UUID{currentId},sameController,sameRequest,noControlledEntity)==expected);
}
const vector origin={0,0,0};
// Inclusive vertical tolerance and just-inside/outside horizontal boundary.
for (float y : {-1.001f,-1.f,-0.2f,0.f,0.2f,1.f,1.001f}) {
assert(PositionClose(vector{0,y,0},origin)==(std::abs(y)<=1));
assert(PositionClose(vector{.599f,y,0},origin)==(std::abs(y)<=1));
assert(!PositionClose(vector{.601f,y,0},origin));
}
assert(PositionClose(vector{.3f,.2f,.3f},origin));
assert(!PositionClose(vector{.5f,0,.5f},origin)); // radial, not independent axis allowance
assert(!PositionClose(vector{0,-20,0},origin)); // fallen below unchanged XZ
assert(!PositionClose(vector{0,20,0},origin)); // displaced above unchanged XZ
// Connection IDs1 and13 must conflict identically; only the caller's own slot is exempt.
for (int id : {1,13,99})
for (int owner : {-1,1,13,99})
for (float x : {-1.201f,-1.199f,0.f,1.199f,1.201f})
for (float y : {-2.001f,-2.f,-1.999f,0.f,1.999f,2.f,2.001f}) {
bool expected=id==owner || std::abs(x)>=1.2 || std::abs(y)>=2;
assert(ReservationAllows(id,owner,origin,vector{x,y,0})==expected);
}
assert(!ReservationAllows(1,13,origin,vector{.8f,0,.8f}));
assert(ReservationAllows(1,13,origin,vector{.9f,0,.9f}));
assert(ReservationAllows(1,13,origin,vector{1.5f,0,0})); // authored grid spacing
// Own reservation is excluded while finalization still checks native geometry separately.
assert(ReservationAllows(13,13,origin,origin));
assert(!ReservationAllows(13,1,origin,origin));
}
''')
print('PASS: player admission phase/life/identity/controller matrix, preload displacement limits, pending reservation overlap and owner exclusion')