diff --git a/SVSim.BattleNode/Lifecycle/ScriptedProfiles.cs b/SVSim.BattleNode/Lifecycle/ScriptedProfiles.cs
index 63c24b5..081bb4b 100644
--- a/SVSim.BattleNode/Lifecycle/ScriptedProfiles.cs
+++ b/SVSim.BattleNode/Lifecycle/ScriptedProfiles.cs
@@ -66,7 +66,4 @@ internal static class ScriptedProfiles
// display state. v1 doesn't simulate the opponent — once this lands,
// the client sits there indefinitely.
public const int OpponentTurnStartSpin = 100;
-
- // BattleFinish result code for v1 no-contest finishes (player wins).
- public const int BattleResultPlayerWins = 1;
}
diff --git a/SVSim.BattleNode/Protocol/BattleResult.cs b/SVSim.BattleNode/Protocol/BattleResult.cs
new file mode 100644
index 0000000..1e0bc11
--- /dev/null
+++ b/SVSim.BattleNode/Protocol/BattleResult.cs
@@ -0,0 +1,19 @@
+namespace SVSim.BattleNode.Protocol;
+
+///
+/// Wire value of result on a BattleFinish frame. The client's
+/// BattleFinishResponsProcessing switch maps these as:
+/// 0 → LOSE, 1 → WIN, 2 → CONSISTENCY (desync / action-list mismatch).
+///
+///
+/// This is NOT the same as the client's in-memory BATTLE_RESULT_TYPE enum
+/// (NONE=0, WIN=1, LOSE=2, CONSISTENCY=3) — the wire codes shift LOSE down to 0.
+/// Always serialize as the int value, not the name; see the
+/// JsonNumberEnumConverter on .
+///
+public enum BattleResult
+{
+ Lose = 0,
+ Win = 1,
+ Consistency = 2,
+}
diff --git a/SVSim.BattleNode/Protocol/Bodies/BattleFinishBody.cs b/SVSim.BattleNode/Protocol/Bodies/BattleFinishBody.cs
index 3ac3c47..42595ec 100644
--- a/SVSim.BattleNode/Protocol/Bodies/BattleFinishBody.cs
+++ b/SVSim.BattleNode/Protocol/Bodies/BattleFinishBody.cs
@@ -3,5 +3,7 @@ using System.Text.Json.Serialization;
namespace SVSim.BattleNode.Protocol.Bodies;
public sealed record BattleFinishBody(
- [property: JsonPropertyName("result")] int Result,
+ [property: JsonPropertyName("result")]
+ [property: JsonConverter(typeof(JsonNumberEnumConverter))]
+ BattleResult Result,
[property: JsonPropertyName("resultCode")] int ResultCode = 1) : IMsgBody;
diff --git a/SVSim.BattleNode/Sessions/BattleSession.cs b/SVSim.BattleNode/Sessions/BattleSession.cs
index dc7ad39..aff41dc 100644
--- a/SVSim.BattleNode/Sessions/BattleSession.cs
+++ b/SVSim.BattleNode/Sessions/BattleSession.cs
@@ -289,7 +289,7 @@ public sealed class BattleSession
Cat: EmitCategory.Battle,
PubSeq: null,
PlaySeq: null,
- Body: new BattleFinishBody(Result: ScriptedProfiles.BattleResultPlayerWins));
+ Body: new BattleFinishBody(Result: BattleResult.Win));
private static IReadOnlyList ExtractIdxList(MsgEnvelope env)
{
diff --git a/SVSim.UnitTests/BattleNode/Protocol/Bodies/SmallBodiesTests.cs b/SVSim.UnitTests/BattleNode/Protocol/Bodies/SmallBodiesTests.cs
index a76f1eb..e51c8ed 100644
--- a/SVSim.UnitTests/BattleNode/Protocol/Bodies/SmallBodiesTests.cs
+++ b/SVSim.UnitTests/BattleNode/Protocol/Bodies/SmallBodiesTests.cs
@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using NUnit.Framework;
+using SVSim.BattleNode.Protocol;
using SVSim.BattleNode.Protocol.Bodies;
namespace SVSim.UnitTests.BattleNode.Protocol.Bodies;
@@ -31,9 +32,11 @@ public class SmallBodiesTests
}
[Test]
- public void BattleFinishBody_SerializesResultAndResultCode()
+ public void BattleFinishBody_SerializesResultAndResultCode_AsNumericWireValues()
{
- var body = new BattleFinishBody(Result: 1);
+ // The wire field is the int code (Win=1); BattleResult uses JsonNumberEnumConverter
+ // to override the default JsonStringEnumConverter (which would emit "Win" instead).
+ var body = new BattleFinishBody(Result: BattleResult.Win);
var node = (JsonObject)JsonSerializer.SerializeToNode(body)!;
@@ -41,6 +44,17 @@ public class SmallBodiesTests
Assert.That(node["resultCode"]!.GetValue(), Is.EqualTo(1));
}
+ [Test]
+ public void BattleFinishBody_LoseAndConsistency_SerializeAsZeroAndTwo()
+ {
+ // Lock the wire values per BattleFinishResponsProcessing's switch (0=LOSE, 2=CONSISTENCY).
+ var lose = (JsonObject)JsonSerializer.SerializeToNode(new BattleFinishBody(BattleResult.Lose))!;
+ var consistency = (JsonObject)JsonSerializer.SerializeToNode(new BattleFinishBody(BattleResult.Consistency))!;
+
+ Assert.That(lose["result"]!.GetValue(), Is.EqualTo(0));
+ Assert.That(consistency["result"]!.GetValue(), Is.EqualTo(2));
+ }
+
[Test]
public void AlivePushBody_SerializesScsAndOcs_AndDoesNotIncludeResultCode()
{