Skip to content
Gary L edited this page Jul 20, 2019 · 2 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](https://github.com/axpendix/tcgone-engine-contrib/blob/master/src/tcgwars/logic/effect/EffectType.java)

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](https://github.com/axpendix/tcgone-engine-contrib/blob/master/src/tcgwars/logic/effect/EffectType.java#L95)

### 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.

“`java 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](https://github.com/axpendix/tcgone-engine-contrib/blob/master/src/tcgwars/logic/groovy/TcgStatics.groovy) class has many common effects in the game imported as static methods.

Clone this wiki locally