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:
parent
7b7ffcd278
commit
07be994d97
84 changed files with 17822 additions and 1051 deletions
|
|
@ -460,8 +460,8 @@ public sealed class GameEventWiringTests
|
|||
public void WireAll_MagicPurgeEnchantments_CallsOnPurgeAll()
|
||||
{
|
||||
var (d, _, _, book, _) = MakeAll();
|
||||
book.OnEnchantmentAdded(1, 1, 100f, 0);
|
||||
book.OnEnchantmentAdded(2, 2, 100f, 0);
|
||||
book.OnEnchantmentAdded(new(1, 1, 100, 0, Bucket: 1));
|
||||
book.OnEnchantmentAdded(new(2, 2, 100, 0, Bucket: 2));
|
||||
Assert.Equal(2, book.ActiveCount);
|
||||
|
||||
var env = GameEventEnvelope.TryParse(WrapEnvelope(GameEventType.MagicPurgeEnchantments, Array.Empty<byte>()));
|
||||
|
|
@ -470,6 +470,32 @@ public sealed class GameEventWiringTests
|
|||
Assert.Equal(0, book.ActiveCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_MagicUpdateEnchantment_ConvertsRelativeTimesAndClassifiesBucket()
|
||||
{
|
||||
var dispatcher = new GameEventDispatcher();
|
||||
var book = new Spellbook();
|
||||
GameEventWiring.WireAll(
|
||||
dispatcher,
|
||||
new ClientObjectTable(),
|
||||
new CombatState(),
|
||||
book,
|
||||
new ChatLog(),
|
||||
clientTime: () => 100d);
|
||||
|
||||
byte[] payload = BuildEnchantment(
|
||||
spellId: 42, layer: 3, duration: 60, caster: 0xBEEF,
|
||||
startTime: 10, lastDegraded: 2, statModType: 0x00008000u);
|
||||
dispatcher.Dispatch(GameEventEnvelope.TryParse(
|
||||
WrapEnvelope(GameEventType.MagicUpdateEnchantment, payload))!.Value);
|
||||
|
||||
ActiveEnchantmentRecord record = Assert.Single(book.ActiveEnchantmentSnapshot);
|
||||
Assert.Equal(110d, record.StartTime);
|
||||
Assert.Equal(102d, record.LastTimeDegraded);
|
||||
Assert.Equal(2u, record.Bucket);
|
||||
Assert.Equal(60d, record.Duration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_WeenieError_RoutesToChatLog()
|
||||
{
|
||||
|
|
@ -1064,4 +1090,27 @@ public sealed class GameEventWiringTests
|
|||
Assert.Equal((0x50C4A54Au, 0x948700u), observed);
|
||||
}
|
||||
|
||||
private static byte[] BuildEnchantment(
|
||||
ushort spellId,
|
||||
ushort layer,
|
||||
double duration,
|
||||
uint caster,
|
||||
double startTime,
|
||||
double lastDegraded,
|
||||
uint statModType)
|
||||
{
|
||||
byte[] payload = new byte[60];
|
||||
int offset = 0;
|
||||
WriteU16(spellId); WriteU16(layer); WriteU16(3); WriteU16(0);
|
||||
WriteU32(8); WriteF64(startTime); WriteF64(duration); WriteU32(caster);
|
||||
WriteF32(0.1f); WriteF32(-1f); WriteF64(lastDegraded);
|
||||
WriteU32(statModType); WriteU32(7); WriteF32(1.25f);
|
||||
return payload;
|
||||
|
||||
void WriteU16(ushort value) { BinaryPrimitives.WriteUInt16LittleEndian(payload.AsSpan(offset), value); offset += 2; }
|
||||
void WriteU32(uint value) { BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(offset), value); offset += 4; }
|
||||
void WriteF32(float value) { BinaryPrimitives.WriteSingleLittleEndian(payload.AsSpan(offset), value); offset += 4; }
|
||||
void WriteF64(double value) { BinaryPrimitives.WriteDoubleLittleEndian(payload.AsSpan(offset), value); offset += 8; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue