From e30fdb75707c0549ae4750ccd0f4c0fd46cf4f4a Mon Sep 17 00:00:00 2001 From: gamer147 Date: Mon, 1 Jun 2026 14:57:49 -0400 Subject: [PATCH] feat(battle-node): scripted opponent turn loop pushes TurnStart + TurnEnd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TurnEnd/TurnEndFinal case in ComputeResponses now returns two envelopes back-to-back — opponent TurnStart followed by opponent TurnEnd. Phase enters OpponentTurn transiently then resets to AfterReady within the same call so the next player TurnEnd can fire the cycle again. Closes the v1 'stays at Opponent's turn… forever' stall. --- SVSim.BattleNode/Sessions/BattleSession.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/SVSim.BattleNode/Sessions/BattleSession.cs b/SVSim.BattleNode/Sessions/BattleSession.cs index d7012b9..0e11500 100644 --- a/SVSim.BattleNode/Sessions/BattleSession.cs +++ b/SVSim.BattleNode/Sessions/BattleSession.cs @@ -257,11 +257,15 @@ public sealed class BattleSession } 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)); + // Scripted opponent: opens its turn, immediately ends it, hands control back. + // Two-frame burst — see docs/superpowers/specs/2026-06-01-battle-node-opponent-turn-loop-design.md. + // OpponentTurn is set then cleared within the same call to document intent; + // the only externally-observable phase after the call is AfterReady, ready for + // the next player TurnEnd to fire the cycle again. Phase = BattleSessionPhase.OpponentTurn; + result.Add((ScriptedLifecycle.BuildOpponentTurnStart(), NoStock: false)); + result.Add((ScriptedLifecycle.BuildOpponentTurnEnd(), NoStock: false)); + Phase = BattleSessionPhase.AfterReady; break; case NetworkBattleUri.Retire: case NetworkBattleUri.Kill: