feat(battle-node): BuildOpponentJudge builder for v1.2 turn-end Judge
Adds the third frame of the burst. Wire shape from prod (spin + resultCode). OpponentJudgeSpin const next to OpponentTurnStartSpin for consistency. Single test locks uri, ViewerId, Cat, and body shape.
This commit is contained in:
@@ -117,6 +117,16 @@ public static class ScriptedLifecycle
|
|||||||
public static MsgEnvelope BuildOpponentTurnEnd() =>
|
public static MsgEnvelope BuildOpponentTurnEnd() =>
|
||||||
EnvelopeForPush(NetworkBattleUri.TurnEnd, new TurnEndBody(TurnState: 0));
|
EnvelopeForPush(NetworkBattleUri.TurnEnd, new TurnEndBody(TurnState: 0));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server-pushed Judge frame that follows the opponent's TurnEnd and unblocks the
|
||||||
|
/// client's <c>JudgeOperation</c> → <c>ControlTurnStartPlayer</c>, transitioning to the
|
||||||
|
/// player's next turn. Without this frame the client hangs on "Opponent's turn…" —
|
||||||
|
/// see <c>data_dumps/captures/battle-traffic.ndjson</c> line 14 (client emits its own
|
||||||
|
/// Judge then waits forever).
|
||||||
|
/// </summary>
|
||||||
|
public static MsgEnvelope BuildOpponentJudge() =>
|
||||||
|
EnvelopeForPush(NetworkBattleUri.Judge, new JudgeBody(Spin: ScriptedProfiles.OpponentJudgeSpin));
|
||||||
|
|
||||||
private static IReadOnlyList<PosIdx> BuildPosIdxList(IReadOnlyList<long> hand)
|
private static IReadOnlyList<PosIdx> BuildPosIdxList(IReadOnlyList<long> hand)
|
||||||
{
|
{
|
||||||
var list = new List<PosIdx>(hand.Count);
|
var list = new List<PosIdx>(hand.Count);
|
||||||
|
|||||||
@@ -51,4 +51,11 @@ internal static class ScriptedProfiles
|
|||||||
// display state. v1 doesn't simulate the opponent — once this lands,
|
// display state. v1 doesn't simulate the opponent — once this lands,
|
||||||
// the client sits there indefinitely.
|
// the client sits there indefinitely.
|
||||||
public const int OpponentTurnStartSpin = 100;
|
public const int OpponentTurnStartSpin = 100;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server-pushed Judge frame spin value. Prod varies per push (55, 175, 73, ...) — it's
|
||||||
|
/// an animation seed, not a stateful value. Fixed at 100 here for test stability;
|
||||||
|
/// the client's <c>JudgeOperation</c> doesn't read it.
|
||||||
|
/// </summary>
|
||||||
|
public const int OpponentJudgeSpin = 100;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,6 +166,19 @@ public class ScriptedLifecycleTests
|
|||||||
Assert.That(body.ResultCode, Is.EqualTo(1));
|
Assert.That(body.ResultCode, Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BuildOpponentJudge_emits_Judge_uri_with_spin_and_default_result_code()
|
||||||
|
{
|
||||||
|
var env = ScriptedLifecycle.BuildOpponentJudge();
|
||||||
|
|
||||||
|
Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.Judge));
|
||||||
|
Assert.That(env.ViewerId, Is.EqualTo(ScriptedLifecycle.FakeOpponentViewerId));
|
||||||
|
Assert.That(env.Cat, Is.EqualTo(EmitCategory.Battle));
|
||||||
|
var body = (JudgeBody)env.Body;
|
||||||
|
Assert.That(body.Spin, Is.EqualTo(ScriptedProfiles.OpponentJudgeSpin));
|
||||||
|
Assert.That(body.ResultCode, Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
private static MatchContext FixtureCtx(IReadOnlyList<long>? deck = null) => new(
|
private static MatchContext FixtureCtx(IReadOnlyList<long>? deck = null) => new(
|
||||||
SelfDeckCardIds: deck ?? Enumerable.Range(1, 30).Select(i => 100_011_010L).ToList(),
|
SelfDeckCardIds: deck ?? Enumerable.Range(1, 30).Select(i => 100_011_010L).ToList(),
|
||||||
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
ClassId: "1", CharaId: "1", CardMasterName: "card_master_node_10015",
|
||||||
|
|||||||
Reference in New Issue
Block a user