-
-
Notifications
You must be signed in to change notification settings - Fork 32
Engine Features
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 are basically modifying effects such as change weakness, retreat, energy type, pokemon type, list of playable moves, etc. List of Getters
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 inCardList
, which is basically used everywhere. Theselect()
itself also returns aCardList
so the calls can be chained if required. -
list.select()
- selects one card fromlist
. 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 surelist
contains at least1
element. -
list.select(count: 3)
- Selects 3 cards fromlist
. Iflist
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.
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.
An ability is always tied to a pokemon in play and it activates once the pokemon is in play. An ability is either:
- an action that the user can initiate during his/her turn (
actionA
pattern), - a persistent delayed effect (
delayedA
pattern) - a getter (modifier) effect (
getterA
pattern) - 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.
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()
}
}
}
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.
TcgStatics class has many common effects in the game imported as static methods.