It is currently 16 Apr 2024, 15:03
   
Text Size

MagicWars 1.2.0 - Script your card - Part II

Moderators: nantuko84, CCGHQ Admins

MagicWars 1.2.0 - Script your card - Part II

Postby nantuko84 » 14 Dec 2009, 20:51

Here is the next part of the article cycle describing scripting engine in MagicWars that allows you to create your own cards.

As I promised, today we will take a look at setting parameters at just created spells and abilities. Here the base list that is available at the moment:

setTarget(pattern)
setTargetCount(count)
setTargetsAreOptional(isOptional)
setTargetsUnique(isUnique)
setSpecificTarget(String description, String pattern)
setSpecificTarget(String description, Closure closure)

setTrigger(pattern)
setCondition(closure)
setLimit(limit)
setCanPlay(closure)

setDescription(String description)
setStackDescription(String description)
setInvisible(Boolean isInvisible)

addCost(Cost cost)

also we'll take a look at

addKicker(cost)
addFlashback(cost)
addTrap(spell, closure, cost)
addUnearth(cost)

and some specific cases:

@upkeepEachTurn(closure)
@effect(Closure apply, Closure discard)
@entersTheBattlefield(closure)
@leavesTheBattlefield(closure)

Let's start!
setTarget(pattern) - indicates that spellability (let's write it so, instead of long "spell or ability") targets smth in the game
pattern is constant string that represent simple variants of targets, and for this function it can be "Creature", "Creature|Player" or "Player".
note: all targeting spells can access selected target(s) by injected $target and $targets variables

example: //Shock

Code: Select all
addSpell({
        dealDamage($target, 2, $this)
})
setTarget("Creature|Player")
setTargetCount(count) - sets number of targets

setTargetsAreOptional(isOptional) - indicates whether you can cancel targeting or not, default is true (usually you can cancel targeting). For default cases, no need to set this parameter. Used for abilities only.
example for setTargetsAreOptional(false) would be Flametongue Kavu that _must_ target any creature when comes into play (even itself if no other creature exists)

setTargetsUnique(isUnique) - if target count is more than 1, then this parameter says whether you can choose one target twice

setSpecificTarget(String description, String pattern) - if target is more complicated just creature in play, use this method to set target type
description - what will be shown to play when he is asked to choose the target
pattern - string that uses "zone=(.*),type=(.*)[,color=(.*)]" pattern

now only zone=Battlefield is available, for types and color you can combine them using & and ^ symbols
examples:
Code: Select all
//Doom Blade
setSpecificTarget("Select nonblack creature to destroy.", "zone=Battlefield,type=Creature,color=^Black")
Code: Select all
//Terror
setSpecificTarget("Select nonartifact, nonblack creature to destroy.", "zone=Battlefield,type=Creature&^Artifact,color=^Black")
Code: Select all
//Creeping Mold
setSpecificTarget("Select artifact, enchantment, or land to destroy.", "zone=Battlefield,type=Land&Artifact&Enchantment") // <-- short pattern without color
colors are "Black","Green","Blue","White","Red","Colorless","Multi"

but it's also for simple cases, as you have more universal method for targeting:
setSpecificTarget(String description, Closure closure)
description - what will be shown to play when he is asked to choose the target
closure - that returns true or false for $permanent
few examples:

Code: Select all
// royal assasin
setSpecificTarget("Select target tapped creature to destroy.", {
        return $permanent.isCreature() && $permanent.isTapped()
})
need to say that to access properties in groovy you can use both variants - getters and just variable names, so royal can be rewritten so:

Code: Select all
// royal assasin
setSpecificTarget("Select target tapped creature to destroy.", {
        return $permanent.creature && $permanent.tapped
})
some more examples:

Code: Select all
abilityTriggered.setSpecificTarget("Select nonbasic land to destroy.", {
            return $permanent.isLand() && !$permanent.isBasicLand()
})
Code: Select all
setSpecificTarget("Select target creature without flying.", {
        return $permanent.creature && !$permanent.hasKeyword(KEYWORD_FLYING_SA)
})
note: keywords will be discussed next time

setTrigger(pattern) - is used for addAbilityTriggered, sets the trigger condition when ability will be triggered. Some of triggers available now:
"@creatureAttacks" - triggers when any create attacks (once for every creature attacking)
"@moveToZone,from=,to=" - when any object moves from one zone to another
is a constant of GameZone type:

Code: Select all
  GameZone {
        Library,
        Hand,
        Battlefield,
        Stack,
        Graveyard,
        Exile,
        Any
  }
Before bringing trigger examples, let's look at another method that is also used for triggers:
setCondition(closure) - filters objects for trigger (in case we don't want to handle all of them)

examples:


Code: Select all
//Hagra Crocodile (ZEN) and Landfall
    addAbilityTriggered(text[1], {
        pumpUntilEOT($this, 2, 2)
    })

    setCondition({
        return $card.isLand() && $card.controller == $this.controller
    })

    setTrigger("@moveToZone,from=Any,to=Battlefield")
Code: Select all
 //Deathgreeter (M10)
    addAbilityTriggered(text[0],  {
        gainLife($this.controller, 1, $this)
    }, "gain 1 life?")

    setCondition({
        return $card.creature && !$card.equals($this)
    })

    setTrigger("@moveToZone,from=Battlefield,to=Graveyard") 
setLimit(limit) - limits how many times ability can be played per turn
setCanPlay(closure) - set the conditions when ability can be played
addCost(Cost cost) - experimental, adds additionaly cost to spellability. at the moment it can be only SacrificeCost() that sacrifices card it self

// from Zektar Shrine Expedition (ZEN): sacrifice and put 7/1 token if there are 3 quest counters
Code: Select all
 addCost(SacrificeCost())

    setCanPlay({
        $this.getCounters(CounterType.QUEST) >= 3
    })
setDescription(String description) - adds description for spell or ability that !important is shown to user, is used only when there are some choices to be made. usually no need to set
setStackDescription(String description) - add message that will be displayed to opponent in stack
you can specify message as string or reference to text from card description:
Code: Select all
text = [ "Flying", "Other Faerie creatures you control get +1/+1."]
you can use text[0] and text[1]
setInvisible(Boolean isInvisible) - if true, opponent won't be asked to response, false by default

...
some coffee here ;)
...

addKicker(cost)
addFlashback(cost)
addTrap(spell, closure[, cost])
(closure indicates the case when it will be available for its trap cost)
addUnearth(cost)
- all of them are rather easy and just adds kicker, flashback, trap and unearth.

Code: Select all
  def spell = spells($this)
  addTrap(spell[0], {
          return $opponent.getGameStatistics().hasSearchedLibraryForACardThisTurn();
  })  // <-- cost is "0" here
and some more with examples:

@upkeepEachTurn(closure) - executed at every your upkeep

Code: Select all
//Vampire Lacerator (ZEN)
@upkeepEachTurn({
    if ($oppLife > 10) {
        loseLife($this.controller, 1, $this)
        mwprint("Upkeep, {Vampire Lacerator}: " + nickname($this.controller) + " loses 1 life.");
    }
})
@effect(Closure apply, Closure discard) - adds so called lord effect to the game (Glorious Anthem)
first closure will be applyed to existing and new permanents, the second will applyed when effect owner will leave player or change controller

Code: Select all
// Scion of Oona
 @effect({
        if ($permanent.isCreature() && $permanent.type('Faerie')
&& $permanent.controller == $this.controller && !$permanent.equals($this)) {
            $permanent.addAttack(1);
            $permanent.addDefense(1);
            $permanent.addKeyword(KEYWORD_SHROUD_SA);
        }   
    },{
        if ($permanent.isCreature() && $permanent.type('Faerie')
&& $permanent.controller == $this.controller && !$permanent.equals($this)) {
            $permanent.subAttack(1);
            $permanent.subDefense(1);
            $permanent.removeKeyword(KEYWORD_SHROUD_SA);
        }
    })
@entersTheBattlefield(closure) - is called when card enters the battlefield
@leavesTheBattlefield(closure) - is called when card leaves the battlefield

Code: Select all
// Flametongue Kavu
    @entersTheBattlefield({
        def abilityTriggered = TriggeredAbility($this, {
            dealDamage($target, 4, $this);
        })
        abilityTriggered.setNeedsTargetCreature(true)
        abilityTriggered.setTargetsAreOptional(false)
        stack.add(abilityTriggered)
    })
setDamagePlayer(Command) - sets command that will be executed when player is damaged by the card (e.g. Shadowmage Infiltrator)

That's it for today.
Next time we'll get to know what objects (from the game and engine) we can use in our spells.

See you
nantuko84
DEVELOPER
 
Posts: 266
Joined: 08 Feb 2009, 21:14
Has thanked: 2 times
Been thanked: 9 times

Re: MagicWars 1.2.0 - Script your card - Part II

Postby Marek14 » 14 Dec 2009, 22:17

"usually you can cancel targeting"? I'm not quite sure what you mean.

Compare Flametongue Kavu to Gempalm Incinerator. The difference is that you have to deal damage with Flametongue Kavu, while with Gempalm Incinerator you can eventually decide to not deal damage, but in both cases you HAVE to target something (and if, say, Horobi, Death's Whisper is in play, something dies).
Marek14
Tester
 
Posts: 2759
Joined: 07 Jun 2008, 07:54
Has thanked: 0 time
Been thanked: 296 times

Re: MagicWars 1.2.0 - Script your card - Part II

Postby nantuko84 » 15 Dec 2009, 06:48

this question made me deep in thought (you know what questions to ask)

you are right saying that all spells without "may" HAVE to target anything in the game. I've reviewed all cards implemented with using setTargetsAreOptional(false) and now I understand that the core reason for that is some type of undo action. Let me explain
Whenever you play any spell from hand that targets something, you are being asked to choose target. At such point, you're able to press "Cancel" that does not mean that you play the spell without any target, but just canceling the whole spell to play.
But in some cases, you can't go back to revert that was already done. Two most common examples:
Flametongue Kavu - once it entered onto battlefield, you MUST choose a target and no option to cancel it (as you can't cancel playing the whole spell, Flametongue Kavu is already on battlefield)

Before the next example, I need to explain a little how some choosing and searching spells work now. After spell is resolved, if it needs some chosen card, it puts another object (inherited from SpellAbility) to the stack - some sort of subspell that is invisible to players and can't be responded (that is what setInvisible() does). But here there are some differences: you can cancel searching card for Nissa Revane (saying I didn't find anything though there are Nissa's Chosen cards in your deck), but can't cancel choosing card for Duress where at first you target a player (here you can cancel playing it), but once a player was chosen and you saw all his cards, you can't refuse playing Duress and need to choose a card to discard

at the moment, have no idea how to implement it other way

regards
nantuko84
DEVELOPER
 
Posts: 266
Joined: 08 Feb 2009, 21:14
Has thanked: 2 times
Been thanked: 9 times

Re: MagicWars 1.2.0 - Script your card - Part II

Postby Marek14 » 15 Dec 2009, 08:59

nantuko84 wrote:this question made me deep in thought (you know what questions to ask)

you are right saying that all spells without "may" HAVE to target anything in the game. I've reviewed all cards implemented with using setTargetsAreOptional(false) and now I understand that the core reason for that is some type of undo action. Let me explain
Whenever you play any spell from hand that targets something, you are being asked to choose target. At such point, you're able to press "Cancel" that does not mean that you play the spell without any target, but just canceling the whole spell to play.
But in some cases, you can't go back to revert that was already done. Two most common examples:
Flametongue Kavu - once it entered onto battlefield, you MUST choose a target and no option to cancel it (as you can't cancel playing the whole spell, Flametongue Kavu is already on battlefield)

Before the next example, I need to explain a little how some choosing and searching spells work now. After spell is resolved, if it needs some chosen card, it puts another object (inherited from SpellAbility) to the stack - some sort of subspell that is invisible to players and can't be responded (that is what setInvisible() does). But here there are some differences: you can cancel searching card for Nissa Revane (saying I didn't find anything though there are Nissa's Chosen cards in your deck), but can't cancel choosing card for Duress where at first you target a player (here you can cancel playing it), but once a player was chosen and you saw all his cards, you can't refuse playing Duress and need to choose a card to discard

at the moment, have no idea how to implement it other way

regards
The issue, basically, is that sometimes you are allowed to cancel the spell or ability and not cast/activate it (game "rewinds" to state just before you started), and sometimes not. But there is an easy way to differ between the two: things that can't be rewound like that are triggered abilities, and only those. (Plus spells cast with suspend - they MUST be cast when time counters run out, if possible). And even in case of triggered abilities, you might want to go back to where the ability triggered and put the abilities in different order.

Also, why having the cancel only in targeting? The cancel button should be available during the whole announcement, up to (and especially during) the part where you actually pay the costs. And this make it useful even for triggered abilities, as some of them make you choose modes, for example (Quiet Disrepair or Blizzard Specter come to mind).

So, I'd say that the basic template should ALWAYS allow cancelling - but if you cancel a mandatory action, like triggered ability, you won't get rid of it, and you must announce it again. Even if you choose to leave mandatory actions without cancel button, the suspend interaction means that this shouldn't be specified at the card level, but at the rules engine level.
Marek14
Tester
 
Posts: 2759
Joined: 07 Jun 2008, 07:54
Has thanked: 0 time
Been thanked: 296 times

Re: MagicWars 1.2.0 - Script your card - Part II

Postby Incantus » 15 Dec 2009, 14:17

Also, you are confusing targeting with choosing a card (targeting happens when playing the spell/ability, choosing when you resolve - so there is no canceling, since you can't cancel the resolution).
Incantus
DEVELOPER
 
Posts: 267
Joined: 29 May 2008, 15:53
Has thanked: 0 time
Been thanked: 3 times

Re: MagicWars 1.2.0 - Script your card - Part II

Postby nantuko84 » 15 Dec 2009, 20:00

Yes, the second example was about choosing, but you still can cancel choosing (e.g. choosing what creature to sacrifice as a cost). from the other side, you can't cancel choosing card for Duress (as you said, it is when you resolve), but again - you can cancel choosing when search any card (still on resolve) though there are cards that you can choose
nantuko84
DEVELOPER
 
Posts: 266
Joined: 08 Feb 2009, 21:14
Has thanked: 2 times
Been thanked: 9 times


Return to MagicWars

Who is online

Users browsing this forum: No registered users and 6 guests


Who is online

In total there are 6 users online :: 0 registered, 0 hidden and 6 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 6 guests

Login Form