feat(char): character progression actions — Raise / Train / CombatMode
Outbound GameActions for XP-spending + combat-mode-change. These complete the wire surface for the character-sheet UI: the player clicks "spend XP on Strength," the panel calls BuildRaiseAttribute, the session sends it, the server responds with updated PlayerDescription or PrivateUpdateAttribute GameEvents. Wire layer: - BuildRaiseAttribute (0x0045): attrId u32, xpSpent u64. - BuildRaiseVital (0x0044): vitalId u32, xpSpent u64. - BuildRaiseSkill (0x0046): skillId u32, xpSpent u64. - BuildTrainSkill (0x0047): skillId u32, credits u32 (note: credits is u32 here, NOT u64 like the xpSpent variants). - BuildChangeCombatMode (0x0053): mode enum as u32 (Undef=0, NonCombat=1, Melee=2, Missile=3, Magic=4, Peaceful=5). Tests (5 new): byte-exact encoding of each, including the Train/ Raise size difference due to u32 vs u64 payloads. Build green, 621 tests pass (up from 616). Ref: r08 §3 rows 0x0044 / 0x0045 / 0x0046 / 0x0047 / 0x0053. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
68efb60b49
commit
d461279207
2 changed files with 139 additions and 0 deletions
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Net.Tests.Messages;
|
||||
|
||||
public sealed class CharacterActionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildRaiseAttribute_HasOpcode0x0045AndXp64()
|
||||
{
|
||||
byte[] body = CharacterActions.BuildRaiseAttribute(seq: 1, attrId: 5, xpSpent: 12345678);
|
||||
Assert.Equal(24, body.Length);
|
||||
Assert.Equal(CharacterActions.RaiseAttributeOpcode,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
Assert.Equal(5u,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
||||
Assert.Equal(12345678u,
|
||||
BinaryPrimitives.ReadUInt64LittleEndian(body.AsSpan(16)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildRaiseVital_HasOpcode0x0044()
|
||||
{
|
||||
byte[] body = CharacterActions.BuildRaiseVital(seq: 1, vitalId: 1, xpSpent: 100);
|
||||
Assert.Equal(CharacterActions.RaiseVitalOpcode,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildRaiseSkill_HasOpcode0x0046()
|
||||
{
|
||||
byte[] body = CharacterActions.BuildRaiseSkill(seq: 1, skillId: 8, xpSpent: 500);
|
||||
Assert.Equal(CharacterActions.RaiseSkillOpcode,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildTrainSkill_U32CreditsNotU64()
|
||||
{
|
||||
byte[] body = CharacterActions.BuildTrainSkill(seq: 1, skillId: 8, credits: 4);
|
||||
Assert.Equal(20, body.Length); // 12 + 4 + 4 vs 16 for 4+8
|
||||
Assert.Equal(CharacterActions.TrainSkillOpcode,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
Assert.Equal(4u,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(16)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildChangeCombatMode_EnumSerialises()
|
||||
{
|
||||
byte[] body = CharacterActions.BuildChangeCombatMode(
|
||||
seq: 1, CharacterActions.CombatMode.Melee);
|
||||
Assert.Equal(CharacterActions.ChangeCombatModeOpcode,
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
||||
Assert.Equal(2u, // Melee = 2
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue