diff --git a/SVSim.BattleNode/README.md b/SVSim.BattleNode/README.md index 886f7ec..4356175 100644 --- a/SVSim.BattleNode/README.md +++ b/SVSim.BattleNode/README.md @@ -83,21 +83,26 @@ There's also a JSON parsing pitfall worth knowing about (and that broke the mull ## v1 scripted opponent — what the client sees -Hardcoded in `ScriptedLifecycle`: +The player half of `Matched` / `BattleStart` reads from a `MatchContext` assembled in +`SVSim.EmulatedEntrypoint/Services/MatchContextBuilder` from the viewer's TK2 run + equipped +cosmetics + config — so the mulligan renders the real drafted deck, drafted class/leader, +and equipped emblem/degree. The opponent half stays scripted in `ScriptedProfiles`: -- **Your deck** is 30 copies of `cardId = 100011010` (a neutral card stable across card-master versions). Your actual TK2 draft is ignored. -- **Your leader** is `classId="1"`, `charaId="1"` regardless of class you drafted. -- **Opponent** is a fixed silhouette: `classId="8"`, KOR sleeve/emblem/degree, viewer id `999999999`. +- **Opponent** is a fixed silhouette: `classId="8"`, JPN sleeve/emblem/degree, viewer id `999999999`. - **Battle seed** is `17548138L` in both info blocks (the seed is *shared* per battle per the spec). - **Mulligan** does real card replacement: any idx in your `idxList` is swapped for the next unused deck idx (`1..3` dealt, so `4..30` are pool). - **Opponent's turn** never actually does anything — we push a single `TurnStart{spin:100}` after your `TurnEnd` so the UI transitions to the opponent-turn display, then sit. +A few player-side fields are still hardcoded pending a follow-up slice — `Rank`, `BattlePoint`, +`cardMasterName`, `fieldId`, and the per-battle RNG seed. See the spec's §Deferred plumbing +table at `docs/superpowers/specs/2026-06-01-battle-node-real-drafted-deck-design.md` for +what each needs. + ## Where to extend | You want to | Touch | |---|---| -| Use the real drafted deck | `ScriptedLifecycle.BuildMatched` (`selfDeck`) + the `IMatchingBridge` to plumb the viewer's TK2 run state through | -| Show the real drafted class | `ScriptedLifecycle.BuildBattleStart` (`selfInfo.classId/charaId`) — read from the same source | +| Wire a new mode's `do_matching` (rank, free, open-room, …) | Add one `BuildForAsync(viewerId, …)` method to `IMatchContextBuilder` reading that mode's deck source; the mode's controller calls `IMatchingBridge.RegisterPendingBattle(vid, ctx)`. No changes to `SVSim.BattleNode`. | | Add a real AI opponent | Replace the static dispatch in `BattleSession.ComputeResponses` (`TurnEnd → opponent TurnStart` case) with one that drives a decision engine. The `OutboundSequencer` already assigns `playSeq` for whatever you push. | | Implement recovery | `IBattleSessionStore` already keeps the pending registry. Add a per-battle archive (the `OutboundSequencer.Archive` already retains every assigned-playSeq push) and bind it to the HTTP `/battle/get_recovery_params` endpoint. | | Validate `battleCode` | Port `NetworkConsistency.GetConsistency` from the client decompilation. Hook into `BattleSession.HandleMsgEventAsync` on `TurnEnd` / `Judge`. |