45 lines
13 KiB
Markdown
45 lines
13 KiB
Markdown
# Physical prison exit and locked door execution
|
||
|
||
The successor's normal lock action invokes `ERR_GameMode.UnlockPrison`. Physical escape now also completes prison: a registered canonical player continuously crossing the enclosure boundary can advance whether the gate remains intact, is destroyed or has been deleted. This supports legitimate alternate exits instead of treating gate destruction as a prerequisite. The separate direct door-action execution check below prevents native AI from bypassing the locked phase.
|
||
|
||
Read-only installed evidence: `Prefabs/Structures/BuildingParts/Doors/Door_MetalGate_01_Short_LEFT_EXT_ST_B.et` has an enabled `SCR_DestructionMultiPhaseComponent`, with base health 1000. Installed `scripts/Game/Destruction/SCR_DestructionMultiPhaseComponent.c` defaults `m_bDeleteAfterFinalPhase` to true and calls `DeleteDestructibleDelayed` during destruction. `SCR_DestructionDamageManagerComponent.c` enables base destruction. No vanilla asset or original Escapists resource is changed.
|
||
|
||
`ERR_PrisonEscapeService` registers the one intact native door with the actual successor lock action inside the prepared, server-owned `Prison.et` composition when preparation transitions into PRISON. The registered location must be exactly the owning `ERR_SiteRuntime.Location`, with the authored prison definition and a root owned by `ERR_SiteService`; no nearby world entity can qualify. A maximum of 64 actual hierarchy entities may be visited. Zero/duplicate lock doors, an incomplete traversal, or a missing/destroyed damage-bearing gate refuse registration. An absent gate at initial registration does not grant escape. The normal unlock action remains unchanged. After this successful initial registration, the existing authoritative mode tick can complete prison, independently of later gate health/open state, when a canonical admitted, conscious, player-controlled, friendly body on foot moves from inside to outside the enclosure. This uses the existing phase transition, so ordinary cache discovery, objectives and companion listeners receive the same event as lock picking.
|
||
|
||
The authored walls occupy roughly ±6 m from the prison center. A body arms its observation only within ±5.5 m; it must then cross outside ±8 m. Both dimensions use X/Z; the actor must remain within -0.5 to +2.5 m of terrain height. Samples expire after 2.5 seconds, and three-dimensional displacement must fit 1.5 m plus 12 m/s over the sample interval. A changed account's body, controller, disconnected connection, vehicle occupant, unconscious/dead actor or distant jump loses continuity. Roster changes invalidate only the affected connection. Terminal service stop clears all samples. These are practical continuity checks, not an anti-cheat movement oracle: a matching short teleport is indistinguishable from walking. The bounds apply specifically to the current unrotated successor prison composition, not arbitrary terrains/prisons.
|
||
|
||
## Checks and native handoff
|
||
|
||
`tests/test_prison_escape_policy.py` compiles the actual production policy bodies with the existing policy harness. It checks initially intact/unique/owned registration, boundary directions independently of later gate health, unarmed/outside-only arrival, stale/reversed time, far jumps, height limits and all sixteen authored initial spawn candidates. It also executes the actual door permission and execution bodies against native API doubles: absent/locked mode and denied native permission cannot reach super; post-unlock execution forwards the original owner/user to vanilla exactly once. The full repository checks pass. This does not prove native compilation, networking or held player input.
|
||
|
||
Existing WORKBENCH helper: `ERR_NativePrisonBreachProbe(ERR_GameMode mode, IEntity canonicalPlayer)`. Invoke its `Tick(float now)` from the existing 500 ms disposable native fixture; results are 0 pending, -1 failure, 1 passed. Call `Stop()` when aborting. The opt-in `prison-breach` outcome already runs before the ordinary fixture's lock completion. Do not invoke the normal lock action for this branch. The fixture must already have the actual admitted player and fully prepared prison/sites, as other base outcomes do. Native player protection is the existing fixture's responsibility; this helper does not alter actor health or create a new body.
|
||
|
||
The revised helper first observes an actual inside sample and resolves the actual successor door action on the intact closed gate. It directly invokes that action with an original living guard and the real prisoner, waits 1.5 seconds, and requires native normalized door state to remain closed, not opening, with zero phase transitions. These explicit calls exercise the native AI execution boundary, not autonomous AI navigation or player input. It then applies native TRUE damage to the actual registered gate, waits for native destruction, and verifies destruction alone does not advance. It asynchronously repositions the real participant far away and outside-only to prove rejection, then inside and just beyond the destroyed gate to exercise the production transition. It verifies both ordinary scavenging site reveals and no repeated transition after later ticks. Native transform state is allowed at least 1.5 seconds to settle, with a 45-second overall deadline. The tested near crossing is deliberately repositioned, not actual walking. The gate is intentionally destroyed only in the disposable test round; world teardown/restart owns restoration, not this helper. Its only callback ownership is a removable phase listener, with no scheduled callbacks or synthetic progress.
|
||
|
||
[Archived 092923](evidence/native-2026-09-19-092923/report.json) completes the revised native fixture with 62 assertions and zero engine or script/VM errors at its recorded source. Held-action lock picking, actual walking through damaged gate geometry and remote-client replication still need separate runtime acceptance. A fixture pass must not be described as those checks.
|
||
|
||
## Registration failure and correction
|
||
|
||
[092026](evidence/native-2026-09-19-092026-prison-registration-failure/report.json) compiles the initial fallback and probe, but the probe fails its first actual-gate-registration assertion. It establishes no destroyed-gate progression pass. The initial lookup required raw `GetPrefabData().GetPrefabName()` equality and only direct children, whereas the previously successful ordinary lock fixture recursively finds the actual successor lock action in owned compositions.
|
||
|
||
Source inspection rules out the proposed separate-root explanation: `ERR_SiteService.Begin` spawns the definition's prison resource, attaches the site's `Location` from that same root, and retains it in its owned entities; `ERR_GameMode` installs the phase listener before preparation and enters PRISON only after preparation is ready. Installed `Door_Base.et` includes Hierarchy. The failed archive did not expose the cause. The later 092923 native diagnostics now identify it precisely: the owned prison root resolves normally, but the gate child’s raw `GetPrefabData().GetPrefabName()` is empty even though `SCR_ResourceNameUtils.GetPrefabName` resolves the exact successor `PrisonDoor.et` and the actual lock/door components exist. Raw child-prefab equality therefore rejected the correct gate.
|
||
|
||
The revised lookup uses the exact prepared site/ownership relation and the actual successor action attached to a native door, traversing only that composition within the fixed bound. It no longer depends on raw child-prefab naming. One-time diagnostics record the selected root's raw/resolved resource names and only gate/action candidates, then the registration result; they do not log account identities or perform an unbounded world scan. The opt-in probe also attempts a fresh isolated registration after native gate destruction and requires it to stay unregistered with `GateLost == false`. This helper has no mode subscriptions or tick and is stopped immediately. The real service's prior successfully registered composition remains the only authority for physical-exit observation; an isolated late registration cannot admit an outside actor.
|
||
|
||
The source policy regression now covers owned identity, zero/duplicate gates, exhausted scan bounds and initially destroyed gates. It passes. Revised native compilation and the raw-child-name cause are confirmed by archived 092923, alongside the complete scripted breach progression. This qualifies that fixture/source only, not subsequent source or ordinary/remote play. No gate health or original resource was altered.
|
||
|
||
Registration and lost-gate detection now explicitly look up the installed `SCR_DestructionMultiPhaseComponent`, require its initial default hit zone and non-destroyed/non-queued state, and use its `GetDestroyed`/`IsDestructionQueued` contract. The fixture calls the same component's inherited native `HandleDamage` with the actual gate/hit zone and records health, enabled state and total-destruction threshold. No destruction callback is synthesized and no replacement damage component is added. Exact installed inheritance is `SCR_DestructionMultiPhaseComponent` → `SCR_DestructionDamageManagerComponent` → `SCR_DamageManagerComponent` → `DamageManagerComponent`; therefore absence of a separately named DamageManager block alone does not prove the earlier generic lookup was null. This explicit lookup uses the intended destruction contract; the observed failure cause is the empty raw child resource name, not proven damage-manager inheritance failure.
|
||
|
||
[Archived raw log](evidence/native-2026-09-19-092923/console.log.txt), lines 485–488, identifies the owned root, empty raw gate name/exact resolved successor name, native health 1000 and successful registration. Lines 561–566 record damage handling enabled with threshold 30000, real `HandleDamage` followed by deletion (`remaining=NULL`, `lost=1`), and the isolated fresh registration refusing zero gate candidates. Destruction alone leaves the mission in PRISON. Subsequent assertions reject the distant jump and outside-only arrival, then accept the deliberately repositioned bounded inside-to-outside crossing, reveal both scavenging sites and observe no duplicate transition. The run completes 62 assertions with zero errors and matching launch/stopped [source manifests](evidence/native-2026-09-19-092923/source-sha256.json). This is actual native destruction plus scripted movement/mission progression; actual walking, held input and multiplayer remain separate acceptance.
|
||
|
||
|
||
## Direct AI execution and intact-gate exits
|
||
|
||
Installed `scripts/Game/AI/ScriptedNodes/Soldier/SCR_AIOpenDoor.c:31–44` calls the selected `SCR_DoorUserAction.PerformAction` directly without the UI `CanBePerformedScript` query. The successor gate inherits the real native door link, fence jump link and `OpenGate` smart action. The previous successor override only guarded the availability query, so inherited `SCR_DoorUserAction.PerformAction` could call `UseDoorAction` while the mission was still locked. The successor `ERR_PrisonDoorAction.PerformAction(IEntity pOwnerEntity, IEntity pUserEntity)` now rechecks its existing permission query before calling unchanged super. It does not add a server-only execution branch or change action broadcast/local-effect flags: vanilla `ScriptedUserAction` defaults remain non-local and broadcastable, and the same replicated phase check applies when the native action executes. The normal lock action still changes phase before its own `UseDoorAction`; post-unlock normal opening/closing retains vanilla behavior.
|
||
|
||
`ERR_PrisonEscapeRules.Crossing` no longer takes or requires gate loss. The service still requires its original successful intact-gate registration and every existing roster/body/controller/faction/phase/ground/displacement/time condition. Legitimate physical crossing is the success criterion; these safeguards do not claim to distinguish a sufficiently short teleport from ordinary movement. The independently authored static wall prefab contains no destructible component, so vanilla wall destruction was not assumed as evidence. The inherited gate's fence link does not by itself establish actual player vault clearance.
|
||
|
||
The source regressions and revised direct-action fixture are newer than archived 092923. That archive proves the earlier destruction/repositioning case only. Native execution of the new locked-action assertion, a genuine walking/vault exit with intact gate, ordinary post-unlock input and remote-client action replication remain pending until new evidence is archived. No new fixture outcome, world resource or original Escapists content was introduced.
|
||
|
||
The follow-up [100148 native run](evidence/native-2026-09-19-100148/report.json) passes 67 assertions with zero captured errors, including actual direct AI/player execution against the locked door before damage. The native gate stays closed and phase unchanged. It then repeats real destruction and bounded scripted-crossing checks. Intact-gate alternate-exit permission remains covered by the actual policy harness; this fixture does not demonstrate walking/vaulting through authored geometry.
|