using AcDream.Core.Physics; using Xunit; namespace AcDream.Core.Tests.Physics; /// /// Unit tests for — Commit A of the /// 2026-04-29 live-entity collision port. Verifies retail-faithful /// conversion from PWD bitfield bits to the boolean collision-decision /// flags that FindObjCollisions consumes for the PvP exemption. /// /// Bit positions confirmed against: /// /// docs/research/named-retail/acclient_2013_pseudo_c.txt:406898-406918 /// (ACCWeenieObject::IsPKLite/IsPK/IsImpenetrable) — the /// retail client itself reads bits 25 / 5 / 21 of pwd._bitfield. /// docs/research/named-retail/acclient.h:6431-6463 /// (PublicWeenieDesc::BitfieldIndex) — names the bits: /// BF_PLAYER=0x8, BF_PLAYER_KILLER=0x20, /// BF_FREE_PKSTATUS=0x200000, BF_PKLITE_PKSTATUS=0x2000000. /// docs/research/named-retail/acclient_2013_pseudo_c.txt:441868-441890 /// (PublicWeenieDesc::SetPlayerKillerStatus) — confirms /// the writer maps PKStatusEnum values onto these exact bits. /// /// public class EntityCollisionFlagsTests { [Fact] public void FromPwdBitfield_AllZeros_NoFlags() { Assert.Equal(EntityCollisionFlags.None, EntityCollisionFlagsExt.FromPwdBitfield(0u)); } [Fact] public void FromPwdBitfield_PlayerBit_SetsIsPlayer() { // BF_PLAYER = 0x8 (bit 3) var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x8u); Assert.True(flags.HasFlag(EntityCollisionFlags.IsPlayer)); Assert.False(flags.HasFlag(EntityCollisionFlags.IsPK)); } [Fact] public void FromPwdBitfield_PlayerKillerBit_SetsIsPK() { // BF_PLAYER_KILLER = 0x20 (bit 5) var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x20u); Assert.True(flags.HasFlag(EntityCollisionFlags.IsPK)); Assert.False(flags.HasFlag(EntityCollisionFlags.IsPKLite)); } [Fact] public void FromPwdBitfield_PkLiteBit_SetsIsPKLite() { // BF_PKLITE_PKSTATUS = 0x2000000 (bit 25) var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x2000000u); Assert.True(flags.HasFlag(EntityCollisionFlags.IsPKLite)); Assert.False(flags.HasFlag(EntityCollisionFlags.IsPK)); } [Fact] public void FromPwdBitfield_FreePkStatusBit_SetsIsImpenetrable() { // BF_FREE_PKSTATUS = 0x200000 (bit 21) var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x200000u); Assert.True(flags.HasFlag(EntityCollisionFlags.IsImpenetrable)); } [Fact] public void FromPwdBitfield_PlayerAndPK_SetsBoth() { // A PK player: BF_PLAYER (0x8) | BF_PLAYER_KILLER (0x20) = 0x28 var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x28u); Assert.True(flags.HasFlag(EntityCollisionFlags.IsPlayer)); Assert.True(flags.HasFlag(EntityCollisionFlags.IsPK)); Assert.False(flags.HasFlag(EntityCollisionFlags.IsPKLite)); Assert.False(flags.HasFlag(EntityCollisionFlags.IsImpenetrable)); } [Fact] public void FromPwdBitfield_UnrelatedBits_Ignored() { // Set BF_OPENABLE (0x1), BF_INSCRIBABLE (0x2), BF_STUCK (0x4) — none // map to collision flags. A creature spawn might have BF_ATTACKABLE // (0x10) set; that's ItemType-derived IsCreature, not a PvP flag. var flags = EntityCollisionFlagsExt.FromPwdBitfield(0x1u | 0x2u | 0x4u | 0x10u); Assert.Equal(EntityCollisionFlags.None, flags); } }