diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md
index 5be9336e..309da372 100644
--- a/docs/architecture/retail-divergence-register.md
+++ b/docs/architecture/retail-divergence-register.md
@@ -130,7 +130,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
| AP-12 | Enchantment family-stacking tiebreak by largest SpellId; retail picks highest Generation, tie-broken by latest cast | `src/AcDream.Core/Spells/EnchantmentMath.cs:89` | `ActiveEnchantmentRecord` doesn't carry Generation; SpellId correlates with generation level in practice | Where spell ids don't track power within a family (or same-generation re-cast), the wrong buff wins — vital-max / stat values diverge from retail | `CEnchantmentRegistry::EnchantAttribute` 0x00594570 (pc:416110) |
| AP-13 | `ComputeDamage` is a simplified retail damage formula (no augmentations/ratings) — verified DEAD CODE as of 2026-06-04, M2 scaffolding | `src/AcDream.Core/Combat/CombatModel.cs:184` | Not on the critical path; stubbed from r02 §5 + ACE CombatManager for the future M2 predictive display | If wired into the M2 attack-bar estimate as-is, predicted numbers diverge whenever augs/ratings apply | r02 §5; ACE CombatManager |
| AP-14 | Encumbrance multiplier is a rough piecewise-linear stand-in (1.0→50%, ~0.7@100%, 0.1@300%) for retail's exact curve | `src/AcDream.Core/Items/ItemInstance.cs:187` | Hand-fit segments capture the curve's shape for scaffolding | Client-side burden-scaled effects (speed prediction) differ from retail at most burden ratios when loaded | r06 §6 (retail encumbered multiplier curve) |
-| AP-15 | WeenieError translation table covers only ~30 common codes (from ACE enum docs, not retail string_table.bin); unknown codes render raw hex | `src/AcDream.Core/Chat/WeenieErrorMessages.cs:26` | Untranslated codes are rare, fall back losslessly, 30-second add when reported | Server messages outside the table show as raw hex instead of the retail sentence | retail string_table.bin; ACE WeenieError*.cs |
+| AP-15 | WeenieError **sentence** table covers only ~30 common codes (from ACE enum docs, not retail string_table.bin); unknown codes render raw hex. The row's older *code-catalog* caveat is superseded: `WeenieError` carried a curated 16-member subset until 2026-07-29 and now holds the full 372-code table, so a code being unnamed is no longer a way for this to bite | `src/AcDream.Core/Chat/WeenieErrorMessages.cs:26` | Untranslated codes are rare, fall back losslessly, 30-second add when reported | Server messages outside the table show as raw hex instead of the retail sentence | retail string_table.bin; ACE WeenieError*.cs; `src/AcDream.Core/Physics/WeenieError.cs` |
| AP-16 | Point/spot lights selected per-object / per-cell as the **8 nearest reaching lights** (sphere-overlap, nearest-first) via `LightManager.SelectForObject`, capped at `MaxLightsPerObject=8`; called from `WbDrawDispatcher.ComputeEntityLightSet` (objects) and `EnvCellRenderer.GetCellLightSet` (cell shells). Retail's bake (`SetStaticLightingVertexColors`) sums ALL reaching static lights per vertex with no count cap. Retail's *hardware* path (`minimize_object_lighting` 0x0054d480) DOES cap at 8 per object, so the cap is faithful to retail's hardware path — not to its bake path. The `LightManager.Tick` UBO path survives for DIRECTIONAL (sun) lights only; `mesh_modern.vert`'s UBO loop skips point/spot entries (`posAndKind.w != 0 → continue`) — point lights reach the shader exclusively via the per-object SSBO (binding 5) | `src/AcDream.Core/Lighting/LightManager.cs:234` (`SelectForObject`); `MaxLightsPerObject` ~line 174; call sites `WbDrawDispatcher.ComputeEntityLightSet` + `EnvCellRenderer.GetCellLightSet` | Matches retail's hardware constraint (8 lights per object/cell); selection is nearest-sphere-overlap which faithfully allocates lights to the surfaces that actually see them | Surfaces reached by >8 point lights are dimmer than retail's uncapped bake — rare (a dungeon room has a handful of torches), but real; see AP-35 for the bake-vs-GPU-evaluate architecture difference | `minimize_object_lighting` 0x0054d480 (retail's 8-light hardware cap); `SetStaticLightingVertexColors` 0x0059cfe0 (retail's bake, no count cap) |
| ~~AP-18~~ | **RETIRED 2026-07-10 — faithful retail radar port.** Exact `RGBAColor_Radar*` floats were recovered from named static data and `gmRadarUI::GetBlipColor` was re-ported with `_blipColor` overrides plus portal/vendor/attackable-creature/admin/PK/PKLite/free-PK/fellowship precedence. The old implementation was not merely hue-tuned: it also had wrong portal/vendor colors and an incomplete dispatch matrix. | `src/AcDream.Core/Ui/RadarBlipColors.cs` + radar classification tests | — | — | `gmRadarUI::GetBlipColor` 0x004d76f0; static RGBA initializers at named decomp pc:1089736-1089804 |
| AP-19 | `PortalSideEpsilon` 0.01 (≈1 cm) instead of retail F_EPSILON ≈ 0.0002 — a documented render-root-lag tolerance, NOT a retail constant. DO-NOT-RETRY: T2 (BR-4) tried the retail value; CornerFloodReplay refuted it | `src/AcDream.App/Rendering/PortalVisibilityBuilder.cs:49` | Retail's tight epsilon only works with eye-exact swept curr_cell tracking; our viewer cell lags the eye by up to ~1 cm at pressed corners. Tighten after the #108-membership family + cdstW near-clip pin land | A 1 cm misclassification band at portal planes can flood or cull a portal the eye hasn't crossed — one-frame leaks / grey flashes at knife-edge doorway/corner positions | F_EPSILON @0x007c8c70; `PView::InitCell` 0x005a4b70 |
diff --git a/docs/research/2026-07-29-enum-verification-campaign.md b/docs/research/2026-07-29-enum-verification-campaign.md
index 25fa66a0..fdc0ecea 100644
--- a/docs/research/2026-07-29-enum-verification-campaign.md
+++ b/docs/research/2026-07-29-enum-verification-campaign.md
@@ -177,22 +177,20 @@ CLAUDE.md these are recorded rather than guessed.
arrived at 864 property members. **Blocker:** source documents absent. Either
they were never committed or they lived in a discarded worktree. Suggest
fixing the MEMORY.md index entries to point at this doc.
-2. **Five of six reference repos are empty** (§1). Everything sourced here came
- from retail + the UtilityBelt catalog + the weenie corpus. **Blocker:** repos
- not cloned in this environment. Re-cloning ACE and Chorizite would let the 456
- single-sourced property members (§3.1) be promoted to two-oracle confirmed.
+2. ~~**Five of six reference repos are empty**~~ **RESOLVED 2026-07-29.** All six
+ were re-cloned from their upstreams into the main checkout's `references/`
+ (still gitignored). See §7. The 456 single-sourced property members (§3.1) can
+ now be promoted to two-oracle confirmed by a follow-up pass; that pass has not
+ been run.
3. **456 of 864 property members are single-sourced.** They are transcribed from
the catalog, not invented, but no weenie in the corpus sets them so there is no
independent attestation. **Blocker:** needs ACE source, or the client DAT's own
`EnumMapper` file type (`acclientlib` has a reader for it — a genuinely retail
oracle, and the most promising unexplored lead).
-4. **`WeenieError` — 362 unadopted status codes.** acdream has 16 members; the
- catalog's `StatusMessage` has 372, with 10 shared and zero conflicts. Retail's
- `charError` (26) is a *different* enum — character-creation errors only — so
- retail cannot arbitrate. Register row **AP-15** already tracks that acdream's
- translation table covers only ~30 common codes. **Blocker:** needs a decision
- on importing 362 server-side codes wholesale, and ideally retail's
- `string_table.bin` for the real sentences. Deliberately not done here.
+4. ~~**`WeenieError` — 362 unadopted status codes.**~~ **RESOLVED 2026-07-29** by
+ user decision: all 362 adopted. See §8.1. What remains open is only the
+ *sentences* — retail's `string_table.bin` — which stays as register row
+ **AP-15**, now narrowed to the translation table alone.
5. **`CombatMode` has no located retail counterpart.** acdream's 7 members match
the catalog's `CombatMode` (`Magic = 8`). Retail's `CombatStyle` (25 members,
`Magic_CombatStyle = 512`) is a different enum — weapon-style, not combat mode.
@@ -258,3 +256,76 @@ absolute paths into `references/`). The method, in order:
| `f3e95a3e` | `DamageType` rotation + `ItemType` craft ladder and composites |
| `8ccaf72a` | `EquipMask` composites, `TransientState`, `PhysicsState`, `AttackHeight` |
| `3efa266a` | `AmmoType`, `CombatUse`, `ItemUseable` |
+
+---
+
+## 7. The reference tree, restored (2026-07-29)
+
+Open question 2 is closed. All six vendored repos were re-cloned from their
+upstreams into the main checkout at
+`C:\Users\erikn\source\repos\acdream\references\`, which every worktree shares.
+`references/*` is gitignored (only `WorldBuilder` is checked in as a gitlink), so
+none of this enters our history.
+
+Each candidate URL was cross-checked against the description in CLAUDE.md and
+`memory/reference_repos.md` before cloning, and the clone was verified to contain
+the files those descriptions name.
+
+| repo | upstream | commit | size | how the URL was confirmed |
+|---|---|---|---|---|
+| ACE | `https://github.com/ACEmulator/ACE.git` | `65f092dd` | 56 MB | `git remote -v` of the recovered copy at `repos/client/ACE`; the three enum files this slice reads are byte-identical between the two |
+| ACViewer | `https://github.com/ACEmulator/ACViewer.git` | `c5c54fb3` | 97 MB | cited by URL in `docs/research/2026-07-13-retail-projectile-vfx-pseudocode.md`; also the remote of `repos/ACViewer`. Cloned `--recurse-submodules`, so its `ACE.DatLoader` submodule (`382edb23`) is present as the docs describe |
+| Chorizite.ACProtocol | `https://github.com/Chorizite/Chorizite.ACProtocol` | `ff7dffd6` | 4.5 MB | Chorizite org + exact repo name; contains the `Chorizite.ACProtocol` + `.SourceGen` projects CLAUDE.md describes as XML-generated |
+| holtburger | `https://github.com/merklejerk/holtburger` | `a7e806cb` | 25 MB | named by URL in `docs/research/2026-05-10-holtburger-network-stack-study.md`; also the remote of `repos/holtburger` |
+| AC2D | `https://github.com/deregtd/AC2D` | `3451f0d2` | 17 MB | no URL was recorded anywhere in the repo, so the candidate was verified by content: it is C++, and it holds `cNetwork.cpp`, `cInterface.cpp`, `cPictureBox.h`/`cStaticText.h`/`cEditBox.h`, and `FSplitNESW` in `Landblocks.cpp` — every file the docs attribute to AC2D |
+| DatReaderWriter | `https://github.com/Chorizite/DatReaderWriter` | `c5359870` | 4.2 MB | cited by URL in `docs/research/2026-04-26-datreaderwriter-reference.md`. Not on the restore list, but it was empty for the same reason and is unambiguously identified |
+
+---
+
+## 8. Adoptions made on the restored oracle set
+
+### 8.1 `WeenieError` — the full 372-code table
+
+Open question 4, closed by user decision. acdream carried 16 members; it now
+carries 372 values under 378 names.
+
+The oracles agree completely. ACE's `WeenieError` has 369 members with no
+internal value collisions; the UtilityBelt catalog's `StatusMessage` has 372,
+shares all 369 ACE names, and disagrees on none of their values — it is a strict
+superset. Its three extra codes are `IsNowOpenFellowship` (0x050B),
+`IsNowClosedFellowship` (0x050C) and `LockedFellowshipCannotRecruit` (0x0518),
+and each is independently present in ACE's *separate* `WeenieErrorWithString`
+enum carrying a `_` where the interpolated name goes. So the catalog is simply
+the less-split view of what ACE keeps as two enums, and the three are adopted on
+two-oracle agreement rather than on the catalog alone.
+
+Retail cannot arbitrate any of this: `acclient.h` has no counterpart enum, and
+its `charError` (26 members) is character-creation only. That is recorded, not
+guessed around.
+
+Ten of acdream's 16 members matched an ACE name at an identical value. The other
+six sit at values ACE names differently, and acdream's names are the better ones
+— each is anchored to a retail decompilation site from the A10/R4 passes, where
+ACE's is a server-side coinage. Rather than choose, the six values carry both
+names, acdream's declared first:
+
+| value | acdream (decomp-anchored) | ACE / catalog |
+|---|---|---|
+| 0x0024 | `NotGrounded` | `YouCantJumpWhileInTheAir` |
+| 0x003F | `CrouchInCombatStance` | `CantCrouchInCombat` |
+| 0x0040 | `SitInCombatStance` | `CantSitInCombat` |
+| 0x0041 | `SleepInCombatStance` | `CantLieDownInCombat` |
+| 0x0042 | `ChatEmoteOutsideNonCombat` | `CantChatEmoteInCombat` |
+| 0x0045 | `ActionDepthExceeded` | `TooManyActions` |
+
+**Behaviour: unchanged.** Nothing in the tree branches on a `WeenieError`
+member. The two switches that mention the type switch on something else —
+`MotionInterpreter.cs:971` switches on a motion type and *returns* a
+`WeenieError`, and `WeenieErrorText.For` switches on a raw `uint`. The chat
+translation table `WeenieErrorMessages` is keyed on `uint` throughout, so
+naming a code does not make it render. The only site touched is
+`RemoteTeleportHook`, whose `(WeenieError)0x3Cu` cast could become the now-named
+`WeenieError.ITeleported` — same value, no behavioural edge.
+
+The enum also moved out of `MotionInterpreter.cs` into its own file at the same
+namespace; at 372 members it no longer belongs inside a physics class file.
diff --git a/src/AcDream.App/Physics/RemoteTeleportHook.cs b/src/AcDream.App/Physics/RemoteTeleportHook.cs
index a0b54769..9cccff6e 100644
--- a/src/AcDream.App/Physics/RemoteTeleportHook.cs
+++ b/src/AcDream.App/Physics/RemoteTeleportHook.cs
@@ -10,7 +10,9 @@ namespace AcDream.App.Physics;
///
public static class RemoteTeleportHook
{
- private const WeenieError TeleportCancelContext = (WeenieError)0x3Cu;
+ // 0x3C had no name until the 2026-07-29 WeenieError adoption; the cast it
+ // needed is gone, the value is unchanged.
+ private const WeenieError TeleportCancelContext = WeenieError.ITeleported;
public static bool Execute(
RemoteTeleportHookActions actions,
diff --git a/src/AcDream.Core/Physics/MotionInterpreter.cs b/src/AcDream.Core/Physics/MotionInterpreter.cs
index 51996a3d..21111451 100644
--- a/src/AcDream.Core/Physics/MotionInterpreter.cs
+++ b/src/AcDream.Core/Physics/MotionInterpreter.cs
@@ -157,142 +157,9 @@ public enum MovementType
TurnToHeading = 9,
}
-///
-/// WeenieError-shaped codes returned by CMotionInterp methods. Values are
-/// the hex constants used directly in the decompiled C code.
-///
-///
-/// R3-W1 renumber (closes J16-codes/A10). Prior to this slice, the
-/// names were shuffled relative to retail's actual numeric semantics
-/// (GeneralMovementFailure was assigned 0x24 and used at the
-/// airborne-jump gate, but retail's 0x24 means "not grounded / no contact"
-/// and retail's 0x47 is the general-movement-failure code; 0x48 meant
-/// "cannot jump while in the air" here, but retail's 0x48 means "jump
-/// blocked by the CURRENT MOTION OR POSITION" — a blocklist check
-/// (motion_allows_jump), not an airborne check). Renumbered per the
-/// definitive, exhaustively-swept table in
-/// docs/research/2026-07-02-r3-motioninterp/W0-pins.md §A10 (19
-/// return sites + 1 store site over raw 304908-306277 + 300150-300540).
-/// These codes are LOCAL-ONLY — never serialized to the wire — so the
-/// renumber is safe (verified: no consumer outside
-/// MotionInterpreter.cs pattern-matches on the numeric value).
-///
-///
-public enum WeenieError : uint
-{
- /// 0x00 — success.
- None = 0x00,
- ///
- /// 0x08 — no physics_obj. Sites (A10): StopCompletely @305214;
- /// DoInterpretedMotion @305579; StopInterpretedMotion @305639;
- /// StopMotion @305680; jump @305798; DoMotion @306165.
- ///
- NoPhysicsObject = 0x08,
- ///
- /// 0x0B — NoMotionInterpreter. R4-V1 addition (M12), per
- /// docs/research/2026-07-03-r4-moveto/r4-moveto-decomp.md §12 constants
- /// inventory (8, 0xb, 0x36, 0x37, 0x38, 0x3d, 0x47). ACE name;
- /// the retail sites that store this code were not individually
- /// extracted in the R4 pass (no MoveToManager consumer in this slice —
- /// V1 pins the numeric value only).
- ///
- NoMotionInterpreter = 0x0B,
- ///
- /// 0x24 — not grounded / no contact. Sites (A10):
- /// jump_is_allowed @305570 (gravity-active creature without
- /// Contact+OnWalkable; also the physics_obj == null case, which
- /// falls out to this same code per the A10 note — NOT 8);
- /// DoInterpretedMotion @305622-305623 (action-class motion
- /// blocked by contact_allows_move).
- ///
- NotGrounded = 0x24,
- ///
- /// 0x3f — Crouch (0x41000012) rejected while in combat stance. Site:
- /// DoMotion @306196.
- ///
- CrouchInCombatStance = 0x3f,
- ///
- /// 0x40 — Sitting (0x41000013) rejected while in combat stance. Site:
- /// DoMotion @306199.
- ///
- SitInCombatStance = 0x40,
- ///
- /// 0x41 — Sleeping (0x41000014) rejected while in combat stance. Site:
- /// DoMotion @306202.
- ///
- SleepInCombatStance = 0x41,
- ///
- /// 0x42 — motion & 0x2000000 (the chat-emote bit) rejected
- /// outside NonCombat (0x8000003d). Site: DoMotion @306205.
- ///
- ChatEmoteOutsideNonCombat = 0x42,
- ///
- /// 0x36 — ActionCancelled. R4-V1 addition (M12). Site:
- /// MoveToManager::PerformMovement (r4-moveto-decomp.md §3a
- /// @0052a901) — every new moveto cancels the previous one with this
- /// code before dispatching; also CPhysicsObj::interrupt_current_movement
- /// → MovementManager::CancelMoveTo(0x36) (§9e — the TS-36 cancel
- /// entry). Per §7c, MoveToManager::CancelMoveTo's WeenieError arg
- /// is NEVER READ in this build's body — kept for parity/logging only.
- ///
- ActionCancelled = 0x36,
- ///
- /// 0x37 — ObjectGone. R4-V1 addition (M12). Site:
- /// MoveToManager::HandleUpdateTarget (§6d @307866-307867) — a
- /// RETARGET delivery arrives with a non-OK target status (the target
- /// object was already being tracked, then went away).
- ///
- ObjectGone = 0x37,
- ///
- /// 0x38 — NoObject. R4-V1 addition (M12). Site:
- /// MoveToManager::HandleUpdateTarget (§6d @307857-307858) — the
- /// FIRST target callback arrives with a non-OK status (the target never
- /// resolved in the first place).
- ///
- NoObject = 0x38,
- ///
- /// 0x45 — action-queue depth cap: an action-class motion (bit
- /// 0x10000000) with GetNumActions() >= 6 pending. Site:
- /// DoMotion @306209.
- ///
- ActionDepthExceeded = 0x45,
- ///
- /// 0x47 — general movement failure. Sites (A10):
- /// jump_is_allowed @305525 (IsFullyConstrained);
- /// @305549+305556 (JumpStaminaCost refusal);
- /// CMotionInterp::PerformMovement @306227 (dispatch type-1 > 4);
- /// MovementManager::PerformMovement @300201 (dispatch type-1 > 8).
- ///
- GeneralMovementFailure = 0x47,
- ///
- /// 0x48 — jump BLOCKED by the current motion or position (A1: the
- /// motion_allows_jump literal-range blocklist — NOT an airborne
- /// check; airborne is 0x24). Sites (A10):
- /// motion_allows_jump @304930; jump_charge_is_allowed
- /// @304948 (Fallen or Crouch..Sleeping); charge_jump @305459
- /// (same predicate); STORED (not returned) as the queue node's
- /// jump_error_code in DoInterpretedMotion @305605 when
- /// disable_jump_during_link is set.
- ///
- YouCantJumpFromThisPosition = 0x48,
- ///
- /// 0x49 — the weenie's CanJump(jump_extent) virtual refused
- /// (e.g. stamina/burden gate). Sites: jump_charge_is_allowed
- /// @304941; charge_jump @305454.
- ///
- CantJumpLoadedDown = 0x49,
- ///
- /// 0x3D — YouChargedTooFar. R4-V1 addition (M12). Site:
- /// MoveToManager::HandleMoveToPosition Phase 2 arrival check
- /// (r4-moveto-decomp.md §6b) — the fail_distance progress gate
- /// exceeded (CheckProgressMade §5b failing for >1s AND the
- /// mover overshot fail_distance). NOTE: numerically out of A10's
- /// increasing order (0x3D < 0x3F/0x40/0x41/0x42/0x45) because it was
- /// not part of the CMotionInterp jump-family sweep this code sits
- /// beside — it belongs to the MoveToManager family instead (§7c, §12).
- ///
- YouChargedTooFar = 0x3D,
-}
+// The WeenieError catalog moved to its own file on 2026-07-29 when it grew
+// from the 16-member CMotionInterp/MoveToManager subset to retail's full
+// 372-code table: src/AcDream.Core/Physics/WeenieError.cs (same namespace).
// ── Motion state structs ───────────────────────────────────────────────────────
diff --git a/src/AcDream.Core/Physics/WeenieError.cs b/src/AcDream.Core/Physics/WeenieError.cs
new file mode 100644
index 00000000..3a28e30c
--- /dev/null
+++ b/src/AcDream.Core/Physics/WeenieError.cs
@@ -0,0 +1,563 @@
+//
+namespace AcDream.Core.Physics;
+
+///
+/// Retail's single client-side status-code enum, as carried on the wire by
+/// WeenieError (0x028A) and WeenieErrorWithString (0x028B), and
+/// returned locally by CMotionInterp / MoveToManager methods.
+///
+///
+/// 2026-07-29 full adoption. The catalog was a curated 16-member subset
+/// until this slice; it now carries all 372 codes. Oracles, in the order they
+/// decided: ACE ACE.Entity.Enum.WeenieError (369 members) and the
+/// UtilityBelt client-side catalog StatusMessage (372) — the two agree
+/// on every one of the 369 names ACE has, with zero value conflicts, and the
+/// catalog is a strict superset. The catalog's three extra codes (0x050B,
+/// 0x050C, 0x0518) are confirmed against ACE's separate
+/// WeenieErrorWithString enum, which holds them with a _
+/// placeholder marking the interpolated name. Retail acclient.h has no
+/// counterpart to arbitrate — its charError (26 members) is
+/// character-creation only — so the two C# oracles carry these alone.
+///
+///
+///
+/// Six values additionally keep an acdream-local name alongside the ACE one,
+/// because the local name is anchored to a retail decompilation site and the
+/// ACE name is not. Both names denote the same value; the local name is
+/// declared first so ToString() is unchanged by this adoption.
+///
+///
+///
+/// This enum is the code catalog only. The human-readable sentences live in
+/// AcDream.Core.Chat.WeenieErrorMessages, which is keyed on the raw
+/// uint and is unaffected by the members named here; register row
+/// AP-15 tracks that table's coverage separately.
+///
+///
+///
+/// The paragraph that follows is the original R3-W1 note, written when this
+/// enum held nothing but the CMotionInterp / MoveToManager
+/// family. Its "these codes" means that family, which is still local-only.
+/// The 362 codes adopted on 2026-07-29 are wire codes and are not covered by
+/// it.
+///
+///
+/// WeenieError-shaped codes returned by CMotionInterp methods. Values are
+/// the hex constants used directly in the decompiled C code.
+///
+///
+/// R3-W1 renumber (closes J16-codes/A10). Prior to this slice, the
+/// names were shuffled relative to retail's actual numeric semantics
+/// (GeneralMovementFailure was assigned 0x24 and used at the
+/// airborne-jump gate, but retail's 0x24 means "not grounded / no contact"
+/// and retail's 0x47 is the general-movement-failure code; 0x48 meant
+/// "cannot jump while in the air" here, but retail's 0x48 means "jump
+/// blocked by the CURRENT MOTION OR POSITION" — a blocklist check
+/// (motion_allows_jump), not an airborne check). Renumbered per the
+/// definitive, exhaustively-swept table in
+/// docs/research/2026-07-02-r3-motioninterp/W0-pins.md §A10 (19
+/// return sites + 1 store site over raw 304908-306277 + 300150-300540).
+/// These codes are LOCAL-ONLY — never serialized to the wire — so the
+/// renumber is safe (verified: no consumer outside
+/// MotionInterpreter.cs pattern-matches on the numeric value).
+///
+///
+public enum WeenieError : uint
+{
+ /// 0x00 — success.
+ None = 0x0000,
+ NoMem = 0x0001,
+ BadParam = 0x0002,
+ DivZero = 0x0003,
+ SegV = 0x0004,
+ Unimplemented = 0x0005,
+ UnknownMessageType = 0x0006,
+ NoAnimationTable = 0x0007,
+
+ ///
+ /// 0x08 — no physics_obj. Sites (A10): StopCompletely @305214;
+ /// DoInterpretedMotion @305579; StopInterpretedMotion @305639;
+ /// StopMotion @305680; jump @305798; DoMotion @306165.
+ ///
+ NoPhysicsObject = 0x0008,
+ NoBookieObject = 0x0009,
+ NoWslObject = 0x000A,
+
+ ///
+ /// 0x0B — NoMotionInterpreter. R4-V1 addition (M12), per
+ /// docs/research/2026-07-03-r4-moveto/r4-moveto-decomp.md §12 constants
+ /// inventory (8, 0xb, 0x36, 0x37, 0x38, 0x3d, 0x47). ACE name;
+ /// the retail sites that store this code were not individually
+ /// extracted in the R4 pass (no MoveToManager consumer in this slice —
+ /// V1 pins the numeric value only).
+ ///
+ NoMotionInterpreter = 0x000B,
+ UnhandledSwitch = 0x000C,
+ DefaultConstructorCalled = 0x000D,
+ InvalidCombatManeuver = 0x000E,
+ BadCast = 0x000F,
+ MissingQuality = 0x0010,
+ MissingDatabaseObject = 0x0012,
+ NoCallbackSet = 0x0013,
+ CorruptQuality = 0x0014,
+ BadContext = 0x0015,
+ NoEphseqManager = 0x0016,
+ BadMovementEvent = 0x0017,
+ CannotCreateNewObject = 0x0018,
+ NoControllerObject = 0x0019,
+ CannotSendEvent = 0x001A,
+ PhysicsCantTransition = 0x001B,
+ PhysicsMaxDistanceExceeded = 0x001C,
+ YoureTooBusy = 0x001D,
+ CannotSendMessage = 0x001F,
+ IllegalInventoryTransaction = 0x0020,
+ ExternalWeenieObject = 0x0021,
+ InternalWeenieObject = 0x0022,
+ MotionFailure = 0x0023,
+
+ ///
+ /// 0x24 — not grounded / no contact. Sites (A10):
+ /// jump_is_allowed @305570 (gravity-active creature without
+ /// Contact+OnWalkable; also the physics_obj == null case, which
+ /// falls out to this same code per the A10 note — NOT 8);
+ /// DoInterpretedMotion @305622-305623 (action-class motion
+ /// blocked by contact_allows_move).
+ ///
+ NotGrounded = 0x0024,
+ /// ACE/catalog name for 0x0024; alias of .
+ YouCantJumpWhileInTheAir = 0x0024,
+ InqCylSphereFailure = 0x0025,
+ ThatIsNotAValidCommand = 0x0026,
+ CarryingItem = 0x0027,
+ Frozen = 0x0028,
+ Stuck = 0x0029,
+ YouAreTooEncumbered = 0x002A,
+ BadContain = 0x002C,
+ BadParent = 0x002D,
+ BadDrop = 0x002E,
+ BadRelease = 0x002F,
+ MsgBadMsg = 0x0030,
+ MsgUnpackFailed = 0x0031,
+ MsgNoMsg = 0x0032,
+ MsgUnderflow = 0x0033,
+ MsgOverflow = 0x0034,
+ MsgCallbackFailed = 0x0035,
+
+ ///
+ /// 0x36 — ActionCancelled. R4-V1 addition (M12). Site:
+ /// MoveToManager::PerformMovement (r4-moveto-decomp.md §3a
+ /// @0052a901) — every new moveto cancels the previous one with this
+ /// code before dispatching; also CPhysicsObj::interrupt_current_movement
+ /// → MovementManager::CancelMoveTo(0x36) (§9e — the TS-36 cancel
+ /// entry). Per §7c, MoveToManager::CancelMoveTo's WeenieError arg
+ /// is NEVER READ in this build's body — kept for parity/logging only.
+ ///
+ ActionCancelled = 0x0036,
+
+ ///
+ /// 0x37 — ObjectGone. R4-V1 addition (M12). Site:
+ /// MoveToManager::HandleUpdateTarget (§6d @307866-307867) — a
+ /// RETARGET delivery arrives with a non-OK target status (the target
+ /// object was already being tracked, then went away).
+ ///
+ ObjectGone = 0x0037,
+
+ ///
+ /// 0x38 — NoObject. R4-V1 addition (M12). Site:
+ /// MoveToManager::HandleUpdateTarget (§6d @307857-307858) — the
+ /// FIRST target callback arrives with a non-OK status (the target never
+ /// resolved in the first place).
+ ///
+ NoObject = 0x0038,
+ CantGetThere = 0x0039,
+ Dead = 0x003A,
+ ILeftTheWorld = 0x003B,
+ ITeleported = 0x003C,
+
+ ///
+ /// 0x3D — YouChargedTooFar. R4-V1 addition (M12). Site:
+ /// MoveToManager::HandleMoveToPosition Phase 2 arrival check
+ /// (r4-moveto-decomp.md §6b) — the fail_distance progress gate
+ /// exceeded (CheckProgressMade §5b failing for >1s AND the
+ /// mover overshot fail_distance). NOTE: numerically out of A10's
+ /// increasing order (0x3D < 0x3F/0x40/0x41/0x42/0x45) because it was
+ /// not part of the CMotionInterp jump-family sweep this code sits
+ /// beside — it belongs to the MoveToManager family instead (§7c, §12).
+ ///
+ YouChargedTooFar = 0x003D,
+ YouAreTooTiredToDoThat = 0x003E,
+
+ ///
+ /// 0x3f — Crouch (0x41000012) rejected while in combat stance. Site:
+ /// DoMotion @306196.
+ ///
+ CrouchInCombatStance = 0x003F,
+ /// ACE/catalog name for 0x003F; alias of .
+ CantCrouchInCombat = 0x003F,
+
+ ///
+ /// 0x40 — Sitting (0x41000013) rejected while in combat stance. Site:
+ /// DoMotion @306199.
+ ///
+ SitInCombatStance = 0x0040,
+ /// ACE/catalog name for 0x0040; alias of .
+ CantSitInCombat = 0x0040,
+
+ ///
+ /// 0x41 — Sleeping (0x41000014) rejected while in combat stance. Site:
+ /// DoMotion @306202.
+ ///
+ SleepInCombatStance = 0x0041,
+ /// ACE/catalog name for 0x0041; alias of .
+ CantLieDownInCombat = 0x0041,
+
+ ///
+ /// 0x42 — motion & 0x2000000 (the chat-emote bit) rejected
+ /// outside NonCombat (0x8000003d). Site: DoMotion @306205.
+ ///
+ ChatEmoteOutsideNonCombat = 0x0042,
+ /// ACE/catalog name for 0x0042; alias of .
+ CantChatEmoteInCombat = 0x0042,
+ NoMtableData = 0x0043,
+ CantChatEmoteNotStanding = 0x0044,
+
+ ///
+ /// 0x45 — action-queue depth cap: an action-class motion (bit
+ /// 0x10000000) with GetNumActions() >= 6 pending. Site:
+ /// DoMotion @306209.
+ ///
+ ActionDepthExceeded = 0x0045,
+ /// ACE/catalog name for 0x0045; alias of .
+ TooManyActions = 0x0045,
+ Hidden = 0x0046,
+
+ ///
+ /// 0x47 — general movement failure. Sites (A10):
+ /// jump_is_allowed @305525 (IsFullyConstrained);
+ /// @305549+305556 (JumpStaminaCost refusal);
+ /// CMotionInterp::PerformMovement @306227 (dispatch type-1 > 4);
+ /// MovementManager::PerformMovement @300201 (dispatch type-1 > 8).
+ ///
+ GeneralMovementFailure = 0x0047,
+
+ ///
+ /// 0x48 — jump BLOCKED by the current motion or position (A1: the
+ /// motion_allows_jump literal-range blocklist — NOT an airborne
+ /// check; airborne is 0x24). Sites (A10):
+ /// motion_allows_jump @304930; jump_charge_is_allowed
+ /// @304948 (Fallen or Crouch..Sleeping); charge_jump @305459
+ /// (same predicate); STORED (not returned) as the queue node's
+ /// jump_error_code in DoInterpretedMotion @305605 when
+ /// disable_jump_during_link is set.
+ ///
+ YouCantJumpFromThisPosition = 0x0048,
+
+ ///
+ /// 0x49 — the weenie's CanJump(jump_extent) virtual refused
+ /// (e.g. stamina/burden gate). Sites: jump_charge_is_allowed
+ /// @304941; charge_jump @305454.
+ ///
+ CantJumpLoadedDown = 0x0049,
+ YouKilledYourself = 0x004A,
+ MsgResponseFailure = 0x004B,
+ ObjectIsStatic = 0x004C,
+ InvalidPkStatus = 0x004D,
+ InvalidXpAmount = 0x03E9,
+ InvalidPpCalculation = 0x03EA,
+ InvalidCpCalculation = 0x03EB,
+ UnhandledStatAnswer = 0x03EC,
+ HeartAttack = 0x03ED,
+ TheContainerIsClosed = 0x03EE,
+ InvalidInventoryLocation = 0x03F0,
+ ChangeCombatModeFailure = 0x03F1,
+ FullInventoryLocation = 0x03F2,
+ ConflictingInventoryLocation = 0x03F3,
+ ItemNotPending = 0x03F4,
+ BeWieldedFailure = 0x03F5,
+ BeDroppedFailure = 0x03F6,
+ YouAreTooFatiguedToAttack = 0x03F7,
+ YouAreOutOfAmmunition = 0x03F8,
+ YourAttackMisfired = 0x03F9,
+ YouveAttemptedAnImpossibleSpellPath = 0x03FA,
+ MagicIncompleteAnimList = 0x03FB,
+ MagicInvalidSpellType = 0x03FC,
+ MagicInqPositionAndVelocityFailure = 0x03FD,
+ YouDontKnowThatSpell = 0x03FE,
+ IncorrectTargetType = 0x03FF,
+ YouDontHaveAllTheComponents = 0x0400,
+ YouDontHaveEnoughManaToCast = 0x0401,
+ YourSpellFizzled = 0x0402,
+ YourSpellTargetIsMissing = 0x0403,
+ YourProjectileSpellMislaunched = 0x0404,
+ MagicSpellbookAddSpellFailure = 0x0405,
+ MagicTargetOutOfRange = 0x0406,
+ YourSpellCannotBeCastOutside = 0x0407,
+ YourSpellCannotBeCastInside = 0x0408,
+ MagicGeneralFailure = 0x0409,
+ YouAreUnpreparedToCastASpell = 0x040A,
+ YouveAlreadySwornAllegiance = 0x040B,
+ CantSwearAllegianceInsufficientXp = 0x040C,
+ AllegianceIgnoringRequests = 0x040D,
+ AllegianceSquelched = 0x040E,
+ AllegianceMaxDistanceExceeded = 0x040F,
+ AllegianceIllegalLevel = 0x0410,
+ AllegianceBadCreation = 0x0411,
+ AllegiancePatronBusy = 0x0412,
+ YouAreNotInAllegiance = 0x0414,
+ AllegianceRemoveHierarchyFailure = 0x0415,
+ FellowshipIgnoringRequests = 0x0417,
+ FellowshipSquelched = 0x0418,
+ FellowshipMaxDistanceExceeded = 0x0419,
+ FellowshipMember = 0x041A,
+ FellowshipIllegalLevel = 0x041B,
+ FellowshipRecruitBusy = 0x041C,
+ YouMustBeLeaderOfFellowship = 0x041D,
+ YourFellowshipIsFull = 0x041E,
+ FellowshipNameIsNotPermitted = 0x041F,
+ LevelTooLow = 0x0420,
+ LevelTooHigh = 0x0421,
+ ThatChannelDoesntExist = 0x0422,
+ YouCantUseThatChannel = 0x0423,
+ YouAreAlreadyOnThatChannel = 0x0424,
+ YouAreNotOnThatChannel = 0x0425,
+ AttunedItem = 0x0426,
+ YouCannotMergeDifferentStacks = 0x0427,
+ YouCannotMergeEnchantedItems = 0x0428,
+ YouMustControlAtLeastOneStack = 0x0429,
+ CurrentlyAttacking = 0x042A,
+ MissileAttackNotOk = 0x042B,
+ TargetNotAcquired = 0x042C,
+ ImpossibleShot = 0x042D,
+ BadWeaponSkill = 0x042E,
+ UnwieldFailure = 0x042F,
+ LaunchFailure = 0x0430,
+ ReloadFailure = 0x0431,
+ UnableToMakeCraftReq = 0x0432,
+ CraftAnimationFailed = 0x0433,
+ YouCantCraftWithThatNumberOfItems = 0x0434,
+ CraftGeneralErrorUiMsg = 0x0435,
+ CraftGeneralErrorNoUiMsg = 0x0436,
+ YouDoNotPassCraftingRequirements = 0x0437,
+ YouDoNotHaveAllTheNecessaryItems = 0x0438,
+ NotAllTheItemsAreAvailable = 0x0439,
+ YouMustBeInPeaceModeToTrade = 0x043A,
+ YouAreNotTrainedInThatTradeSkill = 0x043B,
+ YourHandsMustBeFree = 0x043C,
+ YouCannotLinkToThatPortal = 0x043D,
+ YouHaveSolvedThisQuestTooRecently = 0x043E,
+ YouHaveSolvedThisQuestTooManyTimes = 0x043F,
+ QuestUnknown = 0x0440,
+ QuestTableCorrupt = 0x0441,
+ QuestBad = 0x0442,
+ QuestDuplicate = 0x0443,
+ QuestUnsolved = 0x0444,
+ ItemRequiresQuestToBePickedUp = 0x0445,
+ QuestSolvedTooLongAgo = 0x0446,
+ TradeIgnoringRequests = 0x044C,
+ TradeSquelched = 0x044D,
+ TradeMaxDistanceExceeded = 0x044E,
+ TradeAlreadyTrading = 0x044F,
+ TradeBusy = 0x0450,
+ TradeClosed = 0x0451,
+ TradeExpired = 0x0452,
+ TradeItemBeingTraded = 0x0453,
+ TradeNonEmptyContainer = 0x0454,
+ TradeNonCombatMode = 0x0455,
+ TradeIncomplete = 0x0456,
+ TradeStampMismatch = 0x0457,
+ TradeUnopened = 0x0458,
+ TradeEmpty = 0x0459,
+ TradeAlreadyAccepted = 0x045A,
+ TradeOutOfSync = 0x045B,
+ PKsMayNotUsePortal = 0x045C,
+ NonPKsMayNotUsePortal = 0x045D,
+ HouseAbandoned = 0x045E,
+ HouseEvicted = 0x045F,
+ HouseAlreadyOwned = 0x0460,
+ HouseBuyFailed = 0x0461,
+ HouseRentFailed = 0x0462,
+ Hooked = 0x0463,
+ MagicInvalidPosition = 0x0465,
+ YouMustHaveDarkMajestyToUsePortal = 0x0466,
+ InvalidAmmoType = 0x0467,
+ SkillTooLow = 0x0468,
+ YouHaveUsedAllTheHooks = 0x0469,
+ TradeAiDoesntWant = 0x046A,
+ HookHouseNotOwned = 0x046B,
+ YouMustCompleteQuestToUsePortal = 0x0474,
+ HouseNoAllegiance = 0x047E,
+ YouMustOwnHouseToUseCommand = 0x047F,
+ YourMonarchDoesNotOwnAMansionOrVilla = 0x0480,
+ YourMonarchsHouseIsNotAMansionOrVilla = 0x0481,
+ YourMonarchHasClosedTheMansion = 0x0482,
+ YouMustBeMonarchToPurchaseDwelling = 0x048A,
+ AllegianceTimeout = 0x048D,
+ YourOfferOfAllegianceWasIgnored = 0x048E,
+ ConfirmationInProgress = 0x048F,
+ YouMustBeAMonarchToUseCommand = 0x0490,
+ YouMustSpecifyCharacterToBoot = 0x0491,
+ YouCantBootYourself = 0x0492,
+ ThatCharacterDoesNotExist = 0x0493,
+ ThatPersonIsNotInYourAllegiance = 0x0494,
+ CantBreakFromPatronNotInAllegiance = 0x0495,
+ YourAllegianceHasBeenDissolved = 0x0496,
+ YourPatronsAllegianceHasBeenBroken = 0x0497,
+ YouHaveMovedTooFar = 0x0498,
+ TeleToInvalidPosition = 0x0499,
+ MustHaveDarkMajestyToUse = 0x049A,
+ YouFailToLinkWithLifestone = 0x049B,
+ YouWanderedTooFarToLinkWithLifestone = 0x049C,
+ YouSuccessfullyLinkWithLifestone = 0x049D,
+ YouMustLinkToLifestoneToRecall = 0x049E,
+ YouFailToRecallToLifestone = 0x049F,
+ YouFailToLinkWithPortal = 0x04A0,
+ YouSuccessfullyLinkWithPortal = 0x04A1,
+ YouFailToRecallToPortal = 0x04A2,
+ YouMustLinkToPortalToRecall = 0x04A3,
+ YouFailToSummonPortal = 0x04A4,
+ YouMustLinkToPortalToSummonIt = 0x04A5,
+ YouFailToTeleport = 0x04A6,
+ YouHaveBeenTeleportedTooRecently = 0x04A7,
+ YouMustBeAnAdvocateToUsePortal = 0x04A8,
+ PortalAisNotAllowed = 0x04A9,
+ PlayersMayNotUsePortal = 0x04AA,
+ YouAreNotPowerfulEnoughToUsePortal = 0x04AB,
+ YouAreTooPowerfulToUsePortal = 0x04AC,
+ YouCannotRecallPortal = 0x04AD,
+ YouCannotSummonPortal = 0x04AE,
+ LockAlreadyUnlocked = 0x04AF,
+ YouCannotLockOrUnlockThat = 0x04B0,
+ YouCannotLockWhatIsOpen = 0x04B1,
+ KeyDoesntFitThisLock = 0x04B2,
+ LockUsedTooRecently = 0x04B3,
+ YouArentTrainedInLockpicking = 0x04B4,
+ AllegianceInfoEmptyName = 0x04B5,
+ AllegianceInfoSelf = 0x04B6,
+ AllegianceInfoTooRecent = 0x04B7,
+ AbuseNoSuchCharacter = 0x04B8,
+ AbuseReportedSelf = 0x04B9,
+ AbuseComplaintHandled = 0x04BA,
+ YouDoNotOwnThatSalvageTool = 0x04BD,
+ YouDoNotOwnThatItem = 0x04BE,
+ MaterialCannotBeCreated = 0x04C1,
+ ItemsAttemptingToSalvageIsInvalid = 0x04C2,
+ YouCannotSalvageItemsInTrading = 0x04C3,
+ YouMustBeHouseGuestToUsePortal = 0x04C4,
+ YourAllegianceRankIsTooLowToUseMagic = 0x04C5,
+ YourArcaneLoreIsTooLowToUseMagic = 0x04C7,
+ ItemDoesntHaveEnoughMana = 0x04C8,
+ YouHaveBeenInPKBattleTooRecently = 0x04CC,
+ TradeAiRefuseEmote = 0x04CD,
+ YouFailToAlterSkill = 0x04D0,
+ FellowshipDeclined = 0x04DB,
+ FellowshipTimeout = 0x04DC,
+ YouHaveFailedToAlterAttributes = 0x04DD,
+ CannotTransferAttributesWhileWieldingItem = 0x04E0,
+ YouHaveSucceededTransferringAttributes = 0x04E1,
+ HookIsDuplicated = 0x04E2,
+ ItemIsWrongTypeForHook = 0x04E3,
+ HousingChestIsDuplicated = 0x04E4,
+ HookWillBeDeleted = 0x04E5,
+ HousingChestWillBeDeleted = 0x04E6,
+ CannotSwearAllegianceWhileOwningMansion = 0x04E7,
+ YouCantDoThatWhileInTheAir = 0x04EB,
+ CannotChangePKStatusWhileRecovering = 0x04EC,
+ AdvocatesCannotChangePKStatus = 0x04ED,
+ LevelTooLowToChangePKStatusWithObject = 0x04EE,
+ LevelTooHighToChangePKStatusWithObject = 0x04EF,
+ YouFeelAHarshDissonance = 0x04F0,
+ YouArePKAgain = 0x04F1,
+ YouAreTemporarilyNoLongerPK = 0x04F2,
+ PKLiteMayNotUsePortal = 0x04F3,
+ YouArentTrainedInHealing = 0x04FC,
+ YouDontOwnThatHealingKit = 0x04FD,
+ YouCantHealThat = 0x04FE,
+ YouArentReadyToHeal = 0x0500,
+ YouCanOnlyHealPlayers = 0x0501,
+ LifestoneMagicProtectsYou = 0x0502,
+ PortalEnergyProtectsYou = 0x0503,
+ YouAreNonPKAgain = 0x0504,
+ YoureTooCloseToYourSanctuary = 0x0505,
+ CantDoThatTradeInProgress = 0x0506,
+ OnlyNonPKsMayEnterPKLite = 0x0507,
+ YouAreNowPKLite = 0x0508,
+ IsNowOpenFellowship = 0x050B,
+ IsNowClosedFellowship = 0x050C,
+ YouDoNotBelongToAFellowship = 0x050F,
+ UsingMaxHooksSilent = 0x0511,
+ YouAreNowUsingMaxHooks = 0x0512,
+ YouAreNoLongerUsingMaxHooks = 0x0513,
+ YouAreNotPermittedToUseThatHook = 0x0516,
+ LockedFellowshipCannotRecruit = 0x0518,
+ LockedFellowshipCannotRecruitYou = 0x0519,
+ ActivationNotAllowedNotOwner = 0x051A,
+ TurbineChatIsEnabled = 0x051D,
+ YouCannotAddPeopleToHearList = 0x0520,
+ YouAreNowDeafTo_Screams = 0x0523,
+ YouCanHearAllPlayersOnceAgain = 0x0524,
+ YouChickenOut = 0x0526,
+ YouCanPossiblySucceed = 0x0527,
+ FellowshipIsLocked = 0x0528,
+ TradeComplete = 0x0529,
+ NotASalvageTool = 0x052A,
+ CharacterNotAvailable = 0x052B,
+ YouMustWaitToPurchaseHouse = 0x0532,
+ YouDoNotHaveAuthorityInAllegiance = 0x0535,
+ YouHaveMaxAccountsBanned = 0x0540,
+ YouHaveMaxAllegianceOfficers = 0x0545,
+ YourAllegianceOfficersHaveBeenCleared = 0x0546,
+ YouCannotJoinChannelsWhileGagged = 0x0548,
+ YouAreNoLongerAllegianceOfficer = 0x054A,
+ YourAllegianceDoesNotHaveHometown = 0x054C,
+ HookItemNotUsable_CannotOpen = 0x054E,
+ HookItemNotUsable_CanOpen = 0x054F,
+ MissileOutOfRange = 0x0550,
+ MustPurchaseThroneOfDestinyToUseFunction = 0x0552,
+ MustPurchaseThroneOfDestinyToUseItem = 0x0553,
+ MustPurchaseThroneOfDestinyToUsePortal = 0x0554,
+ MustPurchaseThroneOfDestinyToAccessQuest = 0x0555,
+ YouFailedToCompleteAugmentation = 0x0556,
+ AugmentationUsedTooManyTimes = 0x0557,
+ AugmentationTypeUsedTooManyTimes = 0x0558,
+ AugmentationNotEnoughExperience = 0x0559,
+ ExitTrainingAcademyToUseCommand = 0x055D,
+ OnlyPKsMayUseCommand = 0x055F,
+ OnlyPKLiteMayUseCommand = 0x0560,
+ MaxFriendsExceeded = 0x0561,
+ ThatCharacterNotOnYourFriendsList = 0x0563,
+ OnlyHouseOwnerCanUseCommand = 0x0564,
+ InvalidAllegianceNameCantBeEmpty = 0x0565,
+ InvalidAllegianceNameTooLong = 0x0566,
+ InvalidAllegianceNameBadCharacters = 0x0567,
+ InvalidAllegianceNameInappropriate = 0x0568,
+ InvalidAllegianceNameAlreadyInUse = 0x0569,
+ AllegianceNameCleared = 0x056B,
+ InvalidAllegianceNameSameName = 0x056C,
+ InvalidOfficerLevel = 0x056F,
+ AllegianceOfficerTitleIsNotAppropriate = 0x0570,
+ AllegianceNameIsTooLong = 0x0571,
+ AllegianceOfficerTitlesCleared = 0x0572,
+ AllegianceTitleHasIllegalChars = 0x0573,
+ YouHaveNotPreApprovedVassals = 0x0579,
+ YouHaveClearedPreApprovedVassal = 0x057C,
+ CharIsAlreadyGagged = 0x057D,
+ CharIsNotCurrentlyGagged = 0x057E,
+ YourAllegianceChatPrivilegesRestored = 0x0581,
+ TooManyUniqueItems = 0x0584,
+ HeritageRequiresSpecificArmor = 0x0585,
+ ArmorRequiresSpecificHeritage = 0x0586,
+ OlthoiCannotInteractWithThat = 0x0587,
+ OlthoiCannotUseLifestones = 0x0588,
+ OlthoiVendorLooksInHorror = 0x0589,
+ OlthoiCannotJoinFellowship = 0x058B,
+ OlthoiCannotJoinAllegiance = 0x058C,
+ YouCannotUseThatItem = 0x058D,
+ ThisPersonWillNotInteractWithYou = 0x058E,
+ OnlyOlthoiMayUsePortal = 0x058F,
+ OlthoiMayNotUsePortal = 0x0590,
+ YouMayNotUsePortalWithVitae = 0x0591,
+ YouMustBeTwoWeeksOldToUsePortal = 0x0592,
+ OlthoiCanOnlyRecallToLifestone = 0x0593,
+ ContractError = 0x0594,
+}
diff --git a/tests/AcDream.Core.Tests/Properties/WeenieErrorConformanceTests.cs b/tests/AcDream.Core.Tests/Properties/WeenieErrorConformanceTests.cs
new file mode 100644
index 00000000..281cc440
--- /dev/null
+++ b/tests/AcDream.Core.Tests/Properties/WeenieErrorConformanceTests.cs
@@ -0,0 +1,497 @@
+//
+// Golden conformance table for WeenieError. Regenerated by the 2026-07-29
+// full-adoption slice; see docs/research/2026-07-29-enum-verification-campaign.md.
+//
+using System;
+using System.Linq;
+using AcDream.Core.Physics;
+using Xunit;
+
+namespace AcDream.Core.Tests.Properties;
+
+///
+/// Pins every member of to its wire value. The table is
+/// the verbatim cross-check of ACE ACE.Entity.Enum.WeenieError (369 members)
+/// against the UtilityBelt client-side catalog StatusMessage (372) — the two
+/// agree on every shared name with zero value conflicts, and the catalog is a strict
+/// superset whose three extra codes are confirmed against ACE's separate
+/// WeenieErrorWithString. Retail acclient.h has no counterpart enum, so
+/// it cannot arbitrate; that is recorded rather than guessed around.
+///
+/// Six values carry two names: the acdream-local name anchored to a retail
+/// decompilation site, plus the ACE/catalog name. Both are pinned.
+///
+public sealed class WeenieErrorConformanceTests
+{
+ private static readonly (string Name, uint Value)[] Golden =
+ [
+ ("None", 0x0000),
+ ("NoMem", 0x0001),
+ ("BadParam", 0x0002),
+ ("DivZero", 0x0003),
+ ("SegV", 0x0004),
+ ("Unimplemented", 0x0005),
+ ("UnknownMessageType", 0x0006),
+ ("NoAnimationTable", 0x0007),
+ ("NoPhysicsObject", 0x0008),
+ ("NoBookieObject", 0x0009),
+ ("NoWslObject", 0x000A),
+ ("NoMotionInterpreter", 0x000B),
+ ("UnhandledSwitch", 0x000C),
+ ("DefaultConstructorCalled", 0x000D),
+ ("InvalidCombatManeuver", 0x000E),
+ ("BadCast", 0x000F),
+ ("MissingQuality", 0x0010),
+ ("MissingDatabaseObject", 0x0012),
+ ("NoCallbackSet", 0x0013),
+ ("CorruptQuality", 0x0014),
+ ("BadContext", 0x0015),
+ ("NoEphseqManager", 0x0016),
+ ("BadMovementEvent", 0x0017),
+ ("CannotCreateNewObject", 0x0018),
+ ("NoControllerObject", 0x0019),
+ ("CannotSendEvent", 0x001A),
+ ("PhysicsCantTransition", 0x001B),
+ ("PhysicsMaxDistanceExceeded", 0x001C),
+ ("YoureTooBusy", 0x001D),
+ ("CannotSendMessage", 0x001F),
+ ("IllegalInventoryTransaction", 0x0020),
+ ("ExternalWeenieObject", 0x0021),
+ ("InternalWeenieObject", 0x0022),
+ ("MotionFailure", 0x0023),
+ ("NotGrounded", 0x0024),
+ ("YouCantJumpWhileInTheAir", 0x0024),
+ ("InqCylSphereFailure", 0x0025),
+ ("ThatIsNotAValidCommand", 0x0026),
+ ("CarryingItem", 0x0027),
+ ("Frozen", 0x0028),
+ ("Stuck", 0x0029),
+ ("YouAreTooEncumbered", 0x002A),
+ ("BadContain", 0x002C),
+ ("BadParent", 0x002D),
+ ("BadDrop", 0x002E),
+ ("BadRelease", 0x002F),
+ ("MsgBadMsg", 0x0030),
+ ("MsgUnpackFailed", 0x0031),
+ ("MsgNoMsg", 0x0032),
+ ("MsgUnderflow", 0x0033),
+ ("MsgOverflow", 0x0034),
+ ("MsgCallbackFailed", 0x0035),
+ ("ActionCancelled", 0x0036),
+ ("ObjectGone", 0x0037),
+ ("NoObject", 0x0038),
+ ("CantGetThere", 0x0039),
+ ("Dead", 0x003A),
+ ("ILeftTheWorld", 0x003B),
+ ("ITeleported", 0x003C),
+ ("YouChargedTooFar", 0x003D),
+ ("YouAreTooTiredToDoThat", 0x003E),
+ ("CrouchInCombatStance", 0x003F),
+ ("CantCrouchInCombat", 0x003F),
+ ("SitInCombatStance", 0x0040),
+ ("CantSitInCombat", 0x0040),
+ ("SleepInCombatStance", 0x0041),
+ ("CantLieDownInCombat", 0x0041),
+ ("ChatEmoteOutsideNonCombat", 0x0042),
+ ("CantChatEmoteInCombat", 0x0042),
+ ("NoMtableData", 0x0043),
+ ("CantChatEmoteNotStanding", 0x0044),
+ ("ActionDepthExceeded", 0x0045),
+ ("TooManyActions", 0x0045),
+ ("Hidden", 0x0046),
+ ("GeneralMovementFailure", 0x0047),
+ ("YouCantJumpFromThisPosition", 0x0048),
+ ("CantJumpLoadedDown", 0x0049),
+ ("YouKilledYourself", 0x004A),
+ ("MsgResponseFailure", 0x004B),
+ ("ObjectIsStatic", 0x004C),
+ ("InvalidPkStatus", 0x004D),
+ ("InvalidXpAmount", 0x03E9),
+ ("InvalidPpCalculation", 0x03EA),
+ ("InvalidCpCalculation", 0x03EB),
+ ("UnhandledStatAnswer", 0x03EC),
+ ("HeartAttack", 0x03ED),
+ ("TheContainerIsClosed", 0x03EE),
+ ("InvalidInventoryLocation", 0x03F0),
+ ("ChangeCombatModeFailure", 0x03F1),
+ ("FullInventoryLocation", 0x03F2),
+ ("ConflictingInventoryLocation", 0x03F3),
+ ("ItemNotPending", 0x03F4),
+ ("BeWieldedFailure", 0x03F5),
+ ("BeDroppedFailure", 0x03F6),
+ ("YouAreTooFatiguedToAttack", 0x03F7),
+ ("YouAreOutOfAmmunition", 0x03F8),
+ ("YourAttackMisfired", 0x03F9),
+ ("YouveAttemptedAnImpossibleSpellPath", 0x03FA),
+ ("MagicIncompleteAnimList", 0x03FB),
+ ("MagicInvalidSpellType", 0x03FC),
+ ("MagicInqPositionAndVelocityFailure", 0x03FD),
+ ("YouDontKnowThatSpell", 0x03FE),
+ ("IncorrectTargetType", 0x03FF),
+ ("YouDontHaveAllTheComponents", 0x0400),
+ ("YouDontHaveEnoughManaToCast", 0x0401),
+ ("YourSpellFizzled", 0x0402),
+ ("YourSpellTargetIsMissing", 0x0403),
+ ("YourProjectileSpellMislaunched", 0x0404),
+ ("MagicSpellbookAddSpellFailure", 0x0405),
+ ("MagicTargetOutOfRange", 0x0406),
+ ("YourSpellCannotBeCastOutside", 0x0407),
+ ("YourSpellCannotBeCastInside", 0x0408),
+ ("MagicGeneralFailure", 0x0409),
+ ("YouAreUnpreparedToCastASpell", 0x040A),
+ ("YouveAlreadySwornAllegiance", 0x040B),
+ ("CantSwearAllegianceInsufficientXp", 0x040C),
+ ("AllegianceIgnoringRequests", 0x040D),
+ ("AllegianceSquelched", 0x040E),
+ ("AllegianceMaxDistanceExceeded", 0x040F),
+ ("AllegianceIllegalLevel", 0x0410),
+ ("AllegianceBadCreation", 0x0411),
+ ("AllegiancePatronBusy", 0x0412),
+ ("YouAreNotInAllegiance", 0x0414),
+ ("AllegianceRemoveHierarchyFailure", 0x0415),
+ ("FellowshipIgnoringRequests", 0x0417),
+ ("FellowshipSquelched", 0x0418),
+ ("FellowshipMaxDistanceExceeded", 0x0419),
+ ("FellowshipMember", 0x041A),
+ ("FellowshipIllegalLevel", 0x041B),
+ ("FellowshipRecruitBusy", 0x041C),
+ ("YouMustBeLeaderOfFellowship", 0x041D),
+ ("YourFellowshipIsFull", 0x041E),
+ ("FellowshipNameIsNotPermitted", 0x041F),
+ ("LevelTooLow", 0x0420),
+ ("LevelTooHigh", 0x0421),
+ ("ThatChannelDoesntExist", 0x0422),
+ ("YouCantUseThatChannel", 0x0423),
+ ("YouAreAlreadyOnThatChannel", 0x0424),
+ ("YouAreNotOnThatChannel", 0x0425),
+ ("AttunedItem", 0x0426),
+ ("YouCannotMergeDifferentStacks", 0x0427),
+ ("YouCannotMergeEnchantedItems", 0x0428),
+ ("YouMustControlAtLeastOneStack", 0x0429),
+ ("CurrentlyAttacking", 0x042A),
+ ("MissileAttackNotOk", 0x042B),
+ ("TargetNotAcquired", 0x042C),
+ ("ImpossibleShot", 0x042D),
+ ("BadWeaponSkill", 0x042E),
+ ("UnwieldFailure", 0x042F),
+ ("LaunchFailure", 0x0430),
+ ("ReloadFailure", 0x0431),
+ ("UnableToMakeCraftReq", 0x0432),
+ ("CraftAnimationFailed", 0x0433),
+ ("YouCantCraftWithThatNumberOfItems", 0x0434),
+ ("CraftGeneralErrorUiMsg", 0x0435),
+ ("CraftGeneralErrorNoUiMsg", 0x0436),
+ ("YouDoNotPassCraftingRequirements", 0x0437),
+ ("YouDoNotHaveAllTheNecessaryItems", 0x0438),
+ ("NotAllTheItemsAreAvailable", 0x0439),
+ ("YouMustBeInPeaceModeToTrade", 0x043A),
+ ("YouAreNotTrainedInThatTradeSkill", 0x043B),
+ ("YourHandsMustBeFree", 0x043C),
+ ("YouCannotLinkToThatPortal", 0x043D),
+ ("YouHaveSolvedThisQuestTooRecently", 0x043E),
+ ("YouHaveSolvedThisQuestTooManyTimes", 0x043F),
+ ("QuestUnknown", 0x0440),
+ ("QuestTableCorrupt", 0x0441),
+ ("QuestBad", 0x0442),
+ ("QuestDuplicate", 0x0443),
+ ("QuestUnsolved", 0x0444),
+ ("ItemRequiresQuestToBePickedUp", 0x0445),
+ ("QuestSolvedTooLongAgo", 0x0446),
+ ("TradeIgnoringRequests", 0x044C),
+ ("TradeSquelched", 0x044D),
+ ("TradeMaxDistanceExceeded", 0x044E),
+ ("TradeAlreadyTrading", 0x044F),
+ ("TradeBusy", 0x0450),
+ ("TradeClosed", 0x0451),
+ ("TradeExpired", 0x0452),
+ ("TradeItemBeingTraded", 0x0453),
+ ("TradeNonEmptyContainer", 0x0454),
+ ("TradeNonCombatMode", 0x0455),
+ ("TradeIncomplete", 0x0456),
+ ("TradeStampMismatch", 0x0457),
+ ("TradeUnopened", 0x0458),
+ ("TradeEmpty", 0x0459),
+ ("TradeAlreadyAccepted", 0x045A),
+ ("TradeOutOfSync", 0x045B),
+ ("PKsMayNotUsePortal", 0x045C),
+ ("NonPKsMayNotUsePortal", 0x045D),
+ ("HouseAbandoned", 0x045E),
+ ("HouseEvicted", 0x045F),
+ ("HouseAlreadyOwned", 0x0460),
+ ("HouseBuyFailed", 0x0461),
+ ("HouseRentFailed", 0x0462),
+ ("Hooked", 0x0463),
+ ("MagicInvalidPosition", 0x0465),
+ ("YouMustHaveDarkMajestyToUsePortal", 0x0466),
+ ("InvalidAmmoType", 0x0467),
+ ("SkillTooLow", 0x0468),
+ ("YouHaveUsedAllTheHooks", 0x0469),
+ ("TradeAiDoesntWant", 0x046A),
+ ("HookHouseNotOwned", 0x046B),
+ ("YouMustCompleteQuestToUsePortal", 0x0474),
+ ("HouseNoAllegiance", 0x047E),
+ ("YouMustOwnHouseToUseCommand", 0x047F),
+ ("YourMonarchDoesNotOwnAMansionOrVilla", 0x0480),
+ ("YourMonarchsHouseIsNotAMansionOrVilla", 0x0481),
+ ("YourMonarchHasClosedTheMansion", 0x0482),
+ ("YouMustBeMonarchToPurchaseDwelling", 0x048A),
+ ("AllegianceTimeout", 0x048D),
+ ("YourOfferOfAllegianceWasIgnored", 0x048E),
+ ("ConfirmationInProgress", 0x048F),
+ ("YouMustBeAMonarchToUseCommand", 0x0490),
+ ("YouMustSpecifyCharacterToBoot", 0x0491),
+ ("YouCantBootYourself", 0x0492),
+ ("ThatCharacterDoesNotExist", 0x0493),
+ ("ThatPersonIsNotInYourAllegiance", 0x0494),
+ ("CantBreakFromPatronNotInAllegiance", 0x0495),
+ ("YourAllegianceHasBeenDissolved", 0x0496),
+ ("YourPatronsAllegianceHasBeenBroken", 0x0497),
+ ("YouHaveMovedTooFar", 0x0498),
+ ("TeleToInvalidPosition", 0x0499),
+ ("MustHaveDarkMajestyToUse", 0x049A),
+ ("YouFailToLinkWithLifestone", 0x049B),
+ ("YouWanderedTooFarToLinkWithLifestone", 0x049C),
+ ("YouSuccessfullyLinkWithLifestone", 0x049D),
+ ("YouMustLinkToLifestoneToRecall", 0x049E),
+ ("YouFailToRecallToLifestone", 0x049F),
+ ("YouFailToLinkWithPortal", 0x04A0),
+ ("YouSuccessfullyLinkWithPortal", 0x04A1),
+ ("YouFailToRecallToPortal", 0x04A2),
+ ("YouMustLinkToPortalToRecall", 0x04A3),
+ ("YouFailToSummonPortal", 0x04A4),
+ ("YouMustLinkToPortalToSummonIt", 0x04A5),
+ ("YouFailToTeleport", 0x04A6),
+ ("YouHaveBeenTeleportedTooRecently", 0x04A7),
+ ("YouMustBeAnAdvocateToUsePortal", 0x04A8),
+ ("PortalAisNotAllowed", 0x04A9),
+ ("PlayersMayNotUsePortal", 0x04AA),
+ ("YouAreNotPowerfulEnoughToUsePortal", 0x04AB),
+ ("YouAreTooPowerfulToUsePortal", 0x04AC),
+ ("YouCannotRecallPortal", 0x04AD),
+ ("YouCannotSummonPortal", 0x04AE),
+ ("LockAlreadyUnlocked", 0x04AF),
+ ("YouCannotLockOrUnlockThat", 0x04B0),
+ ("YouCannotLockWhatIsOpen", 0x04B1),
+ ("KeyDoesntFitThisLock", 0x04B2),
+ ("LockUsedTooRecently", 0x04B3),
+ ("YouArentTrainedInLockpicking", 0x04B4),
+ ("AllegianceInfoEmptyName", 0x04B5),
+ ("AllegianceInfoSelf", 0x04B6),
+ ("AllegianceInfoTooRecent", 0x04B7),
+ ("AbuseNoSuchCharacter", 0x04B8),
+ ("AbuseReportedSelf", 0x04B9),
+ ("AbuseComplaintHandled", 0x04BA),
+ ("YouDoNotOwnThatSalvageTool", 0x04BD),
+ ("YouDoNotOwnThatItem", 0x04BE),
+ ("MaterialCannotBeCreated", 0x04C1),
+ ("ItemsAttemptingToSalvageIsInvalid", 0x04C2),
+ ("YouCannotSalvageItemsInTrading", 0x04C3),
+ ("YouMustBeHouseGuestToUsePortal", 0x04C4),
+ ("YourAllegianceRankIsTooLowToUseMagic", 0x04C5),
+ ("YourArcaneLoreIsTooLowToUseMagic", 0x04C7),
+ ("ItemDoesntHaveEnoughMana", 0x04C8),
+ ("YouHaveBeenInPKBattleTooRecently", 0x04CC),
+ ("TradeAiRefuseEmote", 0x04CD),
+ ("YouFailToAlterSkill", 0x04D0),
+ ("FellowshipDeclined", 0x04DB),
+ ("FellowshipTimeout", 0x04DC),
+ ("YouHaveFailedToAlterAttributes", 0x04DD),
+ ("CannotTransferAttributesWhileWieldingItem", 0x04E0),
+ ("YouHaveSucceededTransferringAttributes", 0x04E1),
+ ("HookIsDuplicated", 0x04E2),
+ ("ItemIsWrongTypeForHook", 0x04E3),
+ ("HousingChestIsDuplicated", 0x04E4),
+ ("HookWillBeDeleted", 0x04E5),
+ ("HousingChestWillBeDeleted", 0x04E6),
+ ("CannotSwearAllegianceWhileOwningMansion", 0x04E7),
+ ("YouCantDoThatWhileInTheAir", 0x04EB),
+ ("CannotChangePKStatusWhileRecovering", 0x04EC),
+ ("AdvocatesCannotChangePKStatus", 0x04ED),
+ ("LevelTooLowToChangePKStatusWithObject", 0x04EE),
+ ("LevelTooHighToChangePKStatusWithObject", 0x04EF),
+ ("YouFeelAHarshDissonance", 0x04F0),
+ ("YouArePKAgain", 0x04F1),
+ ("YouAreTemporarilyNoLongerPK", 0x04F2),
+ ("PKLiteMayNotUsePortal", 0x04F3),
+ ("YouArentTrainedInHealing", 0x04FC),
+ ("YouDontOwnThatHealingKit", 0x04FD),
+ ("YouCantHealThat", 0x04FE),
+ ("YouArentReadyToHeal", 0x0500),
+ ("YouCanOnlyHealPlayers", 0x0501),
+ ("LifestoneMagicProtectsYou", 0x0502),
+ ("PortalEnergyProtectsYou", 0x0503),
+ ("YouAreNonPKAgain", 0x0504),
+ ("YoureTooCloseToYourSanctuary", 0x0505),
+ ("CantDoThatTradeInProgress", 0x0506),
+ ("OnlyNonPKsMayEnterPKLite", 0x0507),
+ ("YouAreNowPKLite", 0x0508),
+ ("IsNowOpenFellowship", 0x050B),
+ ("IsNowClosedFellowship", 0x050C),
+ ("YouDoNotBelongToAFellowship", 0x050F),
+ ("UsingMaxHooksSilent", 0x0511),
+ ("YouAreNowUsingMaxHooks", 0x0512),
+ ("YouAreNoLongerUsingMaxHooks", 0x0513),
+ ("YouAreNotPermittedToUseThatHook", 0x0516),
+ ("LockedFellowshipCannotRecruit", 0x0518),
+ ("LockedFellowshipCannotRecruitYou", 0x0519),
+ ("ActivationNotAllowedNotOwner", 0x051A),
+ ("TurbineChatIsEnabled", 0x051D),
+ ("YouCannotAddPeopleToHearList", 0x0520),
+ ("YouAreNowDeafTo_Screams", 0x0523),
+ ("YouCanHearAllPlayersOnceAgain", 0x0524),
+ ("YouChickenOut", 0x0526),
+ ("YouCanPossiblySucceed", 0x0527),
+ ("FellowshipIsLocked", 0x0528),
+ ("TradeComplete", 0x0529),
+ ("NotASalvageTool", 0x052A),
+ ("CharacterNotAvailable", 0x052B),
+ ("YouMustWaitToPurchaseHouse", 0x0532),
+ ("YouDoNotHaveAuthorityInAllegiance", 0x0535),
+ ("YouHaveMaxAccountsBanned", 0x0540),
+ ("YouHaveMaxAllegianceOfficers", 0x0545),
+ ("YourAllegianceOfficersHaveBeenCleared", 0x0546),
+ ("YouCannotJoinChannelsWhileGagged", 0x0548),
+ ("YouAreNoLongerAllegianceOfficer", 0x054A),
+ ("YourAllegianceDoesNotHaveHometown", 0x054C),
+ ("HookItemNotUsable_CannotOpen", 0x054E),
+ ("HookItemNotUsable_CanOpen", 0x054F),
+ ("MissileOutOfRange", 0x0550),
+ ("MustPurchaseThroneOfDestinyToUseFunction", 0x0552),
+ ("MustPurchaseThroneOfDestinyToUseItem", 0x0553),
+ ("MustPurchaseThroneOfDestinyToUsePortal", 0x0554),
+ ("MustPurchaseThroneOfDestinyToAccessQuest", 0x0555),
+ ("YouFailedToCompleteAugmentation", 0x0556),
+ ("AugmentationUsedTooManyTimes", 0x0557),
+ ("AugmentationTypeUsedTooManyTimes", 0x0558),
+ ("AugmentationNotEnoughExperience", 0x0559),
+ ("ExitTrainingAcademyToUseCommand", 0x055D),
+ ("OnlyPKsMayUseCommand", 0x055F),
+ ("OnlyPKLiteMayUseCommand", 0x0560),
+ ("MaxFriendsExceeded", 0x0561),
+ ("ThatCharacterNotOnYourFriendsList", 0x0563),
+ ("OnlyHouseOwnerCanUseCommand", 0x0564),
+ ("InvalidAllegianceNameCantBeEmpty", 0x0565),
+ ("InvalidAllegianceNameTooLong", 0x0566),
+ ("InvalidAllegianceNameBadCharacters", 0x0567),
+ ("InvalidAllegianceNameInappropriate", 0x0568),
+ ("InvalidAllegianceNameAlreadyInUse", 0x0569),
+ ("AllegianceNameCleared", 0x056B),
+ ("InvalidAllegianceNameSameName", 0x056C),
+ ("InvalidOfficerLevel", 0x056F),
+ ("AllegianceOfficerTitleIsNotAppropriate", 0x0570),
+ ("AllegianceNameIsTooLong", 0x0571),
+ ("AllegianceOfficerTitlesCleared", 0x0572),
+ ("AllegianceTitleHasIllegalChars", 0x0573),
+ ("YouHaveNotPreApprovedVassals", 0x0579),
+ ("YouHaveClearedPreApprovedVassal", 0x057C),
+ ("CharIsAlreadyGagged", 0x057D),
+ ("CharIsNotCurrentlyGagged", 0x057E),
+ ("YourAllegianceChatPrivilegesRestored", 0x0581),
+ ("TooManyUniqueItems", 0x0584),
+ ("HeritageRequiresSpecificArmor", 0x0585),
+ ("ArmorRequiresSpecificHeritage", 0x0586),
+ ("OlthoiCannotInteractWithThat", 0x0587),
+ ("OlthoiCannotUseLifestones", 0x0588),
+ ("OlthoiVendorLooksInHorror", 0x0589),
+ ("OlthoiCannotJoinFellowship", 0x058B),
+ ("OlthoiCannotJoinAllegiance", 0x058C),
+ ("YouCannotUseThatItem", 0x058D),
+ ("ThisPersonWillNotInteractWithYou", 0x058E),
+ ("OnlyOlthoiMayUsePortal", 0x058F),
+ ("OlthoiMayNotUsePortal", 0x0590),
+ ("YouMayNotUsePortalWithVitae", 0x0591),
+ ("YouMustBeTwoWeeksOldToUsePortal", 0x0592),
+ ("OlthoiCanOnlyRecallToLifestone", 0x0593),
+ ("ContractError", 0x0594),
+ ];
+
+ /// The 16 members that predate the 2026-07-29 full adoption. Their
+ /// values are load-bearing for the CMotionInterp / MoveToManager port and must
+ /// not move when the catalog grows.
+ private static readonly (string Name, uint Value)[] PreAdoption =
+ [
+ ("None", 0x0000),
+ ("NoPhysicsObject", 0x0008),
+ ("NoMotionInterpreter", 0x000B),
+ ("NotGrounded", 0x0024),
+ ("ActionCancelled", 0x0036),
+ ("ObjectGone", 0x0037),
+ ("NoObject", 0x0038),
+ ("YouChargedTooFar", 0x003D),
+ ("CrouchInCombatStance", 0x003F),
+ ("SitInCombatStance", 0x0040),
+ ("SleepInCombatStance", 0x0041),
+ ("ChatEmoteOutsideNonCombat", 0x0042),
+ ("ActionDepthExceeded", 0x0045),
+ ("GeneralMovementFailure", 0x0047),
+ ("YouCantJumpFromThisPosition", 0x0048),
+ ("CantJumpLoadedDown", 0x0049),
+ ];
+
+ [Fact]
+ public void EveryGoldenMemberHasItsExactValue()
+ {
+ foreach (var (name, value) in Golden)
+ {
+ Assert.True(Enum.IsDefined(typeof(WeenieError), name),
+ $"WeenieError.{name} is missing");
+ Assert.Equal(value, (uint)Enum.Parse(name));
+ }
+ }
+
+ [Fact]
+ public void DeclaresNothingOutsideTheGoldenTable()
+ {
+ var expected = Golden.Select(g => g.Name).OrderBy(n => n, StringComparer.Ordinal);
+ var actual = Enum.GetNames().OrderBy(n => n, StringComparer.Ordinal);
+ Assert.Equal(expected, actual);
+ }
+
+ [Fact]
+ public void CatalogHoldsTheFullRetailCodeSpace()
+ {
+ Assert.Equal(378, Enum.GetNames().Length);
+ Assert.Equal(372, Enum.GetValues().Cast().Distinct().Count());
+ }
+
+ [Fact]
+ public void PreAdoptionMembersKeepTheirValues()
+ {
+ foreach (var (name, value) in PreAdoption)
+ Assert.Equal(value, (uint)Enum.Parse(name));
+ }
+
+ /// The six values that carry both an acdream-local and an ACE name.
+ /// Nothing else may be aliased — a duplicate value elsewhere means the adoption
+ /// picked up a name collision.
+ [Fact]
+ public void OnlyTheSixDecompAnchoredValuesAreAliased()
+ {
+ (uint Value, string Local, string Ace)[] expected =
+ [
+ (0x0024, "NotGrounded", "YouCantJumpWhileInTheAir"),
+ (0x003F, "CrouchInCombatStance", "CantCrouchInCombat"),
+ (0x0040, "SitInCombatStance", "CantSitInCombat"),
+ (0x0041, "SleepInCombatStance", "CantLieDownInCombat"),
+ (0x0042, "ChatEmoteOutsideNonCombat", "CantChatEmoteInCombat"),
+ (0x0045, "ActionDepthExceeded", "TooManyActions"),
+ ];
+
+ var actual = Enum.GetValues()
+ .Cast()
+ .Distinct()
+ .Where(v => Golden.Count(g => g.Value == v) > 1)
+ .OrderBy(v => v)
+ .ToArray();
+ Assert.Equal(expected.Select(e => e.Value).OrderBy(v => v), actual);
+
+ foreach (var (value, local, ace) in expected)
+ {
+ Assert.Equal(value, (uint)Enum.Parse(local));
+ Assert.Equal(value, (uint)Enum.Parse(ace));
+ // Enum.GetName is documented to return an arbitrary one of the
+ // names sharing a value, so only membership is pinned here.
+ Assert.Contains(Enum.GetName(typeof(WeenieError), value), new[] { local, ace });
+ }
+ }
+}