feat(battlenode): evolve resolves on engine state via view-untangle (M-HC-4b)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,15 @@ internal sealed class NodeNativeBattleHarness : IDisposable
|
||||
/// <summary>True while the in-play follower at <paramref name="boardPos"/> can still attack this turn.</summary>
|
||||
public bool InPlayCardAttackable(bool playerSeat, int boardPos) => Engine.InPlayCardAttackable(playerSeat, boardPos);
|
||||
|
||||
/// <summary>True once the in-play follower at <paramref name="boardPos"/> has evolved (M-HC-4b).</summary>
|
||||
public bool IsEvolved(bool playerSeat, int boardPos) => Engine.IsEvolved(playerSeat, boardPos);
|
||||
|
||||
/// <summary>The seat's current evolve-point count (M-HC-4b). An evolve spends one EP.</summary>
|
||||
public int Ep(bool playerSeat) => Engine.Ep(playerSeat);
|
||||
|
||||
/// <summary>Turns remaining until the seat may evolve (0 == unlocked) (M-HC-4b).</summary>
|
||||
public int EvolveWaitTurnCount(bool playerSeat) => Engine.EvolveWaitTurnCount(playerSeat);
|
||||
|
||||
/// <summary>Build an envelope for <paramref name="body"/> and ingest it into the engine for the
|
||||
/// given seat (player == seat A). Mirrors <c>BattleNodeFlowTests.MakeEnvelopeWith</c> +
|
||||
/// <c>SessionBattleEngine.Receive</c>.</summary>
|
||||
@@ -229,6 +238,46 @@ internal sealed class NodeNativeBattleHarness : IDisposable
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>The engine's <c>NetworkBattleDefine.PlayActionType.EVOLUTION</c> opcode — confirmed
|
||||
/// <c>= 20</c> in <c>SVSim.BattleEngine/Engine/NetworkBattleDefine.cs</c> (EVOLUTION_SELECT is 21). The
|
||||
/// receiver maps the wire <c>type</c> int straight to the enum; EVOLUTION/EVOLUTION_SELECT route through
|
||||
/// the SAME InPlayAction dispatch arm as ATTACK (NetworkOperationCollection.cs:163-170).</summary>
|
||||
public const int EvolutionOpcode = 20;
|
||||
|
||||
/// <summary>The engine's <c>NetworkBattleDefine.PlayActionType.EVOLUTION_SELECT</c> opcode — confirmed
|
||||
/// <c>= 21</c> in <c>SVSim.BattleEngine/Engine/NetworkBattleDefine.cs</c>.</summary>
|
||||
public const int EvolutionSelectOpcode = 21;
|
||||
|
||||
/// <summary>Build a PlayActions EVOLUTION frame for the in-play follower addressed by its engine
|
||||
/// <c>Index</c> (<paramref name="cardIdx"/> == the wire <c>playIdx</c>). A plain (non-targeted) evolve
|
||||
/// carries no targetList — the dispatch's <c>list</c> stays empty and the engine evolves the card in
|
||||
/// place (InPlayCardReflection.Evol).</summary>
|
||||
public static Dictionary<string, object?> EvolveBody(int cardIdx) => new()
|
||||
{
|
||||
["playIdx"] = cardIdx,
|
||||
["type"] = EvolutionOpcode,
|
||||
};
|
||||
|
||||
/// <summary>Build a PlayActions EVOLUTION_SELECT frame: the follower at engine <c>Index</c>
|
||||
/// <paramref name="cardIdx"/> evolves and targets the card at <paramref name="targetIdx"/>. The target is
|
||||
/// described in the SAME <c>{targetIdx, vid, selectSkillIndex}</c> shape as <see cref="AttackBody"/>
|
||||
/// (the dispatch resolves the target's owner from <c>vid</c> under IsRecovery, not from an isSelf key);
|
||||
/// <paramref name="targetOnEnemySeat"/> selects the vid stamp.</summary>
|
||||
public static Dictionary<string, object?> EvolveSelectBody(int cardIdx, int targetIdx, bool targetOnEnemySeat) => new()
|
||||
{
|
||||
["playIdx"] = cardIdx,
|
||||
["type"] = EvolutionSelectOpcode,
|
||||
["targetList"] = new List<object?>
|
||||
{
|
||||
new Dictionary<string, object?>
|
||||
{
|
||||
["targetIdx"] = (long)targetIdx,
|
||||
["vid"] = targetOnEnemySeat ? EnemySeatVid : SelfSeatVid,
|
||||
["selectSkillIndex"] = new List<object?>(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
public void Dispose() { /* engine holds no unmanaged resources; nothing to release. */ }
|
||||
|
||||
/// <summary>Minimal test-only <see cref="IBattleParticipant"/> exposing only the
|
||||
|
||||
Reference in New Issue
Block a user