Skip to content
Gary L edited this page Jul 20, 2019 · 5 revisions

Delayed Effects

An effect can either be direct or delayed. Direct effects (like switch, apply special condition) happens instantly. Delayed effects are like scheduled tasks and they listen to some events to run. For example, if we want to increase an attack damage before weakness and resistance, we do it like this:

delayed {
  before APPLY_ATTACK_DAMAGES, {
    // increase damage
  }
  after EVOLVE, self, {unregister()} //after this pokemon is evolved, unregister this effect
  after SWITCH, self, {unregister()} //after this pokemon is evolved, unregister this effect
  unregisterAfter 2 //auto unregister after 2 turns has passed (effectively means after your opponent's next turn or during the start of your next turn)
  register {} //do custom action on register
  unregister {} //do custom action on unregister
  def xyz = ... //you can define any variables with this effect's scope
}  

The given closure will be run every time after this event type. To stop this delayed effect, either call unregister() from inside, call unregisterAfter turns to set auto expire turn count, or assign this delayed effect to a variable then call unregister() method on it. You'll find examples in the code repository. List of EffectTypes

To control the lifecycle manually, create the delayed effect like def eff = delayed { ... }. Then eff.unregister() to unregister it.

The lifecycle will be auto managed if delayedA call was used inside an ability.

Getter Effects

Getter effects are basically modifying effects such as change weakness, retreat, energy type, pokemon type, list of playable moves, etc. List of Getters

User Input (Selections, Choices, Confirmations)

During the resolution of any kind of effect, user input is usually required, such as selecting cards, choosing pokemon, making choices.

  • Selecting cards can be done with the .select() call in CardList, which is basically used everywhere. The select() itself also returns a CardList so the calls can be chained if required.
  • list.select() - selects one card from list. It must contain at least one card or it will result in an engine error.
  • list.select(min: 0) - Selects at most 1 card. Returned list may be empty.
  • list.select(min: 1, max: 4) - Selects at least 1, at most 4 cards. Make sure list contains at least 1 element.
  • list.select(count: 3) - Selects 3 cards from list. If list contains less, it selects less. \
  • list.select(count: 3, "Information Text") - Information text is important for players to understand what are they selecting, please include.

To select pokemon, PcsList contains the selection methods: select(info).

To make a choice, call makeChoice(choices) or makeChoice(choices, labels) for custom labels. It will return one of the elements in choices array.

To just confirm something with yes and no, call confirm("Information") and it will return a boolean for you.

Important: Selection is directed to the player in the current thread (usually the current turn player). This works well if the selection is for a direct attack effect, play trainer, etc. For effects like Wishful Baton which returns energy to hand definitely needs to direct the selection to the owner of the card, not current turn player. In that case we add the PlayerType to the selection as self.owner or thisCard.player. For an example, check Wishful Baton card.

Actions

An action is the buttons you see in the lower left corner. They are automatically generated from the list of available moves, retreat and end turn. So we mainly use them to trigger abilities, although it's possible to register actions for other reasons. For abilities, we use actionA clause that automatically matches its lifecycle with enclosing ability's.

Abilities (incl. PokePower, PokeBody, etc)

An ability is always tied to a pokemon in play and it activates once the pokemon is in play. An ability is either:

  1. an action that the user can initiate during his/her turn (actionA pattern),
  2. a persistent delayed effect (delayedA pattern)
  3. a getter (modifier) effect (getterA pattern)
  4. one-off effect activated on play

Since the abilities in the older times are named differently, we use bwAbility for modern abilities, and pokemonPower, pokePower or pokeBody for the classic ones (did you know 'Pokemon Power' and 'PokePower' are different stuff?)

Abilities have special activation and deactivation flows, advanced stuff can be done by intercepting these points. Use onActivate and onDeactivate closures, respectively.

Moves

They are simply defined in the main section of Pokemon cards.

move "Call for Friend" {
  energyCost W, C
  text "Flip a coin. If heads, you may search your deck for a Basic Pokémon with Misty in its name and put it onto " +
    "your Bench. (You can't use this attack if your Bench is full.) Shuffle your deck afterward."
  attackRequirement { // optional clause to check whether the current situation permits the use of the attack or not
    // use assert statements inside to make sure some conditions hold, else the attack won't be allowed to be performed
    assert deck.notEmpty : "Deck is empty"
    assert bench.notFull : "Bench is full"
  }
  onAttack { // this is the main clause to run during the attack
    flip {
      deck.search({ it.name.contains("Misty") && it.cardTypes.is(BASIC) }).each {
        deck.remove(it)
        benchPCS(it)
      }
      shuffleDeck()
    }
  }
}

Assertions & Checks

Many trainer cards, moves and abilities need to be checked for some preconditions before they are allowed to run. Example: A Revive should not work when there's no space on bench or there are no pokemon in discard. The game rules specifically disallow playing cards for no effect, so we should block them from happening.

We use assert keyword to check effects: assert condition : message (colon and message are optional)

Example: assert my.bench.notFull : "Bench is full" asserts the bench to be not full, else the method will return from the current method while warning the player with the message "Bench is full".

We put them in either playRequirement of trainers, or attackRequirement of moves, or actionA clauses of abilities. When an assert statement is fails, the player sees the a warning text with message (if there is some) and no code in the same closure will run afterwards. This is a very cool feature of the language.

Common Effects

TcgStatics class has many common effects in the game imported as static methods.