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:
@@ -27,7 +27,14 @@ public partial class BuildInfo
|
||||
public Func<bool> _getIsAbleToAttack;
|
||||
public Func<bool> _getIsUnableToAttackClass;
|
||||
public Func<HandCardFrameEffectType> getHandCardFrameEffectType;
|
||||
public BuildInfo(IReadOnlyBattleCardInfo cardInfo, BattlePlayerReadOnlyInfoPair playerInfoPair, GameObject gameObject, BattleCamera battleCamera, BackGroundBase backGround, IBattleResourceMgr resourceMgr, Func<bool> getIsTouchable, Func<bool> getIsMovable, Func<bool> getIsOnMove, Func<int, bool> getIsFixedUseEnable, Func<bool> getIsActionCard, Func<bool> getIsAbleToAttack, Func<bool> getIsUnableToAttackClass, Func<HandCardFrameEffectType> getHandCardFrameEffectType) { }
|
||||
// HEADLESS-FIX (M-HC-4a): store cardInfo so the headless BattleCardView can expose CardInfo (the
|
||||
// backing card) — the receive ATTACK path reads BattleCardView.CardInfo.IsClass via
|
||||
// AttackSelectControl.IsCardTranslatable. (Generated stub body was empty; re-apply on regen.)
|
||||
public BuildInfo(IReadOnlyBattleCardInfo cardInfo, BattlePlayerReadOnlyInfoPair playerInfoPair, GameObject gameObject, BattleCamera battleCamera, BackGroundBase backGround, IBattleResourceMgr resourceMgr, Func<bool> getIsTouchable, Func<bool> getIsMovable, Func<bool> getIsOnMove, Func<int, bool> getIsFixedUseEnable, Func<bool> getIsActionCard, Func<bool> getIsAbleToAttack, Func<bool> getIsUnableToAttackClass, Func<HandCardFrameEffectType> getHandCardFrameEffectType)
|
||||
{
|
||||
this.cardInfo = cardInfo;
|
||||
this._playerInfoPair = playerInfoPair;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public partial class EnemyClassBattleCardView
|
||||
private readonly PlayerClassCharacter _classCharacter;
|
||||
public IClassCharacter ClassCharacter { get; set; }
|
||||
public float OriginalRootYPosition { get; set; }
|
||||
public EnemyClassBattleCardView(BuildInfo buildInfo) { }
|
||||
public EnemyClassBattleCardView(BuildInfo buildInfo) : base(buildInfo) { } // HEADLESS-FIX (M-HC-4a): chain BuildInfo so the leader view's CardInfo resolves (attack-on-leader targetting)
|
||||
public void StartOutFrame() { }
|
||||
public void StartIntoFrame() { }
|
||||
public float GetCurrentClipTime() => default!;
|
||||
|
||||
@@ -9,7 +9,7 @@ public partial class PlayerClassBattleCardView
|
||||
private readonly PlayerClassCharacter _classCharacter;
|
||||
public IClassCharacter ClassCharacter { get; set; }
|
||||
public float OriginalRootYPosition { get; set; }
|
||||
public PlayerClassBattleCardView(BuildInfo buildInfo) { }
|
||||
public PlayerClassBattleCardView(BuildInfo buildInfo) : base(buildInfo) { } // HEADLESS-FIX (M-HC-4a): chain BuildInfo so the leader view's CardInfo resolves (attack-on-leader targetting)
|
||||
public void StartOutFrame() { }
|
||||
public void StartIntoFrame() { }
|
||||
public float GetCurrentClipTime() => default!;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Wizard.Battle.View {
|
||||
Vector3 global::Wizard.Battle.View.IBattleCardView.ForecastIconPosition { get => default!; }
|
||||
Vector3 global::Wizard.Battle.View.IBattleCardView.ForecastIconScale { get => default!; }
|
||||
float global::Wizard.Battle.View.IBattleCardView.OriginalRootYPosition { get => default!; }
|
||||
IReadOnlyBattleCardInfo global::Wizard.Battle.View.IBattleCardView.CardInfo { get => default!; }
|
||||
IReadOnlyBattleCardInfo global::Wizard.Battle.View.IBattleCardView.CardInfo { get => HeadlessCardInfo; } // HEADLESS-FIX (M-HC-4a): the backing card (from BuildInfo) — AttackSelectControl.IsCardTranslatable reads CardInfo.IsClass
|
||||
BattlePlayerReadOnlyInfoPair global::Wizard.Battle.View.IBattleCardView.PlayerInfoPair { get => default!; }
|
||||
IReadOnlyVoiceInfo global::Wizard.Battle.View.IBattleCardView.VoiceInfo { get => global::Wizard.Battle.View.HeadlessVoiceInfo.Instance; } // HEADLESS-FIX (M7): non-null voice info for the death-voice tail
|
||||
GameObject global::Wizard.Battle.View.IBattleCardView.GameObject { get => default!; }
|
||||
@@ -27,7 +27,7 @@ namespace Wizard.Battle.View {
|
||||
BackGroundBase global::Wizard.Battle.View.IBattleCardView.m_BackGround { get => default!; }
|
||||
HandParameter global::Wizard.Battle.View.IBattleCardView.HandParam { get => default!; }
|
||||
BattleCardView.AttackTargetSelectInfo global::Wizard.Battle.View.IBattleCardView._attackTargetSelectInfo { get => default!; set { } }
|
||||
InPlayCardFrameEffectControl global::Wizard.Battle.View.IBattleCardView._inPlayFrameEffect { get => default!; set { } }
|
||||
InPlayCardFrameEffectControl global::Wizard.Battle.View.IBattleCardView._inPlayFrameEffect { get => _headlessInPlayFrameEffect; set { _headlessInPlayFrameEffect = value; } } // HEADLESS-FIX (M-HC-4a): non-null no-op frame-effect control (HideFrameEffect/UpdateCanAttackEffect are no-ops) for the receive ATTACK path
|
||||
bool global::Wizard.Battle.View.IBattleCardView.areArrowsForcedOff { get => default!; set { } }
|
||||
bool global::Wizard.Battle.View.IBattleCardView._isCardQueuedToBePlayed { get => default!; set { } }
|
||||
bool global::Wizard.Battle.View.IBattleCardView.isHiddenFromHandView { get => default!; set { } }
|
||||
@@ -228,7 +228,7 @@ namespace Wizard.Battle.View {
|
||||
// shared no-op PlayQueueViewBase so the presentation call is a safe NullVfx (the authoritative
|
||||
// play mutation runs in PlayHandCardReflection.Play, not in this view).
|
||||
PlayQueueViewBase global::Wizard.Battle.View.IBattlePlayerView.PlayQueueView { get => global::Wizard.Battle.View.HeadlessPlayQueueViewStub.Instance; }
|
||||
AttackSelectControl global::Wizard.Battle.View.IBattlePlayerView.AttackSelectControl { get => default!; }
|
||||
AttackSelectControl global::Wizard.Battle.View.IBattlePlayerView.AttackSelectControl { get => global::Wizard.Battle.View.HeadlessAttackSelectControl.Instance; } // HEADLESS-FIX (M-HC-4a): non-null no-op attack-select-control for the receive ATTACK path (RegisterPairToAttackSelectControl + ActionProcessor.Attack reset arm)
|
||||
InPlayViewBase global::Wizard.Battle.View.IBattlePlayerView.InPlayView { get => default!; }
|
||||
GameObject global::Wizard.Battle.View.IBattlePlayerView.StatusParentPanel { get => default!; }
|
||||
GameObject global::Wizard.Battle.View.IBattlePlayerView.AnchorL { get => default!; }
|
||||
@@ -335,7 +335,7 @@ namespace Wizard.Battle.View {
|
||||
// shared no-op PlayQueueViewBase so the presentation call is a safe NullVfx (the authoritative
|
||||
// play mutation runs in PlayHandCardReflection.Play, not in this view).
|
||||
PlayQueueViewBase global::Wizard.Battle.View.IBattlePlayerView.PlayQueueView { get => global::Wizard.Battle.View.HeadlessPlayQueueViewStub.Instance; }
|
||||
AttackSelectControl global::Wizard.Battle.View.IBattlePlayerView.AttackSelectControl { get => default!; }
|
||||
AttackSelectControl global::Wizard.Battle.View.IBattlePlayerView.AttackSelectControl { get => global::Wizard.Battle.View.HeadlessAttackSelectControl.Instance; } // HEADLESS-FIX (M-HC-4a): non-null no-op attack-select-control for the receive ATTACK path (RegisterPairToAttackSelectControl + ActionProcessor.Attack reset arm)
|
||||
InPlayViewBase global::Wizard.Battle.View.IBattlePlayerView.InPlayView { get => default!; }
|
||||
GameObject global::Wizard.Battle.View.IBattlePlayerView.StatusParentPanel { get => default!; }
|
||||
GameObject global::Wizard.Battle.View.IBattlePlayerView.AnchorL { get => default!; }
|
||||
|
||||
Reference in New Issue
Block a user