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:
gamer147
2026-06-06 21:48:50 -04:00
parent 51419d15cd
commit 0d7136787a
9 changed files with 261 additions and 194 deletions

View File

@@ -139,6 +139,13 @@ internal sealed class NodeNativeBattleHarness : IDisposable
/// Play frame would carry to play it).</summary>
public int PlayerHandCardIndex(int handPos) => Engine.HandCardIndex(playerSeat: true, handPos);
/// <summary>The wire CardId of the hand card at <paramref name="handPos"/> on the given seat. Lets a
/// test find a specific card (e.g. the spellboost reducer) in a shuffled opening hand by identity.</summary>
public int HandCardId(bool playerSeat, int handPos) => Engine.HandCardId(playerSeat, handPos);
/// <summary>The engine Index of the hand card at <paramref name="handPos"/> on the given seat.</summary>
public int HandCardIndex(bool playerSeat, int handPos) => Engine.HandCardIndex(playerSeat, handPos);
/// <summary>The real wire <c>CardId</c> of the in-play follower at <paramref name="boardPos"/> on the
/// given seat (0-based, leader excluded). Asserts an opponent reveal seated the substituted identity
/// (M-HC-2).</summary>