feat(core): adopt the retail members the equipment and physics enums were missing
With the two wrong enums corrected, the remaining wire-adjacent families diff cleanly against the retail header - same values everywhere they overlap, just fewer members on our side. This adopts the gaps. EquipMask gains retail's eleven INVENTORY_LOC composite slot groups (acclient.h: 3193). The 32 primitive slots were already exact and stay pinned by EquipMaskTests; what was missing were the groups the wire and the UI actually reason in - Armor, Jewelry, ReadySlot, Weapon, WeaponReadySlot, the wrist/finger/sigil pairs, and All. These are transcribed as literals, not derived, for the reason the previous commit documents at length. That transcription immediately earned itself. A type remark on EquipMask claimed retail's CLOTHING_LOC composite "also sets bit 31, 0x80000000, which is not a named INVENTORY_LOC primitive". It does not. CLOTHING_LOC is 0x080001FF: the nine wear slots plus bit 27, which is the perfectly well-named Cloak slot. No INVENTORY_LOC member touches bit 31 at all - ALL_LOC stops at bit 30. The remark is corrected and a test now asserts the actual decomposition. TransientStateFlags gains WaterContact (0x8) and CheckEthereal (0x100), the two retail bits acdream's transition never declared. Neither is produced or consumed yet; they are named so those slots cannot be quietly reused for an acdream-local flag and then collide. PhysicsStateFlags gains ReservedUnused1 (0x2) and ReservedUnused2 (0x2000), which retail declares as UNUSED1_PS/UNNUSED2_PS. Same reasoning: reserved is a fact worth recording. AttackHeight gains Undef = 0. The three real heights are 1-based and were already right; retail reserves 0 and the wire sends it, so it is now named instead of arriving as an undefined cast. The numeric values are unchanged, so this renames nothing at runtime. Also checked and found already correct, so left alone: ObjectInfoState (matches ObjectInfoEnum exactly, None being DEFAULT_OI), AttackType (every primitive plus both composites - Unarmed 0x19 and MultiStrike 0x79E0 - land on retail's literals), RadarBlipShape, RadarBehavior, MovementType, HoldKey, ParticleType, and PhysicsDescriptionFlag. AttackType is worth calling out because the campaign's extraction tooling reported it as a conflict; the tool reads one line per member and had truncated a multi-line composite. The enum was fine. RetailEnumConformanceTests grows tables for each of the above, each citing its acclient.h line. Core tests 3,785 -> 3,836. Full suite 9,701 passed / 5 skipped, no failures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f3e95a3ebd
commit
8ccaf72ae7
4 changed files with 173 additions and 2 deletions
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Linq;
|
||||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Properties;
|
||||
|
|
@ -149,4 +150,136 @@ public sealed class RetailEnumConformanceTests
|
|||
Assert.Equal(ItemType.MeleeWeapon | ItemType.MissileWeapon, ItemType.Weapon);
|
||||
Assert.Equal(ItemType.Weapon | ItemType.Caster, ItemType.WeaponOrCaster);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// acclient.h:3193, <c>enum INVENTORY_LOC</c> — the composite slot groups only. The
|
||||
/// 32 primitive slots were already correct and are pinned by <c>EquipMaskTests</c>.
|
||||
/// </summary>
|
||||
public static TheoryData<string, uint> RetailEquipMaskComposites => new()
|
||||
{
|
||||
{ "Clothing", 0x080001FF },
|
||||
{ "Armor", 0x00007E00 },
|
||||
{ "Jewelry", 0x7C0F8000 },
|
||||
{ "WristWear", 0x00030000 },
|
||||
{ "FingerWear", 0x000C0000 },
|
||||
{ "Sigil", 0x70000000 },
|
||||
{ "ReadySlot", 0x03F00000 },
|
||||
{ "Weapon", 0x02500000 },
|
||||
{ "WeaponReadySlot", 0x03500000 },
|
||||
{ "All", 0x7FFFFFFF },
|
||||
{ "CanGoInReadySlot", 0x7FFFFFFF },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailEquipMaskComposites))]
|
||||
public void EquipMaskCompositeMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(EquipMask), name), $"EquipMask.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<EquipMask>(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's CLOTHING_LOC is the nine wear slots plus the cloak slot (bit 27) — it is
|
||||
/// NOT the union of the wear slots alone. A type remark here used to claim the extra
|
||||
/// bit was 31 and unnamed; it is 27 and it is <see cref="EquipMask.Cloak"/>.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ClothingCompositeIsTheWearSlotsPlusCloak()
|
||||
{
|
||||
EquipMask wearSlots =
|
||||
EquipMask.HeadWear | EquipMask.ChestWear | EquipMask.AbdomenWear
|
||||
| EquipMask.UpperArmWear | EquipMask.LowerArmWear | EquipMask.HandWear
|
||||
| EquipMask.UpperLegWear | EquipMask.LowerLegWear | EquipMask.FootWear;
|
||||
|
||||
Assert.Equal(0x000001FFu, (uint)wearSlots);
|
||||
Assert.Equal(EquipMask.Clothing, wearSlots | EquipMask.Cloak);
|
||||
Assert.NotEqual(EquipMask.Clothing, wearSlots);
|
||||
Assert.Equal(0u, (uint)EquipMask.Clothing & 0x80000000u);
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:3688, <c>enum TransientState</c>.</summary>
|
||||
public static TheoryData<string, uint> RetailTransientState => new()
|
||||
{
|
||||
{ "Contact", 0x1 },
|
||||
{ "OnWalkable", 0x2 },
|
||||
{ "Sliding", 0x4 },
|
||||
{ "WaterContact", 0x8 },
|
||||
{ "StationaryFall", 0x10 },
|
||||
{ "StationaryStop", 0x20 },
|
||||
{ "StationaryStuck", 0x40 },
|
||||
{ "Active", 0x80 },
|
||||
{ "CheckEthereal", 0x100 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailTransientState))]
|
||||
public void TransientStateFlagsMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(TransientStateFlags), name),
|
||||
$"TransientStateFlags.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<TransientStateFlags>(name));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:2815, <c>enum PhysicsState</c>, including the two bits retail itself reserves.</summary>
|
||||
public static TheoryData<string, uint> RetailPhysicsState => new()
|
||||
{
|
||||
{ "Static", 0x1 },
|
||||
{ "ReservedUnused1", 0x2 },
|
||||
{ "Ethereal", 0x4 },
|
||||
{ "ReportCollisions", 0x8 },
|
||||
{ "IgnoreCollisions", 0x10 },
|
||||
{ "NoDraw", 0x20 },
|
||||
{ "Missile", 0x40 },
|
||||
{ "Pushable", 0x80 },
|
||||
{ "AlignPath", 0x100 },
|
||||
{ "PathClipped", 0x200 },
|
||||
{ "Gravity", 0x400 },
|
||||
{ "Lighting", 0x800 },
|
||||
{ "ParticleEmitter", 0x1000 },
|
||||
{ "ReservedUnused2", 0x2000 },
|
||||
{ "Hidden", 0x4000 },
|
||||
{ "ScriptedCollision", 0x8000 },
|
||||
{ "HasPhysicsBsp", 0x10000 },
|
||||
{ "Inelastic", 0x20000 },
|
||||
{ "HasDefaultAnim", 0x40000 },
|
||||
{ "HasDefaultScript", 0x80000 },
|
||||
{ "Cloaked", 0x100000 },
|
||||
{ "ReportAsEnvironment", 0x200000 },
|
||||
{ "EdgeSlide", 0x400000 },
|
||||
{ "Sledding", 0x800000 },
|
||||
{ "Frozen", 0x1000000 },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RetailPhysicsState))]
|
||||
public void PhysicsStateFlagsMatchesRetail(string name, uint value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(PhysicsStateFlags), name),
|
||||
$"PhysicsStateFlags.{name} is missing");
|
||||
Assert.Equal(value, (uint)Enum.Parse<PhysicsStateFlags>(name));
|
||||
}
|
||||
|
||||
/// <summary>acclient.h:4371, <c>enum ATTACK_HEIGHT</c>. NUM_ATTACK_HEIGHTS is a count, not a height.</summary>
|
||||
[Theory]
|
||||
[InlineData("Undef", 0)]
|
||||
[InlineData("High", 1)]
|
||||
[InlineData("Medium", 2)]
|
||||
[InlineData("Low", 3)]
|
||||
public void AttackHeightMatchesRetail(string name, int value)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(typeof(AttackHeight), name), $"AttackHeight.{name} is missing");
|
||||
Assert.Equal(value, (int)Enum.Parse<AttackHeight>(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// acclient.h:3807, <c>enum AttackType</c>. The two composites are the interesting
|
||||
/// part: retail spells Unarmed 0x19 and MultiStrike 0x79E0, and acdream derives both
|
||||
/// from its primitives. This asserts the derivation lands on retail's literal.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AttackTypeCompositesMatchRetailLiterals()
|
||||
{
|
||||
Assert.Equal(0x19u, (uint)AttackType.Unarmed);
|
||||
Assert.Equal(0x79E0u, (uint)AttackType.MultiStrike);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue