From 633c29b44f53f3cf59bbee19217d90c4c6102fef Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 3 Jun 2026 10:48:43 -0400 Subject: [PATCH] feat(battle-node): BuildReady overload carrying the opponent's hand Adds BuildReady(selfHand, oppoHand) for the mulligan barrier; the single-arg overload keeps the InitialHand placeholder for non-interactive opponents. Co-Authored-By: Claude Opus 4.8 --- .../Lifecycle/ScriptedLifecycle.cs | 12 ++++++++--- .../Lifecycle/ScriptedLifecycleTests.cs | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs b/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs index fde884b..4703a51 100644 --- a/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs +++ b/SVSim.BattleNode/Lifecycle/ScriptedLifecycle.cs @@ -113,11 +113,17 @@ public static class ScriptedLifecycle EnvelopeForPush(NetworkBattleUri.Swap, new SwapResponseBody(Self: BuildPosIdxList(hand))); - public static MsgEnvelope BuildReady(IReadOnlyList hand) => + /// Non-interactive opponent (scripted single / AINetwork): oppo is the + /// placeholder (v1 behaviour). + public static MsgEnvelope BuildReady(IReadOnlyList hand) => BuildReady(hand, InitialHand); + + /// Both hands known (the mulligan barrier supplies the opponent's + /// post-mulligan hand). + public static MsgEnvelope BuildReady(IReadOnlyList selfHand, IReadOnlyList oppoHand) => EnvelopeForPush(NetworkBattleUri.Ready, new ReadyBody( - Self: BuildPosIdxList(hand), - Oppo: BuildPosIdxList(InitialHand), + Self: BuildPosIdxList(selfHand), + Oppo: BuildPosIdxList(oppoHand), IdxChangeSeed: ScriptedProfiles.ReadyIdxChangeSeed, Spin: ScriptedProfiles.ReadySpin)); diff --git a/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs b/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs index 730de6d..3e62653 100644 --- a/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs +++ b/SVSim.UnitTests/BattleNode/Lifecycle/ScriptedLifecycleTests.cs @@ -145,6 +145,27 @@ public class ScriptedLifecycleTests Assert.That(body.Self[1].Idx, Is.EqualTo(4)); } + [Test] + public void BuildReady_two_arg_sets_oppo_to_supplied_hand() + { + var env = ScriptedLifecycle.BuildReady(new long[] { 1, 4, 3 }, new long[] { 1, 2, 6 }); + var body = (ReadyBody)env.Body; + + Assert.That(body.Self.Select(p => p.Idx), Is.EqualTo(new[] { 1, 4, 3 })); + Assert.That(body.Oppo.Select(p => p.Idx), Is.EqualTo(new[] { 1, 2, 6 }), + "oppo must reflect the opponent's post-mulligan hand, not the placeholder InitialHand."); + } + + [Test] + public void BuildReady_one_arg_defaults_oppo_to_InitialHand() + { + var env = ScriptedLifecycle.BuildReady(new long[] { 1, 4, 3 }); + var body = (ReadyBody)env.Body; + + Assert.That(body.Oppo.Select(p => p.Idx), Is.EqualTo(new[] { 1, 2, 3 }), + "single-arg overload (non-interactive opponent) keeps the placeholder hand."); + } + [Test] public void BuildOpponentTurnStart_HasUriTurnStartAndSpin() {