feat: port retail magic lifecycle and retained spell UI

Complete the retail cast-intent, target, component, enchantment, and busy-state paths; mount the DAT-authored spell bar, spellbook, component book, effects panels, and shared panel lifecycle; and add scoped input plus conformance coverage.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-15 10:55:22 +02:00
parent 7b7ffcd278
commit 07be994d97
84 changed files with 17822 additions and 1051 deletions

View file

@ -628,6 +628,22 @@ public sealed class CreateObjectTests
Assert.Equal(0x50000001u, parsed.Value.PetOwnerId);
}
[Fact]
public void WeenieHeader_spellId_isPreservedForCasterEndowment()
{
byte[] body = BuildMinimalCreateObjectWithWeenieHeader(
guid: 0x50000025u,
name: "Orb",
itemType: (uint)ItemType.Caster,
weenieFlags: 0x00400000u,
spellId: 0x0A6E);
var parsed = CreateObject.TryParse(body);
Assert.NotNull(parsed);
Assert.Equal(0x0A6Eu, parsed.Value.SpellId);
}
private static byte[] BuildMinimalCreateObjectWithWeenieHeader(
uint guid,
string name,
@ -673,7 +689,8 @@ public sealed class CreateObjectTests
uint materialType = 0,
uint cooldownId = 0,
double cooldownDuration = 0,
uint petOwnerId = 0)
uint petOwnerId = 0,
ushort spellId = 0)
{
var bytes = new List<byte>();
WriteU32(bytes, CreateObject.Opcode);
@ -760,7 +777,7 @@ public sealed class CreateObjectTests
bytes.AddRange(tmp.ToArray());
}
if ((weenieFlags & 0x00200000u) != 0) WriteU16(bytes, burden ?? 0); // Burden u16
if ((weenieFlags & 0x00400000u) != 0) WriteU16(bytes, 0); // Spell u16
if ((weenieFlags & 0x00400000u) != 0) WriteU16(bytes, spellId); // Spell u16
if ((weenieFlags & 0x02000000u) != 0) WriteU32(bytes, 0); // HouseOwner u32
// HouseRestrictions (0x04000000): not parameterized (zero entries).
// Wire: Version(u32) + OpenStatus(u32) + MonarchId(u32) + count(u16) + numBuckets(u16) + entries.