From 251dd68a92484918556f4feeb26af1fc949b6700 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 29 Jul 2026 01:11:16 +0200 Subject: [PATCH] feat(core): give AC's seven property tables names, verified against two oracles acdream has carried property IDs as bare uints since the beginning. The wire parsers read `u32 property` and hand it to a `Dictionary`, and every call site that cared re-derived the meaning from a comment - `EncumbranceVal` was spelled `private const uint EncumbranceValProperty = 5u` in two different files, `UiEffects` lived as "ACE enum value 18" in a doc comment, and `AetheriaBitfield` as "322 / 0x142". That is 864 pieces of vocabulary the codebase was expected to remember in prose. This adds the seven enums - PropertyInt, PropertyInt64, PropertyBool, PropertyFloat, PropertyString, PropertyDataId, PropertyInstanceId - under AcDream.Core.Properties. Every member is transcribed from an oracle; none is invented. Two independent sources were extracted and diffed against each other: the vendored client-side enum catalog at references/acclientlib/UtilityBelt.Common/Enums/Enums.cs (which names these tables IntId/BoolId/FloatId/...), and the 38,985-file ACE weenie export corpus at references/weenies/, whose every stat entry carries the numeric key beside the enum member name in its `_comment`. The corpus attests 408 of the 864 members directly. Across all seven tables the two oracles produced zero value conflicts, and the corpus contained no key the catalog was missing - the catalog is a strict superset of everything 38,985 weenies actually set. Three members disagree on spelling, never on value: the catalog says ObjectType/HookObjectType/MerchandiseObjectTypes where ACE says ItemType/HookItemType/MerchandiseItemTypes. acdream takes ACE's spelling, which is what the weenie corpus emits (37,329 attestations for ItemType alone) and what acdream's own ItemType enum already calls it. The catalog's alias is recorded on each member. This commit is vocabulary only - no parser reads these enums yet, so no branch changes and no wire behavior moves. The bundles stay `Dictionary` precisely because an unknown key must still round-trip untouched; the enums describe the keys we know, they do not constrain the ones we receive. PropertyEnumConformanceTests pins the result: the full name/value table per family, the uint underlying type, no two members sharing a value, and a separate 408-case theory asserting each weenie-attested pairing individually. A hand edit to any enum now fails loudly instead of quietly mis-reading the wire. Core tests 3,297 -> 3,726. Co-Authored-By: Claude Fable 5 --- src/AcDream.Core/Properties/PropertyBool.cs | 221 +++ src/AcDream.Core/Properties/PropertyDataId.cs | 134 ++ src/AcDream.Core/Properties/PropertyFloat.cs | 289 ++++ .../Properties/PropertyInstanceId.cs | 79 + src/AcDream.Core/Properties/PropertyInt.cs | 611 +++++++ src/AcDream.Core/Properties/PropertyInt64.cs | 43 + src/AcDream.Core/Properties/PropertyString.cs | 112 ++ .../PropertyEnumConformanceTests.cs | 1398 +++++++++++++++++ 8 files changed, 2887 insertions(+) create mode 100644 src/AcDream.Core/Properties/PropertyBool.cs create mode 100644 src/AcDream.Core/Properties/PropertyDataId.cs create mode 100644 src/AcDream.Core/Properties/PropertyFloat.cs create mode 100644 src/AcDream.Core/Properties/PropertyInstanceId.cs create mode 100644 src/AcDream.Core/Properties/PropertyInt.cs create mode 100644 src/AcDream.Core/Properties/PropertyInt64.cs create mode 100644 src/AcDream.Core/Properties/PropertyString.cs create mode 100644 tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs diff --git a/src/AcDream.Core/Properties/PropertyBool.cs b/src/AcDream.Core/Properties/PropertyBool.cs new file mode 100644 index 00000000..3989866d --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyBool.cs @@ -0,0 +1,221 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyBool property table — the numeric keys the server sends in +/// PrivateUpdatePropertyBool (0x02C9) / PublicUpdatePropertyBool (0x02CA) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is bool. +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table BoolId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyBool : uint +{ + Undef = 0, + /// 21884 weenie attestations. + Stuck = 1, + /// 1077 weenie attestations. + Open = 2, + /// 646 weenie attestations. + Locked = 3, + RotProof = 4, + AllegianceUpdateRequest = 5, + /// 1282 weenie attestations. + AiUsesMana = 6, + /// 16 weenie attestations. + AiUseHumanMagicAnimations = 7, + /// 853 weenie attestations. + AllowGive = 8, + CurrentlyAttacking = 9, + AttackerAi = 10, + /// 6212 weenie attestations. + IgnoreCollisions = 11, + /// 9020 weenie attestations. + ReportCollisions = 12, + /// 15282 weenie attestations. + Ethereal = 13, + /// 7376 weenie attestations. + GravityStatus = 14, + /// 3382 weenie attestations. + LightsStatus = 15, + /// 139 weenie attestations. + ScriptedCollision = 16, + /// 475 weenie attestations. + Inelastic = 17, + /// 1677 weenie attestations. + Visibility = 18, + /// 3972 weenie attestations. + Attackable = 19, + SafeSpellComponents = 20, + AdvocateState = 21, + /// 15455 weenie attestations. + Inscribable = 22, + /// 7755 weenie attestations. + DestroyOnSell = 23, + /// 6728 weenie attestations. + UiHidden = 24, + IgnoreHouseBarriers = 25, + HiddenAdmin = 26, + PkWounder = 27, + PkKiller = 28, + /// 262 weenie attestations. + NoCorpse = 29, + UnderLifestoneProtection = 30, + ItemManaUpdatePending = 31, + GeneratorStatus = 32, + /// 831 weenie attestations. + ResetMessagePending = 33, + /// 831 weenie attestations. + DefaultOpen = 34, + /// 511 weenie attestations. + DefaultLocked = 35, + DefaultOn = 36, + OpenForBusiness = 37, + IsFrozen = 38, + /// 1049 weenie attestations. + DealMagicalItems = 39, + LogoffImDead = 40, + /// 2208 weenie attestations. + ReportCollisionsAsEnvironment = 41, + /// 1327 weenie attestations. + AllowEdgeSlide = 42, + AdvocateQuest = 43, + IsAdmin = 44, + IsArch = 45, + IsSentinel = 46, + IsAdvocate = 47, + CurrentlyPoweringUp = 48, + GeneratorEnteredWorld = 49, + /// 890 weenie attestations. + NeverFailCasting = 50, + /// 264 weenie attestations. + VendorService = 51, + /// 1421 weenie attestations. + AiImmobile = 52, + /// 1 weenie attestations. + DamagedByCollisions = 53, + /// 34 weenie attestations. + IsDynamic = 54, + /// 81 weenie attestations. + IsHot = 55, + IsAffecting = 56, + /// 49 weenie attestations. + AffectsAis = 57, + SpellQueueActive = 58, + GeneratorDisabled = 59, + IsAcceptingTells = 60, + /// 1 weenie attestations. + LoggingChannel = 61, + /// 5 weenie attestations. + OpensAnyLock = 62, + /// 20 weenie attestations. + UnlimitedUse = 63, + GeneratedTreasureItem = 64, + /// 97 weenie attestations. + IgnoreMagicResist = 65, + /// 87 weenie attestations. + IgnoreMagicArmor = 66, + AiAllowTrade = 67, + /// 2 weenie attestations. + SpellComponentsRequired = 68, + /// 4617 weenie attestations. + IsSellable = 69, + /// 4 weenie attestations. + IgnoreShieldsBySkill = 70, + /// 6275 weenie attestations. + NoDraw = 71, + ActivationUntargeted = 72, + HouseHasGottenPriorityBootPos = 73, + /// 12 weenie attestations. + GeneratorAutomaticDestruction = 74, + HouseHooksVisible = 75, + /// 8 weenie attestations. + HouseRequiresMonarch = 76, + HouseHooksEnabled = 77, + HouseNotifiedHudOfHookCount = 78, + /// 15 weenie attestations. + AiAcceptEverything = 79, + IgnorePortalRestrictions = 80, + /// 5 weenie attestations. + RequiresBackpackSlot = 81, + /// 444 weenie attestations. + DontTurnOrMoveWhenGiving = 82, + /// 442 weenie attestations. + NpcLooksLikeObject = 83, + /// 210 weenie attestations. + IgnoreCloIcons = 84, + /// 178 weenie attestations. + AppraisalHasAllowedWielder = 85, + ChestRegenOnClose = 86, + LogoffInMinigame = 87, + /// 17 weenie attestations. + PortalShowDestination = 88, + /// 30 weenie attestations. + PortalIgnoresPkAttackTimer = 89, + /// 43 weenie attestations. + NpcInteractsSilently = 90, + /// 230 weenie attestations. + Retained = 91, + /// 1 weenie attestations. + IgnoreAuthor = 92, + Limbo = 93, + AppraisalHasAllowedActivator = 94, + ExistedBeforeAllegianceXpChanges = 95, + IsDeaf = 96, + IsPsr = 97, + Invincible = 98, + /// 1068 weenie attestations. + Ivoryable = 99, + /// 687 weenie attestations. + Dyable = 100, + CanGenerateRare = 101, + CorpseGeneratedRare = 102, + /// 14 weenie attestations. + NonProjectileMagicImmune = 103, + ActdReceivedItems = 104, + Unknown105 = 105, + FirstEnterWorldDone = 106, + RecallsDisabled = 107, + /// 13 weenie attestations. + 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, + /// 14 weenie attestations. + AutowieldLeft = 130, +} diff --git a/src/AcDream.Core/Properties/PropertyDataId.cs b/src/AcDream.Core/Properties/PropertyDataId.cs new file mode 100644 index 00000000..2d22b902 --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyDataId.cs @@ -0,0 +1,134 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyDataId property table — the numeric keys the server sends in +/// PrivateUpdatePropertyDataId (0x02E1) / PublicUpdatePropertyDataId (0x02E2) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is uint (DAT file id). +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table DataId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyDataId : uint +{ + Undef = 0, + /// 38985 weenie attestations. + Setup = 1, + /// 12219 weenie attestations. + MotionTable = 2, + /// 23696 weenie attestations. + SoundTable = 3, + /// 4319 weenie attestations. + CombatTable = 4, + /// 2 weenie attestations. + QualityFilter = 5, + /// 13875 weenie attestations. + PaletteBase = 6, + /// 9049 weenie attestations. + ClothingBase = 7, + /// 38985 weenie attestations. + Icon = 8, + /// 741 weenie attestations. + EyesTexture = 9, + /// 741 weenie attestations. + NoseTexture = 10, + /// 741 weenie attestations. + MouthTexture = 11, + DefaultEyesTexture = 12, + DefaultNoseTexture = 13, + DefaultMouthTexture = 14, + /// 741 weenie attestations. + HairPalette = 15, + /// 741 weenie attestations. + EyesPalette = 16, + /// 741 weenie attestations. + SkinPalette = 17, + HeadObject = 18, + /// 4 weenie attestations. + ActivationAnimation = 19, + /// 4 weenie attestations. + InitMotion = 20, + ActivationSound = 21, + /// 23266 weenie attestations. + PhysicsEffectTable = 22, + /// 138 weenie attestations. + UseSound = 23, + /// 48 weenie attestations. + UseTargetAnimation = 24, + /// 4 weenie attestations. + UseTargetSuccessAnimation = 25, + /// 2 weenie attestations. + UseTargetFailureAnimation = 26, + /// 232 weenie attestations. + UseUserAnimation = 27, + /// 3696 weenie attestations. + Spell = 28, + /// 156 weenie attestations. + SpellComponent = 29, + /// 794 weenie attestations. + PhysicsScript = 30, + /// 497 weenie attestations. + LinkedPortalOne = 31, + /// 861 weenie attestations. + WieldedTreasureType = 32, + /// 5 weenie attestations. + UnknownGuessedname = 33, + UnknownGuessedname2 = 34, + /// 2000 weenie attestations. + DeathTreasureType = 35, + /// 2276 weenie attestations. + MutateFilter = 36, + /// 1253 weenie attestations. + ItemSkillLimit = 37, + /// 1 weenie attestations. + UseCreateItem = 38, + DeathSpell = 39, + VendorsClassId = 40, + /// 13 weenie attestations. + ItemSpecializedOnly = 41, + /// 6274 weenie attestations. + HouseId = 42, + AccountHouseId = 43, + /// 6274 weenie attestations. + RestrictionEffect = 44, + CreationMutationFilter = 45, + /// 584 weenie attestations. + TsysMutationFilter = 46, + LastPortal = 47, + /// 1 weenie attestations. + LinkedPortalTwo = 48, + OriginalPortal = 49, + /// 1000 weenie attestations. + IconOverlay = 50, + /// 16 weenie attestations. + IconOverlaySecondary = 51, + /// 554 weenie attestations. + IconUnderlay = 52, + AugmentationMutationFilter = 53, + AugmentationEffect = 54, + /// 84 weenie attestations. + ProcSpell = 55, + AugmentationCreateItem = 56, + AlternateCurrency = 57, + BlueSurgeSpell = 58, + YellowSurgeSpell = 59, + RedSurgeSpell = 60, + OlthoiDeathTreasureType = 61, +} diff --git a/src/AcDream.Core/Properties/PropertyFloat.cs b/src/AcDream.Core/Properties/PropertyFloat.cs new file mode 100644 index 00000000..e58caebb --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyFloat.cs @@ -0,0 +1,289 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyFloat property table — the numeric keys the server sends in +/// PrivateUpdatePropertyFloat (0x02D5) / PublicUpdatePropertyFloat (0x02D6) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is double. +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table FloatId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyFloat : uint +{ + Undef = 0, + /// 4243 weenie attestations. + HeartbeatInterval = 1, + /// 4243 weenie attestations. + HeartbeatTimestamp = 2, + /// 4361 weenie attestations. + HealthRate = 3, + /// 4361 weenie attestations. + StaminaRate = 4, + /// 7706 weenie attestations. + ManaRate = 5, + /// 79 weenie attestations. + HealthUponResurrection = 6, + /// 79 weenie attestations. + StaminaUponResurrection = 7, + /// 79 weenie attestations. + ManaUponResurrection = 8, + StartTime = 9, + StopTime = 10, + /// 1749 weenie attestations. + ResetInterval = 11, + /// 3290 weenie attestations. + Shade = 12, + /// 6001 weenie attestations. + ArmorModVsSlash = 13, + /// 6005 weenie attestations. + ArmorModVsPierce = 14, + /// 6005 weenie attestations. + ArmorModVsBludgeon = 15, + /// 6005 weenie attestations. + ArmorModVsCold = 16, + /// 6005 weenie attestations. + ArmorModVsFire = 17, + /// 6005 weenie attestations. + ArmorModVsAcid = 18, + /// 6005 weenie attestations. + ArmorModVsElectric = 19, + CombatSpeed = 20, + /// 2733 weenie attestations. + WeaponLength = 21, + /// 3174 weenie attestations. + DamageVariance = 22, + CurrentPowerMod = 23, + AccuracyMod = 24, + StrengthMod = 25, + /// 1386 weenie attestations. + MaximumVelocity = 26, + /// 233 weenie attestations. + RotationSpeed = 27, + MotionTimestamp = 28, + /// 3892 weenie attestations. + WeaponDefense = 29, + WimpyLevel = 30, + /// 2169 weenie attestations. + VisualAwarenessRange = 31, + AuralAwarenessRange = 32, + PerceptionLevel = 33, + /// 1987 weenie attestations. + PowerupTime = 34, + MaxChargeDistance = 35, + /// 1892 weenie attestations. + ChargeSpeed = 36, + /// 1049 weenie attestations. + BuyPrice = 37, + /// 1049 weenie attestations. + SellPrice = 38, + /// 18233 weenie attestations. + DefaultScale = 39, + /// 10 weenie attestations. + LockpickMod = 40, + /// 2195 weenie attestations. + RegenerationInterval = 41, + RegenerationTimestamp = 42, + /// 2135 weenie attestations. + GeneratorRadius = 43, + /// 66 weenie attestations. + TimeToRot = 44, + DeathTimestamp = 45, + PkTimestamp = 46, + VictimTimestamp = 47, + LoginTimestamp = 48, + CreationTimestamp = 49, + /// 4 weenie attestations. + MinimumTimeSincePk = 50, + DeprecatedHousekeepingPriority = 51, + AbuseLoggingTimestamp = 52, + LastPortalTeleportTimestamp = 53, + /// 9152 weenie attestations. + UseRadius = 54, + /// 46 weenie attestations. + HomeRadius = 55, + ReleasedTimestamp = 56, + MinHomeRadius = 57, + Facing = 58, + ResetTimestamp = 59, + LogoffTimestamp = 60, + EconRecoveryInterval = 61, + /// 3523 weenie attestations. + WeaponOffense = 62, + /// 1223 weenie attestations. + DamageMod = 63, + /// 4361 weenie attestations. + ResistSlash = 64, + /// 4361 weenie attestations. + ResistPierce = 65, + /// 4361 weenie attestations. + ResistBludgeon = 66, + /// 4361 weenie attestations. + ResistFire = 67, + /// 4361 weenie attestations. + ResistCold = 68, + /// 4361 weenie attestations. + ResistAcid = 69, + /// 4361 weenie attestations. + ResistElectric = 70, + /// 4361 weenie attestations. + ResistHealthBoost = 71, + /// 4361 weenie attestations. + ResistStaminaDrain = 72, + /// 4361 weenie attestations. + ResistStaminaBoost = 73, + /// 4361 weenie attestations. + ResistManaDrain = 74, + /// 4361 weenie attestations. + ResistManaBoost = 75, + /// 794 weenie attestations. + Translucency = 76, + /// 269 weenie attestations. + PhysicsScriptIntensity = 77, + /// 761 weenie attestations. + Friction = 78, + /// 761 weenie attestations. + Elasticity = 79, + /// 1076 weenie attestations. + AiUseMagicDelay = 80, + ItemMinSpellcraftMod = 81, + ItemMaxSpellcraftMod = 82, + ItemRankProbability = 83, + Shade2 = 84, + Shade3 = 85, + Shade4 = 86, + /// 15 weenie attestations. + 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, + /// 22 weenie attestations. + HealkitMod = 100, + FrozenTimestamp = 101, + HealthRateMod = 102, + AllegianceSwearTimestamp = 103, + /// 4361 weenie attestations. + ObviousRadarRange = 104, + /// 81 weenie attestations. + HotspotCycleTime = 105, + /// 74 weenie attestations. + HotspotCycleTimeVariance = 106, + SpamTimestamp = 107, + SpamRate = 108, + /// 26 weenie attestations. + BondWieldedTreasure = 109, + /// 1039 weenie attestations. + BulkMod = 110, + /// 1039 weenie attestations. + SizeMod = 111, + GagTimestamp = 112, + GeneratorUpdateTimestamp = 113, + DeathSpamTimestamp = 114, + DeathSpamRate = 115, + WildAttackProbability = 116, + /// 312 weenie attestations. + FocusedProbability = 117, + CrashAndTurnProbability = 118, + CrashAndTurnRadius = 119, + CrashAndTurnBias = 120, + /// 186 weenie attestations. + GeneratorInitialDelay = 121, + /// 575 weenie attestations. + AiAcquireHealth = 122, + /// 6 weenie attestations. + AiAcquireStamina = 123, + /// 6 weenie attestations. + AiAcquireMana = 124, + /// 4361 weenie attestations. + ResistHealthDrain = 125, + LifestoneProtectionTimestamp = 126, + /// 72 weenie attestations. + AiCounteractEnchantment = 127, + /// 10 weenie attestations. + AiDispelEnchantment = 128, + TradeTimestamp = 129, + AiTargetedDetectionRadius = 130, + /// 29 weenie attestations. + EmotePriority = 131, + LastTeleportStartTimestamp = 132, + EventSpamTimestamp = 133, + EventSpamRate = 134, + /// 5 weenie attestations. + InventoryOffset = 135, + /// 396 weenie attestations. + CriticalMultiplier = 136, + /// 15 weenie attestations. + ManaStoneDestroyChance = 137, + /// 351 weenie attestations. + SlayerDamageBonus = 138, + AllegianceInfoSpamTimestamp = 139, + AllegianceInfoSpamRate = 140, + NextSpellcastTimestamp = 141, + AppraisalRequestedTimestamp = 142, + AppraisalHeartbeatDueTimestamp = 143, + /// 209 weenie attestations. + ManaConversionMod = 144, + LastPkAttackTimestamp = 145, + FellowshipUpdateTimestamp = 146, + /// 395 weenie attestations. + CriticalFrequency = 147, + LimboStartTimestamp = 148, + /// 356 weenie attestations. + WeaponMissileDefense = 149, + /// 368 weenie attestations. + WeaponMagicDefense = 150, + /// 87 weenie attestations. + IgnoreShield = 151, + /// 135 weenie attestations. + ElementalDamageMod = 152, + StartMissileAttackTimestamp = 153, + LastRareUsedTimestamp = 154, + /// 69 weenie attestations. + IgnoreArmor = 155, + ProcSpellRate = 156, + /// 93 weenie attestations. + ResistanceModifier = 157, + AllegianceGagTimestamp = 158, + /// 18 weenie attestations. + AbsorbMagicDamage = 159, + CachedMaxAbsorbMagicDamage = 160, + GagDuration = 161, + AllegianceGagDuration = 162, + GlobalXpMod = 163, + HealingModifier = 164, + /// 391 weenie attestations. + ArmorModVsNether = 165, + ResistNether = 166, + /// 369 weenie attestations. + CooldownDuration = 167, + WeaponAuraOffense = 168, + WeaponAuraDefense = 169, + WeaponAuraElemental = 170, + WeaponAuraManaConv = 171, +} diff --git a/src/AcDream.Core/Properties/PropertyInstanceId.cs b/src/AcDream.Core/Properties/PropertyInstanceId.cs new file mode 100644 index 00000000..c386c1ed --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyInstanceId.cs @@ -0,0 +1,79 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyInstanceId property table — the numeric keys the server sends in +/// PrivateUpdatePropertyInstanceId (0x02E5) / PublicUpdatePropertyInstanceId (0x02E6) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is uint (object guid). +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table InstanceId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +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, + /// 130 weenie attestations. + 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, + /// 2 weenie attestations. + AllowedWielder = 38, + AssignedTarget = 39, + LimboSource = 40, + Snooper = 41, + TeleportedCharacter = 42, + Pet = 43, + PetOwner = 44, + PetDevice = 45, +} diff --git a/src/AcDream.Core/Properties/PropertyInt.cs b/src/AcDream.Core/Properties/PropertyInt.cs new file mode 100644 index 00000000..b288dda3 --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyInt.cs @@ -0,0 +1,611 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyInt property table — the numeric keys the server sends in +/// PrivateUpdatePropertyInt (0x02CD) / PublicUpdatePropertyInt (0x02CE) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is int. +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table IntId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyInt : uint +{ + Undef = 0, + /// UtilityBelt catalog calls this ObjectType; 37329 weenie attestations. + ItemType = 1, + /// 6140 weenie attestations. + CreatureType = 2, + /// 8344 weenie attestations. + PaletteTemplate = 3, + /// 1597 weenie attestations. + ClothingPriority = 4, + /// 25793 weenie attestations. + EncumbranceVal = 5, + /// 8633 weenie attestations. + ItemsCapacity = 6, + /// 8618 weenie attestations. + ContainersCapacity = 7, + /// 23605 weenie attestations. + Mass = 8, + /// 20797 weenie attestations. + ValidLocations = 9, + CurrentWieldedLocation = 10, + /// 4373 weenie attestations. + MaxStackSize = 11, + /// 4373 weenie attestations. + StackSize = 12, + /// 3181 weenie attestations. + StackUnitEncumbrance = 13, + /// 3181 weenie attestations. + StackUnitMass = 14, + /// 3181 weenie attestations. + StackUnitValue = 15, + /// 37280 weenie attestations. + ItemUseable = 16, + /// 21 weenie attestations. + RareId = 17, + /// 5705 weenie attestations. + UiEffects = 18, + /// 25852 weenie attestations. + Value = 19, + /// 1 weenie attestations. + CoinValue = 20, + TotalExperience = 21, + AvailableCharacter = 22, + TotalSkillCredits = 23, + AvailableSkillCredits = 24, + /// 6140 weenie attestations. + Level = 25, + /// 28 weenie attestations. + AccountRequirements = 26, + /// 5476 weenie attestations. + ArmorType = 27, + /// 1657 weenie attestations. + ArmorLevel = 28, + AllegianceCpPool = 29, + /// 1 weenie attestations. + AllegianceRank = 30, + ChannelsAllowed = 31, + ChannelsActive = 32, + /// 6252 weenie attestations. + Bonded = 33, + MonarchsRank = 34, + AllegianceFollowers = 35, + /// 997 weenie attestations. + ResistMagic = 36, + /// 575 weenie attestations. + ResistItemAppraisal = 37, + /// 677 weenie attestations. + ResistLockpick = 38, + DeprecatedResistRepair = 39, + /// 1634 weenie attestations. + CombatMode = 40, + CurrentAttackHeight = 41, + CombatCollisions = 42, + /// 1 weenie attestations. + NumDeaths = 43, + /// 3717 weenie attestations. + Damage = 44, + /// 3357 weenie attestations. + DamageType = 45, + /// 2973 weenie attestations. + DefaultCombatStyle = 46, + /// 2364 weenie attestations. + AttackType = 47, + /// 3393 weenie attestations. + WeaponSkill = 48, + /// 3394 weenie attestations. + WeaponTime = 49, + /// 972 weenie attestations. + AmmoType = 50, + /// 4499 weenie attestations. + CombatUse = 51, + /// 440 weenie attestations. + ParentLocation = 52, + /// 440 weenie attestations. + PlacementPosition = 53, + WeaponEncumbrance = 54, + WeaponMass = 55, + ShieldValue = 56, + ShieldEncumbrance = 57, + MissileInventoryLocation = 58, + FullDamageType = 59, + /// 545 weenie attestations. + WeaponRange = 60, + AttackersSkill = 61, + DefendersSkill = 62, + AttackersSkillValue = 63, + AttackersClass = 64, + Placement = 65, + /// 179 weenie attestations. + CheckpointStatus = 66, + /// 219 weenie attestations. + Tolerance = 67, + /// 2151 weenie attestations. + TargetingTactic = 68, + /// 48 weenie attestations. + CombatTactic = 69, + HomesickTargetingTactic = 70, + NumFollowFailures = 71, + /// 494 weenie attestations. + FriendType = 72, + FoeType = 73, + /// UtilityBelt catalog calls this MerchandiseObjectTypes; 1049 weenie attestations. + MerchandiseItemTypes = 74, + /// 1049 weenie attestations. + MerchandiseMinValue = 75, + /// 1049 weenie attestations. + MerchandiseMaxValue = 76, + NumItemsSold = 77, + NumItemsBought = 78, + MoneyIncome = 79, + MoneyOutflow = 80, + /// 2195 weenie attestations. + MaxGeneratedObjects = 81, + /// 2195 weenie attestations. + InitGeneratedObjects = 82, + /// 787 weenie attestations. + ActivationResponse = 83, + OriginalValue = 84, + NumMoveFailures = 85, + /// 815 weenie attestations. + MinLevel = 86, + /// 115 weenie attestations. + MaxLevel = 87, + /// 10 weenie attestations. + LockpickMod = 88, + /// 525 weenie attestations. + BoosterEnum = 89, + /// 532 weenie attestations. + BoostValue = 90, + /// 800 weenie attestations. + MaxStructure = 91, + /// 713 weenie attestations. + Structure = 92, + /// 38985 weenie attestations. + PhysicsState = 93, + /// 3570 weenie attestations. + TargetType = 94, + /// 2668 weenie attestations. + RadarBlipColor = 95, + /// 472 weenie attestations. + EncumbranceCapacity = 96, + LoginTimestamp = 97, + /// 119 weenie attestations. + CreationTimestamp = 98, + /// 4 weenie attestations. + PkLevelModifier = 99, + /// 575 weenie attestations. + GeneratorType = 100, + /// 1265 weenie attestations. + AiAllowedCombatStyle = 101, + LogoffTimestamp = 102, + /// 158 weenie attestations. + GeneratorDestructionType = 103, + ActivationCreateClass = 104, + /// 693 weenie attestations. + ItemWorkmanship = 105, + /// 4213 weenie attestations. + ItemSpellcraft = 106, + /// 4173 weenie attestations. + ItemCurMana = 107, + /// 4211 weenie attestations. + ItemMaxMana = 108, + /// 3091 weenie attestations. + ItemDifficulty = 109, + /// 1043 weenie attestations. + ItemAllegianceRankLimit = 110, + /// 2940 weenie attestations. + PortalBitmask = 111, + AdvocateLevel = 112, + /// 749 weenie attestations. + Gender = 113, + /// 4502 weenie attestations. + Attuned = 114, + /// 1640 weenie attestations. + ItemSkillLevelLimit = 115, + GateLogic = 116, + /// 66 weenie attestations. + ItemManaCost = 117, + Logoff = 118, + /// 365 weenie attestations. + Active = 119, + AttackHeight = 120, + NumAttackFailures = 121, + AiCpThreshold = 122, + AiAdvancementStrategy = 123, + Version = 124, + /// 1 weenie attestations. + Age = 125, + /// 1048 weenie attestations. + VendorHappyMean = 126, + /// 1048 weenie attestations. + VendorHappyVariance = 127, + CloakStatus = 128, + VitaeCpPool = 129, + NumServicesSold = 130, + /// 586 weenie attestations. + MaterialType = 131, + NumAllegianceBreaks = 132, + /// 10541 weenie attestations. + ShowableOnRadar = 133, + /// 3138 weenie attestations. + PlayerKillerStatus = 134, + VendorHappyMaxItems = 135, + /// 5 weenie attestations. + ScorePageNum = 136, + /// 5 weenie attestations. + ScoreConfigNum = 137, + /// 5 weenie attestations. + ScoreNumScores = 138, + DeathLevel = 139, + /// 1411 weenie attestations. + AiOptions = 140, + OpenToEveryone = 141, + /// 297 weenie attestations. + GeneratorTimeType = 142, + /// 51 weenie attestations. + GeneratorStartTime = 143, + /// 50 weenie attestations. + GeneratorEndTime = 144, + /// 257 weenie attestations. + GeneratorEndDestructionType = 145, + /// 4361 weenie attestations. + XpOverride = 146, + NumCrashAndTurns = 147, + ComponentWarningThreshold = 148, + /// 7 weenie attestations. + HouseStatus = 149, + /// 4715 weenie attestations. + HookPlacement = 150, + /// 6799 weenie attestations. + HookType = 151, + /// UtilityBelt catalog calls this HookObjectType; 5 weenie attestations. + HookItemType = 152, + AiPpThreshold = 153, + GeneratorVersion = 154, + /// 6274 weenie attestations. + HouseType = 155, + /// 10 weenie attestations. + PickupEmoteOffset = 156, + /// 1 weenie attestations. + WeenieIteration = 157, + /// 2557 weenie attestations. + WieldRequirements = 158, + /// 2557 weenie attestations. + WieldSkilltype = 159, + /// 2557 weenie attestations. + WieldDifficulty = 160, + /// 654 weenie attestations. + HouseMaxHooksUsable = 161, + HouseCurrentHooksUsable = 162, + /// 8 weenie attestations. + AllegianceMinLevel = 163, + AllegianceMaxLevel = 164, + HouseRelinkHookCount = 165, + /// 537 weenie attestations. + SlayerCreatureType = 166, + ConfirmationInProgress = 167, + ConfirmationTypeInProgress = 168, + /// 728 weenie attestations. + TsysMutationData = 169, + /// 36 weenie attestations. + NumItemsInMaterial = 170, + /// 228 weenie attestations. + NumTimesTinkered = 171, + /// 358 weenie attestations. + AppraisalLongDescDecoration = 172, + /// 133 weenie attestations. + AppraisalLockpickSuccessPercent = 173, + /// 103 weenie attestations. + AppraisalPages = 174, + /// 103 weenie attestations. + AppraisalMaxPages = 175, + /// 355 weenie attestations. + AppraisalItemSkill = 176, + /// 281 weenie attestations. + GemCount = 177, + /// 281 weenie attestations. + GemType = 178, + /// 495 weenie attestations. + ImbuedEffect = 179, + AttackersRawSkillValue = 180, + ChessRank = 181, + ChessTotalGames = 182, + ChessGamesWon = 183, + ChessGamesLost = 184, + /// 71 weenie attestations. + TypeOfAlteration = 185, + /// 71 weenie attestations. + SkillToBeAltered = 186, + SkillAlterationCount = 187, + /// 807 weenie attestations. + HeritageGroup = 188, + /// 30 weenie attestations. + TransferFromAttribute = 189, + /// 30 weenie attestations. + TransferToAttribute = 190, + AttributeTransferCount = 191, + FakeFishingSkill = 192, + /// 3 weenie attestations. + NumKeys = 193, + DeathTimestamp = 194, + PkTimestamp = 195, + VictimTimestamp = 196, + /// 22 weenie attestations. + HookGroup = 197, + AllegianceSwearTimestamp = 198, + HousePurchaseTimestamp = 199, + RedirectableEquippedArmorCount = 200, + MeleedefenseImbuedEffectTypeCache = 201, + MissileDefenseImbuedEffectTypeCache = 202, + MagicDefenseImbuedEffectTypeCache = 203, + /// 121 weenie attestations. + 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, + /// 1 weenie attestations. + ItemAttributeLimit = 257, + /// 1 weenie attestations. + ItemAttributeLevelLimit = 258, + ItemAttribute2ndLimit = 259, + ItemAttribute2ndLevelLimit = 260, + /// 2 weenie attestations. + CharacterTitleId = 261, + /// 1 weenie attestations. + NumCharacterTitles = 262, + /// 93 weenie attestations. + ResistanceModifierType = 263, + FreeTinkersBitfield = 264, + /// 316 weenie attestations. + EquipmentSetId = 265, + PetClass = 266, + /// 119 weenie attestations. + Lifespan = 267, + /// 119 weenie attestations. + RemainingLifespan = 268, + UseCreateQuantity = 269, + /// 62 weenie attestations. + WieldRequirements2 = 270, + /// 62 weenie attestations. + WieldSkilltype2 = 271, + /// 62 weenie attestations. + WieldDifficulty2 = 272, + /// 3 weenie attestations. + WieldRequirements3 = 273, + /// 3 weenie attestations. + WieldSkilltype3 = 274, + /// 3 weenie attestations. + WieldDifficulty3 = 275, + /// 17 weenie attestations. + WieldRequirements4 = 276, + /// 17 weenie attestations. + WieldSkilltype4 = 277, + /// 17 weenie attestations. + WieldDifficulty4 = 278, + /// 14 weenie attestations. + Unique = 279, + /// 365 weenie attestations. + SharedCooldown = 280, + /// 177 weenie attestations. + Faction1Bits = 281, + Faction2Bits = 282, + Faction3Bits = 283, + Hatred1Bits = 284, + Hatred2Bits = 285, + Hatred3Bits = 286, + /// 61 weenie attestations. + SocietyRankCelhan = 287, + /// 60 weenie attestations. + SocietyRankEldweb = 288, + /// 56 weenie attestations. + SocietyRankRadblo = 289, + HearLocalSignals = 290, + HearLocalSignalsRadius = 291, + /// 67 weenie attestations. + Cleaving = 292, + AugmentationSpecializeGearcraft = 293, + AugmentationInfusedCreatureMagic = 294, + AugmentationInfusedItemMagic = 295, + AugmentationInfusedLifeMagic = 296, + AugmentationInfusedWarMagic = 297, + AugmentationCriticalExpertise = 298, + AugmentationCriticalPower = 299, + AugmentationSkilledMelee = 300, + AugmentationSkilledMissile = 301, + AugmentationSkilledMagic = 302, + /// 124 weenie attestations. + ImbuedEffect2 = 303, + /// 124 weenie attestations. + ImbuedEffect3 = 304, + /// 117 weenie attestations. + ImbuedEffect4 = 305, + /// 117 weenie attestations. + ImbuedEffect5 = 306, + /// 1403 weenie attestations. + DamageRating = 307, + /// 729 weenie attestations. + DamageResistRating = 308, + AugmentationDamageBonus = 309, + AugmentationDamageReduction = 310, + ImbueStackingBits = 311, + HealOverTime = 312, + /// 880 weenie attestations. + CritRating = 313, + /// 843 weenie attestations. + CritDamageRating = 314, + /// 713 weenie attestations. + CritResistRating = 315, + /// 732 weenie attestations. + CritDamageResistRating = 316, + HealingResistRating = 317, + DamageOverTime = 318, + /// 79 weenie attestations. + ItemMaxLevel = 319, + /// 79 weenie attestations. + ItemXpStyle = 320, + EquipmentSetExtra = 321, + AetheriaBitfield = 322, + HealingBoostRating = 323, + /// 29 weenie attestations. + 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, + /// 13 weenie attestations. + CloakWeaveProc = 352, + /// 492 weenie attestations. + 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, + /// 259 weenie attestations. + UseRequiresSkill = 366, + /// 259 weenie attestations. + UseRequiresSkillLevel = 367, + /// 36 weenie attestations. + UseRequiresSkillSpec = 368, + /// 271 weenie attestations. + UseRequiresLevel = 369, + /// 869 weenie attestations. + GearDamage = 370, + /// 868 weenie attestations. + GearDamageResist = 371, + /// 854 weenie attestations. + GearCrit = 372, + /// 854 weenie attestations. + GearCritResist = 373, + /// 955 weenie attestations. + GearCritDamage = 374, + /// 953 weenie attestations. + GearCritDamageResist = 375, + /// 610 weenie attestations. + GearHealingBoost = 376, + /// 602 weenie attestations. + GearNetherResist = 377, + /// 602 weenie attestations. + GearLifeResist = 378, + /// 640 weenie attestations. + GearMaxHealth = 379, + Unknown380 = 380, + /// 616 weenie attestations. + PKDamageRating = 381, + /// 602 weenie attestations. + PKDamageResistRating = 382, + /// 643 weenie attestations. + GearPKDamageRating = 383, + /// 640 weenie attestations. + GearPKDamageResistRating = 384, + Unknown385 = 385, + /// 766 weenie attestations. + Overpower = 386, + /// 602 weenie attestations. + OverpowerResist = 387, + /// 602 weenie attestations. + GearOverpower = 388, + /// 602 weenie attestations. + GearOverpowerResist = 389, + /// 2 weenie attestations. + Enlightenment = 390, +} diff --git a/src/AcDream.Core/Properties/PropertyInt64.cs b/src/AcDream.Core/Properties/PropertyInt64.cs new file mode 100644 index 00000000..e6792529 --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyInt64.cs @@ -0,0 +1,43 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyInt64 property table — the numeric keys the server sends in +/// PrivateUpdatePropertyInt64 (0x02D1) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is long. +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table Int64Id) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyInt64 : uint +{ + Undef = 0, + TotalExperience = 1, + AvailableExperience = 2, + /// 7 weenie attestations. + AugmentationCost = 3, + /// 79 weenie attestations. + ItemTotalXp = 4, + /// 79 weenie attestations. + ItemBaseXp = 5, + AvailableLuminance = 6, + MaximumLuminance = 7, + InteractionReqs = 8, +} diff --git a/src/AcDream.Core/Properties/PropertyString.cs b/src/AcDream.Core/Properties/PropertyString.cs new file mode 100644 index 00000000..0e2f0b03 --- /dev/null +++ b/src/AcDream.Core/Properties/PropertyString.cs @@ -0,0 +1,112 @@ +// +// 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. +// + +namespace AcDream.Core.Properties; + +/// +/// AC's PropertyString property table — the numeric keys the server sends in +/// PrivateUpdatePropertyString (0x02DD) / PublicUpdatePropertyString (0x02DE) +/// and in the property bundles carried by CreateObject / PlayerDescription / +/// IdentifyResponse. The CLR payload for this table is string. +/// +/// Verified 2026-07-29 against two independent oracles: the vendored +/// client-side enum catalog (references/acclientlib/UtilityBelt.Common/Enums/Enums.cs, +/// which names this table StringId) and the 38,985-file ACE weenie export corpus +/// (references/weenies/), whose per-stat _comment 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. +/// +/// This enum is a vocabulary, not a parser contract: the wire carries a raw +/// uint and unknown keys must still round-trip untouched, so never assume a +/// received key is a defined member. +/// +public enum PropertyString : uint +{ + Undef = 0, + /// 38984 weenie attestations. + Name = 1, + Title = 2, + /// 1697 weenie attestations. + Sex = 3, + /// 1693 weenie attestations. + HeritageGroup = 4, + /// 1622 weenie attestations. + Template = 5, + AttackersName = 6, + /// 93 weenie attestations. + Inscription = 7, + /// 93 weenie attestations. + ScribeName = 8, + VendorsName = 9, + /// 1 weenie attestations. + Fellowship = 10, + MonarchsName = 11, + /// 449 weenie attestations. + LockCode = 12, + /// 238 weenie attestations. + KeyCode = 13, + /// 3219 weenie attestations. + Use = 14, + /// 5597 weenie attestations. + ShortDesc = 15, + /// 7107 weenie attestations. + LongDesc = 16, + /// 109 weenie attestations. + ActivationTalk = 17, + /// 10 weenie attestations. + UseMessage = 18, + /// 45 weenie attestations. + ItemHeritageGroupRestriction = 19, + /// 1318 weenie attestations. + PluralName = 20, + MonarchsTitle = 21, + /// 96 weenie attestations. + ActivationFailure = 22, + ScribeAccount = 23, + /// 591 weenie attestations. + TownName = 24, + /// 2 weenie attestations. + CraftsmanName = 25, + /// 4 weenie attestations. + UsePkServerError = 26, + ScoreCachedText = 27, + /// 5 weenie attestations. + ScoreDefaultEntryFormat = 28, + /// 5 weenie attestations. + ScoreFirstEntryFormat = 29, + /// 5 weenie attestations. + ScoreLastEntryFormat = 30, + /// 5 weenie attestations. + ScoreOnlyEntryFormat = 31, + /// 5 weenie attestations. + ScoreNoEntry = 32, + /// 859 weenie attestations. + Quest = 33, + /// 249 weenie attestations. + GeneratorEvent = 34, + PatronsTitle = 35, + HouseOwnerName = 36, + /// 170 weenie attestations. + 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, +} diff --git a/tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs b/tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs new file mode 100644 index 00000000..f6e7ed03 --- /dev/null +++ b/tests/AcDream.Core.Tests/Properties/PropertyEnumConformanceTests.cs @@ -0,0 +1,1398 @@ +// +// Golden conformance table for the seven Property* enums. Regenerate with the +// 2026-07-29 enum verification campaign tooling; see +// docs/research/2026-07-29-enum-verification-campaign.md. +// +using System; +using System.Collections.Generic; +using System.Linq; +using AcDream.Core.Properties; +using Xunit; + +namespace AcDream.Core.Tests.Properties; + +/// +/// Pins every member of AC's seven property tables to its wire value. The table +/// below is the verbatim output of the 2026-07-29 oracle cross-check, so any hand +/// edit to a Property* enum — a renamed member, a changed value, an added or +/// dropped key — fails here rather than silently mis-reading the wire. +/// +/// is the stronger subset: members whose +/// name/value pairing is independently attested by the ACE weenie export corpus, +/// not just by the client-side enum catalog. +/// +public sealed class PropertyEnumConformanceTests +{ + private static readonly (string Name, uint Value)[] PropertyIntGolden = + [ + ("Undef", 0), + ("ItemType", 1), + ("CreatureType", 2), + ("PaletteTemplate", 3), + ("ClothingPriority", 4), + ("EncumbranceVal", 5), + ("ItemsCapacity", 6), + ("ContainersCapacity", 7), + ("Mass", 8), + ("ValidLocations", 9), + ("CurrentWieldedLocation", 10), + ("MaxStackSize", 11), + ("StackSize", 12), + ("StackUnitEncumbrance", 13), + ("StackUnitMass", 14), + ("StackUnitValue", 15), + ("ItemUseable", 16), + ("RareId", 17), + ("UiEffects", 18), + ("Value", 19), + ("CoinValue", 20), + ("TotalExperience", 21), + ("AvailableCharacter", 22), + ("TotalSkillCredits", 23), + ("AvailableSkillCredits", 24), + ("Level", 25), + ("AccountRequirements", 26), + ("ArmorType", 27), + ("ArmorLevel", 28), + ("AllegianceCpPool", 29), + ("AllegianceRank", 30), + ("ChannelsAllowed", 31), + ("ChannelsActive", 32), + ("Bonded", 33), + ("MonarchsRank", 34), + ("AllegianceFollowers", 35), + ("ResistMagic", 36), + ("ResistItemAppraisal", 37), + ("ResistLockpick", 38), + ("DeprecatedResistRepair", 39), + ("CombatMode", 40), + ("CurrentAttackHeight", 41), + ("CombatCollisions", 42), + ("NumDeaths", 43), + ("Damage", 44), + ("DamageType", 45), + ("DefaultCombatStyle", 46), + ("AttackType", 47), + ("WeaponSkill", 48), + ("WeaponTime", 49), + ("AmmoType", 50), + ("CombatUse", 51), + ("ParentLocation", 52), + ("PlacementPosition", 53), + ("WeaponEncumbrance", 54), + ("WeaponMass", 55), + ("ShieldValue", 56), + ("ShieldEncumbrance", 57), + ("MissileInventoryLocation", 58), + ("FullDamageType", 59), + ("WeaponRange", 60), + ("AttackersSkill", 61), + ("DefendersSkill", 62), + ("AttackersSkillValue", 63), + ("AttackersClass", 64), + ("Placement", 65), + ("CheckpointStatus", 66), + ("Tolerance", 67), + ("TargetingTactic", 68), + ("CombatTactic", 69), + ("HomesickTargetingTactic", 70), + ("NumFollowFailures", 71), + ("FriendType", 72), + ("FoeType", 73), + ("MerchandiseItemTypes", 74), + ("MerchandiseMinValue", 75), + ("MerchandiseMaxValue", 76), + ("NumItemsSold", 77), + ("NumItemsBought", 78), + ("MoneyIncome", 79), + ("MoneyOutflow", 80), + ("MaxGeneratedObjects", 81), + ("InitGeneratedObjects", 82), + ("ActivationResponse", 83), + ("OriginalValue", 84), + ("NumMoveFailures", 85), + ("MinLevel", 86), + ("MaxLevel", 87), + ("LockpickMod", 88), + ("BoosterEnum", 89), + ("BoostValue", 90), + ("MaxStructure", 91), + ("Structure", 92), + ("PhysicsState", 93), + ("TargetType", 94), + ("RadarBlipColor", 95), + ("EncumbranceCapacity", 96), + ("LoginTimestamp", 97), + ("CreationTimestamp", 98), + ("PkLevelModifier", 99), + ("GeneratorType", 100), + ("AiAllowedCombatStyle", 101), + ("LogoffTimestamp", 102), + ("GeneratorDestructionType", 103), + ("ActivationCreateClass", 104), + ("ItemWorkmanship", 105), + ("ItemSpellcraft", 106), + ("ItemCurMana", 107), + ("ItemMaxMana", 108), + ("ItemDifficulty", 109), + ("ItemAllegianceRankLimit", 110), + ("PortalBitmask", 111), + ("AdvocateLevel", 112), + ("Gender", 113), + ("Attuned", 114), + ("ItemSkillLevelLimit", 115), + ("GateLogic", 116), + ("ItemManaCost", 117), + ("Logoff", 118), + ("Active", 119), + ("AttackHeight", 120), + ("NumAttackFailures", 121), + ("AiCpThreshold", 122), + ("AiAdvancementStrategy", 123), + ("Version", 124), + ("Age", 125), + ("VendorHappyMean", 126), + ("VendorHappyVariance", 127), + ("CloakStatus", 128), + ("VitaeCpPool", 129), + ("NumServicesSold", 130), + ("MaterialType", 131), + ("NumAllegianceBreaks", 132), + ("ShowableOnRadar", 133), + ("PlayerKillerStatus", 134), + ("VendorHappyMaxItems", 135), + ("ScorePageNum", 136), + ("ScoreConfigNum", 137), + ("ScoreNumScores", 138), + ("DeathLevel", 139), + ("AiOptions", 140), + ("OpenToEveryone", 141), + ("GeneratorTimeType", 142), + ("GeneratorStartTime", 143), + ("GeneratorEndTime", 144), + ("GeneratorEndDestructionType", 145), + ("XpOverride", 146), + ("NumCrashAndTurns", 147), + ("ComponentWarningThreshold", 148), + ("HouseStatus", 149), + ("HookPlacement", 150), + ("HookType", 151), + ("HookItemType", 152), + ("AiPpThreshold", 153), + ("GeneratorVersion", 154), + ("HouseType", 155), + ("PickupEmoteOffset", 156), + ("WeenieIteration", 157), + ("WieldRequirements", 158), + ("WieldSkilltype", 159), + ("WieldDifficulty", 160), + ("HouseMaxHooksUsable", 161), + ("HouseCurrentHooksUsable", 162), + ("AllegianceMinLevel", 163), + ("AllegianceMaxLevel", 164), + ("HouseRelinkHookCount", 165), + ("SlayerCreatureType", 166), + ("ConfirmationInProgress", 167), + ("ConfirmationTypeInProgress", 168), + ("TsysMutationData", 169), + ("NumItemsInMaterial", 170), + ("NumTimesTinkered", 171), + ("AppraisalLongDescDecoration", 172), + ("AppraisalLockpickSuccessPercent", 173), + ("AppraisalPages", 174), + ("AppraisalMaxPages", 175), + ("AppraisalItemSkill", 176), + ("GemCount", 177), + ("GemType", 178), + ("ImbuedEffect", 179), + ("AttackersRawSkillValue", 180), + ("ChessRank", 181), + ("ChessTotalGames", 182), + ("ChessGamesWon", 183), + ("ChessGamesLost", 184), + ("TypeOfAlteration", 185), + ("SkillToBeAltered", 186), + ("SkillAlterationCount", 187), + ("HeritageGroup", 188), + ("TransferFromAttribute", 189), + ("TransferToAttribute", 190), + ("AttributeTransferCount", 191), + ("FakeFishingSkill", 192), + ("NumKeys", 193), + ("DeathTimestamp", 194), + ("PkTimestamp", 195), + ("VictimTimestamp", 196), + ("HookGroup", 197), + ("AllegianceSwearTimestamp", 198), + ("HousePurchaseTimestamp", 199), + ("RedirectableEquippedArmorCount", 200), + ("MeleedefenseImbuedEffectTypeCache", 201), + ("MissileDefenseImbuedEffectTypeCache", 202), + ("MagicDefenseImbuedEffectTypeCache", 203), + ("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), + ("ItemAttributeLimit", 257), + ("ItemAttributeLevelLimit", 258), + ("ItemAttribute2ndLimit", 259), + ("ItemAttribute2ndLevelLimit", 260), + ("CharacterTitleId", 261), + ("NumCharacterTitles", 262), + ("ResistanceModifierType", 263), + ("FreeTinkersBitfield", 264), + ("EquipmentSetId", 265), + ("PetClass", 266), + ("Lifespan", 267), + ("RemainingLifespan", 268), + ("UseCreateQuantity", 269), + ("WieldRequirements2", 270), + ("WieldSkilltype2", 271), + ("WieldDifficulty2", 272), + ("WieldRequirements3", 273), + ("WieldSkilltype3", 274), + ("WieldDifficulty3", 275), + ("WieldRequirements4", 276), + ("WieldSkilltype4", 277), + ("WieldDifficulty4", 278), + ("Unique", 279), + ("SharedCooldown", 280), + ("Faction1Bits", 281), + ("Faction2Bits", 282), + ("Faction3Bits", 283), + ("Hatred1Bits", 284), + ("Hatred2Bits", 285), + ("Hatred3Bits", 286), + ("SocietyRankCelhan", 287), + ("SocietyRankEldweb", 288), + ("SocietyRankRadblo", 289), + ("HearLocalSignals", 290), + ("HearLocalSignalsRadius", 291), + ("Cleaving", 292), + ("AugmentationSpecializeGearcraft", 293), + ("AugmentationInfusedCreatureMagic", 294), + ("AugmentationInfusedItemMagic", 295), + ("AugmentationInfusedLifeMagic", 296), + ("AugmentationInfusedWarMagic", 297), + ("AugmentationCriticalExpertise", 298), + ("AugmentationCriticalPower", 299), + ("AugmentationSkilledMelee", 300), + ("AugmentationSkilledMissile", 301), + ("AugmentationSkilledMagic", 302), + ("ImbuedEffect2", 303), + ("ImbuedEffect3", 304), + ("ImbuedEffect4", 305), + ("ImbuedEffect5", 306), + ("DamageRating", 307), + ("DamageResistRating", 308), + ("AugmentationDamageBonus", 309), + ("AugmentationDamageReduction", 310), + ("ImbueStackingBits", 311), + ("HealOverTime", 312), + ("CritRating", 313), + ("CritDamageRating", 314), + ("CritResistRating", 315), + ("CritDamageResistRating", 316), + ("HealingResistRating", 317), + ("DamageOverTime", 318), + ("ItemMaxLevel", 319), + ("ItemXpStyle", 320), + ("EquipmentSetExtra", 321), + ("AetheriaBitfield", 322), + ("HealingBoostRating", 323), + ("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), + ("CloakWeaveProc", 352), + ("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), + ("UseRequiresSkill", 366), + ("UseRequiresSkillLevel", 367), + ("UseRequiresSkillSpec", 368), + ("UseRequiresLevel", 369), + ("GearDamage", 370), + ("GearDamageResist", 371), + ("GearCrit", 372), + ("GearCritResist", 373), + ("GearCritDamage", 374), + ("GearCritDamageResist", 375), + ("GearHealingBoost", 376), + ("GearNetherResist", 377), + ("GearLifeResist", 378), + ("GearMaxHealth", 379), + ("Unknown380", 380), + ("PKDamageRating", 381), + ("PKDamageResistRating", 382), + ("GearPKDamageRating", 383), + ("GearPKDamageResistRating", 384), + ("Unknown385", 385), + ("Overpower", 386), + ("OverpowerResist", 387), + ("GearOverpower", 388), + ("GearOverpowerResist", 389), + ("Enlightenment", 390), + ]; + + private static readonly (string Name, uint Value)[] PropertyInt64Golden = + [ + ("Undef", 0), + ("TotalExperience", 1), + ("AvailableExperience", 2), + ("AugmentationCost", 3), + ("ItemTotalXp", 4), + ("ItemBaseXp", 5), + ("AvailableLuminance", 6), + ("MaximumLuminance", 7), + ("InteractionReqs", 8), + ]; + + private static readonly (string Name, uint Value)[] PropertyBoolGolden = + [ + ("Undef", 0), + ("Stuck", 1), + ("Open", 2), + ("Locked", 3), + ("RotProof", 4), + ("AllegianceUpdateRequest", 5), + ("AiUsesMana", 6), + ("AiUseHumanMagicAnimations", 7), + ("AllowGive", 8), + ("CurrentlyAttacking", 9), + ("AttackerAi", 10), + ("IgnoreCollisions", 11), + ("ReportCollisions", 12), + ("Ethereal", 13), + ("GravityStatus", 14), + ("LightsStatus", 15), + ("ScriptedCollision", 16), + ("Inelastic", 17), + ("Visibility", 18), + ("Attackable", 19), + ("SafeSpellComponents", 20), + ("AdvocateState", 21), + ("Inscribable", 22), + ("DestroyOnSell", 23), + ("UiHidden", 24), + ("IgnoreHouseBarriers", 25), + ("HiddenAdmin", 26), + ("PkWounder", 27), + ("PkKiller", 28), + ("NoCorpse", 29), + ("UnderLifestoneProtection", 30), + ("ItemManaUpdatePending", 31), + ("GeneratorStatus", 32), + ("ResetMessagePending", 33), + ("DefaultOpen", 34), + ("DefaultLocked", 35), + ("DefaultOn", 36), + ("OpenForBusiness", 37), + ("IsFrozen", 38), + ("DealMagicalItems", 39), + ("LogoffImDead", 40), + ("ReportCollisionsAsEnvironment", 41), + ("AllowEdgeSlide", 42), + ("AdvocateQuest", 43), + ("IsAdmin", 44), + ("IsArch", 45), + ("IsSentinel", 46), + ("IsAdvocate", 47), + ("CurrentlyPoweringUp", 48), + ("GeneratorEnteredWorld", 49), + ("NeverFailCasting", 50), + ("VendorService", 51), + ("AiImmobile", 52), + ("DamagedByCollisions", 53), + ("IsDynamic", 54), + ("IsHot", 55), + ("IsAffecting", 56), + ("AffectsAis", 57), + ("SpellQueueActive", 58), + ("GeneratorDisabled", 59), + ("IsAcceptingTells", 60), + ("LoggingChannel", 61), + ("OpensAnyLock", 62), + ("UnlimitedUse", 63), + ("GeneratedTreasureItem", 64), + ("IgnoreMagicResist", 65), + ("IgnoreMagicArmor", 66), + ("AiAllowTrade", 67), + ("SpellComponentsRequired", 68), + ("IsSellable", 69), + ("IgnoreShieldsBySkill", 70), + ("NoDraw", 71), + ("ActivationUntargeted", 72), + ("HouseHasGottenPriorityBootPos", 73), + ("GeneratorAutomaticDestruction", 74), + ("HouseHooksVisible", 75), + ("HouseRequiresMonarch", 76), + ("HouseHooksEnabled", 77), + ("HouseNotifiedHudOfHookCount", 78), + ("AiAcceptEverything", 79), + ("IgnorePortalRestrictions", 80), + ("RequiresBackpackSlot", 81), + ("DontTurnOrMoveWhenGiving", 82), + ("NpcLooksLikeObject", 83), + ("IgnoreCloIcons", 84), + ("AppraisalHasAllowedWielder", 85), + ("ChestRegenOnClose", 86), + ("LogoffInMinigame", 87), + ("PortalShowDestination", 88), + ("PortalIgnoresPkAttackTimer", 89), + ("NpcInteractsSilently", 90), + ("Retained", 91), + ("IgnoreAuthor", 92), + ("Limbo", 93), + ("AppraisalHasAllowedActivator", 94), + ("ExistedBeforeAllegianceXpChanges", 95), + ("IsDeaf", 96), + ("IsPsr", 97), + ("Invincible", 98), + ("Ivoryable", 99), + ("Dyable", 100), + ("CanGenerateRare", 101), + ("CorpseGeneratedRare", 102), + ("NonProjectileMagicImmune", 103), + ("ActdReceivedItems", 104), + ("Unknown105", 105), + ("FirstEnterWorldDone", 106), + ("RecallsDisabled", 107), + ("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), + ("AutowieldLeft", 130), + ]; + + private static readonly (string Name, uint Value)[] PropertyFloatGolden = + [ + ("Undef", 0), + ("HeartbeatInterval", 1), + ("HeartbeatTimestamp", 2), + ("HealthRate", 3), + ("StaminaRate", 4), + ("ManaRate", 5), + ("HealthUponResurrection", 6), + ("StaminaUponResurrection", 7), + ("ManaUponResurrection", 8), + ("StartTime", 9), + ("StopTime", 10), + ("ResetInterval", 11), + ("Shade", 12), + ("ArmorModVsSlash", 13), + ("ArmorModVsPierce", 14), + ("ArmorModVsBludgeon", 15), + ("ArmorModVsCold", 16), + ("ArmorModVsFire", 17), + ("ArmorModVsAcid", 18), + ("ArmorModVsElectric", 19), + ("CombatSpeed", 20), + ("WeaponLength", 21), + ("DamageVariance", 22), + ("CurrentPowerMod", 23), + ("AccuracyMod", 24), + ("StrengthMod", 25), + ("MaximumVelocity", 26), + ("RotationSpeed", 27), + ("MotionTimestamp", 28), + ("WeaponDefense", 29), + ("WimpyLevel", 30), + ("VisualAwarenessRange", 31), + ("AuralAwarenessRange", 32), + ("PerceptionLevel", 33), + ("PowerupTime", 34), + ("MaxChargeDistance", 35), + ("ChargeSpeed", 36), + ("BuyPrice", 37), + ("SellPrice", 38), + ("DefaultScale", 39), + ("LockpickMod", 40), + ("RegenerationInterval", 41), + ("RegenerationTimestamp", 42), + ("GeneratorRadius", 43), + ("TimeToRot", 44), + ("DeathTimestamp", 45), + ("PkTimestamp", 46), + ("VictimTimestamp", 47), + ("LoginTimestamp", 48), + ("CreationTimestamp", 49), + ("MinimumTimeSincePk", 50), + ("DeprecatedHousekeepingPriority", 51), + ("AbuseLoggingTimestamp", 52), + ("LastPortalTeleportTimestamp", 53), + ("UseRadius", 54), + ("HomeRadius", 55), + ("ReleasedTimestamp", 56), + ("MinHomeRadius", 57), + ("Facing", 58), + ("ResetTimestamp", 59), + ("LogoffTimestamp", 60), + ("EconRecoveryInterval", 61), + ("WeaponOffense", 62), + ("DamageMod", 63), + ("ResistSlash", 64), + ("ResistPierce", 65), + ("ResistBludgeon", 66), + ("ResistFire", 67), + ("ResistCold", 68), + ("ResistAcid", 69), + ("ResistElectric", 70), + ("ResistHealthBoost", 71), + ("ResistStaminaDrain", 72), + ("ResistStaminaBoost", 73), + ("ResistManaDrain", 74), + ("ResistManaBoost", 75), + ("Translucency", 76), + ("PhysicsScriptIntensity", 77), + ("Friction", 78), + ("Elasticity", 79), + ("AiUseMagicDelay", 80), + ("ItemMinSpellcraftMod", 81), + ("ItemMaxSpellcraftMod", 82), + ("ItemRankProbability", 83), + ("Shade2", 84), + ("Shade3", 85), + ("Shade4", 86), + ("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), + ("HealkitMod", 100), + ("FrozenTimestamp", 101), + ("HealthRateMod", 102), + ("AllegianceSwearTimestamp", 103), + ("ObviousRadarRange", 104), + ("HotspotCycleTime", 105), + ("HotspotCycleTimeVariance", 106), + ("SpamTimestamp", 107), + ("SpamRate", 108), + ("BondWieldedTreasure", 109), + ("BulkMod", 110), + ("SizeMod", 111), + ("GagTimestamp", 112), + ("GeneratorUpdateTimestamp", 113), + ("DeathSpamTimestamp", 114), + ("DeathSpamRate", 115), + ("WildAttackProbability", 116), + ("FocusedProbability", 117), + ("CrashAndTurnProbability", 118), + ("CrashAndTurnRadius", 119), + ("CrashAndTurnBias", 120), + ("GeneratorInitialDelay", 121), + ("AiAcquireHealth", 122), + ("AiAcquireStamina", 123), + ("AiAcquireMana", 124), + ("ResistHealthDrain", 125), + ("LifestoneProtectionTimestamp", 126), + ("AiCounteractEnchantment", 127), + ("AiDispelEnchantment", 128), + ("TradeTimestamp", 129), + ("AiTargetedDetectionRadius", 130), + ("EmotePriority", 131), + ("LastTeleportStartTimestamp", 132), + ("EventSpamTimestamp", 133), + ("EventSpamRate", 134), + ("InventoryOffset", 135), + ("CriticalMultiplier", 136), + ("ManaStoneDestroyChance", 137), + ("SlayerDamageBonus", 138), + ("AllegianceInfoSpamTimestamp", 139), + ("AllegianceInfoSpamRate", 140), + ("NextSpellcastTimestamp", 141), + ("AppraisalRequestedTimestamp", 142), + ("AppraisalHeartbeatDueTimestamp", 143), + ("ManaConversionMod", 144), + ("LastPkAttackTimestamp", 145), + ("FellowshipUpdateTimestamp", 146), + ("CriticalFrequency", 147), + ("LimboStartTimestamp", 148), + ("WeaponMissileDefense", 149), + ("WeaponMagicDefense", 150), + ("IgnoreShield", 151), + ("ElementalDamageMod", 152), + ("StartMissileAttackTimestamp", 153), + ("LastRareUsedTimestamp", 154), + ("IgnoreArmor", 155), + ("ProcSpellRate", 156), + ("ResistanceModifier", 157), + ("AllegianceGagTimestamp", 158), + ("AbsorbMagicDamage", 159), + ("CachedMaxAbsorbMagicDamage", 160), + ("GagDuration", 161), + ("AllegianceGagDuration", 162), + ("GlobalXpMod", 163), + ("HealingModifier", 164), + ("ArmorModVsNether", 165), + ("ResistNether", 166), + ("CooldownDuration", 167), + ("WeaponAuraOffense", 168), + ("WeaponAuraDefense", 169), + ("WeaponAuraElemental", 170), + ("WeaponAuraManaConv", 171), + ]; + + private static readonly (string Name, uint Value)[] PropertyStringGolden = + [ + ("Undef", 0), + ("Name", 1), + ("Title", 2), + ("Sex", 3), + ("HeritageGroup", 4), + ("Template", 5), + ("AttackersName", 6), + ("Inscription", 7), + ("ScribeName", 8), + ("VendorsName", 9), + ("Fellowship", 10), + ("MonarchsName", 11), + ("LockCode", 12), + ("KeyCode", 13), + ("Use", 14), + ("ShortDesc", 15), + ("LongDesc", 16), + ("ActivationTalk", 17), + ("UseMessage", 18), + ("ItemHeritageGroupRestriction", 19), + ("PluralName", 20), + ("MonarchsTitle", 21), + ("ActivationFailure", 22), + ("ScribeAccount", 23), + ("TownName", 24), + ("CraftsmanName", 25), + ("UsePkServerError", 26), + ("ScoreCachedText", 27), + ("ScoreDefaultEntryFormat", 28), + ("ScoreFirstEntryFormat", 29), + ("ScoreLastEntryFormat", 30), + ("ScoreOnlyEntryFormat", 31), + ("ScoreNoEntry", 32), + ("Quest", 33), + ("GeneratorEvent", 34), + ("PatronsTitle", 35), + ("HouseOwnerName", 36), + ("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), + ]; + + private static readonly (string Name, uint Value)[] PropertyDataIdGolden = + [ + ("Undef", 0), + ("Setup", 1), + ("MotionTable", 2), + ("SoundTable", 3), + ("CombatTable", 4), + ("QualityFilter", 5), + ("PaletteBase", 6), + ("ClothingBase", 7), + ("Icon", 8), + ("EyesTexture", 9), + ("NoseTexture", 10), + ("MouthTexture", 11), + ("DefaultEyesTexture", 12), + ("DefaultNoseTexture", 13), + ("DefaultMouthTexture", 14), + ("HairPalette", 15), + ("EyesPalette", 16), + ("SkinPalette", 17), + ("HeadObject", 18), + ("ActivationAnimation", 19), + ("InitMotion", 20), + ("ActivationSound", 21), + ("PhysicsEffectTable", 22), + ("UseSound", 23), + ("UseTargetAnimation", 24), + ("UseTargetSuccessAnimation", 25), + ("UseTargetFailureAnimation", 26), + ("UseUserAnimation", 27), + ("Spell", 28), + ("SpellComponent", 29), + ("PhysicsScript", 30), + ("LinkedPortalOne", 31), + ("WieldedTreasureType", 32), + ("UnknownGuessedname", 33), + ("UnknownGuessedname2", 34), + ("DeathTreasureType", 35), + ("MutateFilter", 36), + ("ItemSkillLimit", 37), + ("UseCreateItem", 38), + ("DeathSpell", 39), + ("VendorsClassId", 40), + ("ItemSpecializedOnly", 41), + ("HouseId", 42), + ("AccountHouseId", 43), + ("RestrictionEffect", 44), + ("CreationMutationFilter", 45), + ("TsysMutationFilter", 46), + ("LastPortal", 47), + ("LinkedPortalTwo", 48), + ("OriginalPortal", 49), + ("IconOverlay", 50), + ("IconOverlaySecondary", 51), + ("IconUnderlay", 52), + ("AugmentationMutationFilter", 53), + ("AugmentationEffect", 54), + ("ProcSpell", 55), + ("AugmentationCreateItem", 56), + ("AlternateCurrency", 57), + ("BlueSurgeSpell", 58), + ("YellowSurgeSpell", 59), + ("RedSurgeSpell", 60), + ("OlthoiDeathTreasureType", 61), + ]; + + private static readonly (string Name, uint Value)[] PropertyInstanceIdGolden = + [ + ("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), + ("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), + ("AllowedWielder", 38), + ("AssignedTarget", 39), + ("LimboSource", 40), + ("Snooper", 41), + ("TeleportedCharacter", 42), + ("Pet", 43), + ("PetOwner", 44), + ("PetDevice", 45), + ]; + + public static TheoryData Families => + new("PropertyInt", "PropertyInt64", "PropertyBool", "PropertyFloat", "PropertyString", "PropertyDataId", "PropertyInstanceId"); + + private static (Type Enum, (string Name, uint Value)[] Golden) Resolve(string family) => family switch + { + "PropertyInt" => (typeof(PropertyInt), PropertyIntGolden), + "PropertyInt64" => (typeof(PropertyInt64), PropertyInt64Golden), + "PropertyBool" => (typeof(PropertyBool), PropertyBoolGolden), + "PropertyFloat" => (typeof(PropertyFloat), PropertyFloatGolden), + "PropertyString" => (typeof(PropertyString), PropertyStringGolden), + "PropertyDataId" => (typeof(PropertyDataId), PropertyDataIdGolden), + "PropertyInstanceId" => (typeof(PropertyInstanceId), PropertyInstanceIdGolden), + _ => throw new ArgumentOutOfRangeException(nameof(family), family, "unknown property family"), + }; + + [Theory] + [MemberData(nameof(Families))] + public void EnumMatchesGoldenTableExactly(string family) + { + var (type, golden) = Resolve(family); + var actual = Enum.GetNames(type) + .Select(n => (Name: n, Value: Convert.ToUInt32(Enum.Parse(type, n)))) + .OrderBy(e => e.Value).ThenBy(e => e.Name, StringComparer.Ordinal) + .ToArray(); + var expected = golden + .OrderBy(e => e.Value).ThenBy(e => e.Name, StringComparer.Ordinal) + .ToArray(); + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(Families))] + public void UnderlyingTypeIsUInt32(string family) + { + var (type, _) = Resolve(family); + Assert.Equal(typeof(uint), Enum.GetUnderlyingType(type)); + } + + [Theory] + [MemberData(nameof(Families))] + public void NoTwoMembersShareAValue(string family) + { + var (_, golden) = Resolve(family); + var dupes = golden.GroupBy(e => e.Value) + .Where(g => g.Count() > 1) + .Select(g => g.Key + ": " + string.Join(", ", g.Select(e => e.Name))) + .ToArray(); + Assert.Empty(dupes); + } + + /// + /// The name/value pairs the ACE weenie export corpus attests directly. These are + /// the two-oracle-confirmed members; the campaign found zero value conflicts + /// between the corpus and the client-side catalog across all seven tables. + /// + public static TheoryData WeenieAttested + { + get + { + var d = new TheoryData(); + d.Add("PropertyInt", "ItemType", 1); + d.Add("PropertyInt", "CreatureType", 2); + d.Add("PropertyInt", "PaletteTemplate", 3); + d.Add("PropertyInt", "ClothingPriority", 4); + d.Add("PropertyInt", "EncumbranceVal", 5); + d.Add("PropertyInt", "ItemsCapacity", 6); + d.Add("PropertyInt", "ContainersCapacity", 7); + d.Add("PropertyInt", "Mass", 8); + d.Add("PropertyInt", "ValidLocations", 9); + d.Add("PropertyInt", "MaxStackSize", 11); + d.Add("PropertyInt", "StackSize", 12); + d.Add("PropertyInt", "StackUnitEncumbrance", 13); + d.Add("PropertyInt", "StackUnitMass", 14); + d.Add("PropertyInt", "StackUnitValue", 15); + d.Add("PropertyInt", "ItemUseable", 16); + d.Add("PropertyInt", "RareId", 17); + d.Add("PropertyInt", "UiEffects", 18); + d.Add("PropertyInt", "Value", 19); + d.Add("PropertyInt", "CoinValue", 20); + d.Add("PropertyInt", "Level", 25); + d.Add("PropertyInt", "AccountRequirements", 26); + d.Add("PropertyInt", "ArmorType", 27); + d.Add("PropertyInt", "ArmorLevel", 28); + d.Add("PropertyInt", "AllegianceRank", 30); + d.Add("PropertyInt", "Bonded", 33); + d.Add("PropertyInt", "ResistMagic", 36); + d.Add("PropertyInt", "ResistItemAppraisal", 37); + d.Add("PropertyInt", "ResistLockpick", 38); + d.Add("PropertyInt", "CombatMode", 40); + d.Add("PropertyInt", "NumDeaths", 43); + d.Add("PropertyInt", "Damage", 44); + d.Add("PropertyInt", "DamageType", 45); + d.Add("PropertyInt", "DefaultCombatStyle", 46); + d.Add("PropertyInt", "AttackType", 47); + d.Add("PropertyInt", "WeaponSkill", 48); + d.Add("PropertyInt", "WeaponTime", 49); + d.Add("PropertyInt", "AmmoType", 50); + d.Add("PropertyInt", "CombatUse", 51); + d.Add("PropertyInt", "ParentLocation", 52); + d.Add("PropertyInt", "PlacementPosition", 53); + d.Add("PropertyInt", "WeaponRange", 60); + d.Add("PropertyInt", "CheckpointStatus", 66); + d.Add("PropertyInt", "Tolerance", 67); + d.Add("PropertyInt", "TargetingTactic", 68); + d.Add("PropertyInt", "CombatTactic", 69); + d.Add("PropertyInt", "FriendType", 72); + d.Add("PropertyInt", "MerchandiseItemTypes", 74); + d.Add("PropertyInt", "MerchandiseMinValue", 75); + d.Add("PropertyInt", "MerchandiseMaxValue", 76); + d.Add("PropertyInt", "MaxGeneratedObjects", 81); + d.Add("PropertyInt", "InitGeneratedObjects", 82); + d.Add("PropertyInt", "ActivationResponse", 83); + d.Add("PropertyInt", "MinLevel", 86); + d.Add("PropertyInt", "MaxLevel", 87); + d.Add("PropertyInt", "LockpickMod", 88); + d.Add("PropertyInt", "BoosterEnum", 89); + d.Add("PropertyInt", "BoostValue", 90); + d.Add("PropertyInt", "MaxStructure", 91); + d.Add("PropertyInt", "Structure", 92); + d.Add("PropertyInt", "PhysicsState", 93); + d.Add("PropertyInt", "TargetType", 94); + d.Add("PropertyInt", "RadarBlipColor", 95); + d.Add("PropertyInt", "EncumbranceCapacity", 96); + d.Add("PropertyInt", "CreationTimestamp", 98); + d.Add("PropertyInt", "PkLevelModifier", 99); + d.Add("PropertyInt", "GeneratorType", 100); + d.Add("PropertyInt", "AiAllowedCombatStyle", 101); + d.Add("PropertyInt", "GeneratorDestructionType", 103); + d.Add("PropertyInt", "ItemWorkmanship", 105); + d.Add("PropertyInt", "ItemSpellcraft", 106); + d.Add("PropertyInt", "ItemCurMana", 107); + d.Add("PropertyInt", "ItemMaxMana", 108); + d.Add("PropertyInt", "ItemDifficulty", 109); + d.Add("PropertyInt", "ItemAllegianceRankLimit", 110); + d.Add("PropertyInt", "PortalBitmask", 111); + d.Add("PropertyInt", "Gender", 113); + d.Add("PropertyInt", "Attuned", 114); + d.Add("PropertyInt", "ItemSkillLevelLimit", 115); + d.Add("PropertyInt", "ItemManaCost", 117); + d.Add("PropertyInt", "Active", 119); + d.Add("PropertyInt", "Age", 125); + d.Add("PropertyInt", "VendorHappyMean", 126); + d.Add("PropertyInt", "VendorHappyVariance", 127); + d.Add("PropertyInt", "MaterialType", 131); + d.Add("PropertyInt", "ShowableOnRadar", 133); + d.Add("PropertyInt", "PlayerKillerStatus", 134); + d.Add("PropertyInt", "ScorePageNum", 136); + d.Add("PropertyInt", "ScoreConfigNum", 137); + d.Add("PropertyInt", "ScoreNumScores", 138); + d.Add("PropertyInt", "AiOptions", 140); + d.Add("PropertyInt", "GeneratorTimeType", 142); + d.Add("PropertyInt", "GeneratorStartTime", 143); + d.Add("PropertyInt", "GeneratorEndTime", 144); + d.Add("PropertyInt", "GeneratorEndDestructionType", 145); + d.Add("PropertyInt", "XpOverride", 146); + d.Add("PropertyInt", "HouseStatus", 149); + d.Add("PropertyInt", "HookPlacement", 150); + d.Add("PropertyInt", "HookType", 151); + d.Add("PropertyInt", "HookItemType", 152); + d.Add("PropertyInt", "HouseType", 155); + d.Add("PropertyInt", "PickupEmoteOffset", 156); + d.Add("PropertyInt", "WeenieIteration", 157); + d.Add("PropertyInt", "WieldRequirements", 158); + d.Add("PropertyInt", "WieldSkilltype", 159); + d.Add("PropertyInt", "WieldDifficulty", 160); + d.Add("PropertyInt", "HouseMaxHooksUsable", 161); + d.Add("PropertyInt", "AllegianceMinLevel", 163); + d.Add("PropertyInt", "SlayerCreatureType", 166); + d.Add("PropertyInt", "TsysMutationData", 169); + d.Add("PropertyInt", "NumItemsInMaterial", 170); + d.Add("PropertyInt", "NumTimesTinkered", 171); + d.Add("PropertyInt", "AppraisalLongDescDecoration", 172); + d.Add("PropertyInt", "AppraisalLockpickSuccessPercent", 173); + d.Add("PropertyInt", "AppraisalPages", 174); + d.Add("PropertyInt", "AppraisalMaxPages", 175); + d.Add("PropertyInt", "AppraisalItemSkill", 176); + d.Add("PropertyInt", "GemCount", 177); + d.Add("PropertyInt", "GemType", 178); + d.Add("PropertyInt", "ImbuedEffect", 179); + d.Add("PropertyInt", "TypeOfAlteration", 185); + d.Add("PropertyInt", "SkillToBeAltered", 186); + d.Add("PropertyInt", "HeritageGroup", 188); + d.Add("PropertyInt", "TransferFromAttribute", 189); + d.Add("PropertyInt", "TransferToAttribute", 190); + d.Add("PropertyInt", "NumKeys", 193); + d.Add("PropertyInt", "HookGroup", 197); + d.Add("PropertyInt", "ElementalDamageBonus", 204); + d.Add("PropertyInt", "ItemAttributeLimit", 257); + d.Add("PropertyInt", "ItemAttributeLevelLimit", 258); + d.Add("PropertyInt", "CharacterTitleId", 261); + d.Add("PropertyInt", "NumCharacterTitles", 262); + d.Add("PropertyInt", "ResistanceModifierType", 263); + d.Add("PropertyInt", "EquipmentSetId", 265); + d.Add("PropertyInt", "Lifespan", 267); + d.Add("PropertyInt", "RemainingLifespan", 268); + d.Add("PropertyInt", "WieldRequirements2", 270); + d.Add("PropertyInt", "WieldSkilltype2", 271); + d.Add("PropertyInt", "WieldDifficulty2", 272); + d.Add("PropertyInt", "WieldRequirements3", 273); + d.Add("PropertyInt", "WieldSkilltype3", 274); + d.Add("PropertyInt", "WieldDifficulty3", 275); + d.Add("PropertyInt", "WieldRequirements4", 276); + d.Add("PropertyInt", "WieldSkilltype4", 277); + d.Add("PropertyInt", "WieldDifficulty4", 278); + d.Add("PropertyInt", "Unique", 279); + d.Add("PropertyInt", "SharedCooldown", 280); + d.Add("PropertyInt", "Faction1Bits", 281); + d.Add("PropertyInt", "SocietyRankCelhan", 287); + d.Add("PropertyInt", "SocietyRankEldweb", 288); + d.Add("PropertyInt", "SocietyRankRadblo", 289); + d.Add("PropertyInt", "Cleaving", 292); + d.Add("PropertyInt", "ImbuedEffect2", 303); + d.Add("PropertyInt", "ImbuedEffect3", 304); + d.Add("PropertyInt", "ImbuedEffect4", 305); + d.Add("PropertyInt", "ImbuedEffect5", 306); + d.Add("PropertyInt", "DamageRating", 307); + d.Add("PropertyInt", "DamageResistRating", 308); + d.Add("PropertyInt", "CritRating", 313); + d.Add("PropertyInt", "CritDamageRating", 314); + d.Add("PropertyInt", "CritResistRating", 315); + d.Add("PropertyInt", "CritDamageResistRating", 316); + d.Add("PropertyInt", "ItemMaxLevel", 319); + d.Add("PropertyInt", "ItemXpStyle", 320); + d.Add("PropertyInt", "HeritageSpecificArmor", 324); + d.Add("PropertyInt", "CloakWeaveProc", 352); + d.Add("PropertyInt", "WeaponType", 353); + d.Add("PropertyInt", "UseRequiresSkill", 366); + d.Add("PropertyInt", "UseRequiresSkillLevel", 367); + d.Add("PropertyInt", "UseRequiresSkillSpec", 368); + d.Add("PropertyInt", "UseRequiresLevel", 369); + d.Add("PropertyInt", "GearDamage", 370); + d.Add("PropertyInt", "GearDamageResist", 371); + d.Add("PropertyInt", "GearCrit", 372); + d.Add("PropertyInt", "GearCritResist", 373); + d.Add("PropertyInt", "GearCritDamage", 374); + d.Add("PropertyInt", "GearCritDamageResist", 375); + d.Add("PropertyInt", "GearHealingBoost", 376); + d.Add("PropertyInt", "GearNetherResist", 377); + d.Add("PropertyInt", "GearLifeResist", 378); + d.Add("PropertyInt", "GearMaxHealth", 379); + d.Add("PropertyInt", "PKDamageRating", 381); + d.Add("PropertyInt", "PKDamageResistRating", 382); + d.Add("PropertyInt", "GearPKDamageRating", 383); + d.Add("PropertyInt", "GearPKDamageResistRating", 384); + d.Add("PropertyInt", "Overpower", 386); + d.Add("PropertyInt", "OverpowerResist", 387); + d.Add("PropertyInt", "GearOverpower", 388); + d.Add("PropertyInt", "GearOverpowerResist", 389); + d.Add("PropertyInt", "Enlightenment", 390); + d.Add("PropertyInt64", "AugmentationCost", 3); + d.Add("PropertyInt64", "ItemTotalXp", 4); + d.Add("PropertyInt64", "ItemBaseXp", 5); + d.Add("PropertyBool", "Stuck", 1); + d.Add("PropertyBool", "Open", 2); + d.Add("PropertyBool", "Locked", 3); + d.Add("PropertyBool", "AiUsesMana", 6); + d.Add("PropertyBool", "AiUseHumanMagicAnimations", 7); + d.Add("PropertyBool", "AllowGive", 8); + d.Add("PropertyBool", "IgnoreCollisions", 11); + d.Add("PropertyBool", "ReportCollisions", 12); + d.Add("PropertyBool", "Ethereal", 13); + d.Add("PropertyBool", "GravityStatus", 14); + d.Add("PropertyBool", "LightsStatus", 15); + d.Add("PropertyBool", "ScriptedCollision", 16); + d.Add("PropertyBool", "Inelastic", 17); + d.Add("PropertyBool", "Visibility", 18); + d.Add("PropertyBool", "Attackable", 19); + d.Add("PropertyBool", "Inscribable", 22); + d.Add("PropertyBool", "DestroyOnSell", 23); + d.Add("PropertyBool", "UiHidden", 24); + d.Add("PropertyBool", "NoCorpse", 29); + d.Add("PropertyBool", "ResetMessagePending", 33); + d.Add("PropertyBool", "DefaultOpen", 34); + d.Add("PropertyBool", "DefaultLocked", 35); + d.Add("PropertyBool", "DealMagicalItems", 39); + d.Add("PropertyBool", "ReportCollisionsAsEnvironment", 41); + d.Add("PropertyBool", "AllowEdgeSlide", 42); + d.Add("PropertyBool", "NeverFailCasting", 50); + d.Add("PropertyBool", "VendorService", 51); + d.Add("PropertyBool", "AiImmobile", 52); + d.Add("PropertyBool", "DamagedByCollisions", 53); + d.Add("PropertyBool", "IsDynamic", 54); + d.Add("PropertyBool", "IsHot", 55); + d.Add("PropertyBool", "AffectsAis", 57); + d.Add("PropertyBool", "LoggingChannel", 61); + d.Add("PropertyBool", "OpensAnyLock", 62); + d.Add("PropertyBool", "UnlimitedUse", 63); + d.Add("PropertyBool", "IgnoreMagicResist", 65); + d.Add("PropertyBool", "IgnoreMagicArmor", 66); + d.Add("PropertyBool", "SpellComponentsRequired", 68); + d.Add("PropertyBool", "IsSellable", 69); + d.Add("PropertyBool", "IgnoreShieldsBySkill", 70); + d.Add("PropertyBool", "NoDraw", 71); + d.Add("PropertyBool", "GeneratorAutomaticDestruction", 74); + d.Add("PropertyBool", "HouseRequiresMonarch", 76); + d.Add("PropertyBool", "AiAcceptEverything", 79); + d.Add("PropertyBool", "RequiresBackpackSlot", 81); + d.Add("PropertyBool", "DontTurnOrMoveWhenGiving", 82); + d.Add("PropertyBool", "NpcLooksLikeObject", 83); + d.Add("PropertyBool", "IgnoreCloIcons", 84); + d.Add("PropertyBool", "AppraisalHasAllowedWielder", 85); + d.Add("PropertyBool", "PortalShowDestination", 88); + d.Add("PropertyBool", "PortalIgnoresPkAttackTimer", 89); + d.Add("PropertyBool", "NpcInteractsSilently", 90); + d.Add("PropertyBool", "Retained", 91); + d.Add("PropertyBool", "IgnoreAuthor", 92); + d.Add("PropertyBool", "Ivoryable", 99); + d.Add("PropertyBool", "Dyable", 100); + d.Add("PropertyBool", "NonProjectileMagicImmune", 103); + d.Add("PropertyBool", "RareUsesTimer", 108); + d.Add("PropertyBool", "AutowieldLeft", 130); + d.Add("PropertyFloat", "HeartbeatInterval", 1); + d.Add("PropertyFloat", "HeartbeatTimestamp", 2); + d.Add("PropertyFloat", "HealthRate", 3); + d.Add("PropertyFloat", "StaminaRate", 4); + d.Add("PropertyFloat", "ManaRate", 5); + d.Add("PropertyFloat", "HealthUponResurrection", 6); + d.Add("PropertyFloat", "StaminaUponResurrection", 7); + d.Add("PropertyFloat", "ManaUponResurrection", 8); + d.Add("PropertyFloat", "ResetInterval", 11); + d.Add("PropertyFloat", "Shade", 12); + d.Add("PropertyFloat", "ArmorModVsSlash", 13); + d.Add("PropertyFloat", "ArmorModVsPierce", 14); + d.Add("PropertyFloat", "ArmorModVsBludgeon", 15); + d.Add("PropertyFloat", "ArmorModVsCold", 16); + d.Add("PropertyFloat", "ArmorModVsFire", 17); + d.Add("PropertyFloat", "ArmorModVsAcid", 18); + d.Add("PropertyFloat", "ArmorModVsElectric", 19); + d.Add("PropertyFloat", "WeaponLength", 21); + d.Add("PropertyFloat", "DamageVariance", 22); + d.Add("PropertyFloat", "MaximumVelocity", 26); + d.Add("PropertyFloat", "RotationSpeed", 27); + d.Add("PropertyFloat", "WeaponDefense", 29); + d.Add("PropertyFloat", "VisualAwarenessRange", 31); + d.Add("PropertyFloat", "PowerupTime", 34); + d.Add("PropertyFloat", "ChargeSpeed", 36); + d.Add("PropertyFloat", "BuyPrice", 37); + d.Add("PropertyFloat", "SellPrice", 38); + d.Add("PropertyFloat", "DefaultScale", 39); + d.Add("PropertyFloat", "LockpickMod", 40); + d.Add("PropertyFloat", "RegenerationInterval", 41); + d.Add("PropertyFloat", "GeneratorRadius", 43); + d.Add("PropertyFloat", "TimeToRot", 44); + d.Add("PropertyFloat", "MinimumTimeSincePk", 50); + d.Add("PropertyFloat", "UseRadius", 54); + d.Add("PropertyFloat", "HomeRadius", 55); + d.Add("PropertyFloat", "WeaponOffense", 62); + d.Add("PropertyFloat", "DamageMod", 63); + d.Add("PropertyFloat", "ResistSlash", 64); + d.Add("PropertyFloat", "ResistPierce", 65); + d.Add("PropertyFloat", "ResistBludgeon", 66); + d.Add("PropertyFloat", "ResistFire", 67); + d.Add("PropertyFloat", "ResistCold", 68); + d.Add("PropertyFloat", "ResistAcid", 69); + d.Add("PropertyFloat", "ResistElectric", 70); + d.Add("PropertyFloat", "ResistHealthBoost", 71); + d.Add("PropertyFloat", "ResistStaminaDrain", 72); + d.Add("PropertyFloat", "ResistStaminaBoost", 73); + d.Add("PropertyFloat", "ResistManaDrain", 74); + d.Add("PropertyFloat", "ResistManaBoost", 75); + d.Add("PropertyFloat", "Translucency", 76); + d.Add("PropertyFloat", "PhysicsScriptIntensity", 77); + d.Add("PropertyFloat", "Friction", 78); + d.Add("PropertyFloat", "Elasticity", 79); + d.Add("PropertyFloat", "AiUseMagicDelay", 80); + d.Add("PropertyFloat", "ItemEfficiency", 87); + d.Add("PropertyFloat", "HealkitMod", 100); + d.Add("PropertyFloat", "ObviousRadarRange", 104); + d.Add("PropertyFloat", "HotspotCycleTime", 105); + d.Add("PropertyFloat", "HotspotCycleTimeVariance", 106); + d.Add("PropertyFloat", "BondWieldedTreasure", 109); + d.Add("PropertyFloat", "BulkMod", 110); + d.Add("PropertyFloat", "SizeMod", 111); + d.Add("PropertyFloat", "FocusedProbability", 117); + d.Add("PropertyFloat", "GeneratorInitialDelay", 121); + d.Add("PropertyFloat", "AiAcquireHealth", 122); + d.Add("PropertyFloat", "AiAcquireStamina", 123); + d.Add("PropertyFloat", "AiAcquireMana", 124); + d.Add("PropertyFloat", "ResistHealthDrain", 125); + d.Add("PropertyFloat", "AiCounteractEnchantment", 127); + d.Add("PropertyFloat", "AiDispelEnchantment", 128); + d.Add("PropertyFloat", "EmotePriority", 131); + d.Add("PropertyFloat", "InventoryOffset", 135); + d.Add("PropertyFloat", "CriticalMultiplier", 136); + d.Add("PropertyFloat", "ManaStoneDestroyChance", 137); + d.Add("PropertyFloat", "SlayerDamageBonus", 138); + d.Add("PropertyFloat", "ManaConversionMod", 144); + d.Add("PropertyFloat", "CriticalFrequency", 147); + d.Add("PropertyFloat", "WeaponMissileDefense", 149); + d.Add("PropertyFloat", "WeaponMagicDefense", 150); + d.Add("PropertyFloat", "IgnoreShield", 151); + d.Add("PropertyFloat", "ElementalDamageMod", 152); + d.Add("PropertyFloat", "IgnoreArmor", 155); + d.Add("PropertyFloat", "ResistanceModifier", 157); + d.Add("PropertyFloat", "AbsorbMagicDamage", 159); + d.Add("PropertyFloat", "ArmorModVsNether", 165); + d.Add("PropertyFloat", "CooldownDuration", 167); + d.Add("PropertyString", "Name", 1); + d.Add("PropertyString", "Sex", 3); + d.Add("PropertyString", "HeritageGroup", 4); + d.Add("PropertyString", "Template", 5); + d.Add("PropertyString", "Inscription", 7); + d.Add("PropertyString", "ScribeName", 8); + d.Add("PropertyString", "Fellowship", 10); + d.Add("PropertyString", "LockCode", 12); + d.Add("PropertyString", "KeyCode", 13); + d.Add("PropertyString", "Use", 14); + d.Add("PropertyString", "ShortDesc", 15); + d.Add("PropertyString", "LongDesc", 16); + d.Add("PropertyString", "ActivationTalk", 17); + d.Add("PropertyString", "UseMessage", 18); + d.Add("PropertyString", "ItemHeritageGroupRestriction", 19); + d.Add("PropertyString", "PluralName", 20); + d.Add("PropertyString", "ActivationFailure", 22); + d.Add("PropertyString", "TownName", 24); + d.Add("PropertyString", "CraftsmanName", 25); + d.Add("PropertyString", "UsePkServerError", 26); + d.Add("PropertyString", "ScoreDefaultEntryFormat", 28); + d.Add("PropertyString", "ScoreFirstEntryFormat", 29); + d.Add("PropertyString", "ScoreLastEntryFormat", 30); + d.Add("PropertyString", "ScoreOnlyEntryFormat", 31); + d.Add("PropertyString", "ScoreNoEntry", 32); + d.Add("PropertyString", "Quest", 33); + d.Add("PropertyString", "GeneratorEvent", 34); + d.Add("PropertyString", "QuestRestriction", 37); + d.Add("PropertyDataId", "Setup", 1); + d.Add("PropertyDataId", "MotionTable", 2); + d.Add("PropertyDataId", "SoundTable", 3); + d.Add("PropertyDataId", "CombatTable", 4); + d.Add("PropertyDataId", "QualityFilter", 5); + d.Add("PropertyDataId", "PaletteBase", 6); + d.Add("PropertyDataId", "ClothingBase", 7); + d.Add("PropertyDataId", "Icon", 8); + d.Add("PropertyDataId", "EyesTexture", 9); + d.Add("PropertyDataId", "NoseTexture", 10); + d.Add("PropertyDataId", "MouthTexture", 11); + d.Add("PropertyDataId", "HairPalette", 15); + d.Add("PropertyDataId", "EyesPalette", 16); + d.Add("PropertyDataId", "SkinPalette", 17); + d.Add("PropertyDataId", "ActivationAnimation", 19); + d.Add("PropertyDataId", "InitMotion", 20); + d.Add("PropertyDataId", "PhysicsEffectTable", 22); + d.Add("PropertyDataId", "UseSound", 23); + d.Add("PropertyDataId", "UseTargetAnimation", 24); + d.Add("PropertyDataId", "UseTargetSuccessAnimation", 25); + d.Add("PropertyDataId", "UseTargetFailureAnimation", 26); + d.Add("PropertyDataId", "UseUserAnimation", 27); + d.Add("PropertyDataId", "Spell", 28); + d.Add("PropertyDataId", "SpellComponent", 29); + d.Add("PropertyDataId", "PhysicsScript", 30); + d.Add("PropertyDataId", "LinkedPortalOne", 31); + d.Add("PropertyDataId", "WieldedTreasureType", 32); + d.Add("PropertyDataId", "UnknownGuessedname", 33); + d.Add("PropertyDataId", "DeathTreasureType", 35); + d.Add("PropertyDataId", "MutateFilter", 36); + d.Add("PropertyDataId", "ItemSkillLimit", 37); + d.Add("PropertyDataId", "UseCreateItem", 38); + d.Add("PropertyDataId", "ItemSpecializedOnly", 41); + d.Add("PropertyDataId", "HouseId", 42); + d.Add("PropertyDataId", "RestrictionEffect", 44); + d.Add("PropertyDataId", "TsysMutationFilter", 46); + d.Add("PropertyDataId", "LinkedPortalTwo", 48); + d.Add("PropertyDataId", "IconOverlay", 50); + d.Add("PropertyDataId", "IconOverlaySecondary", 51); + d.Add("PropertyDataId", "IconUnderlay", 52); + d.Add("PropertyDataId", "ProcSpell", 55); + d.Add("PropertyInstanceId", "ActivationTarget", 16); + d.Add("PropertyInstanceId", "AllowedWielder", 38); + return d; + } + } + + [Theory] + [MemberData(nameof(WeenieAttested))] + public void WeenieAttestedMemberHasExactWireValue(string family, string name, uint value) + { + var (type, _) = Resolve(family); + Assert.True(Enum.IsDefined(type, value), $"{family}.{name} ({value}) is not defined"); + Assert.Equal(name, Enum.GetName(type, value)); + } +}