fix(battle-node): real mulligan card replacement + opponent TurnStart push
Two issues caught during v1 smoke at the mulligan / first-turn boundary:
1) BuildSwapResponse ignored the player's idxList and echoed the same
3-card hand back. The client diffs the new self[] against the Deal
to compute "drawn cards" — empty diff against the same hand throws
"Card swap failed: AbandonCards[X]/DrawCards[]". Replace swapped
idxs with fresh deck idxs (initial hand was 1/2/3, deck has 4..30
still available). Same hand must flow into Ready since the client
diffs again there. Move the hand computation into a new helper
ComputeHandAfterSwap and have ComputeResponses thread it through
both BuildSwapResponse and BuildReady.
2) The client doesn't transition to the "Opponent's turn…" display
on its own after sending TurnEnd — it waits for the server to push
an opponent TurnStart (per prod TK2 capture line 14). Without it
the UI just sits on the end-of-turn frame. Add a TurnEnd handler
that pushes a minimal TurnStart{spin} and transitions to a new
OpponentTurn phase, which IS the documented v1 stopping point.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -209,10 +209,25 @@ public sealed class BattleSession
|
||||
Phase = BattleSessionPhase.AwaitingSwap;
|
||||
break;
|
||||
case NetworkBattleUri.Swap when Phase == BattleSessionPhase.AwaitingSwap:
|
||||
result.Add((ScriptedLifecycle.BuildSwapResponse(ExtractIdxList(env)), NoStock: false));
|
||||
result.Add((ScriptedLifecycle.BuildReady(), NoStock: false));
|
||||
{
|
||||
// Compute the actual post-mulligan hand: any idx in idxList that's in the initial
|
||||
// 3-card hand gets replaced with a fresh deck idx. Both Swap response AND Ready
|
||||
// need the SAME hand — the client diffs them to compute "drawn cards" and errors
|
||||
// out with "Card swap failed: AbandonCards[...]/DrawCards[]" if they don't agree.
|
||||
var hand = ScriptedLifecycle.ComputeHandAfterSwap(ExtractIdxList(env));
|
||||
result.Add((ScriptedLifecycle.BuildSwapResponse(hand), NoStock: false));
|
||||
result.Add((ScriptedLifecycle.BuildReady(hand), NoStock: false));
|
||||
Phase = BattleSessionPhase.AfterReady;
|
||||
break;
|
||||
}
|
||||
case NetworkBattleUri.TurnEnd when Phase == BattleSessionPhase.AfterReady:
|
||||
case NetworkBattleUri.TurnEndFinal when Phase == BattleSessionPhase.AfterReady:
|
||||
// Push a minimal opponent TurnStart so the client transitions to "Opponent's turn…"
|
||||
// display. v1 doesn't simulate the opponent — once this lands, the client sits
|
||||
// there indefinitely (this IS the documented v1 stopping point).
|
||||
result.Add((ScriptedLifecycle.BuildOpponentTurnStart(), NoStock: false));
|
||||
Phase = BattleSessionPhase.OpponentTurn;
|
||||
break;
|
||||
case NetworkBattleUri.Retire:
|
||||
case NetworkBattleUri.Kill:
|
||||
// These always terminate, regardless of phase.
|
||||
|
||||
@@ -11,5 +11,6 @@ public enum BattleSessionPhase
|
||||
AwaitingLoaded,
|
||||
AwaitingSwap,
|
||||
AfterReady,
|
||||
OpponentTurn,
|
||||
Terminal,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user