using System.Text.Json.Serialization; namespace SVSim.BattleNode.Protocol.Bodies; /// Opponent-facing PlayActions frame the node synthesizes from the active player's /// send. KnownList reveals the played card's identity (null = token reveal deferred, see /// the deterministic-turn slice). OppoTargetList is the renamed targetList /// (independent of KnownList — a targeted hand play carries both). KeyAction forwards a /// choice/Discover play's {type,cardId} so the opponent renders the choice-token generation; /// the pick (selectCard) is stripped for a hidden (open:0) draw-to-hand choice. All three are /// omitted when null via the envelope's WhenWritingNull policy (a vanilla play carries none). public sealed record PlayActionsBroadcastBody( [property: JsonPropertyName("playIdx")] int PlayIdx, [property: JsonPropertyName("type")] int Type, [property: JsonPropertyName("knownList")] IReadOnlyList? KnownList, [property: JsonPropertyName("oppoTargetList")] IReadOnlyList? OppoTargetList, [property: JsonPropertyName("keyAction")] IReadOnlyList? KeyAction = null) : IMsgBody; /// Opponent-facing keyAction entry for a choice/Discover play. type/cardId /// (the GENERATING card) pass through so the opponent re-derives the candidate pool from that card's /// skill; selectCard is stripped (null) for a hidden (open:0) choice — the pick stays secret /// until the chosen card is played — and passed through for a visible (open:1) board choice (§6, /// provisional pending live confirmation). public sealed record KeyActionEntry( [property: JsonPropertyName("type")] int Type, [property: JsonPropertyName("cardId")] long CardId, [property: JsonPropertyName("selectCard")] SelectCardEntry? SelectCard); /// A visible choice's revealed pick: the chosen cardId(s) and the open flag. /// Only emitted for the open:1 pass-through case (open:0 strips the whole selectCard). public sealed record SelectCardEntry( [property: JsonPropertyName("cardId")] IReadOnlyList CardId, [property: JsonPropertyName("open")] int Open); /// One revealed card in a knownList. Vanilla slice fills cardId from the sender's /// deck map and leaves spellboost 0 / attachTarget "" (cost/clan/tribe deferred to the card-master /// port — the receiver re-derives them from cardId). public sealed record KnownCardEntry( [property: JsonPropertyName("idx")] int Idx, [property: JsonPropertyName("cardId")] long CardId, [property: JsonPropertyName("to")] int To, [property: JsonPropertyName("spellboost")] int Spellboost, [property: JsonPropertyName("attachTarget")] string AttachTarget); /// Renamed targetList entry. isSelf is actor-relative and passes through /// verbatim — no perspective flip (bullet-3 audit F2). public sealed record OppoTargetEntry( [property: JsonPropertyName("targetIdx")] int TargetIdx, [property: JsonPropertyName("isSelf")] int IsSelf); /// One entry in a relayed uList (the unapproved-movement list) — a skill-driven /// card movement (fetch / search / summon-from-deck / discard-reveal) the node forwards VERBATIM /// (bullet-3 audit F1; the node makes no reveal decision — cardId presence is the sender's /// call). The first five fields are always emitted; the rest are conditional in /// SendCardDataMaker.MakeUList (cardId when revealed, clan/cost when set, etc.) and omit when /// null. isSelf is actor-relative and passes through unchanged (F2). public sealed record UnapprovedCardEntry( [property: JsonPropertyName("idxList")] IReadOnlyList IdxList, [property: JsonPropertyName("from")] int From, [property: JsonPropertyName("to")] int To, [property: JsonPropertyName("isSelf")] int IsSelf, [property: JsonPropertyName("skill")] string Skill, [property: JsonPropertyName("cardId")] long? CardId = null, [property: JsonPropertyName("clan")] int? Clan = null, [property: JsonPropertyName("cost")] int? Cost = null, [property: JsonPropertyName("skillKeyCardIdx")] IReadOnlyList? SkillKeyCardIdx = null, [property: JsonPropertyName("randomTargetIdx")] IReadOnlyList? RandomTargetIdx = null, [property: JsonPropertyName("isInvoke")] int? IsInvoke = null, [property: JsonPropertyName("attachTarget")] string? AttachTarget = null);