mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Finished documenting scripting system.
This means the documentation is DONE (yay!) git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@606 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
Best practices
|
||||
|
||||
Here are some best practices and tricks for using scripts, try to follow this as much as possible.
|
||||
|
||||
--Use comments--
|
||||
Put comments before function definitions to describe what the function does:
|
||||
> # Transmogrifies a flubble value for in grunk costs
|
||||
> flubble_script := { ... }
|
||||
|
||||
--Use short scripts--
|
||||
Try to put as little as possible in [[type:field]] scripts.
|
||||
Instead define a function in the init script.
|
||||
|
||||
'''bad''':
|
||||
>field:
|
||||
> name: flubble
|
||||
> script:
|
||||
> some very complicted expression
|
||||
'''good''':
|
||||
>init script:
|
||||
> ...
|
||||
> flubble_script := { some very complicted expression }
|
||||
>field:
|
||||
> script: flubble_script()
|
||||
|
||||
--Use rule form--
|
||||
If a function is available in [[script:rule form]] use it where possible.
|
||||
|
||||
Many rules can be chained together using the @+@ operator.
|
||||
Have a look at @text_filter@ in the magic game file for an example.
|
||||
|
||||
--Don't be afraid to nest--
|
||||
Don't be afraid to nest complex things like @if@ expressions inside other expressions.
|
||||
For example the blend scripts for magic use:
|
||||
> colors := sort_text( order: "(wubrg)"
|
||||
> , (if chosen(choice:"white") then "w")
|
||||
> + (if chosen(choice:"blue") then "u")
|
||||
> + (if chosen(choice:"black") then "b")
|
||||
> + (if chosen(choice:"red") then "r")
|
||||
> + (if chosen(choice:"green") then "g"))
|
||||
and:
|
||||
> font_color_positions
|
||||
> [ if chosen(choice:"horizontal") then "horizontal"
|
||||
> else if chosen(choice:"vertical") then "vertical"
|
||||
> else if chosen(choice:"overlay") then "overlay"
|
||||
> else "radial"
|
||||
> ][number_of_items(in: colors)]
|
||||
|
||||
--Re-use functions--
|
||||
If two scripts or functions are very similair put the common part into another function.
|
||||
The differences can be passed in parameters.
|
||||
|
||||
A good example is in the @something 2@ fields used for Magic split and flip cards.
|
||||
They behave exactly the same as the non-2 variants.
|
||||
Don't use:
|
||||
>init script:
|
||||
> automatic_card_color_based_on_casting_cost := { ... card.casting_cost ... }
|
||||
> automatic_card_color_based_on_casting_cost_2 := { ... card.casting_cost_2 ... }
|
||||
>field:
|
||||
> name: card color
|
||||
> default: automatic_card_color_based_on_casting_cost()
|
||||
>field:
|
||||
> name: card color
|
||||
> default: automatic_card_color_based_on_casting_cost_2()
|
||||
Instead use:
|
||||
>init script:
|
||||
> automatic_card_color := { ... input ... }
|
||||
>field:
|
||||
> name: card color
|
||||
> default: automatic_card_color(card.casting_cost)
|
||||
>field:
|
||||
> name: card color
|
||||
> default: automatic_card_color(card.casting_cost_2)
|
||||
|
||||
--Trick: hooks--
|
||||
Use this trick when defining a large function that might be overridden by stylesheets.
|
||||
|
||||
For example things like the blend scripts for Magic cards are very large, and they are shared by all stylesheets.
|
||||
Some stylesheets need to do something special, or they don't support a particular template.
|
||||
This can be handled using hooks.
|
||||
|
||||
What this means is that you don't do:
|
||||
> blend := { large and complicated function bla bla, use "template.png" bla bla }
|
||||
But instead
|
||||
> template := "template.png"
|
||||
> blend := { large and complicated function bla bla, use template bla bla }
|
||||
Now a stylesheet can override this template by just changing that variable.
|
||||
|
||||
Another option is splitting up the template
|
||||
> blend := { large and complicated function bla bla, continue_blend() }
|
||||
> continue_blend := { use template bla bla, etc. }
|
||||
|
||||
A stylesheet can override just a part of this, or even put something in between:
|
||||
> really_continue_blend := continue_blend
|
||||
> continue_blend := { do_this_first; really_continue_blend() }
|
||||
|
||||
--Trick: lookup tables--
|
||||
Sometimes the behaviour of a function depends on a particular value.
|
||||
A good example of this are (again) the magic blend scripts.
|
||||
What way cards are blended depends on the number of colors and on the 'style' of blending.
|
||||
|
||||
Instead of using a lot of @if@ statements it can be cleaner to use a [[type:map]] as a lookup table.
|
||||
So, instead of:
|
||||
> if style == "horizontal" then lots of code
|
||||
> else if style == "vertical" then lots of code
|
||||
> # etc.
|
||||
use:
|
||||
> blends := [
|
||||
> horizontal: { lots of code }
|
||||
> vertical: { lots of code }
|
||||
> ]
|
||||
> ...
|
||||
> blends(style)() # lookup and call the function
|
||||
@@ -0,0 +1,60 @@
|
||||
Control structures
|
||||
|
||||
MSE Script has two types of control structures.
|
||||
|
||||
--If-Then-Else--
|
||||
|
||||
To switch between two options use:
|
||||
|
||||
> if condition then a else b
|
||||
|
||||
If the condition evaluates to @true@,
|
||||
then the expression evaluates to @a@, otherwise it evaluates to @b@.
|
||||
The else part is optional.
|
||||
|
||||
For example:
|
||||
> if 1 + 1 == 2 then "yes sir!" else "something is wrong"
|
||||
Will evaluate to @"yes sir!"@.
|
||||
|
||||
Note that if-then-else is an ''expression'', it can be used almost everywhere:
|
||||
> 1 + (if card.color == "red" then 1 else 2)
|
||||
> color := (if card.color == "red" then "r") +
|
||||
> (if card.color == "green" then "g")
|
||||
|
||||
The @then@ and @else@ parts can also contain assignments and other control structures.
|
||||
> if card.color == "red" then
|
||||
> filter := filter + "r"
|
||||
|
||||
To use multiple statements in the then or else branches you must use parentheses:
|
||||
> # WRONG
|
||||
> if 1 + 1 == 2 then
|
||||
> x := y
|
||||
> y := z
|
||||
> # RIGHT
|
||||
> if 1 + 1 == 2 then (
|
||||
> x := y
|
||||
> y := z
|
||||
> )
|
||||
|
||||
--For-Each--
|
||||
|
||||
To iterate over all elements in a [[type:list]] the @for each@ construct
|
||||
|
||||
> for each variable in list do expression
|
||||
|
||||
If list is a list of items, for example set.cards, the expression is evaluated for each item in that list.
|
||||
The variable becomes set to that each item in succession.
|
||||
The results of the expression are combined using the @+@ [[script:operators|operator]].
|
||||
|
||||
It is also possible to iterate over a range of values
|
||||
|
||||
> for variable from begin to end do expression
|
||||
|
||||
The expression is evaluated for each number from begin to end (including begin, not including end). The variable becomes set to that each number in succession. The results of the expression are combined using +.
|
||||
|
||||
--Summary--
|
||||
|
||||
! Syntax Description
|
||||
| @if a then b else c@ If @a@ is @true@ evaluates to @b@, otherwise evaluates to @c@
|
||||
| @for each x in list do something@ Does something for each element in a list
|
||||
| @for x from 1 to 100 do something@ Does something for all numbers from 1 to 100
|
||||
+11
-1
@@ -1,4 +1,14 @@
|
||||
Scripting language
|
||||
|
||||
MSE uses a custom scripting language to add complicated behaviour to fields and styles.
|
||||
MSE uses a custom scripting language to add complicated behaviour to [[type:field]]s and [[type:style]]s.
|
||||
|
||||
--Topics--
|
||||
* [[script:introduction|Introduction to scripting]]
|
||||
* [[script:Operators]]
|
||||
* [[script:variables|Variables and functions]]
|
||||
* [[script:Control structures]]
|
||||
* [[script:Predefined variables]]
|
||||
* [[script:Best practices]]
|
||||
See also:
|
||||
* [[type:index|Data types used]]
|
||||
* [[fun:index|Built in functions]]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
Scripting introduction
|
||||
|
||||
MSE uses a custom scripting language to add complicated behaviour to [[type:field]]s and [[type:style]]s.
|
||||
|
||||
This documentation assumes that the reader has some programming experience.
|
||||
In particular it is assumed that you know what 'functions', 'variables' and 'operators' are.
|
||||
|
||||
Scripts in MSE are usually small, just a few lines.
|
||||
A piece of script code is also called an ''expression''.
|
||||
Expressions can be ''evaluated'' to give a result.
|
||||
For example the expression @1 + 1@ will evaluate to @2@.
|
||||
|
||||
An important idea behind MSE is that it tries to be ''pure'',
|
||||
there are no global variables and there is no way to change properties of objects.
|
||||
This means that when you evaluate an expression the result is always the same (unless it contains variable assignments).
|
||||
|
||||
Basic elements of scripts are ''constants''.
|
||||
A constant is something with a fixed value.
|
||||
Possible constants are:
|
||||
! Syntax Type
|
||||
| @nil@ Nothing
|
||||
| @true@, @false@ [[type:boolean]]
|
||||
| @123@ [[type:int]]
|
||||
| @123.5@ [[type:double]]
|
||||
| @"abc"@ [[type:string]]
|
||||
| @rgb(255,0,0)@ [[type:color]]
|
||||
| @[a,b,c]@ [[type:list]]
|
||||
Note: Strings, list and colors don't have to be constants, they can contain other code.
|
||||
|
||||
The @nil@ value has a special meaning, it is 'nothing'.
|
||||
Adding @nil@ to something has no effect (it returns 'something'), calling @nil@ as a function just returns the input, etc.
|
||||
|
||||
<div style="text-align:right;">next: <a href="operators">Operators →</a></div>
|
||||
@@ -1,2 +1,102 @@
|
||||
Operators
|
||||
|
||||
To get more complicated expressions you combine them using operators.
|
||||
|
||||
--Basic mathematics--
|
||||
MSE script supports most basic mathamatical operators:
|
||||
! Operator Example Description
|
||||
| @a + b@ @3 + 2 == 5@<br/> Add two numbers,
|
||||
@"3" + "2" == "32"@ concatenate two strings or compose two functions (see below)
|
||||
| @a - b@ @3 - 2 == 1@ Substract two numbers
|
||||
| @a * b@ @3 * 2 == 6@ Multiply two numbers
|
||||
| @a / b@ @3 / 2 == 1@<br/> Divide two numbers.<br/> Rounds towards zero for [[type:int]]s.<br/>
|
||||
@3 / 2.0 == 1.5@ Does not round for [[type:double]]s.
|
||||
| @a mod b@ @3 mod 2 == 1@ Take the remainder after integer division (modulo)
|
||||
| @-a@ @-(3 + 2) == -5@ Negate a number (make it negative if positive and vice versa)
|
||||
|
||||
===The + operator===
|
||||
The @+@ operator has three functions
|
||||
* It adds [[type:int]]s (also [[type:double]]s), @1+1 == 2@
|
||||
* It concatenates strings, @"1" + "1" == "11"@
|
||||
* It composes [[type:function]]s @(f + g) () == g( input: f() )@
|
||||
|
||||
--Comparison--
|
||||
It is also possible to compare values. All comparisons evaluate to either @true@ or @false@.
|
||||
! Operator Example Description
|
||||
| @a == b@ @1 + 1 == 2@<br/>
|
||||
@"x" == "x"@<br/>
|
||||
@1 + 1 == "2"@ Are two numbers or strings the same? @=@ can also be used instead.
|
||||
| @a != b@ @1 + 1 != 3@<br/>
|
||||
@"x" != "y"@ Are two numbers or strings different?
|
||||
| @a < b@ @1 < 2@<br/>
|
||||
@"x" < "y"@ Is a less than b? Uses [[http://en.wikipedia.org/wiki/Lexicographical_order|lexicographic order]] for strings.
|
||||
| @a > b@ @2 > 1@<br/>
|
||||
@"y" > "x"@ Is a greater than b?
|
||||
| @a <= b@ @1 <= 1@<br/>
|
||||
@"x" <= "y"@ Is a less than b or are they equal?
|
||||
| @a >= b@ @2 >= 1@<br/>
|
||||
@"x" >= "x"@ Is a greater than b or are they equal?
|
||||
|
||||
--Booleans--
|
||||
[[type:Boolean]]s (for example from comparisons) can be combined using:
|
||||
| @a and b@ Are both @a@ and @b@ true?
|
||||
| @a or b@ Is at least one of @a@ and @b@ true?
|
||||
| @a xor b@ Is exactly one of @a@ and @b@ true?
|
||||
| @not a@ Is @a@ false?
|
||||
In a table:
|
||||
! @a@ @b@ <tt>a or b</tt> <tt>a and b</tt> <tt>a xor b</tt>
|
||||
| @false@ @false@ @false@ @false@ @false@
|
||||
| @false@ @true@ @true@ @false@ @true@
|
||||
| @true@ @false@ @true@ @false@ @true@
|
||||
| @true@ @true@ @true@ @true@ @false@
|
||||
|
||||
--Grouping and order--
|
||||
|
||||
Operators are ordered as usual, so
|
||||
> 1 + 2 * 3 == 1 + (2 * 3) == 7
|
||||
Operators can be grouped differently using parentheses.
|
||||
> (1 + 2) * 3 == 3 * 3 == 9
|
||||
|
||||
The exact order of precedence is given in the following table,
|
||||
higher in the table means that this operator binds tighter to its arguments, @*@ binds tighter then @+@.
|
||||
| @a(...)@, @a.b@, @a[b]@ Function calls, property access, see below
|
||||
| @-a@, @not@ Unary operators
|
||||
| @*@, @/@, @mod@ Multiplication and division
|
||||
| @+@, @-@ Addition and substraction
|
||||
| @==@, @!=@, @<@, @>@, @<=@, @>=@ Comparisons
|
||||
| @and@, @or@, @xor@ Boolean operators
|
||||
| @:=@ Assignement, see below
|
||||
| @;@ Sequence, see below
|
||||
|
||||
--Properties--
|
||||
Properties of types, as described in the [[type:index|data type section]] of the documentation, can be accessed using the @.@ operator:
|
||||
> set.cards # retrieve the 'cards' property of a set
|
||||
The @[]@ operator has a similair purpose, only the property retrieved is determined by a string, so it can be changed:
|
||||
> set["cards"] # same as above
|
||||
> c := "cards"
|
||||
> set[c] # again, the same
|
||||
|
||||
Multiple uses of these operators can be combined, for example:
|
||||
> set.cards[0].card_color # the card color of the first card in the set
|
||||
|
||||
Note that a property named @card color@ is refered to as @card_color@ when using the @.@ operator, all spaces become underscores.
|
||||
> style.padding left # syntax error
|
||||
> style.padding_left # use this instead
|
||||
|
||||
--Assignment and sequence--
|
||||
Values can be assigned to [[script:variables]] using the @:=@ operator:
|
||||
> variable := 1 + 1
|
||||
The result of this expression is the value assigned, this can be used to assign to multiple variables:
|
||||
> var1 := var2 := 1 + 1
|
||||
> # now var1 == 2 and var2 == 2
|
||||
|
||||
To combine multiple assignments into a single expression the ''sequencing operator'', @;@ can be used.
|
||||
This first executes an expression, discards the result and then evaluates another one:
|
||||
> var1 := 1 + 1 # assign
|
||||
> ; # discard the result (i.e. 2)
|
||||
> var1 * 2 # retrieve the value again, returns 4
|
||||
Semicolons at the end of a line can be omitted, so the above can also be written simply as:
|
||||
> var1 := 1 + 1
|
||||
> var1 * 2
|
||||
|
||||
<div style="text-align:right;">next: <a href="variables">Variables and functions →</a></div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
Predefined variables
|
||||
|
||||
When evaluating scripts several variables are predefined containing useful information.
|
||||
Aside from the [[fun:index|built in functions]] the following variables are provided:
|
||||
! Variable Type When available? Description
|
||||
| @game@ [[type:game]] always The current game, the same as @set.game@
|
||||
| @set@ [[type:set]] always The current set.
|
||||
| @stylesheet@ [[type:stylesheet]] not in the @init script@ of the game
|
||||
The current stylesheet
|
||||
| @card@ [[type:card]] not in @init script@s or when exporting
|
||||
The current card.
|
||||
| @card_style@ [[type:indexmap]] of [[type:style]]s where @card@ is available Style properties for the current card, the same as @stylesheet.card_style@.
|
||||
| @styling@ [[type:indexmap]] of [[type:value]]s where @card@ is available Styling options for the stylesheet/card.
|
||||
| @value@ [[type:value]] when evaluating a [[type:field]]'s @script@ or @default@ script Current value in the field.
|
||||
| @options@ [[type:indexmap]] of [[type:value]]s when exporting Options of the [[type:export template]].
|
||||
Variables are only available where they make sense.
|
||||
@@ -0,0 +1,17 @@
|
||||
Rule form
|
||||
|
||||
Some functions are available in ''rule form''.
|
||||
|
||||
These rule form functions are functions that create a new [[type:function]].
|
||||
That new function, the rule, applies some transformation to the input and returns the result.
|
||||
|
||||
A rule is like a normal function with all parameters given, except for the @input@.
|
||||
|
||||
Rules are often combined using the + operator, for example:
|
||||
> # First all "a"s are replaced, then all "b"s.
|
||||
> remove_as_and_bs := replace_rule(match: "a", replace: "") +
|
||||
> replace_rule(match: "b", replace: "")
|
||||
>
|
||||
> text_with_as_and_bs := "bla bla bla"
|
||||
> text_without_as_and_bs := remove_as_and_bs(text_with_as_and_bs)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
Variables and functions
|
||||
|
||||
--Variables--
|
||||
MSE script has the notion of ''variables''.
|
||||
A variable is a name holding a value, assigned using the @:=@ operator:
|
||||
> variable := 1 + 1
|
||||
From now on (until another value is assigned) @variable@ evaluates to @2@ in the rest of the script.
|
||||
|
||||
--Functions--
|
||||
It is possible to define your own [[type:function]]s in MSE script.
|
||||
The syntax for this is very simple, code in curly braces defines a function:
|
||||
> { code goes here }
|
||||
|
||||
To be able to refer to the function it is usually assigned to a variable:
|
||||
> function := { code goes here }
|
||||
|
||||
Calling a function is done using parentheses:
|
||||
> { code }()
|
||||
> function()
|
||||
this has the effect of evaluating the code inside the curly braces.
|
||||
|
||||
--Scope--
|
||||
|
||||
Assignments to variables are ''local'' to the current function.
|
||||
Consider:
|
||||
> function := {
|
||||
> x := "something else"
|
||||
> # here x is something else
|
||||
> }
|
||||
> x := 1
|
||||
> function()
|
||||
> # here x is still 1
|
||||
|
||||
Unlike most programming languages MSE script uses [[http://en.wikipedia.org/wiki/Dynamic_scoping#Dynamic_scoping|dynamic scoping]].
|
||||
This means that assignments done in the calling function are visible in the called function:
|
||||
> fun := { "xyz is {xyz}" }
|
||||
> one := {
|
||||
> xyz := 1
|
||||
> fun()
|
||||
> }
|
||||
> two := {
|
||||
> xyz := "two"
|
||||
> fun()
|
||||
> }
|
||||
> one() == "xyz is 1"
|
||||
> two() == "xyz is two"
|
||||
|
||||
--Parameters--
|
||||
The scoping can be used to pass parameters to functions as shown above.
|
||||
To make this easier, parameters can be specified inside the parentheses of the function call:
|
||||
> fun(xyz: "a parameter") == "xyz is a parameter"
|
||||
These assignments are ''only'' visible to the called function.
|
||||
> xyz := "outside"
|
||||
> fun(xyz: "a parameter")
|
||||
> # xyz is still "outside"
|
||||
|
||||
The syntax for parameters is @name: value@.
|
||||
Multiple parameters are separated by commas.
|
||||
|
||||
The special syntax @value@ (without a name) means the variable @input@ is used:
|
||||
> fun := { input + var2 }
|
||||
> fun(var2: "yes", "no") == "noyes"
|
||||
|
||||
--Overriding functions--
|
||||
Like custom functions, the [[fun:index|built in functions]] are also stored in variables.
|
||||
It is possible to overwrite them:
|
||||
> to_upper := { input }
|
||||
> to_upper("xyz") == "xyz" # Not what it used to do
|
||||
|
||||
A neat trick is adding 'extra' behaviour to functions.
|
||||
This can be done by first making a copy, and calling that:
|
||||
> real_to_upper := to_upper
|
||||
> to_upper := { "upper case: " + real_to_upper() }
|
||||
> to_upper("xyz") == "upper case: XYZ"
|
||||
Note that @real_to_upper@ is called without extra parameters, the @input@ variable is still set from the outer call to the new @to_upper@ itself.
|
||||
|
||||
<div style="text-align:right;">next: <a href="control_structures">Control structures →</a></div>
|
||||
Reference in New Issue
Block a user