Skip to main content

Cards, Hand, Deck, Discard, and Exhaust

GCS ยท Runtime

How runtime card zones behave, how the draw pile is ordered, and which APIs move cards safely.

Use this when a card ends up in the wrong pile, a generated card never shows in hand, or your UI shows stale hand contents.

Runtime card zones are lists of CardInstance. A card instance is a battle copy of a GameCard asset, with its own instance id, upgraded flag, and master deck cost delta. The Pile tab of the Game Card Monitor shows all zones with live contents while you play.

Game Card Monitor Pile tab listing hand, draw, discard, and exhaust contents during a live battle

Card definition versus card instanceโ€‹

ConceptTypeMeaning
Card definitionGameCardAuthored asset: name, cost, tags, rarity, text, targeting, and behavior.
Card instanceCardInstanceRuntime copy in a battle pile. It points to a GameCard and tracks battle-specific state.
Active cardCardInstance.GetActiveCard() or GCSApi.GetActiveCard(instance)The upgraded variant when the instance is upgraded; otherwise the base card.
Effective costGCSApi.GetEffectiveEnergyCost(instance)Cost after card definition, master deck delta, temporary modifiers, and cost hooks.

Use instances for runtime actions. Use definitions for content browsing, deck construction, and database lookup.


Runtime pilesโ€‹

PileAPIMeaning
HandGCSApi.Hand() or GCSApi.CurrentHandCards currently available in the player's hand.
Draw pileGCSApi.DrawPile()Cards waiting to be drawn. The last element is drawn next.
Discard pileGCSApi.DiscardPile()Cards discarded this battle and available for reshuffle.
Exhaust pileGCSApi.ExhaustPile()Cards removed from play for the rest of the battle.
Played this turnGCSApi.PlayedThisTurn()Cards successfully played during the current turn, in play order.
Drawn this turnGCSApi.DrawnThisTurn()Cards drawn during the current turn, in draw order.

Hand(), DrawPile(), DiscardPile(), ExhaustPile(), PlayedThisTurn(), and DrawnThisTurn() never return null. They return empty lists when no battle is active. CurrentHand mirrors the manager and returns null outside battle.

Cards move between the four zones along a small set of paths:


Draw pile orderโ€‹

The draw pile is ordered so the last element is the top of the pile. When documentation says "top of the draw pile," it means the element that will be drawn next.

Use DrawPilePosition when adding cards:

PositionMeaning
TopDrawn before the current pile contents.
BottomDrawn after the current pile contents.
RandomInserted into a random slot.

Opening hand and per-turn drawโ€‹

At battle start:

  1. GCS builds the draw pile from the run master deck or the player starting deck.
  2. It shuffles the draw pile.
  3. It prioritizes innate cards for the opening hand.
  4. It draws the starting hand.

On later player turns:

  1. DrawnThisTurn is cleared.
  2. OnDrawPhase is raised.
  3. Draw-phase status behavior runs.
  4. GCS draws BattleRules.DrawPerTurn cards.

When a draw finds the draw pile empty, the discard pile shuffles back into the draw pile automatically and OnDrawPileShuffled is raised. If both piles are empty, the draw stops.

The hand holds at most BattleRules.MaxHandSize cards (10 by default; a Hand Size Hook can change the limit during battle). While the hand is full, the per-turn draw flips the leftover cards from the top of the draw pile straight into discard and raises OnCardDiscarded for each. A plain GCSApi.DrawCards call stops early instead, and AddCardToHand or a move into a full hand sends the card to discard.

info

DrawnThisTurn covers the current turn only. Cards drawn on previous turns are not included, even if they are still in hand.


Playing a cardโ€‹

When GCSApi.TryPlayCard(card, target) succeeds:

  1. The card must be in the current hand.
  2. The active card definition is resolved.
  3. Effective cost is checked.
  4. Playability hooks run.
  5. Energy is spent.
  6. The card instance is added to PlayedThisTurn.
  7. OnCardPlayed is raised.
  8. The card's on-play FlowGraph runs.
  9. Card hooks are registered.
  10. The card leaves hand.
  11. If the active card has the Exhaust keyword, it moves to exhaust and raises OnCardExhausted; otherwise it moves to discard.

The card leaves hand after behavior resolves. Do not remove it manually in custom UI.


End-of-turn hand handlingโ€‹

At player turn end, GCS handles the remaining hand:

Card behaviorRuntime result
Normal hand cardMoves to discard.
Card with the Retain keywordStays in hand and raises OnCardRetained.
Card with the Ethereal keywordMoves to exhaust and raises OnCardExhausted.
Exhausting card already playedMoved to exhaust during card play, before turn end.

This table assumes the default HandDiscardRule.DiscardOnTurnEnd. An encounter with Persist skips the discard step: every card that stays raises OnCardRetained, and Ethereal cards still exhaust.

If your UI wants to animate hand cleanup, subscribe to the card lifecycle events and then refresh from GCSApi.Hand().


Creating and moving cardsโ€‹

Use controller-backed APIs for card movement.

TaskAPI
Add a card to handGCSApi.AddCardToHand(card)
Move an existing instanceGCSApi.MoveCardToPile(instance, PileScope.Discard)
Shuffle draw pileGCSApi.ShuffleDrawPile()
Draw cardsGCSApi.DrawCards(count)
Read active card definitionGCSApi.GetActiveCard(instance)
Read effective costGCSApi.GetEffectiveEnergyCost(instance)
warning

Do not edit CardPile.Cards directly. Direct edits skip events, pile rules, and the refresh assumptions your UI depends on.


Master deck versus battle pilesโ€‹

The run master deck is the persistent deck list used to start battles. Runtime piles are battle state.

NeedAPI
Inspect run deckGCSApi.GetMasterDeck()
Replace run deckGCSApi.SetMasterDeck(entries)
Add card to run deckGCSApi.AddCardToMasterDeck(card, isUpgraded)
Remove from run deckGCSApi.RemoveCardFromMasterDeck(cardGuid, removeFirstOnly)
Upgrade in run deckGCSApi.UpgradeCardInMasterDeck(cardGuid)
Clear run deckGCSApi.ClearMasterDeck()

Changing the master deck does not retroactively change cards already in a live battle unless you also mutate the runtime piles.


Card eventsโ€‹

EventPayloadUse for
OnCardPlayedCardPlayedEventArgsCard play UI, statistics, combo counters.
OnCardModifiedCardModifiedEventArgsCost or upgrade refresh.
OnCardDrawnCardLifecycleEventArgsDraw animation and hand widget creation.
OnCardDiscardedCardLifecycleEventArgsDiscard animation.
OnCardExhaustedCardLifecycleEventArgsExhaust animation.
OnCardRetainedCardLifecycleEventArgsRetain feedback.
OnCardAddedToHandCardLifecycleEventArgsGenerated-card UI.
OnCardAddedToDeckCardLifecycleEventArgsRun deck updates.
OnDrawPileShuffledNo payloadShuffle feedback.

Always refresh from the current piles after handling an event. The event tells you what changed; the API tells you the current truth.