feat(physics): D4+W1 — entry-restrictions register row + named PvP/missile dispatch terms

Part A (D4): CObjCell::check_entry_restrictions (pc:309576) gate is omitted from
FindEnvCollisions. Investigation confirmed the gate requires restriction_obj (per-cell
access-lock entity id) + weenie-object-table dispatch (CanMoveInto / CanBypassMoveRestrictions)
— neither exist in acdream. CellPhysics has no restriction_obj field; DatReaderWriter
models no per-cell access locks. Gap is inert in all dev content (ACE starter area has
no access-locked env cells). Documented as AP-50 in the retail divergence register.

Part B (W1): Replace the hardcoded-false smell in BspOnlyDispatch with named internal
helpers PvpExempt() and MissileIgnore() that return false with retail oracle citations
(pc:276808-276841 and pc:274385 respectively). BspOnlyDispatch now folds all three
terms in retail's exact predicate structure. Behavior is byte-identical in M1.5 scope
(both stubs false ⇒ reduces to HAS_PHYSICS_BSP_PS check alone, same as before).
A6.P7 door dispatch unchanged.

Tests: 3 new guard tests in A6P7DispatchRulesTests — W1_PvpExempt_ReturnsFalseInM15Scope,
W1_MissileIgnore_ReturnsFalseInM15Scope, W1_BspOnlyDispatch_DoorStateStillDispatchesBspOnly.
Suite: 1590 pass / 0 fail / 2 skip (was 1587 + 3 = 1590).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-24 19:45:39 +02:00
parent a9f8775110
commit 6c7e3ef1ab
3 changed files with 110 additions and 16 deletions

View file

@ -75,4 +75,48 @@ public class A6P7DispatchRulesTests
{
Assert.Equal(expected, Transition.BspOnlyDispatch(entityState));
}
// -----------------------------------------------------------------------
// W1 (2026-06-24) — named PvP/missile terms are false in M1.5
// -----------------------------------------------------------------------
/// <summary>
/// W1: <see cref="Transition.PvpExempt"/> must return false in M1.5.
/// When PK ships (M2+) this stub will accept mover + target state;
/// the test pins the current named-false value as a guard.
/// Retail oracle: <c>CPhysicsObj::FindObjCollisions</c> pc:276808276841.
/// </summary>
[Fact]
public void W1_PvpExempt_ReturnsFalseInM15Scope()
{
// PvP hasn't shipped (M1.5); the stub always returns false.
Assert.False(Transition.PvpExempt());
}
/// <summary>
/// W1: <see cref="Transition.MissileIgnore"/> must return false in M1.5.
/// When missiles ship (F.3) this stub will accept mover OBJECTINFO +
/// target state; the test pins the current named-false value as a guard.
/// Retail oracle: <c>OBJECTINFO::missile_ignore</c> pc:274385;
/// dispatch use pc:276858276861.
/// </summary>
[Fact]
public void W1_MissileIgnore_ReturnsFalseInM15Scope()
{
// Missiles haven't shipped (M1.5); the stub always returns false.
Assert.False(Transition.MissileIgnore());
}
/// <summary>
/// Guard: with both W1 terms false, a BSP-flagged entity still
/// dispatches BSP-only — A6.P7 door behavior is unchanged after W1.
/// This protects the A6.P7 cottage-door / dungeon-wall fix.
/// </summary>
[Fact]
public void W1_BspOnlyDispatch_DoorStateStillDispatchesBspOnly()
{
// Cottage door state from A6.P7 investigation: 0x10008 = STATIC | REPORT | HAS_BSP
const uint doorState = 0x00010008u;
Assert.True(Transition.BspOnlyDispatch(doorState));
}
}