MagicWars 1.2.0 - Script your card - Part I
by nantuko84
Moderators: nantuko84, CCGHQ Admins
MagicWars 1.2.0 - Script your card - Part I
by nantuko84 » 14 Dec 2009, 20:39
Today I will continue describing how to create your own card in MagicWars starting version 1.2.0 (that will be published soon). At the moment, we have about 200 script cards and they work fine.
Let's get cracking!
Before reading this article, you'll need to read all about where and how cards are stored now here: http://www.slightlymagic.net/forum/viewtopic.php?f=45&t=1938
First, I'd like to write about Groovy Closures in groovy script, this is what can be found by google:
A Groovy Closure is like a "code block" or a method pointer. It is a piece of code that is defined and then executed at a later point.
Yep, it's true. You write code, then pass it as a parameter and it will be executed later in the game. It is put into "{" and "}" symbols. For well-known Shock the closure is: {dealDamage($target, 2, $this)}
So later it will be executed with injected $target that can be permanent or player and $this that is card itself.
When you create your card, you should follow these two steps:
1. First add spell or ability (using addSpell, addAbility, addAbilityTap, addAbilityTriggered, etc.)
2. Then add specific parameters for just added spell or ability (setCondition, setTrigger, setTarget, setSpecificTarget, addKicker, etc.)
Tip: in cards/scripts you can find common.g that contains all API function you can use.
Adding a spell or an ability
addSpell(Closure) - add any spell without any targeting or condition or spell that you will set parameters later (Step 2.)
mana cost is taken from card description
examples:
Cost - mana cost to pay
May - is Yes\No question that will be asked before paying for ability
Cost and May are optional parameters with default values "0" and "" correspondingly
examples:
- the same as Ability but permanent will also be tapped as a cost
addTriggeredAbility(Closure[,Cost="0"[,May=""]])
- ability that will be triggered by some condition. you do need to set trigger condition later in the script by setTrigger function that will be described later
examples:
Let's get cracking!
Before reading this article, you'll need to read all about where and how cards are stored now here: http://www.slightlymagic.net/forum/viewtopic.php?f=45&t=1938
First, I'd like to write about Groovy Closures in groovy script, this is what can be found by google:
A Groovy Closure is like a "code block" or a method pointer. It is a piece of code that is defined and then executed at a later point.
Yep, it's true. You write code, then pass it as a parameter and it will be executed later in the game. It is put into "{" and "}" symbols. For well-known Shock the closure is: {dealDamage($target, 2, $this)}
So later it will be executed with injected $target that can be permanent or player and $this that is card itself.
When you create your card, you should follow these two steps:
1. First add spell or ability (using addSpell, addAbility, addAbilityTap, addAbilityTriggered, etc.)
2. Then add specific parameters for just added spell or ability (setCondition, setTrigger, setTarget, setSpecificTarget, addKicker, etc.)
Tip: in cards/scripts you can find common.g that contains all API function you can use.
Adding a spell or an ability
addSpell(Closure) - add any spell without any targeting or condition or spell that you will set parameters later (Step 2.)
mana cost is taken from card description
examples:
- Code: Select all
//Wrath of God
addSpell({
for (creature in $allCreatures) {
destroyNoRegeneration(creature)
}
})
- Code: Select all
// Roar of the Wurm
addSpell({
def token = createToken(card,"name=Wurm,type=Creature&Wurm,
color=Green,abilities=no,power=6,toughness=6");
addPermanent(token);
})
addFlashback("3 G") // <-- this just adds Flashback to the spell
Cost - mana cost to pay
May - is Yes\No question that will be asked before paying for ability
Cost and May are optional parameters with default values "0" and "" correspondingly
examples:
- Code: Select all
// Ant Queen (M10)
addAbility({
def token = createToken(card, "name=Insect,type=Creature&Insect,
color=Green,abilities=no,power=1,toughness=1");
addPermanent(token);
}, "1 G")
- the same as Ability but permanent will also be tapped as a cost
addTriggeredAbility(Closure[,Cost="0"[,May=""]])
- ability that will be triggered by some condition. you do need to set trigger condition later in the script by setTrigger function that will be described later
examples:
- Code: Select all
// Reckless Scholar (ZEN)
addAbilityTap({
drawCard($target, 1)
def discard = Action($this, {
discardCard($targetPlayer, $chosen);
})
discard.setTargetPlayerID($target)
discard.setNeedsDiscardCard(true)
run(discard)
})
setTarget("Player")
- Code: Select all
// Hagra Crocodile (ZEN)
addAbilityTriggered("Landfall - Whenever a land enters the battlefield
under your control, Hagra Crocodile gets +2/+2 until end of turn.",
{
pumpUntilEOT($this, 2, 2)
})
setCondition({
return $card.isLand() && $card.controller == $this.controller
})
setTrigger("@moveToZone,from=Any,to=Battlefield")
Re: MagicWars 1.2.0 - Script your card - Part I
by Arch » 14 Dec 2009, 22:13
Some comments on your post although not really on the actual content of it:
It looks like you're passing a string to many of your functions containing various configuration. Don't you have access to named parameters in Groovy? Such that you could write something like:
Also, since I'm a fan of useful programming features, I must point out that a closure is only a closure if it captures something from it's surrounding environment. A block of code that doesn't do that should probably "just" be called a first class function. Looking at your examples I would say that Wrath, Scholar and Croc are probably functions while the others are actual closures.
It looks like you're passing a string to many of your functions containing various configuration. Don't you have access to named parameters in Groovy? Such that you could write something like:
- Code: Select all
createToken(card, name="Wurm", power=6, ...)
Also, since I'm a fan of useful programming features, I must point out that a closure is only a closure if it captures something from it's surrounding environment. A block of code that doesn't do that should probably "just" be called a first class function. Looking at your examples I would say that Wrath, Scholar and Croc are probably functions while the others are actual closures.
Re: MagicWars 1.2.0 - Script your card - Part I
by nantuko84 » 15 Dec 2009, 08:22
not many, but there is someIt looks like you're passing a string to many of your functions containing various configuration. Don't you have access to named parameters in Groovy? Such that you could write something like:
it was our initial idea to pass strings and then parse them by regexp in java
it was at the very beginning on connection groovy to MagicWars and it worked
now I reread groovy specs time to time to find such new features that can make you life easier and code more readable. One thing I was happy to find yesterday is the code replacing this one in java:
- Code: Select all
for (int i = 0; i < stack.size(); i++) {
SpellAbility sa = stack.peek(i);
if (sa.isSpell() && GameManager.canBeCountered(sa.getSourceCard())) {
return true;
}
}
- Code: Select all
return stack.any{it.spell && canBeCountered(it.sourceCard)}
when you know this, you can update api that is used from scripted cards
now all of them in api.mw that goes with groovy folder, and there you can find this method:
- Code: Select all
def createToken = {source, pattern -> tokenFactory.createToken(source, pattern)}
- Code: Select all
def createToken(params, source) {
tokenFactory.createToken(source, params.name, params.types, params.abilities, params.colors, params.power, params.toughness)
}
- Code: Select all
addAbility({
def token = createToken($this, name:"Insect", types:"Creature&Insect", colors:"Green", abilities:"no", power:1, toughness:1)
addPermanent(token)
}, "1 G")
- Code: Select all
Also, since I'm a fan of useful programming features, I must point out that a closure is only a closure if it captures something from it's surrounding environment. A block of code that doesn't do that should probably "just" be called a first class function. Looking at your examples I would say that Wrath, Scholar and Croc are probably functions while the others are actual closures.
regards
Re: MagicWars 1.2.0 - Script your card - Part I
by Arch » 15 Dec 2009, 09:46
That's definitly better looking code. The only thing I would consider changing is the "Creature&Insect"; I'd use a list there instead.
The closure comment is only about terminology as you figured. I'm assuming it's this site you got it from. It does seem they've got it slightly wrong.
It's not until you get to "free variables" that the block of code actually becomes a closure. Looking at the second example in that chapter for instance;
One easy way to check if you have a closure or a first class function would be to move the code block. If it's possible to define it anywhere it's not a closure. The below code for instance would not work, hence { println localVariable } is a closure.
The closure comment is only about terminology as you figured. I'm assuming it's this site you got it from. It does seem they've got it slightly wrong.
It's not until you get to "free variables" that the block of code actually becomes a closure. Looking at the second example in that chapter for instance;
- Code: Select all
def localMethod() {
def localVariable = new java.util.Date()
return { println localVariable }
}
def clos = localMethod()
println "Executing the Closure:"
clos() //prints the date when "localVariable" was defined
One easy way to check if you have a closure or a first class function would be to move the code block. If it's possible to define it anywhere it's not a closure. The below code for instance would not work, hence { println localVariable } is a closure.
- Code: Select all
def clos = { println localVariable }
clos()
Re: MagicWars 1.2.0 - Script your card - Part I
by nantuko84 » 15 Dec 2009, 12:38
I guess from that point of view def localVariable = new java.util.Date() is just the same as using final keyword in java. at least can't see any differences
so localMethod just returns predefined closure: { println SomeDate@Instance }
so localMethod just returns predefined closure: { println SomeDate@Instance }
Re: MagicWars 1.2.0 - Script your card - Part I
by Arch » 15 Dec 2009, 12:53
I belive that's a valid way of thinking of it. Except that Groovy quite possibly handles it differently under the hood.
Re: MagicWars 1.2.0 - Script your card - Part I
by Incantus » 15 Dec 2009, 13:54
All functions close over global variables. Closures basically generalize that concept to allow nested functions to work intuitively (you should be able to access any variables defined in parent scopes all the way up to the global namespace). Under the covers, however, global variables and free variables are implemented differently, but that's an implementation issue.
7 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 8 guests