summaryrefslogtreecommitdiff
path: root/doc/spell-language
blob: 5f70f89a858b946fd242c415a64c4ff8d7e07e41 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
--------------------------------------------------------------------------------
== 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 <id> : <invocation-string> = <location>

  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 <id> : <name> = <spelldef>

  or as

    [spell-modifiers] SPELL <id> (<id> : <sty>) : <name> = <spelldef>

  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 <spelldef>; 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 <expr>:  Test that the specified expression evaluates to
    nonzero and does not fail.  Requirements are cumulative.
  * CATALYSTS <items>: Ensure that the caster possesses all specified
    items.  This effect is cumulative.
  * COMPONENTS <items>: 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 <effectlist> [ ATTRIGGER <effectlist> ] [ ATEND <effectlist> ]

  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 <expr>;    #  Wait <expr> milliseconds before continuing
  * <id> = <expr>;  #  Set <id> to the result of evaluating <expr>
  * (<s1> ... <sn>) #  Execute statements <s1> through <sn> in sequence
  * IF <c>	    #  Test condition <c>.  If nonzero,
    THEN <s1>	    #    execute <s1>.  Otherwise,
    ELSE <s2> 	    #    execute <s2>.
                    #    The `ELSE' branch can be omitted.

  * FOREACH <k> <id> IN <expr> DO <s>
                    # Evaluate <expr> to an area, find all entities in
		    #   the area that match <k>, randomise this list,
		    #   bind <id> to each in turn and execute <s>.

    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 <k> are
    + ENTITY : PC or mob
    + PC
    + MOB
    + TARGET : mob, PC (but only if we are on a PvP map)

  * FOR <id> = <start> TO <stop> DO <stmt>
    # This will iterate from <start> to <stop> (inclusively), bind
    # <id> to the current iteration value, and execute <stmt>.

  * BREAK;
    # This will break out of the current FOR loop, FOREACH loop, or
    # procedure.

  * <id> ( <expr1>,  ..., <exprn> );
    # This executes an operation.  See `Operations', below, for a
    # list.

  * CALL <id> ( <expr1>,  ..., <exprn> );
    # This will execute a procedure, after binding the actual
    # parameters from <expr1> to <exprn> 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 <id> ( <id>, ... , <id> ) = <effectlist>

  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 ::= <int>
          | <hexint>
          | <id>
	  | <string>
	  | <dir>	(* 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)
	 | <id> '(' ((EXPR) /* ',') ')'
	 | <id>
	 | '(' (EXPR) ')'

  INVOCATION ::= <string>  (* used for convenience *)

  LOCATION ::= '@' '(' (EXPR) ',' (EXPR) ',' (EXPR) ')'  (* <map name, x, y> *)

  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)
          | <int> '*' (ITEM)

  ITEM  ::= <int>
          | <id>

* global

  GLOBAL ::= 'CONST'? <id> '=' (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' <id> ':' (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' <id> (ARG)? ':' (INVOCATION) '=' (SPELLDEF)

  SPELLMOD ::= ('SILENT' | 'LOCAL')*

  (silent:  invocation silently dropped.  local:  `location' variable doesn't change after spell init.)

  ARG ::= '(' <id> ':' (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 ::= <id> '=' (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 *)
	   | <id> '=' (EXPR)		(* interpreted in the current context, eval'ds trictly *)
	   | 'FOREACH' (SELECTION) <id> 'IN' (AREA) 'DO' (EFFECT)	(* randomises the subjects first *)
	   | 'FOR' <id> '=' (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. *)
       	   | <id> '(' (EXPR) /* ',' ')'	(* operation *)
	   | 'CALL' <id> '(' (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' <id> '(' <id> /* ',' ')' '=' (EFFECT)