merge(core): the enum verification campaign, onto the post-deletion tree
Brings `github/overnight/enums` (`c19680fd`) forward onto the V11 tree. The branch was cut at `b70b9832`, before the OpenGL deletion, and the two lines of work turned out to be disjoint: the enum campaign lives entirely in `AcDream.Core` and its tests, while V11 emptied `AcDream.App`. The merge is clean — no conflicting file on either side. What it carries: names for AC's seven property tables verified against two oracles, a correction to `DamageType`'s rotated bits and `ItemType`'s shifted craft ladder, the retail members the equipment and physics enums were missing, and names for `AmmoType`, `CombatUse` and `ItemUseable`. Five commits, seventeen files, +3,767 / -27 lines. Verified on the merge result rather than on the branch: Release build 0 errors, no new warning attributable to any file the branch touches, and `AcDream.Core.Tests` at 3,893 passed / 2 skipped / 3,895. The campaign's claimed +597 is exact — the `Properties` namespace alone runs 597 tests, all passing. The campaign's open decision items — whether to adopt `WeenieError` wholesale, whether the `SoundId` subset is the right cut, and the re-clone of the ACE and Chorizite references that `references/` no longer holds — are not settled here. They are carried into the morning report as questions for the user. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
commit
cd2f3feae2
17 changed files with 3767 additions and 27 deletions
|
|
@ -17,7 +17,7 @@ once Campaign V closes.
|
|||
|
||||
| Item | Existing foundation | Shape of the work |
|
||||
|---|---|---|
|
||||
| **VERIFY all enums** | `docs/research/2026-06-04-property-enum-divergence.md`: 929 values missing across 7 `Property*` enums, plus the `acdream-property-enum-sweep` skill for regeneration. | Extend the sweep beyond Property* to every wire-adjacent enum family; adopt values with ACE as oracle; conformance tests per enum. |
|
||||
| **VERIFY all enums** — **DONE 2026-07-29**, see [`docs/research/2026-07-29-enum-verification-campaign.md`](../research/2026-07-29-enum-verification-campaign.md) | The 2026-06-04 divergence note turned out to be missing from the repo entirely (open question 1 in the successor), so the sweep was regenerated from scratch. | Seven `Property*` tables created (864 members); `DamageType` and `ItemType` value bugs fixed; equipment/physics/item families closed against retail. Twelve open questions remain, headed by five empty `references/` repos. |
|
||||
| **Verify networking stack** | `docs/research/2026-06-04-wire-message-catalog.md`: 256 opcodes catalogued, 114 parsed. | Audit parsed-vs-stub-vs-missing against the catalog; prioritize by what live servers actually send (Coldeve session logs are now a real corpus); holtburger remains the client-side oracle. |
|
||||
| **Physics review — retail landing bounce et al.** | The named-retail decomp workflow; carried debt #116 (slide response), TS-50/51/53 in the divergence register. | A focused fidelity pass: start with the user's observed case — long-jump landing should bounce/recoil as retail does (grep `named-retail` for the landing/impact path in `MovementManager`/`SetMotion` land states before writing anything). Each fix follows grep→decompile→pseudocode→port→conformance. |
|
||||
|
||||
|
|
|
|||
260
docs/research/2026-07-29-enum-verification-campaign.md
Normal file
260
docs/research/2026-07-29-enum-verification-campaign.md
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# Enum verification campaign — 2026-07-29
|
||||
|
||||
Bucket B item 1 of [`docs/plans/2026-07-29-post-vulkan-work-intake.md`](../plans/2026-07-29-post-vulkan-work-intake.md).
|
||||
Successor to the (missing — see Open question 1) 2026-06-04 property-enum
|
||||
divergence note. Run unattended against base `b70b9832`.
|
||||
|
||||
**Headline: the campaign found two real value bugs and one wrong comment, all in
|
||||
enums nothing currently reads.** They were traps armed for the next person to
|
||||
write a comparison, not live defects. Everything else diffs clean.
|
||||
|
||||
---
|
||||
|
||||
## 1. The oracle set actually used
|
||||
|
||||
CLAUDE.md's reference hierarchy names six vendored repos. In this environment
|
||||
**`references/ACE`, `references/Chorizite.ACProtocol`, `references/holtburger`,
|
||||
`references/ACViewer`, `references/AC2D` and `references/DatReaderWriter` are all
|
||||
empty directories** (`references/*` is gitignored; only WorldBuilder is checked
|
||||
in). The campaign therefore re-anchored on the oracles that *are* present, which
|
||||
is no loss of rigour — CLAUDE.md ranks the first of them above ACE anyway:
|
||||
|
||||
| # | Oracle | What it is | Weight |
|
||||
|---|---|---|---|
|
||||
| 1 | `docs/research/named-retail/acclient.h` | The Sept 2013 EoR retail header, verbatim. 348 parseable `enum` blocks. | **Decides.** CLAUDE.md: "beats every other reference for what the real client does." |
|
||||
| 2 | `references/acclientlib/UtilityBelt.Common/Enums/Enums.cs` | 18,313-line client-side enum catalog, shipped beside a vendored `ACE.Entity` tree. Holds the seven property tables as `IntId`/`BoolId`/`FloatId`/… | Strong, **not infallible** — see §2. |
|
||||
| 3 | `references/weenies/**/*.json` | 38,985 ACE weenie exports. Every stat entry carries the numeric key *and* the enum member name in its `_comment`; enum-valued ints also name the value member after `=`. | Independent mechanical attestation, limited to values some weenie actually sets. |
|
||||
| 4 | `references/WorldBuilder/.../ACEnums.cs` | 233 lines, a handful of property members. | Spot check only. |
|
||||
|
||||
Retail does **not** name the seven property tables anywhere in `acclient.h`
|
||||
(searched as `SType*`, `Property*`, and by member name). The client receives
|
||||
property keys as opaque `u32`, so oracles 2 and 3 carry those alone.
|
||||
|
||||
**Why having more than one oracle mattered:** on `ItemType.CraftFletchingBase`
|
||||
the *catalog* is wrong (says `0x02000000`) and acdream was right (`0x01000000`,
|
||||
matching retail). Had the campaign trusted the catalog the way the mission brief
|
||||
assumed it would trust ACE, it would have introduced a bug. No single source was
|
||||
taken on faith; retail's header decided every disagreement, with the weenie
|
||||
corpus as tiebreak.
|
||||
|
||||
---
|
||||
|
||||
## 2. Bugs found and fixed
|
||||
|
||||
### 2.1 `DamageType` — four bits rotated (`f3e95a3e`)
|
||||
|
||||
acdream assigned `Nether/Mana/Health/Stamina` to `0x80/0x100/0x200/0x400`.
|
||||
Retail `DAMAGE_TYPE` (acclient.h:3788) assigns `Health/Stamina/Mana/Nether`.
|
||||
|
||||
| value | acdream (before) | retail | catalog | weenie corpus |
|
||||
|---|---|---|---|---|
|
||||
| 0x80 | Nether | **Health** | — | — |
|
||||
| 0x100 | Mana | **Stamina** | — | Stamina (n=2) |
|
||||
| 0x200 | Health | **Mana** | — | Mana (n=8) |
|
||||
| 0x400 | Stamina | **Nether** | — | Nether (n=9) |
|
||||
|
||||
`BASE_DAMAGE_TYPE = 0x10000000` was also absent. Both of acdream's live
|
||||
damage-type name tables — `CombatChatTranslator.FormatDamageType` (ported from
|
||||
holtburger) and `ItemAppraisalTextFormatter.TryDamageTypeName` — already used
|
||||
retail's order reading the raw wire `uint`, and `CombatChatTranslator` already
|
||||
knew about `base`. The enum was the only thing in the tree that was wrong, and
|
||||
nothing consumed the rotated members. **No live mislabel; latent trap removed.**
|
||||
|
||||
### 2.2 `ItemType` — shifted craft ladder + locally recomputed composites (`f3e95a3e`)
|
||||
|
||||
Retail `ITEM_TYPE` (acclient.h:3300) leaves `0x02000000` unused and puts
|
||||
`CRAFT_ALCHEMY_INTERMEDIATE` on `0x04000000`. acdream had alchemy-intermediate
|
||||
on `0x02000000` and an **invented `CraftCookingIntermediate`** squatting on
|
||||
`0x04000000`. The weenie corpus attests `0x04000000` as
|
||||
`Craft_Alchemy_Intermediate` 235 times and contains no cooking-intermediate at
|
||||
all — there is no such item type.
|
||||
|
||||
Separately the composite masks were derived from the local primitives instead of
|
||||
transcribed, which is how the ladder drifted in the first place:
|
||||
|
||||
| member | acdream (before) | retail |
|
||||
|---|---|---|
|
||||
| `Weapon` | `0x8101` (= melee\|missile\|caster) | `0x101` — melee\|missile, **no caster** |
|
||||
| `WeaponOrCaster` | `0x8101` (alias of `Weapon`) | `0x8101` |
|
||||
| `Item` | `0x830F` | `0x2DFBEF` |
|
||||
|
||||
Five retail composites acdream never had (`PortalMagicTarget`,
|
||||
`LockableMagicTarget`, `ItemEnchantableTarget`,
|
||||
`RedirectableItemEnchantmentTarget`, `VendorShopkeep`, `VendorGrocer`) came
|
||||
along. Only one site in the tree referenced any changed member — a test that
|
||||
wants a nonzero `HookItemTypes` and does not care which — so **no branch changed.**
|
||||
|
||||
### 2.3 `EquipMask` — a wrong type remark (`8ccaf72a`)
|
||||
|
||||
A remark claimed retail's `CLOTHING_LOC` composite "also sets bit 31,
|
||||
0x80000000, which is not a named INVENTORY_LOC primitive". It does not.
|
||||
`CLOTHING_LOC` is `0x080001FF`: the nine wear slots plus **bit 27**, which is the
|
||||
perfectly well-named `Cloak` slot. No `INVENTORY_LOC` member touches bit 31 at
|
||||
all — `ALL_LOC` stops at bit 30. Caught by writing the composite test.
|
||||
|
||||
---
|
||||
|
||||
## 3. Per-family end state
|
||||
|
||||
### 3.1 The seven `Property*` tables (`251dd68a`)
|
||||
|
||||
acdream had **no** `Property*` enums at all — property IDs were bare `uint`s with
|
||||
the meaning carried in prose (`private const uint EncumbranceValProperty = 5u`
|
||||
duplicated across two files, `UiEffects` as "ACE enum value 18" in a doc comment).
|
||||
All seven are now created under `AcDream.Core.Properties`.
|
||||
|
||||
| family | members adopted | attested by both oracles | single-sourced |
|
||||
|---|---|---|---|
|
||||
| `PropertyInt` | 391 | 189 | 202 |
|
||||
| `PropertyFloat` | 172 | 86 | 86 |
|
||||
| `PropertyBool` | 131 | 59 | 72 |
|
||||
| `PropertyDataId` | 62 | 41 | 21 |
|
||||
| `PropertyString` | 53 | 28 | 25 |
|
||||
| `PropertyInstanceId` | 46 | 2 | 44 |
|
||||
| `PropertyInt64` | 9 | 3 | 6 |
|
||||
| **total** | **864** | **408** | **456** |
|
||||
|
||||
Zero value conflicts between the two oracles across all seven, and the corpus
|
||||
contained no key the catalog lacked — the catalog is a strict superset of
|
||||
everything 38,985 weenies set. Three members differ in *spelling* only; acdream
|
||||
took ACE's (`ItemType`/`HookItemType`/`MerchandiseItemTypes` over the catalog's
|
||||
`ObjectType`/…), which is what the corpus emits and what acdream's own `ItemType`
|
||||
already called it.
|
||||
|
||||
### 3.2 Existing wire-adjacent enums, diffed against retail
|
||||
|
||||
Legend: **closed** = every retail member present at retail's value.
|
||||
|
||||
| acdream enum | retail enum | end state |
|
||||
|---|---|---|
|
||||
| `ItemType` | `ITEM_TYPE` | **closed** (2 fixed) — retail's `TYPE_UNDEF`/`TYPE_SELF` are both 0; acdream spells the single 0 `None` |
|
||||
| `DamageType` | `DAMAGE_TYPE` | **closed** (4 fixed, 1 added) |
|
||||
| `EquipMask` | `INVENTORY_LOC` | **closed** — 43/43, 11 composites adopted |
|
||||
| `TransientStateFlags` | `TransientState` | **closed** — `WaterContact`, `CheckEthereal` adopted |
|
||||
| `PhysicsStateFlags` | `PhysicsState` | **closed** — `ReservedUnused1/2` adopted |
|
||||
| `ObjectInfoState` | `ObjectInfoEnum` | **closed** — was already exact (`None` = `DEFAULT_OI`) |
|
||||
| `AttackHeight` | `ATTACK_HEIGHT` | **closed** — `Undef` adopted; `NUM_ATTACK_HEIGHTS` is a count, deliberately not a member |
|
||||
| `HoldKey` | `HoldKey` | **closed** — `Num_HoldKeys` is a count |
|
||||
| `AttackType` | `AttackType` | **already correct**, incl. both composites (`Unarmed` 0x19, `MultiStrike` 0x79E0) |
|
||||
| `RadarBlipShape` | `RadarBlipShape` | already exact (14/14) |
|
||||
| `RadarBehavior` | `RadarEnum` | already exact (5/5) |
|
||||
| `MovementType` | `MovementTypes::Type` | already exact (10/10) |
|
||||
| `ParticleType` | `ParticleType` | already exact (14/14) |
|
||||
| `PhysicsDescriptionFlag` | `PhysicsDesc::PhysicsDescInfo` | already exact (naming only: `AnimationFrame`/`ANIMFRAME_ID`) |
|
||||
|
||||
### 3.3 New enums for wire fields acdream parsed but never named (`3efa266a`)
|
||||
|
||||
| enum | retail source | members | why |
|
||||
|---|---|---|---|
|
||||
| `AmmoType` | acclient.h:4221 | 10 | parsed via `PublicWeenieDesc._ammo_type`; the appraisal sentence matched raw hex |
|
||||
| `CombatUse` | acclient.h:6523 | 6 | `PropertyInt.CombatUse` (51) |
|
||||
| `ItemUseable` | acclient.h:6478 | 39 | `PropertyInt.ItemUseable` (16); two 16-bit halves, ~30 named combinations |
|
||||
|
||||
`ItemAppraisalTextFormatter`'s ammunition fold now reads through `AmmoType`. Its
|
||||
crystal/chorizite → base collapse was verified correct against retail's bit
|
||||
layout before the change; behaviour is unchanged.
|
||||
|
||||
### 3.4 Verified correct, no change needed
|
||||
|
||||
- `ItemAppraisalTextFormatter` ammo fold (§3.3) against `AMMO_TYPE`.
|
||||
- `CombatChatTranslator.FormatDamageType` and
|
||||
`ItemAppraisalTextFormatter.TryDamageTypeName` against `DAMAGE_TYPE` — both
|
||||
already retail-correct, which is what exposed §2.1.
|
||||
|
||||
---
|
||||
|
||||
## 4. Open questions
|
||||
|
||||
Each is something the campaign could **not** settle from an oracle. Per
|
||||
CLAUDE.md these are recorded rather than guessed.
|
||||
|
||||
1. **The 2026-06-04 research drop is missing.** `claude-memory/MEMORY.md` indexes
|
||||
`research/2026-06-04-property-enum-divergence.md` (the "929 values across 7
|
||||
enums" ledger) and `research/2026-06-04-magic-number-audit.md`. Neither exists
|
||||
in the working tree, under any ref (`git log --all --diff-filter=A`), or in the
|
||||
memory directory — which has no `research/` subfolder at all. The 929 figure
|
||||
could not be reproduced or audited; this campaign regenerated from scratch and
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
A retail `COMBAT_MODE` was not found in `acclient.h`. **Blocker:** oracle not
|
||||
located; acdream is probably right but is single-sourced.
|
||||
6. **`PublicWeenieFlags` counterpart unidentified.** acdream's 17 members
|
||||
(`Attackable`, `Door`, `Vendor`, …) share exactly one name with retail's
|
||||
`PublicWeenieDescPackHeader` (34), which is the *pack-header* bitfield — a
|
||||
different thing. `PublicWeenieDesc::BitfieldIndex` (31 members) is the likely
|
||||
real counterpart. **Blocker:** pairing unconfirmed; not diffed.
|
||||
7. **`SoundId` is a curated 23-member local subset** of retail's `SoundType`
|
||||
(206), with acdream-local names (`FootstepGrass`, `BuffApplied`) that do not
|
||||
correspond 1:1 to retail members. **Blocker:** needs a decision — adopt
|
||||
retail's table wholesale, or record the subset as an intentional divergence
|
||||
with a register row. Neither was done.
|
||||
8. **`ChatType` / `BlobType` / `DispatchType`** (TurbineChat) share no members
|
||||
with retail's `eChatTypes` (26) or `ChatTypeEnum` (12); they describe chat
|
||||
*channels*, not text-display types. **Blocker:** correct counterpart not
|
||||
identified. Relevant to the Bucket C chatbox cleanup.
|
||||
9. **`GameEventType` (103 members) was not verified.** No counterpart found under
|
||||
a matching name in either retail or the catalog. **Blocker:** needs the wire
|
||||
catalog (Bucket B item 2) to supply the pairing.
|
||||
10. **`MaterialType` not adopted.** acdream parses it as raw `uint` from
|
||||
`PublicWeenieDesc`; the catalog has 108 members; no retail counterpart located
|
||||
in `acclient.h`. **Blocker:** single-sourced, and unused so far.
|
||||
11. **`SpellTargetType`, `ItemPrimaryUseResult`, `PositionFlags`,
|
||||
`EnchantmentMask`/`EnchantmentBucket`, `GameMessageGroup`, `InventoryRequestKind`
|
||||
are acdream-local** with no retail counterpart at the same semantics. Confirmed
|
||||
for `EnchantmentMask`/`EnchantmentBucket`: they are PlayerDescription wire
|
||||
*trailer bucket* flags (ACE's `EnchantmentMask`), not retail's
|
||||
`EnchantmentTypeEnum` — the apparent value conflict is a false pairing. The
|
||||
others were not individually run down.
|
||||
12. **Tooling limitation, for whoever re-runs this.** The extractor reads one line
|
||||
per enum member, so a member whose value spans multiple lines is truncated.
|
||||
This produced a false `AttackType.MultiStrike` conflict (read as `0x1E0`,
|
||||
actually `0x79E0`). Any automated re-run must join continuation lines before
|
||||
trusting a reported conflict.
|
||||
|
||||
---
|
||||
|
||||
## 5. Reproducing
|
||||
|
||||
Extraction and diff scripts were scratch tooling, not committed (they hardcode
|
||||
absolute paths into `references/`). The method, in order:
|
||||
|
||||
1. Parse `acclient.h` for `enum NAME {` blocks; resolve member expressions
|
||||
(hex, digit separators, references to earlier members).
|
||||
2. Parse the UtilityBelt catalog the same way.
|
||||
3. Walk `references/weenies/**/*.json`, pulling `(key, value, _comment)` from each
|
||||
`*Stats` array; the comment's left side is the property name, the right side
|
||||
(after `=`) names the value member for enum-typed ints.
|
||||
4. Normalise names across conventions (retail `SCREAMING_SNAKE` with family
|
||||
prefix/suffix ↔ acdream `PascalCase`) and diff on the normalised key, comparing
|
||||
values exactly.
|
||||
5. Any disagreement: retail decides, corpus breaks ties, and anything still
|
||||
unresolved becomes an open question above rather than a guess.
|
||||
|
||||
## 6. Commits
|
||||
|
||||
| commit | slice |
|
||||
|---|---|
|
||||
| `251dd68a` | the seven `Property*` tables (864 members, 429 tests) |
|
||||
| `f3e95a3e` | `DamageType` rotation + `ItemType` craft ladder and composites |
|
||||
| `8ccaf72a` | `EquipMask` composites, `TransientState`, `PhysicsState`, `AttackHeight` |
|
||||
| `3efa266a` | `AmmoType`, `CombatUse`, `ItemUseable` |
|
||||
|
|
@ -398,22 +398,24 @@ public static class ItemAppraisalTextFormatter
|
|||
if (ammoType == 0u)
|
||||
return;
|
||||
|
||||
uint baseAmmoType = ammoType switch
|
||||
// Retail gives the crystal and chorizite variants their own bits
|
||||
// (acclient.h:4221); each folds back to the base kind for this sentence.
|
||||
AmmoType baseAmmoType = (AmmoType)ammoType switch
|
||||
{
|
||||
0x08u or 0x40u => 1u,
|
||||
0x10u or 0x80u => 2u,
|
||||
0x20u or 0x100u => 4u,
|
||||
_ => ammoType,
|
||||
AmmoType.ArrowCrystal or AmmoType.ArrowChorizite => AmmoType.Arrow,
|
||||
AmmoType.BoltCrystal or AmmoType.BoltChorizite => AmmoType.Bolt,
|
||||
AmmoType.AtlatlCrystal or AmmoType.AtlatlChorizite => AmmoType.Atlatl,
|
||||
var other => other,
|
||||
};
|
||||
bool launcher = (validLocations & (uint)EquipMask.MissileWeapon) != 0;
|
||||
string? description = (launcher, baseAmmoType) switch
|
||||
{
|
||||
(true, 1u) => "Uses arrows as ammunition.",
|
||||
(true, 2u) => "Uses quarrels as ammunition.",
|
||||
(true, 4u) => "Uses atlatl darts as ammunition.",
|
||||
(false, 1u) => "Used as ammunition by bows.",
|
||||
(false, 2u) => "Used as ammunition by crossbows.",
|
||||
(false, 4u) => "Used as ammunition by atlatls.",
|
||||
(true, AmmoType.Arrow) => "Uses arrows as ammunition.",
|
||||
(true, AmmoType.Bolt) => "Uses quarrels as ammunition.",
|
||||
(true, AmmoType.Atlatl) => "Uses atlatl darts as ammunition.",
|
||||
(false, AmmoType.Arrow) => "Used as ammunition by bows.",
|
||||
(false, AmmoType.Bolt) => "Used as ammunition by crossbows.",
|
||||
(false, AmmoType.Atlatl) => "Used as ammunition by atlatls.",
|
||||
_ => null,
|
||||
};
|
||||
if (description is not null)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,14 @@ public enum CombatMode
|
|||
CombatCombat = Melee | Missile | Magic,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verbatim retail <c>ATTACK_HEIGHT</c> (<c>docs/research/named-retail/acclient.h:4371</c>).
|
||||
/// The three real heights are 1-based; retail reserves 0 for "not chosen yet", which the
|
||||
/// wire does send, so it is named rather than left to fall through as an undefined cast.
|
||||
/// </summary>
|
||||
public enum AttackHeight
|
||||
{
|
||||
Undef = 0,
|
||||
High = 1,
|
||||
Medium = 2,
|
||||
Low = 3,
|
||||
|
|
@ -204,6 +210,18 @@ public enum AttackType : uint
|
|||
| OffhandDoubleThrust | OffhandTripleThrust,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verbatim retail <c>DAMAGE_TYPE</c> (<c>docs/research/named-retail/acclient.h:3788</c>).
|
||||
///
|
||||
/// <para>The four drain/restore bits used to be rotated here: Nether/Mana/Health/Stamina
|
||||
/// were assigned 0x80/0x100/0x200/0x400 where retail assigns Health/Stamina/Mana/Nether.
|
||||
/// Retail, the vendored client-side enum catalog, and the ACE weenie corpus all agree on
|
||||
/// retail's order (the corpus attests 0x100=Stamina, 0x200=Mana, 0x400=Nether directly),
|
||||
/// and both of acdream's live damage-type name tables — <c>CombatChatTranslator</c> and
|
||||
/// <c>ItemAppraisalTextFormatter</c> — were already reading the retail order off the raw
|
||||
/// wire uint. Nothing consumed the rotated members, so this corrects a latent trap rather
|
||||
/// than a live mislabel.</para>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DamageType : uint
|
||||
{
|
||||
|
|
@ -215,10 +233,11 @@ public enum DamageType : uint
|
|||
Fire = 0x0010,
|
||||
Acid = 0x0020,
|
||||
Electric = 0x0040,
|
||||
Nether = 0x0080,
|
||||
Mana = 0x0100,
|
||||
Health = 0x0200,
|
||||
Stamina = 0x0400,
|
||||
Health = 0x0080,
|
||||
Stamina = 0x0100,
|
||||
Mana = 0x0200,
|
||||
Nether = 0x0400,
|
||||
Base = 0x10000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,17 @@ namespace AcDream.Core.Items;
|
|||
|
||||
/// <summary>
|
||||
/// AC's <c>ItemType</c> is a 32-bit flags enum — a single dat weenie can
|
||||
/// assert multiple type bits. From <c>ACE.Entity.Enum.ItemType</c>
|
||||
/// cross-checked against the decompile paperdoll tooltip dispatcher.
|
||||
/// Full bit list in the research doc §1.
|
||||
/// assert multiple type bits. Verbatim retail <c>ITEM_TYPE</c>
|
||||
/// (<c>docs/research/named-retail/acclient.h:3300</c>).
|
||||
///
|
||||
/// <para>The craft ladder used to be shifted: <c>CraftAlchemyIntermediate</c> sat on
|
||||
/// 0x02000000 (retail leaves that bit unused and puts alchemy-intermediate on
|
||||
/// 0x04000000), and an invented <c>CraftCookingIntermediate</c> occupied the real
|
||||
/// alchemy-intermediate bit. The ACE weenie corpus attests 0x04000000 =
|
||||
/// Craft_Alchemy_Intermediate 235 times and contains no cooking-intermediate at all.
|
||||
/// The composite masks were likewise recomputed locally rather than transcribed, which
|
||||
/// made <c>Weapon</c> an alias of <c>WeaponOrCaster</c> and left <c>Item</c> two orders
|
||||
/// of magnitude narrower than retail's. Full bit list in the research doc §1.</para>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ItemType : uint
|
||||
|
|
@ -43,17 +51,27 @@ public enum ItemType : uint
|
|||
CraftCookingBase = 0x00400000,
|
||||
CraftAlchemyBase = 0x00800000,
|
||||
CraftFletchingBase = 0x01000000,
|
||||
CraftAlchemyIntermediate= 0x02000000,
|
||||
CraftCookingIntermediate= 0x04000000,
|
||||
// 0x02000000 is deliberately unused in retail's ladder.
|
||||
CraftAlchemyIntermediate= 0x04000000,
|
||||
CraftFletchingIntermediate = 0x08000000,
|
||||
LifeStone = 0x10000000,
|
||||
TinkeringTool = 0x20000000,
|
||||
TinkeringMaterial = 0x40000000,
|
||||
Gameboard = 0x80000000u,
|
||||
Vestements = Armor | Clothing,
|
||||
Weapon = MeleeWeapon | MissileWeapon | Caster,
|
||||
WeaponOrCaster = Weapon,
|
||||
Item = Weapon | Armor | Clothing | Jewelry | Container,
|
||||
|
||||
// Composite masks, transcribed verbatim rather than recomputed from the bits
|
||||
// above — retail's TYPE_ITEM in particular is far broader than any obvious
|
||||
// union, and deriving these locally is how the craft ladder drifted.
|
||||
Vestements = 0x00000006, // TYPE_VESTEMENTS
|
||||
Weapon = 0x00000101, // TYPE_WEAPON: melee | missile, no caster
|
||||
WeaponOrCaster = 0x00008101, // TYPE_WEAPON_OR_CASTER
|
||||
LockableMagicTarget = 0x00000280, // TYPE_LOCKABLE_MAGIC_TARGET
|
||||
RedirectableItemEnchantmentTarget = 0x00008107,
|
||||
PortalMagicTarget = 0x10010000, // TYPE_PORTAL_MAGIC_TARGET
|
||||
ItemEnchantableTarget = 0x00088B8F, // TYPE_ITEM_ENCHANTABLE_TARGET
|
||||
Item = 0x002DFBEF, // TYPE_ITEM
|
||||
VendorShopkeep = 0x480467A7, // TYPE_VENDOR_SHOPKEEP
|
||||
VendorGrocer = 0x00446220, // TYPE_VENDOR_GROCER
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -61,8 +79,10 @@ public enum ItemType : uint
|
|||
/// (docs/research/named-retail/acclient.h:3193; identical to ACE's EquipMask).
|
||||
/// The wire (ValidLocations / CurrentWieldedLocation / WieldObject EquipLoc) delivers
|
||||
/// these exact bits. Pinned by EquipMaskTests — do NOT renumber.
|
||||
/// (The header's <c>CLOTHING_LOC</c> composite also sets bit 31, 0x80000000, which is
|
||||
/// not a named INVENTORY_LOC primitive and has no member here; <c>ALL_LOC</c> tops out at bit 30.)
|
||||
/// (A remark here used to claim the header's <c>CLOTHING_LOC</c> composite sets an
|
||||
/// unnamed bit 31. It does not: <c>CLOTHING_LOC</c> is 0x080001FF, the nine wear slots
|
||||
/// plus bit 27, which is the named <see cref="EquipMask.Cloak"/> slot. <c>ALL_LOC</c>
|
||||
/// tops out at bit 30, so no INVENTORY_LOC member uses bit 31 at all.)
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EquipMask : uint
|
||||
|
|
@ -99,6 +119,23 @@ public enum EquipMask : uint
|
|||
SigilOne = 0x10000000,
|
||||
SigilTwo = 0x20000000,
|
||||
SigilThree = 0x40000000,
|
||||
|
||||
// Retail's composite slot groups, transcribed verbatim from the same header
|
||||
// block rather than recomputed from the primitives above. Clothing in
|
||||
// particular is not the union of the wear slots — it also carries the cloak
|
||||
// slot (bit 27).
|
||||
Clothing = 0x080001FF, // CLOTHING_LOC: the nine wear slots | Cloak
|
||||
Armor = 0x00007E00, // ARMOR_LOC
|
||||
Jewelry = 0x7C0F8000, // JEWELRY_LOC
|
||||
WristWear = 0x00030000, // WRIST_WEAR_LOC
|
||||
FingerWear = 0x000C0000, // FINGER_WEAR_LOC
|
||||
Sigil = 0x70000000, // SIGIL_LOC
|
||||
ReadySlot = 0x03F00000, // READY_SLOT_LOC
|
||||
Weapon = 0x02500000, // WEAPON_LOC
|
||||
WeaponReadySlot = 0x03500000, // WEAPON_READY_SLOT_LOC
|
||||
All = 0x7FFFFFFF, // ALL_LOC
|
||||
/// <summary>Retail gives CAN_GO_IN_READY_SLOT_LOC the same value as ALL_LOC.</summary>
|
||||
CanGoInReadySlot = 0x7FFFFFFF,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
104
src/AcDream.Core/Items/ItemWireEnums.cs
Normal file
104
src/AcDream.Core/Items/ItemWireEnums.cs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
|
||||
namespace AcDream.Core.Items;
|
||||
|
||||
/// <summary>
|
||||
/// Verbatim retail <c>AMMO_TYPE</c> (<c>docs/research/named-retail/acclient.h:4221</c>).
|
||||
/// Arrives on the wire as <c>PublicWeenieDesc._ammo_type</c> (CreateObject weenie flag
|
||||
/// 0x00000100) and in the appraisal weapon profile.
|
||||
///
|
||||
/// <para>The three base kinds each have a crystal and a chorizite variant on their own
|
||||
/// bit, so a launcher's "uses arrows" test must fold the variants back to the base — see
|
||||
/// <c>ItemAppraisalTextFormatter</c>, which does exactly that.</para>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AmmoType : uint
|
||||
{
|
||||
None = 0x000,
|
||||
Arrow = 0x001,
|
||||
Bolt = 0x002,
|
||||
Atlatl = 0x004,
|
||||
ArrowCrystal = 0x008,
|
||||
BoltCrystal = 0x010,
|
||||
AtlatlCrystal = 0x020,
|
||||
ArrowChorizite = 0x040,
|
||||
BoltChorizite = 0x080,
|
||||
AtlatlChorizite = 0x100,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verbatim retail <c>COMBAT_USE</c> (<c>docs/research/named-retail/acclient.h:6523</c>) —
|
||||
/// how a wieldable participates in combat. This is a plain sequence, not a bitfield.
|
||||
/// Arrives as <c>PropertyInt.CombatUse</c> (51).
|
||||
/// </summary>
|
||||
public enum CombatUse : uint
|
||||
{
|
||||
None = 0,
|
||||
Melee = 1,
|
||||
Missile = 2,
|
||||
Ammo = 3,
|
||||
Shield = 4,
|
||||
TwoHanded = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verbatim retail <c>ITEM_USEABLE</c> (<c>docs/research/named-retail/acclient.h:6478</c>) —
|
||||
/// where an object must be, and where its target must be, for a use attempt to be legal.
|
||||
/// Arrives as <c>PropertyInt.ItemUseable</c> (16).
|
||||
///
|
||||
/// <para>The layout is two 16-bit halves: the low half (<see cref="SourceMask"/>) constrains
|
||||
/// the object being used, the high half (<see cref="TargetMask"/>) constrains its target.
|
||||
/// Retail names the useful combinations explicitly rather than expecting callers to compose
|
||||
/// them, and they are transcribed here the same way — several are not the obvious union
|
||||
/// (<see cref="SourceContainedTargetObjSelfOrContained"/> is 0x880008, not
|
||||
/// <c>ObjSelf | Contained | (Contained << 16)</c>).</para>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ItemUseable : uint
|
||||
{
|
||||
Undef = 0x0,
|
||||
No = 0x1,
|
||||
Self = 0x2,
|
||||
Wielded = 0x4,
|
||||
Contained = 0x8,
|
||||
Viewed = 0x10,
|
||||
Remote = 0x20,
|
||||
NeverWalk = 0x40,
|
||||
ObjSelf = 0x80,
|
||||
|
||||
ContainedViewed = 0x18,
|
||||
ViewedRemote = 0x30,
|
||||
ContainedViewedRemote = 0x38,
|
||||
RemoteNeverWalk = 0x60,
|
||||
ViewedRemoteNeverWalk = 0x70,
|
||||
ContainedViewedRemoteNeverWalk = 0x78,
|
||||
|
||||
SourceWieldedTargetWielded = 0x00040004,
|
||||
SourceWieldedTargetContained = 0x00080004,
|
||||
SourceWieldedTargetViewed = 0x00100004,
|
||||
SourceWieldedTargetRemote = 0x00200004,
|
||||
SourceWieldedTargetRemoteNeverWalk = 0x00600004,
|
||||
|
||||
SourceContainedTargetWielded = 0x00040008,
|
||||
SourceContainedTargetContained = 0x00080008,
|
||||
SourceContainedTargetSelfOrContained = 0x000A0008,
|
||||
SourceContainedTargetViewed = 0x00100008,
|
||||
SourceContainedTargetRemote = 0x00200008,
|
||||
SourceContainedTargetRemoteOrSelf = 0x00220008,
|
||||
SourceContainedTargetRemoteNeverWalk = 0x00600008,
|
||||
SourceContainedTargetObjSelfOrContained = 0x00880008,
|
||||
|
||||
SourceViewedTargetWielded = 0x00040010,
|
||||
SourceViewedTargetContained = 0x00080010,
|
||||
SourceViewedTargetViewed = 0x00100010,
|
||||
SourceViewedTargetRemote = 0x00200010,
|
||||
|
||||
SourceRemoteTargetWielded = 0x00040020,
|
||||
SourceRemoteTargetContained = 0x00080020,
|
||||
SourceRemoteTargetViewed = 0x00100020,
|
||||
SourceRemoteTargetRemote = 0x00200020,
|
||||
SourceRemoteTargetRemoteNeverWalk = 0x00600020,
|
||||
|
||||
SourceMask = 0x0000FFFF,
|
||||
TargetMask = 0xFFFF0000,
|
||||
}
|
||||
|
|
@ -27,6 +27,12 @@ public enum PhysicsStateFlags : uint
|
|||
{
|
||||
None = 0x00000000,
|
||||
Static = 0x00000001,
|
||||
/// <summary>
|
||||
/// Retail declares 0x2 and 0x2000 as UNUSED1_PS / UNNUSED2_PS (acclient.h:2818,
|
||||
/// 2830) — reserved, never set. Named here so neither bit gets repurposed for an
|
||||
/// acdream-local flag and then collides with a server that starts using it.
|
||||
/// </summary>
|
||||
ReservedUnused1 = 0x00000002,
|
||||
Ethereal = 0x00000004,
|
||||
ReportCollisions = 0x00000008,
|
||||
IgnoreCollisions = 0x00000010,
|
||||
|
|
@ -38,6 +44,8 @@ public enum PhysicsStateFlags : uint
|
|||
Gravity = 0x00000400,
|
||||
Lighting = 0x00000800,
|
||||
ParticleEmitter = 0x00001000,
|
||||
/// <summary>Retail UNNUSED2_PS — reserved, never set. See <see cref="ReservedUnused1"/>.</summary>
|
||||
ReservedUnused2 = 0x00002000,
|
||||
Hidden = 0x00004000,
|
||||
ScriptedCollision = 0x00008000,
|
||||
/// <summary>
|
||||
|
|
@ -88,6 +96,11 @@ public enum TransientStateFlags : uint
|
|||
StationaryStop = 0x00000020, // bit 5 — fsf == 2
|
||||
StationaryStuck = 0x00000040, // bit 6 — fsf == 3
|
||||
Active = 0x00000080, // bit 7 — object needs per-frame update
|
||||
// Declared to complete retail's TransientState (acclient.h:3688). Neither bit is
|
||||
// produced or consumed by acdream's transition yet; they are here so the two free
|
||||
// slots cannot be reused for something else and quietly collide with the wire.
|
||||
WaterContact = 0x00000008, // bit 3 — WATER_CONTACT_TS
|
||||
CheckEthereal = 0x00000100, // bit 8 — CHECK_ETHEREAL_TS
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
221
src/AcDream.Core/Properties/PropertyBool.cs
Normal file
221
src/AcDream.Core/Properties/PropertyBool.cs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyBool</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyBool (0x02C9) / PublicUpdatePropertyBool (0x02CA)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>bool</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>BoolId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 59 of these 131 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyBool : uint
|
||||
{
|
||||
Undef = 0,
|
||||
/// <summary>21884 weenie attestations.</summary>
|
||||
Stuck = 1,
|
||||
/// <summary>1077 weenie attestations.</summary>
|
||||
Open = 2,
|
||||
/// <summary>646 weenie attestations.</summary>
|
||||
Locked = 3,
|
||||
RotProof = 4,
|
||||
AllegianceUpdateRequest = 5,
|
||||
/// <summary>1282 weenie attestations.</summary>
|
||||
AiUsesMana = 6,
|
||||
/// <summary>16 weenie attestations.</summary>
|
||||
AiUseHumanMagicAnimations = 7,
|
||||
/// <summary>853 weenie attestations.</summary>
|
||||
AllowGive = 8,
|
||||
CurrentlyAttacking = 9,
|
||||
AttackerAi = 10,
|
||||
/// <summary>6212 weenie attestations.</summary>
|
||||
IgnoreCollisions = 11,
|
||||
/// <summary>9020 weenie attestations.</summary>
|
||||
ReportCollisions = 12,
|
||||
/// <summary>15282 weenie attestations.</summary>
|
||||
Ethereal = 13,
|
||||
/// <summary>7376 weenie attestations.</summary>
|
||||
GravityStatus = 14,
|
||||
/// <summary>3382 weenie attestations.</summary>
|
||||
LightsStatus = 15,
|
||||
/// <summary>139 weenie attestations.</summary>
|
||||
ScriptedCollision = 16,
|
||||
/// <summary>475 weenie attestations.</summary>
|
||||
Inelastic = 17,
|
||||
/// <summary>1677 weenie attestations.</summary>
|
||||
Visibility = 18,
|
||||
/// <summary>3972 weenie attestations.</summary>
|
||||
Attackable = 19,
|
||||
SafeSpellComponents = 20,
|
||||
AdvocateState = 21,
|
||||
/// <summary>15455 weenie attestations.</summary>
|
||||
Inscribable = 22,
|
||||
/// <summary>7755 weenie attestations.</summary>
|
||||
DestroyOnSell = 23,
|
||||
/// <summary>6728 weenie attestations.</summary>
|
||||
UiHidden = 24,
|
||||
IgnoreHouseBarriers = 25,
|
||||
HiddenAdmin = 26,
|
||||
PkWounder = 27,
|
||||
PkKiller = 28,
|
||||
/// <summary>262 weenie attestations.</summary>
|
||||
NoCorpse = 29,
|
||||
UnderLifestoneProtection = 30,
|
||||
ItemManaUpdatePending = 31,
|
||||
GeneratorStatus = 32,
|
||||
/// <summary>831 weenie attestations.</summary>
|
||||
ResetMessagePending = 33,
|
||||
/// <summary>831 weenie attestations.</summary>
|
||||
DefaultOpen = 34,
|
||||
/// <summary>511 weenie attestations.</summary>
|
||||
DefaultLocked = 35,
|
||||
DefaultOn = 36,
|
||||
OpenForBusiness = 37,
|
||||
IsFrozen = 38,
|
||||
/// <summary>1049 weenie attestations.</summary>
|
||||
DealMagicalItems = 39,
|
||||
LogoffImDead = 40,
|
||||
/// <summary>2208 weenie attestations.</summary>
|
||||
ReportCollisionsAsEnvironment = 41,
|
||||
/// <summary>1327 weenie attestations.</summary>
|
||||
AllowEdgeSlide = 42,
|
||||
AdvocateQuest = 43,
|
||||
IsAdmin = 44,
|
||||
IsArch = 45,
|
||||
IsSentinel = 46,
|
||||
IsAdvocate = 47,
|
||||
CurrentlyPoweringUp = 48,
|
||||
GeneratorEnteredWorld = 49,
|
||||
/// <summary>890 weenie attestations.</summary>
|
||||
NeverFailCasting = 50,
|
||||
/// <summary>264 weenie attestations.</summary>
|
||||
VendorService = 51,
|
||||
/// <summary>1421 weenie attestations.</summary>
|
||||
AiImmobile = 52,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
DamagedByCollisions = 53,
|
||||
/// <summary>34 weenie attestations.</summary>
|
||||
IsDynamic = 54,
|
||||
/// <summary>81 weenie attestations.</summary>
|
||||
IsHot = 55,
|
||||
IsAffecting = 56,
|
||||
/// <summary>49 weenie attestations.</summary>
|
||||
AffectsAis = 57,
|
||||
SpellQueueActive = 58,
|
||||
GeneratorDisabled = 59,
|
||||
IsAcceptingTells = 60,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
LoggingChannel = 61,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
OpensAnyLock = 62,
|
||||
/// <summary>20 weenie attestations.</summary>
|
||||
UnlimitedUse = 63,
|
||||
GeneratedTreasureItem = 64,
|
||||
/// <summary>97 weenie attestations.</summary>
|
||||
IgnoreMagicResist = 65,
|
||||
/// <summary>87 weenie attestations.</summary>
|
||||
IgnoreMagicArmor = 66,
|
||||
AiAllowTrade = 67,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
SpellComponentsRequired = 68,
|
||||
/// <summary>4617 weenie attestations.</summary>
|
||||
IsSellable = 69,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
IgnoreShieldsBySkill = 70,
|
||||
/// <summary>6275 weenie attestations.</summary>
|
||||
NoDraw = 71,
|
||||
ActivationUntargeted = 72,
|
||||
HouseHasGottenPriorityBootPos = 73,
|
||||
/// <summary>12 weenie attestations.</summary>
|
||||
GeneratorAutomaticDestruction = 74,
|
||||
HouseHooksVisible = 75,
|
||||
/// <summary>8 weenie attestations.</summary>
|
||||
HouseRequiresMonarch = 76,
|
||||
HouseHooksEnabled = 77,
|
||||
HouseNotifiedHudOfHookCount = 78,
|
||||
/// <summary>15 weenie attestations.</summary>
|
||||
AiAcceptEverything = 79,
|
||||
IgnorePortalRestrictions = 80,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
RequiresBackpackSlot = 81,
|
||||
/// <summary>444 weenie attestations.</summary>
|
||||
DontTurnOrMoveWhenGiving = 82,
|
||||
/// <summary>442 weenie attestations.</summary>
|
||||
NpcLooksLikeObject = 83,
|
||||
/// <summary>210 weenie attestations.</summary>
|
||||
IgnoreCloIcons = 84,
|
||||
/// <summary>178 weenie attestations.</summary>
|
||||
AppraisalHasAllowedWielder = 85,
|
||||
ChestRegenOnClose = 86,
|
||||
LogoffInMinigame = 87,
|
||||
/// <summary>17 weenie attestations.</summary>
|
||||
PortalShowDestination = 88,
|
||||
/// <summary>30 weenie attestations.</summary>
|
||||
PortalIgnoresPkAttackTimer = 89,
|
||||
/// <summary>43 weenie attestations.</summary>
|
||||
NpcInteractsSilently = 90,
|
||||
/// <summary>230 weenie attestations.</summary>
|
||||
Retained = 91,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
IgnoreAuthor = 92,
|
||||
Limbo = 93,
|
||||
AppraisalHasAllowedActivator = 94,
|
||||
ExistedBeforeAllegianceXpChanges = 95,
|
||||
IsDeaf = 96,
|
||||
IsPsr = 97,
|
||||
Invincible = 98,
|
||||
/// <summary>1068 weenie attestations.</summary>
|
||||
Ivoryable = 99,
|
||||
/// <summary>687 weenie attestations.</summary>
|
||||
Dyable = 100,
|
||||
CanGenerateRare = 101,
|
||||
CorpseGeneratedRare = 102,
|
||||
/// <summary>14 weenie attestations.</summary>
|
||||
NonProjectileMagicImmune = 103,
|
||||
ActdReceivedItems = 104,
|
||||
Unknown105 = 105,
|
||||
FirstEnterWorldDone = 106,
|
||||
RecallsDisabled = 107,
|
||||
/// <summary>13 weenie attestations.</summary>
|
||||
RareUsesTimer = 108,
|
||||
ActdPreorderReceivedItems = 109,
|
||||
Afk = 110,
|
||||
IsGagged = 111,
|
||||
ProcSpellSelfTargeted = 112,
|
||||
IsAllegianceGagged = 113,
|
||||
EquipmentSetTriggerPiece = 114,
|
||||
Uninscribe = 115,
|
||||
WieldOnUse = 116,
|
||||
ChestClearedWhenClosed = 117,
|
||||
NeverAttack = 118,
|
||||
SuppressGenerateEffect = 119,
|
||||
TreasureCorpse = 120,
|
||||
EquipmentSetAddLevel = 121,
|
||||
BarberActive = 122,
|
||||
TopLayerPriority = 123,
|
||||
NoHeldItemShown = 124,
|
||||
LoginAtLifestone = 125,
|
||||
OlthoiPk = 126,
|
||||
Account15Days = 127,
|
||||
HadNoVitae = 128,
|
||||
NoOlthoiTalk = 129,
|
||||
/// <summary>14 weenie attestations.</summary>
|
||||
AutowieldLeft = 130,
|
||||
}
|
||||
134
src/AcDream.Core/Properties/PropertyDataId.cs
Normal file
134
src/AcDream.Core/Properties/PropertyDataId.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyDataId</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyDataId (0x02E1) / PublicUpdatePropertyDataId (0x02E2)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>uint (DAT file id)</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>DataId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 41 of these 62 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyDataId : uint
|
||||
{
|
||||
Undef = 0,
|
||||
/// <summary>38985 weenie attestations.</summary>
|
||||
Setup = 1,
|
||||
/// <summary>12219 weenie attestations.</summary>
|
||||
MotionTable = 2,
|
||||
/// <summary>23696 weenie attestations.</summary>
|
||||
SoundTable = 3,
|
||||
/// <summary>4319 weenie attestations.</summary>
|
||||
CombatTable = 4,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
QualityFilter = 5,
|
||||
/// <summary>13875 weenie attestations.</summary>
|
||||
PaletteBase = 6,
|
||||
/// <summary>9049 weenie attestations.</summary>
|
||||
ClothingBase = 7,
|
||||
/// <summary>38985 weenie attestations.</summary>
|
||||
Icon = 8,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
EyesTexture = 9,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
NoseTexture = 10,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
MouthTexture = 11,
|
||||
DefaultEyesTexture = 12,
|
||||
DefaultNoseTexture = 13,
|
||||
DefaultMouthTexture = 14,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
HairPalette = 15,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
EyesPalette = 16,
|
||||
/// <summary>741 weenie attestations.</summary>
|
||||
SkinPalette = 17,
|
||||
HeadObject = 18,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
ActivationAnimation = 19,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
InitMotion = 20,
|
||||
ActivationSound = 21,
|
||||
/// <summary>23266 weenie attestations.</summary>
|
||||
PhysicsEffectTable = 22,
|
||||
/// <summary>138 weenie attestations.</summary>
|
||||
UseSound = 23,
|
||||
/// <summary>48 weenie attestations.</summary>
|
||||
UseTargetAnimation = 24,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
UseTargetSuccessAnimation = 25,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
UseTargetFailureAnimation = 26,
|
||||
/// <summary>232 weenie attestations.</summary>
|
||||
UseUserAnimation = 27,
|
||||
/// <summary>3696 weenie attestations.</summary>
|
||||
Spell = 28,
|
||||
/// <summary>156 weenie attestations.</summary>
|
||||
SpellComponent = 29,
|
||||
/// <summary>794 weenie attestations.</summary>
|
||||
PhysicsScript = 30,
|
||||
/// <summary>497 weenie attestations.</summary>
|
||||
LinkedPortalOne = 31,
|
||||
/// <summary>861 weenie attestations.</summary>
|
||||
WieldedTreasureType = 32,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
UnknownGuessedname = 33,
|
||||
UnknownGuessedname2 = 34,
|
||||
/// <summary>2000 weenie attestations.</summary>
|
||||
DeathTreasureType = 35,
|
||||
/// <summary>2276 weenie attestations.</summary>
|
||||
MutateFilter = 36,
|
||||
/// <summary>1253 weenie attestations.</summary>
|
||||
ItemSkillLimit = 37,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
UseCreateItem = 38,
|
||||
DeathSpell = 39,
|
||||
VendorsClassId = 40,
|
||||
/// <summary>13 weenie attestations.</summary>
|
||||
ItemSpecializedOnly = 41,
|
||||
/// <summary>6274 weenie attestations.</summary>
|
||||
HouseId = 42,
|
||||
AccountHouseId = 43,
|
||||
/// <summary>6274 weenie attestations.</summary>
|
||||
RestrictionEffect = 44,
|
||||
CreationMutationFilter = 45,
|
||||
/// <summary>584 weenie attestations.</summary>
|
||||
TsysMutationFilter = 46,
|
||||
LastPortal = 47,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
LinkedPortalTwo = 48,
|
||||
OriginalPortal = 49,
|
||||
/// <summary>1000 weenie attestations.</summary>
|
||||
IconOverlay = 50,
|
||||
/// <summary>16 weenie attestations.</summary>
|
||||
IconOverlaySecondary = 51,
|
||||
/// <summary>554 weenie attestations.</summary>
|
||||
IconUnderlay = 52,
|
||||
AugmentationMutationFilter = 53,
|
||||
AugmentationEffect = 54,
|
||||
/// <summary>84 weenie attestations.</summary>
|
||||
ProcSpell = 55,
|
||||
AugmentationCreateItem = 56,
|
||||
AlternateCurrency = 57,
|
||||
BlueSurgeSpell = 58,
|
||||
YellowSurgeSpell = 59,
|
||||
RedSurgeSpell = 60,
|
||||
OlthoiDeathTreasureType = 61,
|
||||
}
|
||||
289
src/AcDream.Core/Properties/PropertyFloat.cs
Normal file
289
src/AcDream.Core/Properties/PropertyFloat.cs
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyFloat</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyFloat (0x02D5) / PublicUpdatePropertyFloat (0x02D6)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>double</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>FloatId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 86 of these 172 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyFloat : uint
|
||||
{
|
||||
Undef = 0,
|
||||
/// <summary>4243 weenie attestations.</summary>
|
||||
HeartbeatInterval = 1,
|
||||
/// <summary>4243 weenie attestations.</summary>
|
||||
HeartbeatTimestamp = 2,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
HealthRate = 3,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
StaminaRate = 4,
|
||||
/// <summary>7706 weenie attestations.</summary>
|
||||
ManaRate = 5,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
HealthUponResurrection = 6,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
StaminaUponResurrection = 7,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
ManaUponResurrection = 8,
|
||||
StartTime = 9,
|
||||
StopTime = 10,
|
||||
/// <summary>1749 weenie attestations.</summary>
|
||||
ResetInterval = 11,
|
||||
/// <summary>3290 weenie attestations.</summary>
|
||||
Shade = 12,
|
||||
/// <summary>6001 weenie attestations.</summary>
|
||||
ArmorModVsSlash = 13,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsPierce = 14,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsBludgeon = 15,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsCold = 16,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsFire = 17,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsAcid = 18,
|
||||
/// <summary>6005 weenie attestations.</summary>
|
||||
ArmorModVsElectric = 19,
|
||||
CombatSpeed = 20,
|
||||
/// <summary>2733 weenie attestations.</summary>
|
||||
WeaponLength = 21,
|
||||
/// <summary>3174 weenie attestations.</summary>
|
||||
DamageVariance = 22,
|
||||
CurrentPowerMod = 23,
|
||||
AccuracyMod = 24,
|
||||
StrengthMod = 25,
|
||||
/// <summary>1386 weenie attestations.</summary>
|
||||
MaximumVelocity = 26,
|
||||
/// <summary>233 weenie attestations.</summary>
|
||||
RotationSpeed = 27,
|
||||
MotionTimestamp = 28,
|
||||
/// <summary>3892 weenie attestations.</summary>
|
||||
WeaponDefense = 29,
|
||||
WimpyLevel = 30,
|
||||
/// <summary>2169 weenie attestations.</summary>
|
||||
VisualAwarenessRange = 31,
|
||||
AuralAwarenessRange = 32,
|
||||
PerceptionLevel = 33,
|
||||
/// <summary>1987 weenie attestations.</summary>
|
||||
PowerupTime = 34,
|
||||
MaxChargeDistance = 35,
|
||||
/// <summary>1892 weenie attestations.</summary>
|
||||
ChargeSpeed = 36,
|
||||
/// <summary>1049 weenie attestations.</summary>
|
||||
BuyPrice = 37,
|
||||
/// <summary>1049 weenie attestations.</summary>
|
||||
SellPrice = 38,
|
||||
/// <summary>18233 weenie attestations.</summary>
|
||||
DefaultScale = 39,
|
||||
/// <summary>10 weenie attestations.</summary>
|
||||
LockpickMod = 40,
|
||||
/// <summary>2195 weenie attestations.</summary>
|
||||
RegenerationInterval = 41,
|
||||
RegenerationTimestamp = 42,
|
||||
/// <summary>2135 weenie attestations.</summary>
|
||||
GeneratorRadius = 43,
|
||||
/// <summary>66 weenie attestations.</summary>
|
||||
TimeToRot = 44,
|
||||
DeathTimestamp = 45,
|
||||
PkTimestamp = 46,
|
||||
VictimTimestamp = 47,
|
||||
LoginTimestamp = 48,
|
||||
CreationTimestamp = 49,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
MinimumTimeSincePk = 50,
|
||||
DeprecatedHousekeepingPriority = 51,
|
||||
AbuseLoggingTimestamp = 52,
|
||||
LastPortalTeleportTimestamp = 53,
|
||||
/// <summary>9152 weenie attestations.</summary>
|
||||
UseRadius = 54,
|
||||
/// <summary>46 weenie attestations.</summary>
|
||||
HomeRadius = 55,
|
||||
ReleasedTimestamp = 56,
|
||||
MinHomeRadius = 57,
|
||||
Facing = 58,
|
||||
ResetTimestamp = 59,
|
||||
LogoffTimestamp = 60,
|
||||
EconRecoveryInterval = 61,
|
||||
/// <summary>3523 weenie attestations.</summary>
|
||||
WeaponOffense = 62,
|
||||
/// <summary>1223 weenie attestations.</summary>
|
||||
DamageMod = 63,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistSlash = 64,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistPierce = 65,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistBludgeon = 66,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistFire = 67,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistCold = 68,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistAcid = 69,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistElectric = 70,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistHealthBoost = 71,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistStaminaDrain = 72,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistStaminaBoost = 73,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistManaDrain = 74,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistManaBoost = 75,
|
||||
/// <summary>794 weenie attestations.</summary>
|
||||
Translucency = 76,
|
||||
/// <summary>269 weenie attestations.</summary>
|
||||
PhysicsScriptIntensity = 77,
|
||||
/// <summary>761 weenie attestations.</summary>
|
||||
Friction = 78,
|
||||
/// <summary>761 weenie attestations.</summary>
|
||||
Elasticity = 79,
|
||||
/// <summary>1076 weenie attestations.</summary>
|
||||
AiUseMagicDelay = 80,
|
||||
ItemMinSpellcraftMod = 81,
|
||||
ItemMaxSpellcraftMod = 82,
|
||||
ItemRankProbability = 83,
|
||||
Shade2 = 84,
|
||||
Shade3 = 85,
|
||||
Shade4 = 86,
|
||||
/// <summary>15 weenie attestations.</summary>
|
||||
ItemEfficiency = 87,
|
||||
ItemManaUpdateTimestamp = 88,
|
||||
SpellGestureSpeedMod = 89,
|
||||
SpellStanceSpeedMod = 90,
|
||||
AllegianceAppraisalTimestamp = 91,
|
||||
PowerLevel = 92,
|
||||
AccuracyLevel = 93,
|
||||
AttackAngle = 94,
|
||||
AttackTimestamp = 95,
|
||||
CheckpointTimestamp = 96,
|
||||
SoldTimestamp = 97,
|
||||
UseTimestamp = 98,
|
||||
UseLockTimestamp = 99,
|
||||
/// <summary>22 weenie attestations.</summary>
|
||||
HealkitMod = 100,
|
||||
FrozenTimestamp = 101,
|
||||
HealthRateMod = 102,
|
||||
AllegianceSwearTimestamp = 103,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ObviousRadarRange = 104,
|
||||
/// <summary>81 weenie attestations.</summary>
|
||||
HotspotCycleTime = 105,
|
||||
/// <summary>74 weenie attestations.</summary>
|
||||
HotspotCycleTimeVariance = 106,
|
||||
SpamTimestamp = 107,
|
||||
SpamRate = 108,
|
||||
/// <summary>26 weenie attestations.</summary>
|
||||
BondWieldedTreasure = 109,
|
||||
/// <summary>1039 weenie attestations.</summary>
|
||||
BulkMod = 110,
|
||||
/// <summary>1039 weenie attestations.</summary>
|
||||
SizeMod = 111,
|
||||
GagTimestamp = 112,
|
||||
GeneratorUpdateTimestamp = 113,
|
||||
DeathSpamTimestamp = 114,
|
||||
DeathSpamRate = 115,
|
||||
WildAttackProbability = 116,
|
||||
/// <summary>312 weenie attestations.</summary>
|
||||
FocusedProbability = 117,
|
||||
CrashAndTurnProbability = 118,
|
||||
CrashAndTurnRadius = 119,
|
||||
CrashAndTurnBias = 120,
|
||||
/// <summary>186 weenie attestations.</summary>
|
||||
GeneratorInitialDelay = 121,
|
||||
/// <summary>575 weenie attestations.</summary>
|
||||
AiAcquireHealth = 122,
|
||||
/// <summary>6 weenie attestations.</summary>
|
||||
AiAcquireStamina = 123,
|
||||
/// <summary>6 weenie attestations.</summary>
|
||||
AiAcquireMana = 124,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
ResistHealthDrain = 125,
|
||||
LifestoneProtectionTimestamp = 126,
|
||||
/// <summary>72 weenie attestations.</summary>
|
||||
AiCounteractEnchantment = 127,
|
||||
/// <summary>10 weenie attestations.</summary>
|
||||
AiDispelEnchantment = 128,
|
||||
TradeTimestamp = 129,
|
||||
AiTargetedDetectionRadius = 130,
|
||||
/// <summary>29 weenie attestations.</summary>
|
||||
EmotePriority = 131,
|
||||
LastTeleportStartTimestamp = 132,
|
||||
EventSpamTimestamp = 133,
|
||||
EventSpamRate = 134,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
InventoryOffset = 135,
|
||||
/// <summary>396 weenie attestations.</summary>
|
||||
CriticalMultiplier = 136,
|
||||
/// <summary>15 weenie attestations.</summary>
|
||||
ManaStoneDestroyChance = 137,
|
||||
/// <summary>351 weenie attestations.</summary>
|
||||
SlayerDamageBonus = 138,
|
||||
AllegianceInfoSpamTimestamp = 139,
|
||||
AllegianceInfoSpamRate = 140,
|
||||
NextSpellcastTimestamp = 141,
|
||||
AppraisalRequestedTimestamp = 142,
|
||||
AppraisalHeartbeatDueTimestamp = 143,
|
||||
/// <summary>209 weenie attestations.</summary>
|
||||
ManaConversionMod = 144,
|
||||
LastPkAttackTimestamp = 145,
|
||||
FellowshipUpdateTimestamp = 146,
|
||||
/// <summary>395 weenie attestations.</summary>
|
||||
CriticalFrequency = 147,
|
||||
LimboStartTimestamp = 148,
|
||||
/// <summary>356 weenie attestations.</summary>
|
||||
WeaponMissileDefense = 149,
|
||||
/// <summary>368 weenie attestations.</summary>
|
||||
WeaponMagicDefense = 150,
|
||||
/// <summary>87 weenie attestations.</summary>
|
||||
IgnoreShield = 151,
|
||||
/// <summary>135 weenie attestations.</summary>
|
||||
ElementalDamageMod = 152,
|
||||
StartMissileAttackTimestamp = 153,
|
||||
LastRareUsedTimestamp = 154,
|
||||
/// <summary>69 weenie attestations.</summary>
|
||||
IgnoreArmor = 155,
|
||||
ProcSpellRate = 156,
|
||||
/// <summary>93 weenie attestations.</summary>
|
||||
ResistanceModifier = 157,
|
||||
AllegianceGagTimestamp = 158,
|
||||
/// <summary>18 weenie attestations.</summary>
|
||||
AbsorbMagicDamage = 159,
|
||||
CachedMaxAbsorbMagicDamage = 160,
|
||||
GagDuration = 161,
|
||||
AllegianceGagDuration = 162,
|
||||
GlobalXpMod = 163,
|
||||
HealingModifier = 164,
|
||||
/// <summary>391 weenie attestations.</summary>
|
||||
ArmorModVsNether = 165,
|
||||
ResistNether = 166,
|
||||
/// <summary>369 weenie attestations.</summary>
|
||||
CooldownDuration = 167,
|
||||
WeaponAuraOffense = 168,
|
||||
WeaponAuraDefense = 169,
|
||||
WeaponAuraElemental = 170,
|
||||
WeaponAuraManaConv = 171,
|
||||
}
|
||||
79
src/AcDream.Core/Properties/PropertyInstanceId.cs
Normal file
79
src/AcDream.Core/Properties/PropertyInstanceId.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyInstanceId</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyInstanceId (0x02E5) / PublicUpdatePropertyInstanceId (0x02E6)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>uint (object guid)</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>InstanceId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 2 of these 46 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyInstanceId : uint
|
||||
{
|
||||
Undef = 0,
|
||||
Owner = 1,
|
||||
Container = 2,
|
||||
Wielder = 3,
|
||||
Freezer = 4,
|
||||
Viewer = 5,
|
||||
Generator = 6,
|
||||
Scribe = 7,
|
||||
CurrentCombatTarget = 8,
|
||||
CurrentEnemy = 9,
|
||||
ProjectileLauncher = 10,
|
||||
CurrentAttacker = 11,
|
||||
CurrentDamager = 12,
|
||||
CurrentFollowTarget = 13,
|
||||
CurrentAppraisalTarget = 14,
|
||||
CurrentFellowshipAppraisalTarget = 15,
|
||||
/// <summary>130 weenie attestations.</summary>
|
||||
ActivationTarget = 16,
|
||||
Creator = 17,
|
||||
Victim = 18,
|
||||
Killer = 19,
|
||||
Vendor = 20,
|
||||
Customer = 21,
|
||||
Bonded = 22,
|
||||
Wounder = 23,
|
||||
Allegiance = 24,
|
||||
Patron = 25,
|
||||
Monarch = 26,
|
||||
CombatTarget = 27,
|
||||
HealthQueryTarget = 28,
|
||||
LastUnlocker = 29,
|
||||
CrashAndTurnTarget = 30,
|
||||
AllowedActivator = 31,
|
||||
HouseOwner = 32,
|
||||
House = 33,
|
||||
Slumlord = 34,
|
||||
ManaQueryTarget = 35,
|
||||
CurrentGame = 36,
|
||||
RequestedAppraisalTarget = 37,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
AllowedWielder = 38,
|
||||
AssignedTarget = 39,
|
||||
LimboSource = 40,
|
||||
Snooper = 41,
|
||||
TeleportedCharacter = 42,
|
||||
Pet = 43,
|
||||
PetOwner = 44,
|
||||
PetDevice = 45,
|
||||
}
|
||||
611
src/AcDream.Core/Properties/PropertyInt.cs
Normal file
611
src/AcDream.Core/Properties/PropertyInt.cs
Normal file
|
|
@ -0,0 +1,611 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyInt</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyInt (0x02CD) / PublicUpdatePropertyInt (0x02CE)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>int</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>IntId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 189 of these 391 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyInt : uint
|
||||
{
|
||||
Undef = 0,
|
||||
/// <summary>UtilityBelt catalog calls this <c>ObjectType</c>; 37329 weenie attestations.</summary>
|
||||
ItemType = 1,
|
||||
/// <summary>6140 weenie attestations.</summary>
|
||||
CreatureType = 2,
|
||||
/// <summary>8344 weenie attestations.</summary>
|
||||
PaletteTemplate = 3,
|
||||
/// <summary>1597 weenie attestations.</summary>
|
||||
ClothingPriority = 4,
|
||||
/// <summary>25793 weenie attestations.</summary>
|
||||
EncumbranceVal = 5,
|
||||
/// <summary>8633 weenie attestations.</summary>
|
||||
ItemsCapacity = 6,
|
||||
/// <summary>8618 weenie attestations.</summary>
|
||||
ContainersCapacity = 7,
|
||||
/// <summary>23605 weenie attestations.</summary>
|
||||
Mass = 8,
|
||||
/// <summary>20797 weenie attestations.</summary>
|
||||
ValidLocations = 9,
|
||||
CurrentWieldedLocation = 10,
|
||||
/// <summary>4373 weenie attestations.</summary>
|
||||
MaxStackSize = 11,
|
||||
/// <summary>4373 weenie attestations.</summary>
|
||||
StackSize = 12,
|
||||
/// <summary>3181 weenie attestations.</summary>
|
||||
StackUnitEncumbrance = 13,
|
||||
/// <summary>3181 weenie attestations.</summary>
|
||||
StackUnitMass = 14,
|
||||
/// <summary>3181 weenie attestations.</summary>
|
||||
StackUnitValue = 15,
|
||||
/// <summary>37280 weenie attestations.</summary>
|
||||
ItemUseable = 16,
|
||||
/// <summary>21 weenie attestations.</summary>
|
||||
RareId = 17,
|
||||
/// <summary>5705 weenie attestations.</summary>
|
||||
UiEffects = 18,
|
||||
/// <summary>25852 weenie attestations.</summary>
|
||||
Value = 19,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
CoinValue = 20,
|
||||
TotalExperience = 21,
|
||||
AvailableCharacter = 22,
|
||||
TotalSkillCredits = 23,
|
||||
AvailableSkillCredits = 24,
|
||||
/// <summary>6140 weenie attestations.</summary>
|
||||
Level = 25,
|
||||
/// <summary>28 weenie attestations.</summary>
|
||||
AccountRequirements = 26,
|
||||
/// <summary>5476 weenie attestations.</summary>
|
||||
ArmorType = 27,
|
||||
/// <summary>1657 weenie attestations.</summary>
|
||||
ArmorLevel = 28,
|
||||
AllegianceCpPool = 29,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
AllegianceRank = 30,
|
||||
ChannelsAllowed = 31,
|
||||
ChannelsActive = 32,
|
||||
/// <summary>6252 weenie attestations.</summary>
|
||||
Bonded = 33,
|
||||
MonarchsRank = 34,
|
||||
AllegianceFollowers = 35,
|
||||
/// <summary>997 weenie attestations.</summary>
|
||||
ResistMagic = 36,
|
||||
/// <summary>575 weenie attestations.</summary>
|
||||
ResistItemAppraisal = 37,
|
||||
/// <summary>677 weenie attestations.</summary>
|
||||
ResistLockpick = 38,
|
||||
DeprecatedResistRepair = 39,
|
||||
/// <summary>1634 weenie attestations.</summary>
|
||||
CombatMode = 40,
|
||||
CurrentAttackHeight = 41,
|
||||
CombatCollisions = 42,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
NumDeaths = 43,
|
||||
/// <summary>3717 weenie attestations.</summary>
|
||||
Damage = 44,
|
||||
/// <summary>3357 weenie attestations.</summary>
|
||||
DamageType = 45,
|
||||
/// <summary>2973 weenie attestations.</summary>
|
||||
DefaultCombatStyle = 46,
|
||||
/// <summary>2364 weenie attestations.</summary>
|
||||
AttackType = 47,
|
||||
/// <summary>3393 weenie attestations.</summary>
|
||||
WeaponSkill = 48,
|
||||
/// <summary>3394 weenie attestations.</summary>
|
||||
WeaponTime = 49,
|
||||
/// <summary>972 weenie attestations.</summary>
|
||||
AmmoType = 50,
|
||||
/// <summary>4499 weenie attestations.</summary>
|
||||
CombatUse = 51,
|
||||
/// <summary>440 weenie attestations.</summary>
|
||||
ParentLocation = 52,
|
||||
/// <summary>440 weenie attestations.</summary>
|
||||
PlacementPosition = 53,
|
||||
WeaponEncumbrance = 54,
|
||||
WeaponMass = 55,
|
||||
ShieldValue = 56,
|
||||
ShieldEncumbrance = 57,
|
||||
MissileInventoryLocation = 58,
|
||||
FullDamageType = 59,
|
||||
/// <summary>545 weenie attestations.</summary>
|
||||
WeaponRange = 60,
|
||||
AttackersSkill = 61,
|
||||
DefendersSkill = 62,
|
||||
AttackersSkillValue = 63,
|
||||
AttackersClass = 64,
|
||||
Placement = 65,
|
||||
/// <summary>179 weenie attestations.</summary>
|
||||
CheckpointStatus = 66,
|
||||
/// <summary>219 weenie attestations.</summary>
|
||||
Tolerance = 67,
|
||||
/// <summary>2151 weenie attestations.</summary>
|
||||
TargetingTactic = 68,
|
||||
/// <summary>48 weenie attestations.</summary>
|
||||
CombatTactic = 69,
|
||||
HomesickTargetingTactic = 70,
|
||||
NumFollowFailures = 71,
|
||||
/// <summary>494 weenie attestations.</summary>
|
||||
FriendType = 72,
|
||||
FoeType = 73,
|
||||
/// <summary>UtilityBelt catalog calls this <c>MerchandiseObjectTypes</c>; 1049 weenie attestations.</summary>
|
||||
MerchandiseItemTypes = 74,
|
||||
/// <summary>1049 weenie attestations.</summary>
|
||||
MerchandiseMinValue = 75,
|
||||
/// <summary>1049 weenie attestations.</summary>
|
||||
MerchandiseMaxValue = 76,
|
||||
NumItemsSold = 77,
|
||||
NumItemsBought = 78,
|
||||
MoneyIncome = 79,
|
||||
MoneyOutflow = 80,
|
||||
/// <summary>2195 weenie attestations.</summary>
|
||||
MaxGeneratedObjects = 81,
|
||||
/// <summary>2195 weenie attestations.</summary>
|
||||
InitGeneratedObjects = 82,
|
||||
/// <summary>787 weenie attestations.</summary>
|
||||
ActivationResponse = 83,
|
||||
OriginalValue = 84,
|
||||
NumMoveFailures = 85,
|
||||
/// <summary>815 weenie attestations.</summary>
|
||||
MinLevel = 86,
|
||||
/// <summary>115 weenie attestations.</summary>
|
||||
MaxLevel = 87,
|
||||
/// <summary>10 weenie attestations.</summary>
|
||||
LockpickMod = 88,
|
||||
/// <summary>525 weenie attestations.</summary>
|
||||
BoosterEnum = 89,
|
||||
/// <summary>532 weenie attestations.</summary>
|
||||
BoostValue = 90,
|
||||
/// <summary>800 weenie attestations.</summary>
|
||||
MaxStructure = 91,
|
||||
/// <summary>713 weenie attestations.</summary>
|
||||
Structure = 92,
|
||||
/// <summary>38985 weenie attestations.</summary>
|
||||
PhysicsState = 93,
|
||||
/// <summary>3570 weenie attestations.</summary>
|
||||
TargetType = 94,
|
||||
/// <summary>2668 weenie attestations.</summary>
|
||||
RadarBlipColor = 95,
|
||||
/// <summary>472 weenie attestations.</summary>
|
||||
EncumbranceCapacity = 96,
|
||||
LoginTimestamp = 97,
|
||||
/// <summary>119 weenie attestations.</summary>
|
||||
CreationTimestamp = 98,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
PkLevelModifier = 99,
|
||||
/// <summary>575 weenie attestations.</summary>
|
||||
GeneratorType = 100,
|
||||
/// <summary>1265 weenie attestations.</summary>
|
||||
AiAllowedCombatStyle = 101,
|
||||
LogoffTimestamp = 102,
|
||||
/// <summary>158 weenie attestations.</summary>
|
||||
GeneratorDestructionType = 103,
|
||||
ActivationCreateClass = 104,
|
||||
/// <summary>693 weenie attestations.</summary>
|
||||
ItemWorkmanship = 105,
|
||||
/// <summary>4213 weenie attestations.</summary>
|
||||
ItemSpellcraft = 106,
|
||||
/// <summary>4173 weenie attestations.</summary>
|
||||
ItemCurMana = 107,
|
||||
/// <summary>4211 weenie attestations.</summary>
|
||||
ItemMaxMana = 108,
|
||||
/// <summary>3091 weenie attestations.</summary>
|
||||
ItemDifficulty = 109,
|
||||
/// <summary>1043 weenie attestations.</summary>
|
||||
ItemAllegianceRankLimit = 110,
|
||||
/// <summary>2940 weenie attestations.</summary>
|
||||
PortalBitmask = 111,
|
||||
AdvocateLevel = 112,
|
||||
/// <summary>749 weenie attestations.</summary>
|
||||
Gender = 113,
|
||||
/// <summary>4502 weenie attestations.</summary>
|
||||
Attuned = 114,
|
||||
/// <summary>1640 weenie attestations.</summary>
|
||||
ItemSkillLevelLimit = 115,
|
||||
GateLogic = 116,
|
||||
/// <summary>66 weenie attestations.</summary>
|
||||
ItemManaCost = 117,
|
||||
Logoff = 118,
|
||||
/// <summary>365 weenie attestations.</summary>
|
||||
Active = 119,
|
||||
AttackHeight = 120,
|
||||
NumAttackFailures = 121,
|
||||
AiCpThreshold = 122,
|
||||
AiAdvancementStrategy = 123,
|
||||
Version = 124,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
Age = 125,
|
||||
/// <summary>1048 weenie attestations.</summary>
|
||||
VendorHappyMean = 126,
|
||||
/// <summary>1048 weenie attestations.</summary>
|
||||
VendorHappyVariance = 127,
|
||||
CloakStatus = 128,
|
||||
VitaeCpPool = 129,
|
||||
NumServicesSold = 130,
|
||||
/// <summary>586 weenie attestations.</summary>
|
||||
MaterialType = 131,
|
||||
NumAllegianceBreaks = 132,
|
||||
/// <summary>10541 weenie attestations.</summary>
|
||||
ShowableOnRadar = 133,
|
||||
/// <summary>3138 weenie attestations.</summary>
|
||||
PlayerKillerStatus = 134,
|
||||
VendorHappyMaxItems = 135,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScorePageNum = 136,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreConfigNum = 137,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreNumScores = 138,
|
||||
DeathLevel = 139,
|
||||
/// <summary>1411 weenie attestations.</summary>
|
||||
AiOptions = 140,
|
||||
OpenToEveryone = 141,
|
||||
/// <summary>297 weenie attestations.</summary>
|
||||
GeneratorTimeType = 142,
|
||||
/// <summary>51 weenie attestations.</summary>
|
||||
GeneratorStartTime = 143,
|
||||
/// <summary>50 weenie attestations.</summary>
|
||||
GeneratorEndTime = 144,
|
||||
/// <summary>257 weenie attestations.</summary>
|
||||
GeneratorEndDestructionType = 145,
|
||||
/// <summary>4361 weenie attestations.</summary>
|
||||
XpOverride = 146,
|
||||
NumCrashAndTurns = 147,
|
||||
ComponentWarningThreshold = 148,
|
||||
/// <summary>7 weenie attestations.</summary>
|
||||
HouseStatus = 149,
|
||||
/// <summary>4715 weenie attestations.</summary>
|
||||
HookPlacement = 150,
|
||||
/// <summary>6799 weenie attestations.</summary>
|
||||
HookType = 151,
|
||||
/// <summary>UtilityBelt catalog calls this <c>HookObjectType</c>; 5 weenie attestations.</summary>
|
||||
HookItemType = 152,
|
||||
AiPpThreshold = 153,
|
||||
GeneratorVersion = 154,
|
||||
/// <summary>6274 weenie attestations.</summary>
|
||||
HouseType = 155,
|
||||
/// <summary>10 weenie attestations.</summary>
|
||||
PickupEmoteOffset = 156,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
WeenieIteration = 157,
|
||||
/// <summary>2557 weenie attestations.</summary>
|
||||
WieldRequirements = 158,
|
||||
/// <summary>2557 weenie attestations.</summary>
|
||||
WieldSkilltype = 159,
|
||||
/// <summary>2557 weenie attestations.</summary>
|
||||
WieldDifficulty = 160,
|
||||
/// <summary>654 weenie attestations.</summary>
|
||||
HouseMaxHooksUsable = 161,
|
||||
HouseCurrentHooksUsable = 162,
|
||||
/// <summary>8 weenie attestations.</summary>
|
||||
AllegianceMinLevel = 163,
|
||||
AllegianceMaxLevel = 164,
|
||||
HouseRelinkHookCount = 165,
|
||||
/// <summary>537 weenie attestations.</summary>
|
||||
SlayerCreatureType = 166,
|
||||
ConfirmationInProgress = 167,
|
||||
ConfirmationTypeInProgress = 168,
|
||||
/// <summary>728 weenie attestations.</summary>
|
||||
TsysMutationData = 169,
|
||||
/// <summary>36 weenie attestations.</summary>
|
||||
NumItemsInMaterial = 170,
|
||||
/// <summary>228 weenie attestations.</summary>
|
||||
NumTimesTinkered = 171,
|
||||
/// <summary>358 weenie attestations.</summary>
|
||||
AppraisalLongDescDecoration = 172,
|
||||
/// <summary>133 weenie attestations.</summary>
|
||||
AppraisalLockpickSuccessPercent = 173,
|
||||
/// <summary>103 weenie attestations.</summary>
|
||||
AppraisalPages = 174,
|
||||
/// <summary>103 weenie attestations.</summary>
|
||||
AppraisalMaxPages = 175,
|
||||
/// <summary>355 weenie attestations.</summary>
|
||||
AppraisalItemSkill = 176,
|
||||
/// <summary>281 weenie attestations.</summary>
|
||||
GemCount = 177,
|
||||
/// <summary>281 weenie attestations.</summary>
|
||||
GemType = 178,
|
||||
/// <summary>495 weenie attestations.</summary>
|
||||
ImbuedEffect = 179,
|
||||
AttackersRawSkillValue = 180,
|
||||
ChessRank = 181,
|
||||
ChessTotalGames = 182,
|
||||
ChessGamesWon = 183,
|
||||
ChessGamesLost = 184,
|
||||
/// <summary>71 weenie attestations.</summary>
|
||||
TypeOfAlteration = 185,
|
||||
/// <summary>71 weenie attestations.</summary>
|
||||
SkillToBeAltered = 186,
|
||||
SkillAlterationCount = 187,
|
||||
/// <summary>807 weenie attestations.</summary>
|
||||
HeritageGroup = 188,
|
||||
/// <summary>30 weenie attestations.</summary>
|
||||
TransferFromAttribute = 189,
|
||||
/// <summary>30 weenie attestations.</summary>
|
||||
TransferToAttribute = 190,
|
||||
AttributeTransferCount = 191,
|
||||
FakeFishingSkill = 192,
|
||||
/// <summary>3 weenie attestations.</summary>
|
||||
NumKeys = 193,
|
||||
DeathTimestamp = 194,
|
||||
PkTimestamp = 195,
|
||||
VictimTimestamp = 196,
|
||||
/// <summary>22 weenie attestations.</summary>
|
||||
HookGroup = 197,
|
||||
AllegianceSwearTimestamp = 198,
|
||||
HousePurchaseTimestamp = 199,
|
||||
RedirectableEquippedArmorCount = 200,
|
||||
MeleedefenseImbuedEffectTypeCache = 201,
|
||||
MissileDefenseImbuedEffectTypeCache = 202,
|
||||
MagicDefenseImbuedEffectTypeCache = 203,
|
||||
/// <summary>121 weenie attestations.</summary>
|
||||
ElementalDamageBonus = 204,
|
||||
ImbueAttempts = 205,
|
||||
ImbueSuccesses = 206,
|
||||
CreatureKills = 207,
|
||||
PlayerKillsPk = 208,
|
||||
PlayerKillsPkl = 209,
|
||||
RaresTierOne = 210,
|
||||
RaresTierTwo = 211,
|
||||
RaresTierThree = 212,
|
||||
RaresTierFour = 213,
|
||||
RaresTierFive = 214,
|
||||
AugmentationStat = 215,
|
||||
AugmentationFamilyStat = 216,
|
||||
AugmentationInnateFamily = 217,
|
||||
AugmentationInnateStrength = 218,
|
||||
AugmentationInnateEndurance = 219,
|
||||
AugmentationInnateCoordination = 220,
|
||||
AugmentationInnateQuickness = 221,
|
||||
AugmentationInnateFocus = 222,
|
||||
AugmentationInnateSelf = 223,
|
||||
AugmentationSpecializeSalvaging = 224,
|
||||
AugmentationSpecializeItemTinkering = 225,
|
||||
AugmentationSpecializeArmorTinkering = 226,
|
||||
AugmentationSpecializeMagicItemTinkering = 227,
|
||||
AugmentationSpecializeWeaponTinkering = 228,
|
||||
AugmentationExtraPackSlot = 229,
|
||||
AugmentationIncreasedCarryingCapacity = 230,
|
||||
AugmentationLessDeathItemLoss = 231,
|
||||
AugmentationSpellsRemainPastDeath = 232,
|
||||
AugmentationCriticalDefense = 233,
|
||||
AugmentationBonusXp = 234,
|
||||
AugmentationBonusSalvage = 235,
|
||||
AugmentationBonusImbueChance = 236,
|
||||
AugmentationFasterRegen = 237,
|
||||
AugmentationIncreasedSpellDuration = 238,
|
||||
AugmentationResistanceFamily = 239,
|
||||
AugmentationResistanceSlash = 240,
|
||||
AugmentationResistancePierce = 241,
|
||||
AugmentationResistanceBlunt = 242,
|
||||
AugmentationResistanceAcid = 243,
|
||||
AugmentationResistanceFire = 244,
|
||||
AugmentationResistanceFrost = 245,
|
||||
AugmentationResistanceLightning = 246,
|
||||
RaresTierOneLogin = 247,
|
||||
RaresTierTwoLogin = 248,
|
||||
RaresTierThreeLogin = 249,
|
||||
RaresTierFourLogin = 250,
|
||||
RaresTierFiveLogin = 251,
|
||||
RaresLoginTimestamp = 252,
|
||||
RaresTierSix = 253,
|
||||
RaresTierSeven = 254,
|
||||
RaresTierSixLogin = 255,
|
||||
RaresTierSevenLogin = 256,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
ItemAttributeLimit = 257,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
ItemAttributeLevelLimit = 258,
|
||||
ItemAttribute2ndLimit = 259,
|
||||
ItemAttribute2ndLevelLimit = 260,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
CharacterTitleId = 261,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
NumCharacterTitles = 262,
|
||||
/// <summary>93 weenie attestations.</summary>
|
||||
ResistanceModifierType = 263,
|
||||
FreeTinkersBitfield = 264,
|
||||
/// <summary>316 weenie attestations.</summary>
|
||||
EquipmentSetId = 265,
|
||||
PetClass = 266,
|
||||
/// <summary>119 weenie attestations.</summary>
|
||||
Lifespan = 267,
|
||||
/// <summary>119 weenie attestations.</summary>
|
||||
RemainingLifespan = 268,
|
||||
UseCreateQuantity = 269,
|
||||
/// <summary>62 weenie attestations.</summary>
|
||||
WieldRequirements2 = 270,
|
||||
/// <summary>62 weenie attestations.</summary>
|
||||
WieldSkilltype2 = 271,
|
||||
/// <summary>62 weenie attestations.</summary>
|
||||
WieldDifficulty2 = 272,
|
||||
/// <summary>3 weenie attestations.</summary>
|
||||
WieldRequirements3 = 273,
|
||||
/// <summary>3 weenie attestations.</summary>
|
||||
WieldSkilltype3 = 274,
|
||||
/// <summary>3 weenie attestations.</summary>
|
||||
WieldDifficulty3 = 275,
|
||||
/// <summary>17 weenie attestations.</summary>
|
||||
WieldRequirements4 = 276,
|
||||
/// <summary>17 weenie attestations.</summary>
|
||||
WieldSkilltype4 = 277,
|
||||
/// <summary>17 weenie attestations.</summary>
|
||||
WieldDifficulty4 = 278,
|
||||
/// <summary>14 weenie attestations.</summary>
|
||||
Unique = 279,
|
||||
/// <summary>365 weenie attestations.</summary>
|
||||
SharedCooldown = 280,
|
||||
/// <summary>177 weenie attestations.</summary>
|
||||
Faction1Bits = 281,
|
||||
Faction2Bits = 282,
|
||||
Faction3Bits = 283,
|
||||
Hatred1Bits = 284,
|
||||
Hatred2Bits = 285,
|
||||
Hatred3Bits = 286,
|
||||
/// <summary>61 weenie attestations.</summary>
|
||||
SocietyRankCelhan = 287,
|
||||
/// <summary>60 weenie attestations.</summary>
|
||||
SocietyRankEldweb = 288,
|
||||
/// <summary>56 weenie attestations.</summary>
|
||||
SocietyRankRadblo = 289,
|
||||
HearLocalSignals = 290,
|
||||
HearLocalSignalsRadius = 291,
|
||||
/// <summary>67 weenie attestations.</summary>
|
||||
Cleaving = 292,
|
||||
AugmentationSpecializeGearcraft = 293,
|
||||
AugmentationInfusedCreatureMagic = 294,
|
||||
AugmentationInfusedItemMagic = 295,
|
||||
AugmentationInfusedLifeMagic = 296,
|
||||
AugmentationInfusedWarMagic = 297,
|
||||
AugmentationCriticalExpertise = 298,
|
||||
AugmentationCriticalPower = 299,
|
||||
AugmentationSkilledMelee = 300,
|
||||
AugmentationSkilledMissile = 301,
|
||||
AugmentationSkilledMagic = 302,
|
||||
/// <summary>124 weenie attestations.</summary>
|
||||
ImbuedEffect2 = 303,
|
||||
/// <summary>124 weenie attestations.</summary>
|
||||
ImbuedEffect3 = 304,
|
||||
/// <summary>117 weenie attestations.</summary>
|
||||
ImbuedEffect4 = 305,
|
||||
/// <summary>117 weenie attestations.</summary>
|
||||
ImbuedEffect5 = 306,
|
||||
/// <summary>1403 weenie attestations.</summary>
|
||||
DamageRating = 307,
|
||||
/// <summary>729 weenie attestations.</summary>
|
||||
DamageResistRating = 308,
|
||||
AugmentationDamageBonus = 309,
|
||||
AugmentationDamageReduction = 310,
|
||||
ImbueStackingBits = 311,
|
||||
HealOverTime = 312,
|
||||
/// <summary>880 weenie attestations.</summary>
|
||||
CritRating = 313,
|
||||
/// <summary>843 weenie attestations.</summary>
|
||||
CritDamageRating = 314,
|
||||
/// <summary>713 weenie attestations.</summary>
|
||||
CritResistRating = 315,
|
||||
/// <summary>732 weenie attestations.</summary>
|
||||
CritDamageResistRating = 316,
|
||||
HealingResistRating = 317,
|
||||
DamageOverTime = 318,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
ItemMaxLevel = 319,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
ItemXpStyle = 320,
|
||||
EquipmentSetExtra = 321,
|
||||
AetheriaBitfield = 322,
|
||||
HealingBoostRating = 323,
|
||||
/// <summary>29 weenie attestations.</summary>
|
||||
HeritageSpecificArmor = 324,
|
||||
AlternateRacialSkills = 325,
|
||||
AugmentationJackOfAllTrades = 326,
|
||||
AugmentationResistanceNether = 327,
|
||||
AugmentationInfusedVoidMagic = 328,
|
||||
WeaknessRating = 329,
|
||||
NetherOverTime = 330,
|
||||
NetherResistRating = 331,
|
||||
LuminanceAward = 332,
|
||||
LumAugDamageRating = 333,
|
||||
LumAugDamageReductionRating = 334,
|
||||
LumAugCritDamageRating = 335,
|
||||
LumAugCritReductionRating = 336,
|
||||
LumAugSurgeEffectRating = 337,
|
||||
LumAugSurgeChanceRating = 338,
|
||||
LumAugItemManaUsage = 339,
|
||||
LumAugItemManaGain = 340,
|
||||
LumAugVitality = 341,
|
||||
LumAugHealingRating = 342,
|
||||
LumAugSkilledCraft = 343,
|
||||
LumAugSkilledSpec = 344,
|
||||
LumAugNoDestroyCraft = 345,
|
||||
RestrictInteraction = 346,
|
||||
OlthoiLootTimestamp = 347,
|
||||
OlthoiLootStep = 348,
|
||||
UseCreatesContractId = 349,
|
||||
DotResistRating = 350,
|
||||
LifeResistRating = 351,
|
||||
/// <summary>13 weenie attestations.</summary>
|
||||
CloakWeaveProc = 352,
|
||||
/// <summary>492 weenie attestations.</summary>
|
||||
WeaponType = 353,
|
||||
MeleeMastery = 354,
|
||||
RangedMastery = 355,
|
||||
SneakAttackRating = 356,
|
||||
RecklessnessRating = 357,
|
||||
DeceptionRating = 358,
|
||||
CombatPetRange = 359,
|
||||
WeaponAuraDamage = 360,
|
||||
WeaponAuraSpeed = 361,
|
||||
SummoningMastery = 362,
|
||||
HeartbeatLifespan = 363,
|
||||
UseLevelRequirement = 364,
|
||||
LumAugAllSkills = 365,
|
||||
/// <summary>259 weenie attestations.</summary>
|
||||
UseRequiresSkill = 366,
|
||||
/// <summary>259 weenie attestations.</summary>
|
||||
UseRequiresSkillLevel = 367,
|
||||
/// <summary>36 weenie attestations.</summary>
|
||||
UseRequiresSkillSpec = 368,
|
||||
/// <summary>271 weenie attestations.</summary>
|
||||
UseRequiresLevel = 369,
|
||||
/// <summary>869 weenie attestations.</summary>
|
||||
GearDamage = 370,
|
||||
/// <summary>868 weenie attestations.</summary>
|
||||
GearDamageResist = 371,
|
||||
/// <summary>854 weenie attestations.</summary>
|
||||
GearCrit = 372,
|
||||
/// <summary>854 weenie attestations.</summary>
|
||||
GearCritResist = 373,
|
||||
/// <summary>955 weenie attestations.</summary>
|
||||
GearCritDamage = 374,
|
||||
/// <summary>953 weenie attestations.</summary>
|
||||
GearCritDamageResist = 375,
|
||||
/// <summary>610 weenie attestations.</summary>
|
||||
GearHealingBoost = 376,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
GearNetherResist = 377,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
GearLifeResist = 378,
|
||||
/// <summary>640 weenie attestations.</summary>
|
||||
GearMaxHealth = 379,
|
||||
Unknown380 = 380,
|
||||
/// <summary>616 weenie attestations.</summary>
|
||||
PKDamageRating = 381,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
PKDamageResistRating = 382,
|
||||
/// <summary>643 weenie attestations.</summary>
|
||||
GearPKDamageRating = 383,
|
||||
/// <summary>640 weenie attestations.</summary>
|
||||
GearPKDamageResistRating = 384,
|
||||
Unknown385 = 385,
|
||||
/// <summary>766 weenie attestations.</summary>
|
||||
Overpower = 386,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
OverpowerResist = 387,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
GearOverpower = 388,
|
||||
/// <summary>602 weenie attestations.</summary>
|
||||
GearOverpowerResist = 389,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
Enlightenment = 390,
|
||||
}
|
||||
43
src/AcDream.Core/Properties/PropertyInt64.cs
Normal file
43
src/AcDream.Core/Properties/PropertyInt64.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyInt64</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyInt64 (0x02D1)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>long</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>Int64Id</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 3 of these 9 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyInt64 : uint
|
||||
{
|
||||
Undef = 0,
|
||||
TotalExperience = 1,
|
||||
AvailableExperience = 2,
|
||||
/// <summary>7 weenie attestations.</summary>
|
||||
AugmentationCost = 3,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
ItemTotalXp = 4,
|
||||
/// <summary>79 weenie attestations.</summary>
|
||||
ItemBaseXp = 5,
|
||||
AvailableLuminance = 6,
|
||||
MaximumLuminance = 7,
|
||||
InteractionReqs = 8,
|
||||
}
|
||||
112
src/AcDream.Core/Properties/PropertyString.cs
Normal file
112
src/AcDream.Core/Properties/PropertyString.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// <auto-generated-source>
|
||||
// Regenerate with the 2026-07-29 enum verification campaign tooling; see
|
||||
// docs/research/2026-07-29-enum-verification-campaign.md for the oracle set,
|
||||
// the extraction method, and the open-question ledger.
|
||||
// Hand edits are pinned by PropertyEnumConformanceTests — change the oracle,
|
||||
// not the enum.
|
||||
// </auto-generated-source>
|
||||
|
||||
namespace AcDream.Core.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// AC's <c>PropertyString</c> property table — the numeric keys the server sends in
|
||||
/// <c>PrivateUpdatePropertyString (0x02DD) / PublicUpdatePropertyString (0x02DE)</c>
|
||||
/// and in the property bundles carried by <c>CreateObject</c> / <c>PlayerDescription</c> /
|
||||
/// <c>IdentifyResponse</c>. The CLR payload for this table is <c>string</c>.
|
||||
///
|
||||
/// <para>Verified 2026-07-29 against two independent oracles: the vendored
|
||||
/// client-side enum catalog (<c>references/acclientlib/UtilityBelt.Common/Enums/Enums.cs</c>,
|
||||
/// which names this table <c>StringId</c>) and the 38,985-file ACE weenie export corpus
|
||||
/// (<c>references/weenies/</c>), whose per-stat <c>_comment</c> carries the enum member
|
||||
/// name beside the numeric key. 28 of these 53 members are attested by BOTH
|
||||
/// oracles with zero value conflicts; the remainder are single-sourced from the
|
||||
/// catalog because no weenie in the corpus sets them.</para>
|
||||
///
|
||||
/// <para>This enum is a <b>vocabulary</b>, not a parser contract: the wire carries a raw
|
||||
/// <c>uint</c> and unknown keys must still round-trip untouched, so never assume a
|
||||
/// received key is a defined member.</para>
|
||||
/// </summary>
|
||||
public enum PropertyString : uint
|
||||
{
|
||||
Undef = 0,
|
||||
/// <summary>38984 weenie attestations.</summary>
|
||||
Name = 1,
|
||||
Title = 2,
|
||||
/// <summary>1697 weenie attestations.</summary>
|
||||
Sex = 3,
|
||||
/// <summary>1693 weenie attestations.</summary>
|
||||
HeritageGroup = 4,
|
||||
/// <summary>1622 weenie attestations.</summary>
|
||||
Template = 5,
|
||||
AttackersName = 6,
|
||||
/// <summary>93 weenie attestations.</summary>
|
||||
Inscription = 7,
|
||||
/// <summary>93 weenie attestations.</summary>
|
||||
ScribeName = 8,
|
||||
VendorsName = 9,
|
||||
/// <summary>1 weenie attestations.</summary>
|
||||
Fellowship = 10,
|
||||
MonarchsName = 11,
|
||||
/// <summary>449 weenie attestations.</summary>
|
||||
LockCode = 12,
|
||||
/// <summary>238 weenie attestations.</summary>
|
||||
KeyCode = 13,
|
||||
/// <summary>3219 weenie attestations.</summary>
|
||||
Use = 14,
|
||||
/// <summary>5597 weenie attestations.</summary>
|
||||
ShortDesc = 15,
|
||||
/// <summary>7107 weenie attestations.</summary>
|
||||
LongDesc = 16,
|
||||
/// <summary>109 weenie attestations.</summary>
|
||||
ActivationTalk = 17,
|
||||
/// <summary>10 weenie attestations.</summary>
|
||||
UseMessage = 18,
|
||||
/// <summary>45 weenie attestations.</summary>
|
||||
ItemHeritageGroupRestriction = 19,
|
||||
/// <summary>1318 weenie attestations.</summary>
|
||||
PluralName = 20,
|
||||
MonarchsTitle = 21,
|
||||
/// <summary>96 weenie attestations.</summary>
|
||||
ActivationFailure = 22,
|
||||
ScribeAccount = 23,
|
||||
/// <summary>591 weenie attestations.</summary>
|
||||
TownName = 24,
|
||||
/// <summary>2 weenie attestations.</summary>
|
||||
CraftsmanName = 25,
|
||||
/// <summary>4 weenie attestations.</summary>
|
||||
UsePkServerError = 26,
|
||||
ScoreCachedText = 27,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreDefaultEntryFormat = 28,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreFirstEntryFormat = 29,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreLastEntryFormat = 30,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreOnlyEntryFormat = 31,
|
||||
/// <summary>5 weenie attestations.</summary>
|
||||
ScoreNoEntry = 32,
|
||||
/// <summary>859 weenie attestations.</summary>
|
||||
Quest = 33,
|
||||
/// <summary>249 weenie attestations.</summary>
|
||||
GeneratorEvent = 34,
|
||||
PatronsTitle = 35,
|
||||
HouseOwnerName = 36,
|
||||
/// <summary>170 weenie attestations.</summary>
|
||||
QuestRestriction = 37,
|
||||
AppraisalPortalDestination = 38,
|
||||
TinkerName = 39,
|
||||
ImbuerName = 40,
|
||||
HouseOwnerAccount = 41,
|
||||
DisplayName = 42,
|
||||
DateOfBirth = 43,
|
||||
ThirdPartyApi = 44,
|
||||
KillQuest = 45,
|
||||
Afk = 46,
|
||||
AllegianceName = 47,
|
||||
AugmentationAddQuest = 48,
|
||||
KillQuest2 = 49,
|
||||
KillQuest3 = 50,
|
||||
UseSendsSignal = 51,
|
||||
GearPlatingName = 52,
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using AcDream.Core.Items;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// Pins the three item-data enums the 2026-07-29 campaign added — all of them describe
|
||||
/// fields acdream already parses off the wire and stored as bare numbers. Tables are
|
||||
/// transcribed from <c>docs/research/named-retail/acclient.h</c>.
|
||||
/// </summary>
|
||||
public sealed class ItemWireEnumConformanceTests
|
||||
{
|
||||
/// <summary>acclient.h:4221, <c>enum AMMO_TYPE</c>.</summary>
|
||||
public static TheoryData<string, uint> RetailAmmoType => new()
|
||||
{
|
||||
{ "None", 0x0 },
|
||||
{ "Arrow", 0x1 },
|
||||
{ "Bolt", 0x2 },
|
||||
{ "Atlatl", 0x4 },
|
||||
{ "ArrowCrystal", 0x8 },
|
||||
{ "BoltCrystal", 0x10 },
|
||||
{ "AtlatlCrystal", 0x20 },
|
||||
{ "ArrowChorizite", 0x40 },
|
||||
{ "BoltChorizite", 0x80 },
|
||||
{ "AtlatlChorizite", 0x100 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailAmmoType))]
|
||||
public void AmmoTypeMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(AmmoType), name), $"AmmoType.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<AmmoType>(name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AmmoTypeDeclaresNothingRetailDoesNot()
|
||||
{
|
||||
var expected = RetailAmmoType.Select(r => (string)r[0]).OrderBy(n => n, StringComparer.Ordinal);
|
||||
Assert.Equal(expected, Enum.GetNames<AmmoType>().OrderBy(n => n, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:6523, <c>enum COMBAT_USE</c>.</summary>
|
||||
[Theory]
|
||||
[InlineData("None", 0u)]
|
||||
[InlineData("Melee", 1u)]
|
||||
[InlineData("Missile", 2u)]
|
||||
[InlineData("Ammo", 3u)]
|
||||
[InlineData("Shield", 4u)]
|
||||
[InlineData("TwoHanded", 5u)]
|
||||
public void CombatUseMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(CombatUse), name), $"CombatUse.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<CombatUse>(name));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:6478, <c>enum ITEM_USEABLE</c>, including the composites.</summary>
|
||||
public static TheoryData<string, uint> RetailItemUseable => new()
|
||||
{
|
||||
{ "Undef", 0x0 },
|
||||
{ "No", 0x1 },
|
||||
{ "Self", 0x2 },
|
||||
{ "Wielded", 0x4 },
|
||||
{ "Contained", 0x8 },
|
||||
{ "Viewed", 0x10 },
|
||||
{ "ContainedViewed", 0x18 },
|
||||
{ "Remote", 0x20 },
|
||||
{ "ViewedRemote", 0x30 },
|
||||
{ "ContainedViewedRemote", 0x38 },
|
||||
{ "NeverWalk", 0x40 },
|
||||
{ "RemoteNeverWalk", 0x60 },
|
||||
{ "ViewedRemoteNeverWalk", 0x70 },
|
||||
{ "ContainedViewedRemoteNeverWalk", 0x78 },
|
||||
{ "ObjSelf", 0x80 },
|
||||
{ "SourceMask", 0xFFFF },
|
||||
{ "SourceWieldedTargetWielded", 0x40004 },
|
||||
{ "SourceContainedTargetWielded", 0x40008 },
|
||||
{ "SourceViewedTargetWielded", 0x40010 },
|
||||
{ "SourceRemoteTargetWielded", 0x40020 },
|
||||
{ "SourceWieldedTargetContained", 0x80004 },
|
||||
{ "SourceContainedTargetContained", 0x80008 },
|
||||
{ "SourceViewedTargetContained", 0x80010 },
|
||||
{ "SourceRemoteTargetContained", 0x80020 },
|
||||
{ "SourceContainedTargetSelfOrContained", 0xA0008 },
|
||||
{ "SourceWieldedTargetViewed", 0x100004 },
|
||||
{ "SourceContainedTargetViewed", 0x100008 },
|
||||
{ "SourceViewedTargetViewed", 0x100010 },
|
||||
{ "SourceRemoteTargetViewed", 0x100020 },
|
||||
{ "SourceWieldedTargetRemote", 0x200004 },
|
||||
{ "SourceContainedTargetRemote", 0x200008 },
|
||||
{ "SourceViewedTargetRemote", 0x200010 },
|
||||
{ "SourceRemoteTargetRemote", 0x200020 },
|
||||
{ "SourceContainedTargetRemoteOrSelf", 0x220008 },
|
||||
{ "SourceWieldedTargetRemoteNeverWalk", 0x600004 },
|
||||
{ "SourceContainedTargetRemoteNeverWalk", 0x600008 },
|
||||
{ "SourceRemoteTargetRemoteNeverWalk", 0x600020 },
|
||||
{ "SourceContainedTargetObjSelfOrContained", 0x880008 },
|
||||
{ "TargetMask", 0xFFFF0000 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailItemUseable))]
|
||||
public void ItemUseableMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(ItemUseable), name), $"ItemUseable.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<ItemUseable>(name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItemUseableDeclaresNothingRetailDoesNot()
|
||||
{
|
||||
var expected = RetailItemUseable.Select(r => (string)r[0]).OrderBy(n => n, StringComparer.Ordinal);
|
||||
Assert.Equal(expected, Enum.GetNames<ItemUseable>().OrderBy(n => n, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The two halves partition the word, and at least one retail composite is
|
||||
/// deliberately not the obvious union — which is why they are transcribed rather
|
||||
/// than composed.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ItemUseableSourceAndTargetMasksPartitionTheWord()
|
||||
{
|
||||
Assert.Equal(0u, (uint)ItemUseable.SourceMask & (uint)ItemUseable.TargetMask);
|
||||
Assert.Equal(uint.MaxValue, (uint)ItemUseable.SourceMask | (uint)ItemUseable.TargetMask);
|
||||
|
||||
uint naiveUnion = (uint)ItemUseable.ObjSelf | (uint)ItemUseable.Contained
|
||||
| ((uint)ItemUseable.Contained << 16);
|
||||
Assert.NotEqual(naiveUnion, (uint)ItemUseable.SourceContainedTargetObjSelfOrContained);
|
||||
}
|
||||
}
|
||||
1398
tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs
Normal file
1398
tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,285 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// Pins the wire-adjacent enums acdream shares with the retail client to the values
|
||||
/// in the Sept 2013 EoR header, <c>docs/research/named-retail/acclient.h</c>. Each
|
||||
/// table below is transcribed from the cited <c>enum</c> block, so a hand edit that
|
||||
/// drifts from retail fails here.
|
||||
///
|
||||
/// <para>These tables are the reason the 2026-07-29 enum campaign found real bugs:
|
||||
/// <see cref="DamageType"/> had its four drain/restore bits rotated, and
|
||||
/// <see cref="ItemType"/> had a shifted craft ladder plus locally recomputed composite
|
||||
/// masks. Both are corrected and pinned below.</para>
|
||||
/// </summary>
|
||||
public sealed class RetailEnumConformanceTests
|
||||
{
|
||||
/// <summary>acclient.h:3788, <c>enum DAMAGE_TYPE</c>. FORCE_*_32_BIT omitted.</summary>
|
||||
public static TheoryData<string, uint> RetailDamageType => new()
|
||||
{
|
||||
{ "Undef", 0x0 },
|
||||
{ "Slash", 0x1 },
|
||||
{ "Pierce", 0x2 },
|
||||
{ "Bludgeon", 0x4 },
|
||||
{ "Cold", 0x8 },
|
||||
{ "Fire", 0x10 },
|
||||
{ "Acid", 0x20 },
|
||||
{ "Electric", 0x40 },
|
||||
{ "Health", 0x80 },
|
||||
{ "Stamina", 0x100 },
|
||||
{ "Mana", 0x200 },
|
||||
{ "Nether", 0x400 },
|
||||
{ "Base", 0x10000000 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailDamageType))]
|
||||
public void DamageTypeMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(DamageType), name),
|
||||
$"DamageType.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<DamageType>(name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DamageTypeDeclaresNothingRetailDoesNot()
|
||||
{
|
||||
var expected = RetailDamageType.Select(r => (string)r[0]).OrderBy(n => n, StringComparer.Ordinal);
|
||||
var actual = Enum.GetNames<DamageType>().OrderBy(n => n, StringComparer.Ordinal);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// acclient.h:3300, <c>enum ITEM_TYPE</c>. TYPE_UNDEF and TYPE_SELF both sit on 0
|
||||
/// in retail; acdream spells that single zero <c>None</c>. FORCE_*_32_BIT omitted.
|
||||
/// </summary>
|
||||
public static TheoryData<string, uint> RetailItemType => new()
|
||||
{
|
||||
{ "None", 0x0 },
|
||||
{ "MeleeWeapon", 0x1 },
|
||||
{ "Armor", 0x2 },
|
||||
{ "Vestements", 0x6 },
|
||||
{ "Clothing", 0x4 },
|
||||
{ "Jewelry", 0x8 },
|
||||
{ "Creature", 0x10 },
|
||||
{ "Food", 0x20 },
|
||||
{ "Money", 0x40 },
|
||||
{ "Misc", 0x80 },
|
||||
{ "MissileWeapon", 0x100 },
|
||||
{ "Weapon", 0x101 },
|
||||
{ "Container", 0x200 },
|
||||
{ "LockableMagicTarget", 0x280 },
|
||||
{ "Useless", 0x400 },
|
||||
{ "Gem", 0x800 },
|
||||
{ "SpellComponents", 0x1000 },
|
||||
{ "Writable", 0x2000 },
|
||||
{ "Key", 0x4000 },
|
||||
{ "Caster", 0x8000 },
|
||||
{ "WeaponOrCaster", 0x8101 },
|
||||
{ "RedirectableItemEnchantmentTarget", 0x8107 },
|
||||
{ "Portal", 0x10000 },
|
||||
{ "Lockable", 0x20000 },
|
||||
{ "PromissoryNote", 0x40000 },
|
||||
{ "ManaStone", 0x80000 },
|
||||
{ "ItemEnchantableTarget", 0x88B8F },
|
||||
{ "Service", 0x100000 },
|
||||
{ "MagicWieldable", 0x200000 },
|
||||
{ "Item", 0x2DFBEF },
|
||||
{ "CraftCookingBase", 0x400000 },
|
||||
{ "VendorGrocer", 0x446220 },
|
||||
{ "CraftAlchemyBase", 0x800000 },
|
||||
{ "CraftFletchingBase", 0x1000000 },
|
||||
{ "CraftAlchemyIntermediate", 0x4000000 },
|
||||
{ "CraftFletchingIntermediate", 0x8000000 },
|
||||
{ "LifeStone", 0x10000000 },
|
||||
{ "PortalMagicTarget", 0x10010000 },
|
||||
{ "TinkeringTool", 0x20000000 },
|
||||
{ "TinkeringMaterial", 0x40000000 },
|
||||
{ "VendorShopkeep", 0x480467A7 },
|
||||
{ "Gameboard", 0x80000000 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailItemType))]
|
||||
public void ItemTypeMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(ItemType), name),
|
||||
$"ItemType.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<ItemType>(name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItemTypeDeclaresNothingRetailDoesNot()
|
||||
{
|
||||
var expected = RetailItemType.Select(r => (string)r[0]).OrderBy(n => n, StringComparer.Ordinal);
|
||||
var actual = Enum.GetNames<ItemType>().OrderBy(n => n, StringComparer.Ordinal);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The craft ladder specifically: retail leaves 0x02000000 unused, and the ACE
|
||||
/// weenie corpus attests 0x04000000 as Craft_Alchemy_Intermediate 235 times. This
|
||||
/// is the pairing acdream had wrong.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CraftLadderMatchesRetailAndLeavesTheUnusedBitUnclaimed()
|
||||
{
|
||||
Assert.Equal(0x00400000u, (uint)ItemType.CraftCookingBase);
|
||||
Assert.Equal(0x00800000u, (uint)ItemType.CraftAlchemyBase);
|
||||
Assert.Equal(0x01000000u, (uint)ItemType.CraftFletchingBase);
|
||||
Assert.Equal(0x04000000u, (uint)ItemType.CraftAlchemyIntermediate);
|
||||
Assert.Equal(0x08000000u, (uint)ItemType.CraftFletchingIntermediate);
|
||||
Assert.DoesNotContain(Enum.GetValues<ItemType>(), t => (uint)t == 0x02000000u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>Weapon</c> and <c>WeaponOrCaster</c> were aliases before the campaign, because
|
||||
/// <c>Weapon</c> was recomputed locally as melee|missile|caster. Retail keeps them
|
||||
/// distinct and only the latter includes the caster bit.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WeaponExcludesCasterAndWeaponOrCasterIncludesIt()
|
||||
{
|
||||
Assert.NotEqual(ItemType.Weapon, ItemType.WeaponOrCaster);
|
||||
Assert.Equal(ItemType.MeleeWeapon | ItemType.MissileWeapon, ItemType.Weapon);
|
||||
Assert.Equal(ItemType.Weapon | ItemType.Caster, ItemType.WeaponOrCaster);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// acclient.h:3193, <c>enum INVENTORY_LOC</c> — the composite slot groups only. The
|
||||
/// 32 primitive slots were already correct and are pinned by <c>EquipMaskTests</c>.
|
||||
/// </summary>
|
||||
public static TheoryData<string, uint> RetailEquipMaskComposites => new()
|
||||
{
|
||||
{ "Clothing", 0x080001FF },
|
||||
{ "Armor", 0x00007E00 },
|
||||
{ "Jewelry", 0x7C0F8000 },
|
||||
{ "WristWear", 0x00030000 },
|
||||
{ "FingerWear", 0x000C0000 },
|
||||
{ "Sigil", 0x70000000 },
|
||||
{ "ReadySlot", 0x03F00000 },
|
||||
{ "Weapon", 0x02500000 },
|
||||
{ "WeaponReadySlot", 0x03500000 },
|
||||
{ "All", 0x7FFFFFFF },
|
||||
{ "CanGoInReadySlot", 0x7FFFFFFF },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailEquipMaskComposites))]
|
||||
public void EquipMaskCompositeMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(EquipMask), name), $"EquipMask.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<EquipMask>(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's CLOTHING_LOC is the nine wear slots plus the cloak slot (bit 27) — it is
|
||||
/// NOT the union of the wear slots alone. A type remark here used to claim the extra
|
||||
/// bit was 31 and unnamed; it is 27 and it is <see cref="EquipMask.Cloak"/>.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ClothingCompositeIsTheWearSlotsPlusCloak()
|
||||
{
|
||||
EquipMask wearSlots =
|
||||
EquipMask.HeadWear | EquipMask.ChestWear | EquipMask.AbdomenWear
|
||||
| EquipMask.UpperArmWear | EquipMask.LowerArmWear | EquipMask.HandWear
|
||||
| EquipMask.UpperLegWear | EquipMask.LowerLegWear | EquipMask.FootWear;
|
||||
|
||||
Assert.Equal(0x000001FFu, (uint)wearSlots);
|
||||
Assert.Equal(EquipMask.Clothing, wearSlots | EquipMask.Cloak);
|
||||
Assert.NotEqual(EquipMask.Clothing, wearSlots);
|
||||
Assert.Equal(0u, (uint)EquipMask.Clothing & 0x80000000u);
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:3688, <c>enum TransientState</c>.</summary>
|
||||
public static TheoryData<string, uint> RetailTransientState => new()
|
||||
{
|
||||
{ "Contact", 0x1 },
|
||||
{ "OnWalkable", 0x2 },
|
||||
{ "Sliding", 0x4 },
|
||||
{ "WaterContact", 0x8 },
|
||||
{ "StationaryFall", 0x10 },
|
||||
{ "StationaryStop", 0x20 },
|
||||
{ "StationaryStuck", 0x40 },
|
||||
{ "Active", 0x80 },
|
||||
{ "CheckEthereal", 0x100 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailTransientState))]
|
||||
public void TransientStateFlagsMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(TransientStateFlags), name),
|
||||
$"TransientStateFlags.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<TransientStateFlags>(name));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:2815, <c>enum PhysicsState</c>, including the two bits retail itself reserves.</summary>
|
||||
public static TheoryData<string, uint> RetailPhysicsState => new()
|
||||
{
|
||||
{ "Static", 0x1 },
|
||||
{ "ReservedUnused1", 0x2 },
|
||||
{ "Ethereal", 0x4 },
|
||||
{ "ReportCollisions", 0x8 },
|
||||
{ "IgnoreCollisions", 0x10 },
|
||||
{ "NoDraw", 0x20 },
|
||||
{ "Missile", 0x40 },
|
||||
{ "Pushable", 0x80 },
|
||||
{ "AlignPath", 0x100 },
|
||||
{ "PathClipped", 0x200 },
|
||||
{ "Gravity", 0x400 },
|
||||
{ "Lighting", 0x800 },
|
||||
{ "ParticleEmitter", 0x1000 },
|
||||
{ "ReservedUnused2", 0x2000 },
|
||||
{ "Hidden", 0x4000 },
|
||||
{ "ScriptedCollision", 0x8000 },
|
||||
{ "HasPhysicsBsp", 0x10000 },
|
||||
{ "Inelastic", 0x20000 },
|
||||
{ "HasDefaultAnim", 0x40000 },
|
||||
{ "HasDefaultScript", 0x80000 },
|
||||
{ "Cloaked", 0x100000 },
|
||||
{ "ReportAsEnvironment", 0x200000 },
|
||||
{ "EdgeSlide", 0x400000 },
|
||||
{ "Sledding", 0x800000 },
|
||||
{ "Frozen", 0x1000000 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailPhysicsState))]
|
||||
public void PhysicsStateFlagsMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(PhysicsStateFlags), name),
|
||||
$"PhysicsStateFlags.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<PhysicsStateFlags>(name));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:4371, <c>enum ATTACK_HEIGHT</c>. NUM_ATTACK_HEIGHTS is a count, not a height.</summary>
|
||||
[Theory]
|
||||
[InlineData("Undef", 0)]
|
||||
[InlineData("High", 1)]
|
||||
[InlineData("Medium", 2)]
|
||||
[InlineData("Low", 3)]
|
||||
public void AttackHeightMatchesRetail(string name, int value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(AttackHeight), name), $"AttackHeight.{name} is missing");
|
||||
Assert.Equal(value, (int)Enum.Parse<AttackHeight>(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// acclient.h:3807, <c>enum AttackType</c>. The two composites are the interesting
|
||||
/// part: retail spells Unarmed 0x19 and MultiStrike 0x79E0, and acdream derives both
|
||||
/// from its primitives. This asserts the derivation lands on retail's literal.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AttackTypeCompositesMatchRetailLiterals()
|
||||
{
|
||||
Assert.Equal(0x19u, (uint)AttackType.Unarmed);
|
||||
Assert.Equal(0x79E0u, (uint)AttackType.MultiStrike);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue