Cards, Hand, Deck, Discard, and Exhaust
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.

Card definition versus card instanceโ
| Concept | Type | Meaning |
|---|---|---|
| Card definition | GameCard | Authored asset: name, cost, tags, rarity, text, targeting, and behavior. |
| Card instance | CardInstance | Runtime copy in a battle pile. It points to a GameCard and tracks battle-specific state. |
| Active card | CardInstance.GetActiveCard() or GCSApi.GetActiveCard(instance) | The upgraded variant when the instance is upgraded; otherwise the base card. |
| Effective cost | GCSApi.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โ
| Pile | API | Meaning |
|---|---|---|
| Hand | GCSApi.Hand() or GCSApi.CurrentHand | Cards currently available in the player's hand. |
| Draw pile | GCSApi.DrawPile() | Cards waiting to be drawn. The last element is drawn next. |
| Discard pile | GCSApi.DiscardPile() | Cards discarded this battle and available for reshuffle. |
| Exhaust pile | GCSApi.ExhaustPile() | Cards removed from play for the rest of the battle. |
| Played this turn | GCSApi.PlayedThisTurn() | Cards successfully played during the current turn, in play order. |
| Drawn this turn | GCSApi.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:
| Position | Meaning |
|---|---|
| Top | Drawn before the current pile contents. |
| Bottom | Drawn after the current pile contents. |
| Random | Inserted into a random slot. |
Opening hand and per-turn drawโ
At battle start:
- GCS builds the draw pile from the run master deck or the player starting deck.
- It shuffles the draw pile.
- It prioritizes innate cards for the opening hand.
- It draws the starting hand.
On later player turns:
DrawnThisTurnis cleared.OnDrawPhaseis raised.- Draw-phase status behavior runs.
- GCS draws
BattleRules.DrawPerTurncards.
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.
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:
- The card must be in the current hand.
- The active card definition is resolved.
- Effective cost is checked.
- Playability hooks run.
- Energy is spent.
- The card instance is added to
PlayedThisTurn. OnCardPlayedis raised.- The card's on-play FlowGraph runs.
- Card hooks are registered.
- The card leaves hand.
- If the active card has the
Exhaustkeyword, it moves to exhaust and raisesOnCardExhausted; 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 behavior | Runtime result |
|---|---|
| Normal hand card | Moves to discard. |
Card with the Retain keyword | Stays in hand and raises OnCardRetained. |
Card with the Ethereal keyword | Moves to exhaust and raises OnCardExhausted. |
| Exhausting card already played | Moved 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.
| Task | API |
|---|---|
| Add a card to hand | GCSApi.AddCardToHand(card) |
| Move an existing instance | GCSApi.MoveCardToPile(instance, PileScope.Discard) |
| Shuffle draw pile | GCSApi.ShuffleDrawPile() |
| Draw cards | GCSApi.DrawCards(count) |
| Read active card definition | GCSApi.GetActiveCard(instance) |
| Read effective cost | GCSApi.GetEffectiveEnergyCost(instance) |
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.
| Need | API |
|---|---|
| Inspect run deck | GCSApi.GetMasterDeck() |
| Replace run deck | GCSApi.SetMasterDeck(entries) |
| Add card to run deck | GCSApi.AddCardToMasterDeck(card, isUpgraded) |
| Remove from run deck | GCSApi.RemoveCardFromMasterDeck(cardGuid, removeFirstOnly) |
| Upgrade in run deck | GCSApi.UpgradeCardInMasterDeck(cardGuid) |
| Clear run deck | GCSApi.ClearMasterDeck() |
Changing the master deck does not retroactively change cards already in a live battle unless you also mutate the runtime piles.
Card eventsโ
| Event | Payload | Use for |
|---|---|---|
| OnCardPlayed | CardPlayedEventArgs | Card play UI, statistics, combo counters. |
| OnCardModified | CardModifiedEventArgs | Cost or upgrade refresh. |
| OnCardDrawn | CardLifecycleEventArgs | Draw animation and hand widget creation. |
| OnCardDiscarded | CardLifecycleEventArgs | Discard animation. |
| OnCardExhausted | CardLifecycleEventArgs | Exhaust animation. |
| OnCardRetained | CardLifecycleEventArgs | Retain feedback. |
| OnCardAddedToHand | CardLifecycleEventArgs | Generated-card UI. |
| OnCardAddedToDeck | CardLifecycleEventArgs | Run deck updates. |
| OnDrawPileShuffled | No payload | Shuffle feedback. |
Always refresh from the current piles after handling an event. The event tells you what changed; the API tells you the current truth.