feat(battlenode): attack resolves on engine state via view-untangle (M-HC-4a)

Drive ATTACK frames through the headless receive conductor and assert on engine
board state (node-native harness). Two cases: follower -> enemy leader (leader
life drops by atk, attacker spent) and a lethal follower-vs-follower trade (both
removed). ATTACK opcode confirmed = 10 (NetworkBattleDefine.PlayActionType).

Headless view-untangle (no Engine logic edits; drift clean):
- IBattlePlayerView.AttackSelectControl -> non-null HeadlessAttackSelectControl
  (no-op RegisterAttackPair/ResetCardAfterAttack); IsCardTranslatable left to base.
- IBattleCardView.CardInfo -> backing card via BuildInfo (so IsCardTranslatable
  reads authentic IsClass); class/null view ctors now chain : base(buildInfo).
- IBattleCardView._inPlayFrameEffect -> non-null no-op control.
- Seed Certification.viewer_id headless so the IsRecovery target parse
  (vid != UserViewerID) does not throw inside SavedataManager and silently drop
  the parsed targetList.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-06 22:48:26 -04:00
parent 0d7136787a
commit c5a511e4fe
11 changed files with 291 additions and 9 deletions

View File

@@ -14,7 +14,19 @@ namespace Wizard.Battle.View
// Parameterless ctor lets the no-op subclass hand stubs (ClassBattleCardViewBase,
// NullBattleCardView) and any non-chaining stub satisfy their implicit base() call.
public BattleCardView() { }
public BattleCardView(BuildInfo buildInfo) { }
public BattleCardView(BuildInfo buildInfo) { _buildInfo = buildInfo; }
// HEADLESS-FIX (M-HC-4a): the receive ATTACK path reads BattleCardView.CardInfo (the backing
// card) and BattleCardView._inPlayFrameEffect on the resolve path (InPlayCardReflection /
// ActionProcessor.Attack). The interface getters in Generated/_IfaceImpl.g.cs surface these two
// fields. CardInfo comes from the stored BuildInfo (cardInfo == the card, IReadOnlyBattleCardInfo,
// so IsClass etc. are authentic); _inPlayFrameEffect is a non-null no-op frame-effect control
// whose HideFrameEffect/UpdateCanAttackEffect are empty (Generated/InPlayCardFrameEffectControl.g.cs).
internal BuildInfo _buildInfo;
internal IReadOnlyBattleCardInfo HeadlessCardInfo => _buildInfo?.cardInfo;
internal InPlayCardFrameEffectControl _headlessInPlayFrameEffect =
new InPlayCardFrameEffectControl(null, null, null);
// AttackTargetSelectInfo provided by Generated/BattleCardView_AttackTargetSelectInfo.g.cs
public virtual UnityEngine.GameObject GameObject { get; protected set; }
public HandCardFrameEffectControl HandFrameEffect { get; private set; }