Skip to main content

Targeting And Selection

GCS ยท Core Concepts

Targeting is a contract between three parties: what the card text promises, what the player selects, and what the graph resolves at runtime. This page pins down the exact semantics of each.

Read this before authoring any effect that says 'the enemy', 'all enemies', or 'a random card', so the words mean what the player thinks they mean.

The moment targeting starts to matter is the moment there is more than one valid target. Three enemies, three separate intents, and a hand full of cards that each need to know who they hit:

A Coven of Wax battle with three enemies, each telegraphing its own intent above its head

Targeting is the contract between what a card says, what the player selects, and what the FlowGraph resolves. Use the same words in card text, fields, and node names so users never have to guess.

Card target modeโ€‹

The card's target mode describes player input: what, if anything, the player must click before the card can leave the hand. Exactly one of the four modes asks for a click.

ModePlayer clicks?What happens
NoneNoThe card plays without a selected target. The graph decides everything through its own unit and card sources.
SingleEnemyUnitYesThe card cannot be played until the player selects one living enemy. That clicked enemy becomes the selected target the graph reads through Opponent.
AllEnemyUnitsNoThe card is aimed at every living enemy. Inside the graph, use AllEnemies so the nodes hit the same set the player expects.
SelfNoThe card is aimed at the player unit. Inside the graph, use Self.

From code, GCSApi.RequiresTarget returns true exactly for SingleEnemyUnit. That is the check a drag-to-target UI uses to decide whether to draw an aiming arrow.

Card target mode is about player input. FlowGraph target sources, below, are about what a node resolves when it runs. The two work together: a SingleEnemyUnit card whose graph uses Opponent hits the clicked enemy. Mixing them up is the most common targeting bug. An AllEnemyUnits card whose only damage node targets Opponent looks like AoE in the hand and hits one enemy on resolve.


Unit sourceโ€‹

Nodes that act on units take a UnitSource. The resolution rules are exact, so card text can promise exact things:

SourceResolves to
SelfThe unit running the graph: the player on a player card, the holder on a status, the acting enemy on an enemy graph.
OpponentWhen the acting unit is the player (a player card, or a status the player holds): the selected enemy if one was clicked and it is still alive, otherwise the first living enemy. When the acting unit is an enemy: the player unit.
AllEnemiesEvery living enemy. Dead enemies are always excluded.
AllUnitsThe player unit plus every living enemy. Dead units are excluded here too.

Use Self for block, healing, and self-buffs. Use Opponent for the selected enemy on targeted attacks. Use AllEnemies for AoE cards.

The Target port outranks the dropdown

When a node's Target port is connected, the connection supplies the units and the TargetSource dropdown is ignored entirely. Pick the dropdown for the common cases above; reserve the port for computed targets coming out of selector chains.


Card sourceโ€‹

Nodes that act on cards take a CardSource:

SourceMeaning
ThisCardThe card instance currently resolving.
HandPileThe current hand.
DrawPileThe draw pile.
DiscardPileThe discard pile.
ExhaustPileThe exhaust pile.
AllPilesThe four battle piles together: hand, draw, discard, and exhaust.

Be explicit in card text. "Draw pile" does not mean "all cards", and "hand" means the hand at the moment the node runs, including cards drawn earlier in the same turn.


Selector nodesโ€‹

When the rule is authored rather than clicked, selectors narrow a collection down. Filter keeps the matching elements (it filters in, not out), Sort orders them, and Pick Element, Take, and Random Elements reduce them to the few you want. Typical builds:

  • the lowest-HP enemy: All Enemies into Filter (Alive) into Sort (HP) into Pick Element;
  • two random cards in hand: a hand collection into Random Elements with count 2;
  • cards filtered by type, rarity, keyword, tag, or a cost threshold;
  • units filtered by team, tags, tier, a carried status, HP percentage, or alive state.

Use selectors when the graph should decide. Use card target mode when the player should decide.


No valid targetโ€‹

Built-in action nodes fail soft. Each one resolves its targets, skips anything dead or missing, and continues with whatever remains, which may be nothing at all. A Deal Damage aimed at AllEnemies when no enemy is left alive deals no damage and does not block the battle.

"Do nothing and continue" is the right default. When the empty case matters to the player, make the condition visible instead of relying on the silence:

  • put a Condition into a Branch or Gate before the action;
  • say it in the card text ("If there is a valid enemy...");
  • design the fallback on purpose instead of inheriting whatever the graph happens to do.

Text guidanceโ€‹

Write card descriptions in gameplay language, not in source-enum language:

SayAvoid
Deal 8 damage to the selected enemy.Deal 8 to Opponent.
Gain 6 armor.Apply armor to Self.
Draw 2 cards from your draw pile.Move 2 card refs.
Apply 2 Burn to all enemies.Add status to unit collection.

The implementation can use UnitSource, CardSource, and selectors freely. The card text should describe what the player experiences.