-------------------------------------------------------------------------------- == Spell language * Comments: One-line comments using '#' or '//' as prefix * names vs. invocations: spells and anchors have both names and invocations. Names are used to refer to spells and anchors from within scripts without revealing their invocations. Each spell specification file consists of a sequence of definitions of globals, anchors, spells, and procedures. Types: ------ These are the primitive types: * int * string * dir (a direction: N, S, SE, NW etc.) * location (a single spot in the world) * entity (a PC, NPC, or monster) * area (a set of locations) * spell (a spell) * invocation (a spell instance) * fail (a separate type that arises implicitly when a function fails) `fail' arises in special circumstances in some functions, e.g. when trying to divide by zero, or when function parameters fail to type-check. When a fail value flows into an operation, than that operation is skipped. When a fail value flows into a function, then that function evaluates to fail, except for * if_then_else (which permits `fail' in its true or false branch) * failed (which returns true iff the argument is of type fail). We will use standard functional type notation: `int * string -> entity' denotes the type of a function that accepts an integer and a string and returns an entity. We use the return `()' for operations (which return nothing). Globals: -------- A `global' is a global variable, declared and defined in the same line: foo = "this is a string"; Expressions can be arbitrary language expressions. Note that globals can only reference the values of globals defined earlier in the same program. There are two special globals: * min_casttime : int Minimal number of milliseconds of cast delay, no matter what any given spell may say. Cast delay is the time before the next spell can be cast. * obscure : int Chance of a character of a given spell to be obscured (masked out by an asterisk). Globals can also be defined as CONST (though this should not be done for the special globals listed above or they will not take effect.) CONST-defined globals cannot be re-defined. Expressions: ------------ Expressions occur in globals, anchors, spells, and procedures. Expressions evaluate to values of a given type. Expressions can be simple literals, area literals, infix expressions, function applications, or variable refecences. - Simple literals: * 42, 0xff: int literals * N, S, E, W, NE, NW, SE, SW : dir literals * "foo bar" : string literals - Area literals: * @("new_3-1.gat", 26, 26) This area denotes a single field on a map (new_3-1.gat, co-ordinates (26,26). * @("new_3-1.gat", 26, 26) @+ (10, 10) This area is 100 fields, stretching to a field of size 10x10 to the south and east of (26,26) in new_3-1.gat. * @("new_3-1.gat", 26, 26) towards S (5, 3) This area is a rectangular field of depth 3 and width 5 to the south of (26,26) in new_3-1.gat. `depth 3' here means that it stretches three fields to the south. `width 5' here means that it extends five fields to the east and five to the west of the specified coordinate, for a total width of 11. The only directions supported here are S, E, N, W. - Infix expressions: Infix expressions are special functions in which the operator is written between two expressions. Infix expressions largely follow the C precedence rules. The following infix operators are supported: * addition (+) (operates on ints, concatenates strings, and constructs the union of areas) * subtraction (- : int * int -> int) * multiplication (* : int * int -> int) * division (/ : int * int -> int, may fail) * modulo (% : int * int -> int, may fail) * comparison (<, >, >=, <=, =, <> : int * int -> int) Comparison operators generally work on ints and strings. (In)equality checks work on all values except for areas. Note that "==" and "!=" are available as alternatives for "=" and "<>" (respectively). * conjunction and disjunction (&&, || : int * int -> int). Note that these are not short-circuit. * Bitwise or (| : int * int -> int) * Bitwise and (& : int * int -> int) * Bitwise xor (^ : int * int -> int) * Shift left and right (<<, >> : int * int -> int) - Function applications: Function applications are written as f(arg1, ..., argn) where each `argi' is an arbitrary expression. For a complete list of Functions, see `functions' below. - Variable references: The expression (foo + 1) references a variable `foo' which must have been previously defined. Anchors: -------- Anchors are teleport anchors; spells can look them up by name. Each teleport anchor is written as TELEPORT-ANCHOR : = For example: TELEPORT-ANCHOR tulimshar : "home" = @("new_3-1.gat", 26, 26) @+(10, 10) This maps the teleport anchor `tulimshar' to an area in new_3-1.gat binds it to the name "home". The function `anchor' can look up teleport anchors (cf. the function library). Spells: ------- Each spell is written either as [spell-modifiers] SPELL : = or as [spell-modifiers] SPELL ( : ) : = For example, SPELL random_dance : "zxr" = ... creates a spell `random_dance' and makes it available under the invocation "zxr". The string `...' is not a valid ; we will look at proper spelldefs below. An alternative example illustrates the use of parameters: SPELL shout (message : STRING) : "zzx" = ... This defines a spell `shout' (bound to invocation "zzx") that takes a parameter `message' of type STRING. Valid types are STRING and PC: PC parameters are automatically translated into a player character entity of that name, or mapped to the caster if the specified name does not map to a player character or is missing. The list of spell modifiers is short: * SILENT means that the spell's invocation will never be broadcast, not even obscured. * LOCAL means that the spell is bound to the location it was cast at and will not `travel' with the caster. * NONMAGIC means that the spell is not affected by the caster's ability to perform magic. Typically used for special (quest) keywords. Modifiers may be given in any order, but only once. - Spell bodies Spell bodies consist of three parts: LET bindings, spell guards and effects. LET bindings locally bind names to values. Spell guards are constraints that must be satisfied for the spell to be triggered. Effects describes what happens if the spell is successfully triggered. Spells may have multiple guards and effects. Consider the following example: SPELL plugh (message : STRING) : "zzx" = LET x = "kappa" y = "sigma" IN (MANA 1, CATALYSTS ["Pearl"]) => EFFECT message (caster, "First branch"); | (MANA 20) => EFFECT message (caster, "Second branch") This defines a spell `plugh' with two let bindings (`x' bound to "kappa" and `y' bound to "sigma") and two `branches' which are tested in sequence. The first branch tests whether the caster has one spellpoint and owns a pearl-- if so, the effect of sending a message "First branch" to the caster is triggered. However, if the spell guard is not satisfied, the magic engine examines the second branch. Now, if the caster has 20 spellpoints, we INSTEAD trigger the effect of sending the message "Second branch" to the caster. - Spell guards Spell guards can be omitted; in that case, just use the effect itself. Otherwise they can be any of the following: * MANA x: Require x spellpoints to be present. If this spellguard is taken, x mana will be consumed implicitly. This requirement is cumulative. * CASTTIME x: Require that the caster spend x milliseconds until the next spell can be cast. This requirement is cumulative. If the total casttime for a spell is less than the global variable min_casttime, then the latter supercedes the specified spell cast delay. * REQUIRE : Test that the specified expression evaluates to nonzero and does not fail. Requirements are cumulative. * CATALYSTS : Ensure that the caster possesses all specified items. This effect is cumulative. * COMPONENTS : Ensure that the caster possesses all specified items. If the branch suceeeds, all items specified here are consumed. This effect is cumulative. Items can be specified as follows: * [ 700, 701 ] -- require one item of item ID 700 and one of 701. * [ 700, 3 * 701 ] -- require 1 item of item ID 700 and 3 of 701. * [ "Pearl" ] -- require one item named `Pearl' in the item DB. Spell guards can be combined as follows: * `or': disjunction. The first matching path is taken. * (a, ..., n): conjunction. a, n, and everything in between must be satisfied. * a => b: Implication. If `a' is satisfied, try to satisfy `b'. This operation is useful to combine different branches (see below.) Different branches of spell effects are separated by the vertical bar `|'. For example, SPELL plugh (message : STRING) : "zzx" = MANA 5 => ( (CATALYSTS ["Pearl"]) => EFFECT message (caster, "First branch"); | (MANA 20) => EFFECT message (caster, "Second branch");) will always try to deduct 5 Mana points but then make a choice of whether to go to the first branch (if the player has `Pearl') or to the second (if the player does not have `Pear' but has another 20 spell points, for a total of 25 spell points, all of which will be consumed in that case.) - Effects Effects describe what happens when a spell is triggered. Each spell has at least one EFFECT specification, which looks as follows: EFFECT [ ATTRIGGER ] [ ATEND ] The three parts are as follows: * EFFECT: All effects here are executed as soon as the spell is triggered. * ATEND: All steps described here are executed when the spell finishes. Note that the spell may remain active after all steps from the EFFECT section have been taken; this happens when the spell triggers a status change (using the `status_change' operation, as described in the operations library). In that case the spell will terminate only after all status changes have finished. The ATEND section is not called when the spell finishes due to the caster dying. * ATTRIGGER: This section is used only for the `override_attack' operation and described there. Before effects are executed, the engine defines the following variables: * The parameter (if any) * caster : entity (the caster of this spell) * spellpower : int (the caster's spellpower, normally 6 -- 198) * location : location (the location the spell is currently at) * self_spell : spell (the current spell) * self_invocation : invocation (the current spell instance) The engine can then execute the following effects from the effect list: * SKIP; # a no-op * ABORT; # Abort the spell, don't run ATEND, don't consume a # trigger charge (cf. `override_attack') * END; # Skip to the ATEND block * WAIT ; # Wait milliseconds before continuing * = ; # Set to the result of evaluating * ( ... ) # Execute statements through in sequence * IF # Test condition . If nonzero, THEN # execute . Otherwise, ELSE # execute . # The `ELSE' branch can be omitted. * FOREACH IN DO # Evaluate to an area, find all entities in # the area that match , randomise this list, # bind to each in turn and execute . Example: FOREACH ENTITY t IN rbox(location(caster), 20) DO aggravate(t, 0, caster); # This aggravates all entities within 20 paces of # the caster to attack the caster. Valid values for are + ENTITY : PC or mob + PC + MOB + TARGET : mob, PC (but only if we are on a PvP map) * FOR = TO DO # This will iterate from to (inclusively), bind # to the current iteration value, and execute . * BREAK; # This will break out of the current FOR loop, FOREACH loop, or # procedure. * ( , ..., ); # This executes an operation. See `Operations', below, for a # list. * CALL ( , ..., ); # This will execute a procedure, after binding the actual # parameters from to to the formal procedure # parameters. * { ... } # This executes arbitrary eAthena script code. The following # variables script variables are bound implicitly: # - @caster_name$ is the name of the spellcaster # - @caster and @target are also bound, to useless values (sorry.) # # By default, script popup boxes are sent to the caster. This can # be overridden by setting the special `script_target' variable. Procedures: ----------- Procedures are defined as PROCEDURE ( , ... , ) = For example, PROCEDURE testproc(x) = message(caster, "foo(" + x + )"); y = 10; x = 20; defines a procedure `testproc' with one formal parameter `x'. Procedure execution is nonrecursive and uses dynamic scoping. The latter means that it can modify variables in the caller's scope. In the above example, the assignment to `y' will be visible to the caller. The assignment to `x' however will be not be visible, since that assignment goes to the parameter and is therefore limited in scope to `testproc'. More precisely, EFFECT x = 0; y = 0; testproc(1); message(caster, "x=" + x + ", y=" + y); would print foo(1) x=0, y=10 (note how the update to x is isolated from the caller.) Functions: ---------- This section documents the function API. The following functions are available: + max : int * int -> int Pick the greater of two values. + min : int * int -> int Lesser of two values. + is_in : location * area -> bool Test whether a location is within an area. + if_then_else : bool * 'a * 'a -> 'a Test a condition (first parameter). If the contition is nonzero, return the second parameter, otherwise the third parameter. + skill : entity * int -> int Get the skill level that the `entity' has for the skill id. + dex : entity -> int + agi : entity -> int + int : entity -> int + vit : entity -> int + str : entity -> int + luk : entity -> int + hp : entity -> int + sp : entity -> int + def : entity -> int + mdef : entity -> int + max_hp : entity -> int + max_sp : entity -> int + level : entity -> int Status attributes. + dir : entity -> dir Direction that the entity is currently facing. + not : int -> int Logical negation. (NOT bitwise negation.) + neg : int -> int Bitwise negation. + name_of : entity -> string | spell -> string Retrieves the name either of an entity or of a spell. + location : entity -> location Determines the location that the specified entity presently occupies. + random : int -> int random(6) yields a random value from 0 to 5. + random_dir : int -> dir random_dir(0) yields N, S, E, or W. random_dir(1) yields N, S, E, W, SE, SW, NE, or NW. + hash_entity : entity -> int Retrieve a number idenfying the entity. + is_married : entity -> int Tests whether the entity is married. + partner : entity -> entity Retrieves the entity's partner, if online, or fails otherwise. + awayfrom : location * dir * int -> location awayfrom(loc, dir, distance) returns a location obtained by moving at most `distance' towards `dir', starting at `loc'. If the move hits an obstacle, we stop before the obstacle. + failed : 'a -> bool True iff the input was the special failure value. + pc : string -> entity Looks up a player character by name. Fails if there is no match. + npc : string -> entity Looks up an NPC by name. Fails if there is no match. + distance : location * location -> int This is the `fake square distance': The maximum of delta x and delta y between the two locations. + rdistance : location * location -> int This is the `real' distance (square root of the square of dx, dy) + anchor : string -> area Looks up a teleport anchor by name and returns the associated area. Fails if the result is not an area. + random_location : area -> location Pick a random location from within an area. + script_int : entity * string -> int Read a player script variable as an integer. + rbox : location * int -> area rbox(l, n) computes rectangular box centered at `l', with a `radius' of n. The box contains (n*2 + 1)^2 squares. + count_item : entity * int -> int | entity * string -> int Counts the number of instances of the specified item that the entity has. Items may be given by ID or by name. + line_of_sight : location * location -> int Determines whether there is a line-of-sight connection between the two locations. + running_status_update : entity * int -> bool Determines whether the specified status update is still active. + element : entity -> int Determines what element the entity is associated with + element_level : entity -> int Determines what element level the entity has + has_shroud : entity -> int Determines whether the player is presently shrouded (i.e., whether the player's name is being obscured.) + is_equipped : entity * int -> int : entity * string -> int Determines whether the player has equipped the specified item + spell_index : spell -> int Determines a unique index assigned to each spell + is_exterior : location -> bool Determines whether the location is under an open sky + contains_string : string * string -> bool contains_string(a, b) determines whether the string `a' contains the string `b' as a substring. + strstr : string * string -> bool strstr(a, b) returns the offset of the first instance of the string `b' in the string `a', or fails if there is none. The offset is reported with a base of zero, i.e., strstr("xyz", "x") = 0. + strlen : string -> int Compute the length of a string, in characters. + substr : string * int * int -> string substr(s, offset, len) extracts a substring of the length `len' at offset `offset'. The substring is automatically clipped, i.e., the function will never fail. + sqrt : int -> int Computes the square root of the specified number + map_level : location -> int Determines the map level: 0 for outside, 1 for inside, 2ff for dungeon levels (going down) + map_nr : location -> int Computes the map number. Map number and map level together uniquely identify a map. + dir_towards : location * location * int -> dir dir_towards(start, end, flag) computes the direction from `start' to `end'. If flag is zero, directions are limited to N, S, E, W; otherwise NE, SE, NW, SW will also be used. The two locations must be on the same map. + is_dead : entity -> bool Determines whether the specified entity is no longer alive. + extract_healer_experience : entity * int -> int Extracts `healer experience points' from the specified entity. Non-PCs always have an empty pool of healer exprerience points. PCs gain such experience points by killing/completing quests, though this `healer experience point pool' slowly drains itself. extract_healer_experience(pc, xp) extracts up to `xp' points. + is_pc : entity -> bool Determines whether the target is a player character Operations: ----------- This section documents the operations API. + sfx : entity * int * int -> () | location * int *int -> () Trigger a special effect (specified by sfx ID) for an entity or a location. The int specifies a delay until the effect is issued. + itemheal : entity * int * int -> () itemheal(entity, hp, sp) triggers item healing. This will hopefully be slowed down if an appropriate server patch is installed. + instaheal : entity * int * int -> () itemheal(entity, hp, sp) heals instantly. + shroud : entity * int -> () shroud(entity, flags) hides the entity's name (only for PCs). Flags: 0x01: Hide PC'ss name when talking 0x02: Shroud vanishes when player picks something up 0x04: Shroud vanishes when player talks The shroud will not affect players in visible range until the entity has left and re-entered their field of vision. This can be enforced by warping. + unshroud : entity -> () Counter a shroud. + message : entity * string -> () Send a message to the entity. + messenger_npc : location * int * string * string * int -> () messenger_npc(location, image, npc_name, message, duration) creates a messenger NPC looking like the `image' at `location', with name `npc_name' and delivering the `message' when clicked upon. The NPC disappears after the `duration' in ms has expired. + move : entity * dir -> () Move the entity into the specified direction, unless that direction is blocked. + warp : entity * location -> () Warp entity to specified location. + spawn : area * entity * int * int * int * int -> () spawn(area, owner, mob_id, attitude, count, lifetime) spawns for a limited `lifetime' (in ms) a total of `count' monsters of kind `mob_id' in `area'. `attitude' specifies how they behave: 0 : attack everyone 1 : be friendly 2 : attack enemies of `owner' and give XP to `owner' if successful + banish : entity -> () If the entity was spawned by the `spawn' operation, eliminate it. + status_change : entity * int * int * int * int * int * int -> () status_change(entity, status, v1, v2, v3, v4, duration) initiates a status change. The precise status change (and the meaning of `v1', `v2', `v3', `v4') varies depending on `status'. This operation may delay spell termination (and the ATEND effect). ATEND can therefore be used to notify the caster that the status change has finished. + stop_status_change : entity * int Stops a status change + override_attack : entity * int * int * int * int *int * int -> () override_attack(entity, charges, delay, range, icon, animation, stop) overrides the entity's current attack (only for PCs). The entity will have `charges' attacks that instead result in calling the ATTRIGGER effect for this spell. When this operation is called, the spell environment (variables etc.) is cloned into a separate invocation. This special invocation will be triggered every time the entity tries to attack, until they have run out of charages (at which time the previous behaviour is restored.) `delay' specifies the attack delay. `range' specifies the attack range as shown in the client GUI. `icon' is the ID of a status-effect ID that will be displayed while the spell is in effect. `animation' is the attack animation ID that should be used. `stop' decides whether attacking should stop once the spell is out of charges (1) or continue (0). (Note: this doesn't properly work, possibly due to client issues. Always use 0.) Note that if the ATTRIGGER effect ABORTs, no charge will be consumed. ATTRIGGER can refernece the player's target via the `target' variable. Example: SPELL tshuffle : "zvp" = (MANA 1, CATALYSTS ["Pearl"]) => EFFECT override_attack(caster, 3, 500, 10, 700, 31); ATTRIGGER IF (not (line_of_sight(location, location(target)))) THEN (message (caster, "No line of sight!"); ABORT;) FOR i = 0 TO 10 DO (move(target, random_dir(0));); This overrides the caster's attack for three attacks (attack delay 500, range 10) to instead randomly move around any entity that the player tries to attack by up to 11 paces. + create_item : entity * int * int -> () | entity * string * int -> () create_item(entity, item, count) gives the `entity' `count' instances of `item'. As usual, `item' can be either a string name or an item ID. + aggravate : entity * int * entity -> () aggravate (victim, mode, target) causes the `victim' to mode = 0: attack `target' mode = 1: become universally permanently aggressive mode = 2: both of the above + injure : entity * entity * int * int -> () injure(attacker, defender, hp, sp) causes damage to the defender from the attacker (possibly killing, giving XP etc.), unless the defender is immortal (NPC) or a PC on a non-PvP map. + emote : entity * int -> () Issues the specified emotion to the specified entity. + set_script_variable : entity * string * int -> () Sets a script variable to the specified value + set_hair_colour : entity * int -> () Sets the hair colour of the specified entity to the specified value (must be a PC). + set_hair_style : entity * int -> () Adjusts the hair style of a PC. + drop_item : location * (int | string) * int * int -> () drop_item(place, "name", count, duration) drops `count' items (where count may be zero) of name "name" at `place'. The items vanish again after `duration'. + drop_item_for : location * (int | string) * int * int * entity * int -> () drop_item_for(place, obj, count, duration, owner, delay) works like drop_item(place, obj, count, duration), except that the item can only be picked up by `owner' for the next `delay' milliseconds, modulo pickup rules (spousal pickup, out-of-range). + gain_experience : entity * int * int * int -> () gain_experience(player, base_xp, job_xp, reason) gives expereince points to `player'. If reason is 0, they are handed out normally, but if reason is 1, then those experience points are NOT added to the pool of experience points that healers can draw from `player'. Script API updates: ------------------- Two new script API functions are available: * getspellinvocation : string -> string Looks up a spell by spell ID and returns the spell's invocation. * getanchorinvocation : string -> string Looks up a teleport anchor by anchor ID and returns the invocation. Syntax Reference: ----------------- SPELLCONF ::= (GLOBAL | ANCHOR | SPELL | PROCEDURE | ';')* (* The ';' are only for decorative purposes *) VALUE ::= | | | | (* one of {N, S, E, W, NE, SE, NW, SW} *) EXPR ::= (VALUE) | (AREA) | (EXPR) '+' (EXPR) | (EXPR) '*' (EXPR) | (EXPR) '-' (EXPR) | (EXPR) '/' (EXPR) | (EXPR) '%' (EXPR) | (EXPR) '<' (EXPR) | (EXPR) '>' (EXPR) | (EXPR) '<>' (EXPR) | (EXPR) '=' (EXPR) | (EXPR) '!=' (EXPR) | (EXPR) '==' (EXPR) | (EXPR) '<=' (EXPR) | (EXPR) '>=' (EXPR) | (EXPR) '&&' (EXPR) | (EXPR) '||' (EXPR) | (EXPR) '|' (EXPR) | (EXPR) '^' (EXPR) (* XOR *) | (EXPR) '&' (EXPR) (* binary AND *) | (EXPR) '<<' (EXPR) | (EXPR) '>>' (EXPR) | '(' ((EXPR) /* ',') ')' | | '(' (EXPR) ')' INVOCATION ::= (* used for convenience *) LOCATION ::= '@' '(' (EXPR) ',' (EXPR) ',' (EXPR) ')' (* *) AREA ::= (LOCATION) | (LOCATION) '@' '+' '(' (EXPR) ',' (EXPR) ')' (* width and height around location, passable only *) | (LOCATION) 'towards' (EXPR) ':' '(' (EXPR) ',' (EXPR) ')' (* towards dir: Bar-shaped, width and depth; only NSEW supported *) ITEMS ::= '[' (ITEMC) /+ ',' ']' ITEMC ::= (ITEM) | '*' (ITEM) ITEM ::= | * global GLOBAL ::= 'CONST'? '=' (EXPR) ';' Available globals: - min_casttime : int (* min # of ticks for each spell (BEFORE scaling according to battle.conf) *) - obscure : int (* obscure percentage *) * teleport-anchor ANCHOR ::= 'TELEPORT-ANCHOR' ':' (INVOCATION) '=' (EXPR) For example, TELEPORT-ANCHOR t : "tulimshar" = @("map_3-1.gat", 44, 70) creates a teleport anchor with name `t' and invocation `tulimshar' at the speicfied location. * spell SPELL ::= spellmod 'SPELL' (ARG)? ':' (INVOCATION) '=' (SPELLDEF) SPELLMOD ::= ('SILENT' | 'LOCAL')* (silent: invocation silently dropped. local: `location' variable doesn't change after spell init.) ARG ::= '(' ':' (ARGTYPE) ')" ARGTYPE ::= 'PC' (* yields a pc, or self *) | 'STRING' SPELLDEF ::= ((SPELLBODY) /* '|') | 'LET' (DECL)* 'IN' ((SPELLBODY) /* '|') SPELLBODY ::= (SPELLGUARD) '=>' (SPELLBODY) | 'EFFECT' (EFFECT) ('ATTRIGGER' (EFFECT))? ('ATEND' (EFFECT))? (* `ATTRIGGER' is executed as a separate effect if the spell is `triggered'. If the trigger has a target, it is denoted * in the variable `target'. * `ATEND' is invoked for normal termination, i.e., after the spell is over or has been dispelled. `ATEND' is NOT used * for the triggered effect; instead, spawn a separate spell in ATTRIGGER if that is desired. *) DECL ::= '=' (EXPR) ';' SPELLGUARD ::= (PREREQ) | (SPELLGUARD) 'or' (SPELLGUARD) | '(' (SPELLGUARD) /+ ',' ')' PREREQ ::= 'REQUIRE' (EXPR) | 'CATALYSTS' (ITEMS) | 'COMPONENTS' (ITEMS) | 'MANA' (EXPR) | 'CASTTIME' (EXPR) (* # of ticks until the next spell can be cast *0 EFFECT ::= EFFECT ';' EFFECT | '(' (EFFECT) ')' | 'SKIP' (* no-op *) | 'ABORT' (* abort spell. If used in a trigger, no charges will be consumed. *) | 'END' (* skip to atend *) | 'BREAK' (* break out of loop *) | '=' (EXPR) (* interpreted in the current context, eval'ds trictly *) | 'FOREACH' (SELECTION) 'IN' (AREA) 'DO' (EFFECT) (* randomises the subjects first *) | 'FOR' '=' (EXPR) 'TO' (EXPR) 'DO' (EFFECT) (* bounds are evaluated when the loop begins, no updates are respected *) | 'IF' (EXPR) 'THEN' (EFFECT) ('ELSE' (EFFECT))? | 'WAIT' (EXPR) (* amount in ticks before continuing. *) | '(' (EXPR) /* ',' ')' (* operation *) | 'CALL' '(' (EXPR) /* ',' ')' (* procedure call *) | '{' ... '}' (* script *) SELECTION ::= 'PC' | 'MOB' | 'ENTITY' (* MOB or PC *) | 'TARGET' (* like ENTITY, but includes PCs only on PvP maps *) | 'SPELL' | 'NPC' * procedures Procedures may be invoked via `CALL'. They use dynamic scoping. PROCEDURE ::= 'PROCEDURE' '(' /* ',' ')' '=' (EFFECT)