fix #234: port retail appraisal floaty and inscriptions
This commit is contained in:
parent
643cdfe66e
commit
f1a7912160
23 changed files with 1310 additions and 106 deletions
|
|
@ -93,6 +93,39 @@ public sealed class GameEventWiringTests
|
|||
Assert.False(items.Get(guid)!.Properties.Ints.ContainsKey(25u));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_LiteralAceCreatureFlag_ForwardsAndUpdatesHealth()
|
||||
{
|
||||
const uint guid = 0x50000001u;
|
||||
var dispatcher = new GameEventDispatcher();
|
||||
var combat = new CombatState();
|
||||
AppraiseInfoParser.Parsed? received = null;
|
||||
GameEventWiring.WireAll(
|
||||
dispatcher,
|
||||
new ClientObjectTable(),
|
||||
combat,
|
||||
new Spellbook(),
|
||||
new ChatLog(),
|
||||
onAppraisal: appraisal => received = appraisal);
|
||||
|
||||
byte[] payload = new byte[24];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload, guid);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(
|
||||
payload.AsSpan(4),
|
||||
0x0100u); // ACE IdentifyResponseFlags.CreatureProfile
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(8), 1u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(12), 0u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(16), 75u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(20), 100u);
|
||||
|
||||
dispatcher.Dispatch(GameEventEnvelope.TryParse(
|
||||
WrapEnvelope(GameEventType.IdentifyObjectResponse, payload))!.Value);
|
||||
|
||||
Assert.NotNull(received);
|
||||
Assert.NotNull(received.Value.CreatureProfile);
|
||||
Assert.Equal(0.75f, combat.GetHealthPercent(guid), 3);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WireAll_ChannelBroadcast_RoutesToChatLog()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,27 @@ namespace AcDream.Core.Net.Tests.Messages;
|
|||
|
||||
public sealed class AppraiseInfoParserTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.IntStatsTable, 0x0001u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.BoolStatsTable, 0x0002u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.FloatStatsTable, 0x0004u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.StringStatsTable, 0x0008u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.SpellBook, 0x0010u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.WeaponProfile, 0x0020u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.HookProfile, 0x0040u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.ArmorProfile, 0x0080u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.CreatureProfile, 0x0100u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.ArmorEnchantmentBitfield, 0x0200u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.ResistEnchantmentBitfield, 0x0400u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.WeaponEnchantmentBitfield, 0x0800u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.DidStatsTable, 0x1000u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.Int64StatsTable, 0x2000u)]
|
||||
[InlineData(AppraiseInfoParser.IdentifyResponseFlags.ArmorLevels, 0x4000u)]
|
||||
public void IdentifyResponseFlags_MatchRetailWireValues(
|
||||
AppraiseInfoParser.IdentifyResponseFlags flag,
|
||||
uint wireValue)
|
||||
=> Assert.Equal(wireValue, (uint)flag);
|
||||
|
||||
/// <summary>
|
||||
/// Build an AppraiseInfo payload matching ACE's wire format. Starts
|
||||
/// with (guid, flags, success) then per-flag tables.
|
||||
|
|
@ -319,6 +340,27 @@ public sealed class AppraiseInfoParserTests
|
|||
Assert.Equal(300u, c.Health);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParse_LiteralAceCreatureFlag_DoesNotDecodeAsWeapon()
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
using var bw = new BinaryWriter(ms);
|
||||
bw.Write(0x50000001u);
|
||||
bw.Write(0x0100u); // ACE IdentifyResponseFlags.CreatureProfile
|
||||
bw.Write(1u);
|
||||
bw.Write(0u);
|
||||
bw.Write(300u);
|
||||
bw.Write(400u);
|
||||
|
||||
AppraiseInfoParser.Parsed? parsed =
|
||||
AppraiseInfoParser.TryParse(ms.ToArray());
|
||||
|
||||
Assert.NotNull(parsed);
|
||||
Assert.NotNull(parsed.Value.CreatureProfile);
|
||||
Assert.Null(parsed.Value.WeaponProfile);
|
||||
Assert.Equal(300u, parsed.Value.CreatureProfile.Value.Health);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParse_ArmorEnchantmentBitfield_HighlightAndColor()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -148,4 +148,42 @@ public sealed class InventoryActionsTests
|
|||
Assert.Equal(0x0195u, BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(8)));
|
||||
Assert.Equal(0x500000C9u, BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(12)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSetInscription_UsesRetailOpcodeGuidAndCp1252String16L()
|
||||
{
|
||||
byte[] body = InventoryActions.BuildSetInscription(
|
||||
seq: 7,
|
||||
itemGuid: 0x50000A01u,
|
||||
inscription: "Café");
|
||||
|
||||
Assert.Equal(24, body.Length);
|
||||
Assert.Equal(0xF7B1u,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body));
|
||||
Assert.Equal(7u,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4)));
|
||||
Assert.Equal(0x00BFu,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
Assert.Equal(0x50000A01u,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
||||
Assert.Equal(4,
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(body.AsSpan(16)));
|
||||
Assert.Equal(new byte[] { 0x43, 0x61, 0x66, 0xE9 },
|
||||
body.AsSpan(18, 4).ToArray());
|
||||
Assert.Equal(new byte[] { 0, 0 }, body.AsSpan(22, 2).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSetInscription_EmptyTextPacksAlignedClearRequest()
|
||||
{
|
||||
byte[] body = InventoryActions.BuildSetInscription(
|
||||
seq: 1,
|
||||
itemGuid: 0x50000A01u,
|
||||
inscription: string.Empty);
|
||||
|
||||
Assert.Equal(20, body.Length);
|
||||
Assert.Equal(0,
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(body.AsSpan(16)));
|
||||
Assert.Equal(new byte[] { 0, 0 }, body.AsSpan(18, 2).ToArray());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,19 @@ public sealed class WorldSessionInventoryActionTests
|
|||
InventoryActions.BuildGiveObjectRequest(1, 0xAAu, 0xBBu, 3u),
|
||||
captured);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendSetInscription_UsesNextSequenceAndExactBuilderBytes()
|
||||
{
|
||||
using var session = new WorldSession(
|
||||
new IPEndPoint(IPAddress.Loopback, 65000));
|
||||
byte[]? captured = null;
|
||||
session.GameActionCapture = body => captured = body;
|
||||
|
||||
session.SendSetInscription(0xAAu, "Remember.");
|
||||
|
||||
Assert.Equal(
|
||||
InventoryActions.BuildSetInscription(1, 0xAAu, "Remember."),
|
||||
captured);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue