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>
133 lines
5.1 KiB
C#
133 lines
5.1 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
using AcDream.Core.Net.Messages;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Net.Tests.Messages;
|
|
|
|
public sealed class CastSpellTests
|
|
{
|
|
[Fact]
|
|
public void BuildUntargeted_EmitsCorrectWireBytes()
|
|
{
|
|
byte[] body = CastSpellRequest.BuildUntargeted(gameActionSequence: 5, spellId: 0x3E1);
|
|
|
|
Assert.Equal(16, body.Length);
|
|
Assert.Equal(CastSpellRequest.GameActionEnvelope,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body));
|
|
Assert.Equal(5u,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4)));
|
|
Assert.Equal(CastSpellRequest.UntargetedSubOpcode,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
|
Assert.Equal(0x3E1u,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildTargeted_EmitsCorrectWireBytes()
|
|
{
|
|
byte[] body = CastSpellRequest.BuildTargeted(
|
|
gameActionSequence: 7, targetGuid: 0xAAAAu, spellId: 0x3E1);
|
|
|
|
Assert.Equal(20, body.Length);
|
|
Assert.Equal(CastSpellRequest.TargetedSubOpcode,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
|
Assert.Equal(0xAAAAu,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
|
Assert.Equal(0x3E1u,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(16)));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicUpdateSpell_RoundTrip()
|
|
{
|
|
byte[] payload = new byte[4];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(payload, 0x123403E1u);
|
|
var parsed = GameEvents.ParseMagicUpdateSpell(payload);
|
|
Assert.Equal(0x123403E1u, parsed);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicUpdateEnchantment_RoundTrip()
|
|
{
|
|
byte[] payload = BuildEnchantment(spellId: 42, layer: 7, duration: 300, caster: 0xBEEF);
|
|
|
|
var parsed = GameEvents.ParseMagicUpdateEnchantment(payload);
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal((ushort)42, parsed!.Value.SpellId);
|
|
Assert.Equal((ushort)7, parsed.Value.Layer);
|
|
Assert.Equal(300d, parsed.Value.Duration, 4);
|
|
Assert.Equal(0xBEEFu, parsed.Value.CasterGuid);
|
|
Assert.Equal(0x20u, parsed.Value.StatModType);
|
|
Assert.Equal(7u, parsed.Value.StatModKey);
|
|
Assert.Equal(1.25f, parsed.Value.StatModValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicRemoveEnchantment_RoundTrip()
|
|
{
|
|
byte[] payload = new byte[4];
|
|
BinaryPrimitives.WriteUInt16LittleEndian(payload, 42);
|
|
BinaryPrimitives.WriteUInt16LittleEndian(payload.AsSpan(2), 7);
|
|
|
|
var parsed = GameEvents.ParseMagicRemoveEnchantment(payload);
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal((ushort)7, parsed!.Value.Layer);
|
|
Assert.Equal((ushort)42, parsed.Value.SpellId);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicUpdateMultipleEnchantments_ReadsEveryRecord()
|
|
{
|
|
byte[] first = BuildEnchantment(42, 1, 60, 0xAA);
|
|
byte[] second = BuildEnchantment(43, 2, 120, 0xBB);
|
|
byte[] payload = new byte[4 + first.Length + second.Length];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(payload, 2);
|
|
first.CopyTo(payload, 4);
|
|
second.CopyTo(payload, 4 + first.Length);
|
|
|
|
var parsed = GameEvents.ParseMagicUpdateMultipleEnchantments(payload);
|
|
|
|
Assert.NotNull(parsed);
|
|
Assert.Equal(2, parsed!.Count);
|
|
Assert.Equal((ushort)42, parsed[0].SpellId);
|
|
Assert.Equal((ushort)43, parsed[1].SpellId);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicUpdateEnchantment_RejectsTruncatedOptionalSpellSetId()
|
|
{
|
|
byte[] payload = BuildEnchantment(42, 1, 60, 0xAA);
|
|
BinaryPrimitives.WriteUInt16LittleEndian(payload.AsSpan(6), 1);
|
|
|
|
Assert.Null(GameEvents.ParseMagicUpdateEnchantment(payload));
|
|
}
|
|
|
|
[Fact]
|
|
public void ParseMagicLayeredSpellList_RejectsTruncation()
|
|
{
|
|
byte[] payload = new byte[8];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(payload, 2);
|
|
BinaryPrimitives.WriteUInt16LittleEndian(payload.AsSpan(4), 42);
|
|
BinaryPrimitives.WriteUInt16LittleEndian(payload.AsSpan(6), 1);
|
|
|
|
Assert.Null(GameEvents.ParseMagicLayeredSpellList(payload));
|
|
}
|
|
|
|
private static byte[] BuildEnchantment(
|
|
ushort spellId, ushort layer, double duration, uint caster)
|
|
{
|
|
byte[] payload = new byte[60];
|
|
int offset = 0;
|
|
WriteU16(spellId); WriteU16(layer); WriteU16(3); WriteU16(0);
|
|
WriteU32(8); WriteF64(10); WriteF64(duration); WriteU32(caster);
|
|
WriteF32(0.1f); WriteF32(-1f); WriteF64(0);
|
|
WriteU32(0x20); 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; }
|
|
}
|
|
}
|