refactor(battlenode): retire spellboost bookkeeping, engine owns cost+spellboost (M-HC-3)
The headless engine accumulates spell-charge for real on the receive path (each spell play runs the played card's own AddSpellChargeCount) and resolves the discounted cost by construction, so the wire-derived spellboost-count bookkeeping is redundant. Engine-source the knownList spellboost COUNT too (prod-faithful) via a new SessionBattleEngine.PlayedCardSpellboost, using the same persist-post-play zone search as PlayedCardCost (SpellChargeCount survives PlayCard; only ctor/ReturnCard zero it). - Delete IdxToSpellboost/SpellboostMap/GetSpellboostMap/RecordSpellboostFrom (BattleSessionState) and MineAlterSpellboosts (KnownListBuilder); token/choice/ copy identity maps are untouched. - BuildPlayedCard takes an engine-sourced spellboost int (drops spellboostMap). - Seed BattleLogManager fusion lists headless (the per-frame filter cleanup NREs on null EnemyFusionCard when a fanfare card registers a CalledCreateFilter) so real spell-charge grantor plays resolve. - Add committed real-charge regression tests (no SeedHandCardSpellboostCost seam): one grantor play accumulates +1 on the reducer -> cost 5->4, count 1, persisting post-play; handler emits cost 4 + spellboost 1 engine-sourced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -48,13 +48,16 @@ internal sealed class PlayActionsHandler : IFrameHandler
|
||||
bool senderSeat = ReferenceEquals(ctx.From, ctx.A);
|
||||
int playedCost = ctx.Engine.PlayedCardCost(senderSeat, playIdx, fallback: 0);
|
||||
|
||||
// Spellboost count still rides the played card's knownList (prod-faithful; Task 6 retires this
|
||||
// bookkeeping now that cost is engine-sourced). Read the CURRENT map (state before this frame's
|
||||
// grant) for the emit, then fold THIS frame's alter ops in afterwards — a play that grants
|
||||
// spellboost (e.g. Fate's Hand) targets the REST of the hand, not the card just played.
|
||||
// The spellboost (spell-charge) COUNT is now ALSO engine-sourced (M-HC-3b) — the wire-derived
|
||||
// bookkeeping is retired. The engine accumulated the true count for the played card during the
|
||||
// ShadowIngest's engine.Receive (each spell play runs the card's own AddSpellChargeCount), so
|
||||
// PlayedCardSpellboost reads it straight off the resolved card (persist-post-play, same zone search
|
||||
// as the cost). Cost already folds the discount in by construction; the count rides the entry only
|
||||
// to stay prod-faithful (prod sends the real count). Same senderSeat mapping as the cost read.
|
||||
int playedSpellboost = ctx.Engine.PlayedCardSpellboost(senderSeat, playIdx, fallback: 0);
|
||||
|
||||
var played = KnownListBuilder.BuildPlayedCard(
|
||||
deckMap, playIdx, orderList, ctx.State.GetSpellboostMap(ctx.From), cost: playedCost);
|
||||
ctx.State.RecordSpellboostFrom(ctx.From, ctx.Other, orderList);
|
||||
deckMap, playIdx, orderList, cost: playedCost, spellboost: playedSpellboost);
|
||||
var oppoTargets = KnownListBuilder.RenameTargets(entries.GetValueOrDefault(WireKeys.TargetList));
|
||||
|
||||
// Deck-sourced movements (fetch / search / summon-from-deck) ride the uList — a verbatim,
|
||||
|
||||
Reference in New Issue
Block a user