summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--spell-language773
-rw-r--r--src/common/timer.h4
-rw-r--r--src/map/GNUmakefile19
-rw-r--r--src/map/Makefile19
-rw-r--r--src/map/atcommand.c104
-rw-r--r--src/map/atcommand.h2
-rw-r--r--src/map/battle.c37
-rw-r--r--src/map/battle.h2
-rw-r--r--src/map/clif.c120
-rw-r--r--src/map/clif.h1
-rw-r--r--src/map/magic-expr-eval.h43
-rw-r--r--src/map/magic-expr.c1286
-rw-r--r--src/map/magic-expr.h92
-rw-r--r--src/map/magic-interpreter-aux.h8
-rw-r--r--src/map/magic-interpreter-base.c540
-rw-r--r--src/map/magic-interpreter-lexer.c2370
-rw-r--r--src/map/magic-interpreter-parser.c3198
-rw-r--r--src/map/magic-interpreter-parser.h195
-rw-r--r--src/map/magic-interpreter-parser.y1057
-rw-r--r--src/map/magic-interpreter.h418
-rw-r--r--src/map/magic-interpreter.l137
-rw-r--r--src/map/magic-stmt.c1323
-rw-r--r--src/map/magic.c116
-rw-r--r--src/map/magic.h95
-rw-r--r--src/map/map.c28
-rw-r--r--src/map/map.h79
-rw-r--r--src/map/mob.c11
-rw-r--r--src/map/npc.c123
-rw-r--r--src/map/npc.h15
-rw-r--r--src/map/pc.c324
-rw-r--r--src/map/pc.h7
-rw-r--r--src/map/pet.c2
-rw-r--r--src/map/script.c62
-rw-r--r--src/map/script.h8
-rw-r--r--src/map/skill.c37
-rw-r--r--src/map/skill.h20
-rw-r--r--src/map/storage.c2
-rw-r--r--src/map/trade.c2
38 files changed, 12523 insertions, 156 deletions
diff --git a/spell-language b/spell-language
new file mode 100644
index 0000000..21a03a9
--- /dev/null
+++ b/spell-language
@@ -0,0 +1,773 @@
+--------------------------------------------------------------------------------
+== 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.
+
+ 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
+ + 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.)
+
+ + 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
+
+
+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.
+
+ + override_attack : entity * int * int
+ * int * int *int -> ()
+ override_attack(entity, charges, delay, range, icon, animation)
+ 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 presently unused.
+ `animation' is the attack animation ID that should be used.
+
+ 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
+
+
+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' *)
+
+* procedures
+
+ Procedures may be invoked via `CALL'. They use dynamic scoping.
+
+ PROCEDURE ::= 'PROCEDURE' <id> '(' <id> /* ',' ')' '=' (EFFECT)
diff --git a/src/common/timer.h b/src/common/timer.h
index f6fc5c8..a96f011 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -28,8 +28,8 @@ struct TimerData {
unsigned int gettick_nocache(void);
unsigned int gettick(void);
-int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int);
-int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int);
+int add_timer(unsigned int delay,int (*)(int index /* for deletion */,unsigned int orig_delay,int id, int data),int id, int data);
+int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int interval);
int delete_timer(int,int (*)(int,unsigned int,int,int));
int addtick_timer(int tid,unsigned int tick);
diff --git a/src/map/GNUmakefile b/src/map/GNUmakefile
index 852bbb9..4597c6d 100644
--- a/src/map/GNUmakefile
+++ b/src/map/GNUmakefile
@@ -1,3 +1,6 @@
+LEX=flex
+BISON=bison
+
all: txt sql
txt: txtobj map-server
@@ -13,7 +16,7 @@ sqlobj:
COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/grfio.o ../common/db.o ../common/lock.o ../common/nullpo.o ../common/malloc.o
LIBS = -lz -lm
-map-server: txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
+map-server: txtobj/magic-interpreter-lexer.o txtobj/magic-interpreter-parser.o txtobj/magic-interpreter-base.o txtobj/magic-expr.o txtobj/magic-stmt.o txtobj/magic.o txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
$(CC) -o ../../$@ $^ $(LIBS)
map-server_sql: sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/npc.o sqlobj/chat.o sqlobj/path.o sqlobj/itemdb.o sqlobj/mob.o sqlobj/script.o sqlobj/storage.o sqlobj/skill.o sqlobj/atcommand.o sqlobj/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/mail.o $(COMMON_OBJ)
@@ -25,9 +28,21 @@ txtobj/%.o: %.c
sqlobj/%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
+magic-interpreter-lexer.c: magic-interpreter.l
+ $(LEX) -o magic-interpreter-lexer.c magic-interpreter.l
+
+magic-interpreter-parser.c: magic-interpreter-parser.y
+ $(BISON) -v -d -o magic-interpreter-parser.c magic-interpreter-parser.y
+
+txtobj/magic-interpreter-lexer.o: magic-interpreter-lexer.c magic-interpreter-parser.c magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-interpreter-parser.o: magic-interpreter-parser.c magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-interpreter-base.o: magic-interpreter-base.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-expr.o: magic-expr.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-stmt.o: magic-stmt.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic.o: magic.c magic.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h
-txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h
+txtobj/clif.o: magic.h clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h
txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h
txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h
txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
diff --git a/src/map/Makefile b/src/map/Makefile
index 3f5396e..70fc049 100644
--- a/src/map/Makefile
+++ b/src/map/Makefile
@@ -1,3 +1,6 @@
+LEX=flex
+BISON=bison
+
all: txt sql
txt: txtobj map-server
@@ -13,7 +16,7 @@ sqlobj:
COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/grfio.o ../common/db.o ../common/lock.o ../common/nullpo.o ../common/malloc.o
LIBS = -lz -lm
-map-server: txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
+map-server: txtobj/magic-interpreter-lexer.o txtobj/magic-interpreter-parser.o txtobj/magic-interpreter-base.o txtobj/magic-expr.o txtobj/magic-stmt.o txtobj/magic.o txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.o txtobj/chat.o txtobj/path.o txtobj/itemdb.o txtobj/mob.o txtobj/script.o txtobj/storage.o txtobj/skill.o txtobj/atcommand.o txtobj/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
$(CC) -o ../../$@ $> $(LIBS)
map-server_sql: sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/npc.o sqlobj/chat.o sqlobj/path.o sqlobj/itemdb.o sqlobj/mob.o sqlobj/script.o sqlobj/storage.o sqlobj/skill.o sqlobj/atcommand.o sqlobj/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/mail.o $(COMMON_OBJ)
@@ -25,9 +28,21 @@ txtobj/%.o: %.c
sqlobj/%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
+magic-interpreter-lexer.c: magic-interpreter.l
+ $(LEX) -o magic-interpreter-lexer.c magic-interpreter.l
+
+magic-interpreter-parser.c: magic-interpreter-parser.y
+ $(BISON) -v -d -o magic-interpreter-parser.c magic-interpreter-parser.y
+
+txtobj/magic-interpreter-lexer.o: magic-interpreter-lexer.c magic-interpreter-parser.c magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-interpreter-parser.o: magic-interpreter-parser.c magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-interpreter-base.o: magic-interpreter-base.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-expr.o: magic-expr.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic-stmt.o: magic-stmt.c magic-expr-eval.h magic-interpreter-aux.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/magic.o: magic.c magic.h magic-expr.h magic-interpreter.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h
txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h
-txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h
+txtobj/clif.o: magic.h clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h
txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h
txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h
txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 03e7675..ca2c38a 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -210,6 +210,8 @@ ATCOMMAND_FUNC(adjgmlvl); // by MouseJstr
ATCOMMAND_FUNC(adjcmdlvl); // by MouseJstr
ATCOMMAND_FUNC(trade); // by MouseJstr
ATCOMMAND_FUNC(unmute); // [Valaris]
+ATCOMMAND_FUNC(set_magic);
+ATCOMMAND_FUNC(magic_info);
#ifndef TXT_ONLY
ATCOMMAND_FUNC(checkmail); // [Valaris]
@@ -454,6 +456,8 @@ static AtCommandInfo atcommand_info[] = {
{ AtCommand_AdjCmdLvl, "@adjcmdlvl", 99, atcommand_adjcmdlvl },
{ AtCommand_Trade, "@trade", 60, atcommand_trade },
{ AtCommand_UnMute, "@unmute", 60, atcommand_unmute }, // [Valaris]
+ { AtCommand_SetMagic, "@setmagic", 99, atcommand_set_magic }, // [Fate]
+ { AtCommand_MagicInfo, "@magicinfo", 99, atcommand_magic_info }, // [Fate]
#ifndef TXT_ONLY // sql-only commands
{ AtCommand_CheckMail, "@checkmail", 1, atcommand_listmail }, // [Valaris]
@@ -1976,7 +1980,7 @@ int atcommand_item(
item_tmp.nameid = item_id;
item_tmp.identify = 1;
if ((flag = pc_additem((struct map_session_data*)sd, &item_tmp, get_count)))
- clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
}
}
clif_displaymessage(fd, msg_table[18]); // Item created.
@@ -7761,3 +7765,101 @@ int atcommand_refreshonline(
}
#endif /* end sql only */
+
+/* Magic atcommands by Fate */
+
+static int magic_base = TMW_MAGIC;
+#define magic_skills_nr 6
+static char *magic_skill_names[magic_skills_nr] = {"magic", "life", "war", "transmute", "nature", "ether"};
+
+int
+atcommand_magic_info(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[100];
+ char buf[200];
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Usage: @magicinfo <char_name>");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ int i;
+
+ sprintf(buf, "`%s' has the following magic skills:", character);
+ clif_displaymessage(fd, buf);
+
+ for (i = 0; i < magic_skills_nr; i++) {
+ sprintf(buf, "%d in %s", pl_sd->status.skill[i + magic_base].lv, magic_skill_names[i]);
+ if (pl_sd->status.skill[i + magic_base].id == i + magic_base)
+ clif_displaymessage(fd, buf);
+ }
+
+ return 0;
+ } else
+ clif_displaymessage(fd, "Character not found.");
+
+ return -1;
+}
+
+static void
+set_skill(struct map_session_data* sd, int i, int level)
+{
+ sd->status.skill[i].id = level? i : 0;
+ sd->status.skill[i].lv = level;
+ sd->status.skill[i].flag = 0;
+}
+
+int
+atcommand_set_magic(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[100];
+ char magic_type[20];
+ int skill_index = -1; // 0: all
+ int value;
+ struct map_session_data *pl_sd;
+
+ memset(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%99s %19s %i", character, magic_type, &value) < 1) {
+ clif_displaymessage(fd, "Usage: @setmagic <char_name> <school> <value>, where <school> is either `magic', one of the school names, or `all'.");
+ return -1;
+ }
+
+ if (!strcasecmp("all", magic_type))
+ skill_index = 0;
+ else {
+ int i;
+ for (i = 0; i < magic_skills_nr; i++) {
+ if (!strcasecmp(magic_skill_names[i], magic_type)) {
+ skill_index = i + magic_base;
+ break;
+ }
+ }
+ }
+
+ if (skill_index == -1) {
+ clif_displaymessage(fd, "Incorrect school of magic. Use `magic', `nature', `life', `war', `transmute', `ether', or `all'.");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ int i;
+ if (skill_index == 0)
+ for (i = 0; i < magic_skills_nr; i++)
+ set_skill(pl_sd, i + magic_base, value);
+ else
+ set_skill(pl_sd, skill_index, value);
+
+ return 0;
+ } else
+ clif_displaymessage(fd, "Character not found.");
+
+ return -1;
+}
+
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
index 06029a5..7f6d469 100644
--- a/src/map/atcommand.h
+++ b/src/map/atcommand.h
@@ -208,6 +208,8 @@ enum AtCommandType {
// SQL-only commands end
#endif
+ AtCommand_SetMagic,
+ AtCommand_MagicInfo,
// end
AtCommand_Unknown,
AtCommand_MAX
diff --git a/src/map/battle.c b/src/map/battle.c
index 1a954f3..d455d78 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -275,7 +275,7 @@ int battle_get_vit(struct block_list *bl)
vit=mob_db[((struct pet_data *)bl)->class].vit;
if(sc_data) {
if(sc_data[SC_STRIPARMOR].timer != -1 && bl->type!=BL_PC)
- vit = vit*60/100;
+ vit = vit*60/100;
if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト
vit += 5;
}
@@ -785,6 +785,11 @@ int battle_get_mdef(struct block_list *bl)
if(mdef < 1000000) {
if(sc_data) {
//バリアー状態時はMDEF100
+ if(mdef < 90 && sc_data[SC_MBARRIER].timer != -1) {
+ mdef += sc_data[SC_MBARRIER].val1;
+ if (mdef > 90)
+ mdef = 90;
+ }
if(sc_data[SC_BARRIER].timer != -1)
mdef = 100;
//凍結、石化時は1.25倍
@@ -957,8 +962,11 @@ int battle_get_adelay(struct block_list *bl)
if(sc_data[SC_STEELBODY].timer!=-1) // 金剛
aspd_rate += 25;
//増速ポーション使用時は減算
- if( sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1)
+ if(sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1)
aspd_rate -= sc_data[i].val2;
+ // Fate's `haste' spell works the same as the above
+ if (sc_data[SC_HASTE].timer != -1)
+ aspd_rate -= sc_data[SC_HASTE].val1;
//ディフェンダー時は加算
if(sc_data[SC_DEFENDER].timer != -1)
adelay += (1100 - sc_data[SC_DEFENDER].val1*100);
@@ -1004,8 +1012,10 @@ int battle_get_amotion(struct block_list *bl)
aspd_rate += sc_data[SC_DONTFORGETME].val1*3 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3>>16);
if(sc_data[SC_STEELBODY].timer!=-1) // 金剛
aspd_rate += 25;
- if( sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1)
+ if(sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1)
aspd_rate -= sc_data[i].val2;
+ if (sc_data[SC_HASTE].timer != -1)
+ aspd_rate -= sc_data[SC_HASTE].val1;
if(sc_data[SC_DEFENDER].timer != -1)
amotion += (550 - sc_data[SC_DEFENDER].val1*50);
}
@@ -1190,8 +1200,9 @@ int battle_get_mode(struct block_list *bl)
int battle_get_mexp(struct block_list *bl)
{
nullpo_retr(0, bl);
- if(bl->type==BL_MOB && (struct mob_data *)bl)
+ if(bl->type==BL_MOB && (struct mob_data *)bl) {
return mob_db[((struct mob_data *)bl)->class].mexp;
+ }
else if(bl->type==BL_PET && (struct pet_data *)bl)
return mob_db[((struct pet_data *)bl)->class].mexp;
else
@@ -4316,6 +4327,7 @@ int battle_weapon_attack( struct block_list *src,struct block_list *target,
short *opt1;
int race = 7, ele = 0;
int damage,rdamage = 0;
+ int i;
struct Damage wd;
nullpo_retr(0, src);
@@ -4365,6 +4377,19 @@ int battle_weapon_attack( struct block_list *src,struct block_list *target,
}
else
wd=battle_calc_weapon_attack(src,target,0,0,0);
+
+ // significantly increase injuries for hasted characters
+ if (wd.damage > 0
+ && (t_sc_data[SC_HASTE].timer != -1)) {
+ wd.damage = (wd.damage * (16 + t_sc_data[SC_HASTE].val1)) >> 4;
+ }
+
+ if (t_sc_data[SC_PHYS_SHIELD].timer != -1 && target->type == BL_PC) {
+ wd.damage -= t_sc_data[SC_PHYS_SHIELD].val1;
+ if (wd.damage < 0)
+ wd.damage = 0;
+ }
+
if((damage = wd.damage + wd.damage2) > 0 && src != target) {
if(wd.flag&BF_SHORT) {
if(target->type == BL_PC) {
@@ -4388,6 +4413,7 @@ int battle_weapon_attack( struct block_list *src,struct block_list *target,
}
}
}
+
if(rdamage > 0)
clif_damage(src,src,tick, wd.amotion,0,rdamage,1,4,0);
}
@@ -4416,6 +4442,7 @@ int battle_weapon_attack( struct block_list *src,struct block_list *target,
if(sd && sd->splash_range > 0 && (wd.damage > 0 || wd.damage2 > 0) )
skill_castend_damage_id(src,target,0,-1,tick,0);
map_freeblock_lock();
+
battle_damage(src,target,(wd.damage+wd.damage2),0);
if(target->prev != NULL &&
(target->type != BL_PC || (target->type == BL_PC && !pc_isdead((struct map_session_data *)target) ) ) ) {
@@ -4898,6 +4925,7 @@ int battle_config_read(const char *cfgName)
battle_config.natural_healsp_interval=8000;
battle_config.natural_heal_skill_interval=10000;
battle_config.natural_heal_weight_rate=50;
+ battle_config.itemheal_regeneration_factor = 1;
battle_config.item_name_override_grffile=1;
battle_config.arrow_decrement=1;
battle_config.max_aspd = 199;
@@ -5130,6 +5158,7 @@ int battle_config_read(const char *cfgName)
{ "natural_healsp_interval", &battle_config.natural_healsp_interval },
{ "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval},
{ "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate },
+ { "itemheal_regeneration_factor", &battle_config.itemheal_regeneration_factor },
{ "item_name_override_grffile", &battle_config.item_name_override_grffile},
{ "arrow_decrement", &battle_config.arrow_decrement },
{ "max_aspd", &battle_config.max_aspd },
diff --git a/src/map/battle.h b/src/map/battle.h
index 02283c9..f8a3205 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -342,6 +342,8 @@ extern struct Battle_Config {
int mail_system; // [Valaris]
#endif
+ int itemheal_regeneration_factor; // [Fate] itemheal speed factor
+
} battle_config;
int battle_config_read(const char *cfgName);
diff --git a/src/map/clif.c b/src/map/clif.c
index 618e05a..98dd0fc 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -43,6 +43,7 @@
#include "guild.h"
#include "vending.h"
#include "pet.h"
+#include "magic.h"
#ifdef MEMWATCH
#include "memwatch.h"
@@ -670,6 +671,16 @@ int clif_clearchar_id(int id, int type, int fd) {
return 0;
}
+static int
+current_weapon(struct map_session_data *sd)
+{
+ if (sd->attack_spell_override)
+ return sd->attack_spell_look_override;
+ else {
+ return sd->status.weapon;
+ }
+}
+
/*==========================================
*
*------------------------------------------
@@ -709,7 +720,7 @@ static int clif_set0078(struct map_session_data *sd, unsigned char *buf) {
WBUFW(buf,14)= sd->view_class;
WBUFW(buf,16)= sd->status.hair;
if (sd->view_class != 22)
- WBUFW(buf,18) = sd->status.weapon;
+ WBUFW(buf,18) = current_weapon(sd);
else
WBUFW(buf,18)=0;
WBUFW(buf,20)=sd->status.head_bottom;
@@ -741,13 +752,17 @@ static int clif_set0078(struct map_session_data *sd, unsigned char *buf) {
WBUFW(buf,12) = sd->status.option;
WBUFW(buf,14) = sd->view_class;
WBUFW(buf,16) = sd->status.hair;
- if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) {
- if (sd->inventory_data[sd->equip_index[9]]->view_id > 0)
- WBUFW(buf,18) = sd->inventory_data[sd->equip_index[9]]->view_id;
- else
- WBUFW(buf,18) = sd->status.inventory[sd->equip_index[9]].nameid;
- } else
- WBUFW(buf,18) = 0;
+ if (sd->attack_spell_override)
+ WBUFB(buf, 18) = sd->attack_spell_look_override;
+ else {
+ if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) {
+ if (sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ WBUFW(buf,18) = sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ WBUFW(buf,18) = sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ WBUFW(buf,18) = 0;
+ }
if (sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && sd->view_class != 22) {
if (sd->inventory_data[sd->equip_index[8]]->view_id > 0)
WBUFW(buf,20) = sd->inventory_data[sd->equip_index[8]]->view_id;
@@ -817,7 +832,7 @@ static int clif_set007b(struct map_session_data *sd,unsigned char *buf) {
WBUFW(buf,14)=sd->view_class;
WBUFW(buf,16)=sd->status.hair;
if(sd->view_class != 22)
- WBUFW(buf,18)=sd->status.weapon;
+ WBUFW(buf,18)= current_weapon(sd);
else
WBUFW(buf,18)=0;
WBUFW(buf,20)=sd->status.head_bottom;
@@ -1304,6 +1319,39 @@ int clif_spawnnpc(struct npc_data *nd)
return 0;
}
+int
+clif_spawn_fake_npc_for_player(struct map_session_data *sd, int fake_npc_id)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd = sd->fd;
+ if (!fd)
+ return 0;
+
+ WFIFOW(fd, 0) = 0x7c;
+ WFIFOL(fd, 2) = fake_npc_id;
+ WFIFOW(fd, 6) = 0;
+ WFIFOW(fd, 20) = 127;
+ WFIFOPOS(fd, 36, sd->bl.x, sd->bl.y);
+ WFIFOSET(fd, packet_len_table[0x7c]);
+
+ WFIFOW(fd, 0)=0x78;
+ WFIFOL(fd, 2) = fake_npc_id;
+ WFIFOW(fd, 6) = 0;
+ WFIFOW(fd, 14) = 127; // identifies as NPC
+ WFIFOW(fd, 20) = 127;
+ WFIFOPOS(fd, 46, sd->bl.x, sd->bl.y);
+ WFIFOPOS(fd, 36, sd->bl.x, sd->bl.y);
+ WFIFOB(fd, 49) = 5;
+ WFIFOB(fd, 50) = 5;
+ WFIFOSET(fd, packet_len_table[0x78]);
+
+ return 0;
+}
+
+
/*==========================================
*
*------------------------------------------
@@ -2413,7 +2461,9 @@ int clif_updatestatus(struct map_session_data *sd,int type)
// 013a 終了
case SP_ATTACKRANGE:
WFIFOW(fd,0)=0x13a;
- WFIFOW(fd,2)=sd->attackrange;
+ WFIFOW(fd,2) = (sd->attack_spell_override)
+ ? sd->attack_spell_range
+ : sd->attackrange;
len=4;
break;
@@ -2550,13 +2600,17 @@ int clif_changelook(struct block_list *bl,int type,int val)
}
else {
WBUFB(buf,6)=2;
- if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) {
- if(sd->inventory_data[sd->equip_index[9]]->view_id > 0)
- WBUFW(buf,7)=sd->inventory_data[sd->equip_index[9]]->view_id;
- else
- WBUFW(buf,7)=sd->status.inventory[sd->equip_index[9]].nameid;
- } else
- WBUFW(buf,7)=0;
+ if (sd->attack_spell_override)
+ WBUFW(buf, 7) = sd->attack_spell_look_override;
+ else {
+ if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) {
+ if(sd->inventory_data[sd->equip_index[9]]->view_id > 0)
+ WBUFW(buf,7)=sd->inventory_data[sd->equip_index[9]]->view_id;
+ else
+ WBUFW(buf,7)=sd->status.inventory[sd->equip_index[9]].nameid;
+ } else
+ WBUFW(buf,7)=0;
+ }
if(sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] &&
sd->view_class != 22) {
if(sd->inventory_data[sd->equip_index[8]]->view_id > 0)
@@ -4089,7 +4143,8 @@ int clif_skillinfoblock(struct map_session_data *sd)
fd=sd->fd;
WFIFOW(fd,0)=0x10f;
for ( i = c = 0; i < MAX_SKILL; i++){
- if( (id=sd->status.skill[i].id)!=0 ){
+ if( (id=sd->status.skill[i].id)!=0
+ && (i < TMW_MAGIC || i > TMW_MAGIC_END)){ // [Fate] Hack: Prevent killing the client
WFIFOW(fd,len ) = id;
WFIFOW(fd,len+2) = skill_get_inf(id);
WFIFOW(fd,len+4) = 0;
@@ -7132,7 +7187,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
// view equipment item
#if PACKETVER < 4
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_WEAPON, current_weapon(sd));
clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
#else
clif_changelook(&sd->bl,LOOK_WEAPON,0);
@@ -7318,7 +7373,10 @@ void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) {
nullpo_retv(ssd);
- memcpy(WFIFOP(fd,6), ssd->status.name, 24);
+ if (ssd->state.shroud_active)
+ memset(WFIFOP(fd,6), 0, 24);
+ else
+ memcpy(WFIFOP(fd,6), ssd->status.name, 24);
if (ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL &&
(ssd->status.party_id == 0 || (p = party_search(ssd->status.party_id)) != NULL)) {
// ギルド所属ならパケット0195を返す
@@ -7488,12 +7546,18 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data *sd) { // S 008c <
}
// but for the hacker, we display on his screen (he see/look no difference).
} else {
+ int magic_status;
// send message to others
WBUFW(buf,0) = 0x8d;
WBUFW(buf,2) = RFIFOW(fd,2) + 4; // len of message - 4 + 8
WBUFL(buf,4) = sd->bl.id;
memcpy(WBUFP(buf,8), RFIFOP(fd,4), RFIFOW(fd,2) - 4);
- clif_send(buf, WBUFW(buf,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
+ magic_status = magic_message(sd, buf, WBUFW(buf, 2));
+ if (magic_status)
+ sd->chat_threshold = 0; /* Don't treat repeated magic as spamming */
+
+ if (magic_status >= 0)
+ clif_send(buf, WBUFW(buf,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
}
// send back message to the speaker
@@ -7852,6 +7916,10 @@ void clif_parse_TakeItem(int fd, struct map_session_data *sd) {
if (abs(sd->bl.x - fitem->bl.x) >= 2 || abs(sd->bl.y - fitem->bl.y) >= 2)
return; // too far away to pick up
+ if (sd->state.shroud_active
+ && sd->state.shroud_disappears_on_pickup)
+ magic_unshroud(sd);
+
pc_takeitem(sd, fitem);
}
@@ -8486,7 +8554,7 @@ void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
nullpo_retv(sd);
sd->npc_menu=RFIFOB(fd,6);
- npc_scriptcont(sd,RFIFOL(fd,2));
+ map_scriptcont(sd,RFIFOL(fd,2));
}
/*==========================================
@@ -8495,7 +8563,7 @@ void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
*/
void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
{
- npc_scriptcont(sd,RFIFOL(fd,2));
+ map_scriptcont(sd,RFIFOL(fd,2));
}
/*==========================================
@@ -8512,7 +8580,7 @@ void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
#undef RFIFOL_
- npc_scriptcont(sd,RFIFOL(fd,2));
+ map_scriptcont(sd,RFIFOL(fd,2));
}
/*==========================================
@@ -8529,7 +8597,7 @@ void clif_parse_NpcStringInput(int fd,struct map_session_data *sd)
sd->npc_str[sizeof(sd->npc_str)-1]=0;
} else
strcpy(sd->npc_str,RFIFOP(fd,8));
- npc_scriptcont(sd,RFIFOL(fd,4));
+ map_scriptcont(sd,RFIFOL(fd,4));
}
/*==========================================
@@ -8538,7 +8606,7 @@ void clif_parse_NpcStringInput(int fd,struct map_session_data *sd)
*/
void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
{
- npc_scriptcont(sd,RFIFOL(fd,2));
+ map_scriptcont(sd,RFIFOL(fd,2));
}
/*==========================================
diff --git a/src/map/clif.h b/src/map/clif.h
index cdddd8b..d04d821 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -34,6 +34,7 @@ int clif_clearchar_delay(unsigned int,struct block_list *,int);
int clif_clearchar_id(int,int,int);
int clif_spawnpc(struct map_session_data*); //area
int clif_spawnnpc(struct npc_data*); // area
+int clif_spawn_fake_npc_for_player(struct map_session_data *sd, int fake_npc_id);
int clif_spawnmob(struct mob_data*); // area
int clif_spawnpet(struct pet_data*); // area
int clif_walkok(struct map_session_data*); // self
diff --git a/src/map/magic-expr-eval.h b/src/map/magic-expr-eval.h
new file mode 100644
index 0000000..b859919
--- /dev/null
+++ b/src/map/magic-expr-eval.h
@@ -0,0 +1,43 @@
+#ifndef MAGIC_EXPR_EVAL
+#define MAGIC_EXPR_EVAL
+
+/* Helper definitions for dealing with functions and operations */
+
+static int heading_x[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
+static int heading_y[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
+
+int
+magic_signature_check(char *opname, char *funname, char *signature, int args_nr, val_t *args, int line, int column);
+
+void
+magic_area_rect(int *m, int *x, int *y, int *width, int *height, area_t *area);
+
+
+#define ARGINT(x) args[x].v.v_int
+#define ARGDIR(x) args[x].v.v_int
+#define ARGSTR(x) args[x].v.v_string
+#define ARGENTITY(x) args[x].v.v_entity
+#define ARGLOCATION(x) args[x].v.v_location
+#define ARGAREA(x) args[x].v.v_area
+#define ARGSPELL(x) args[x].v.v_spell
+#define ARGINVOCATION(x) args[x].v.v_invocation
+
+#define RESULTINT result->v.v_int
+#define RESULTDIR result->v.v_int
+#define RESULTSTR result->v.v_string
+#define RESULTENTITY result->v.v_entity
+#define RESULTLOCATION result->v.v_location
+#define RESULTAREA result->v.v_area
+#define RESULTSPELL result->v.v_spell
+#define RESULTINVOCATION result->v.v_invocation
+
+#define TY(x) args[x].ty
+#define ETY(x) ARGENTITY(x)->type
+
+#define ARGPC(x) ((struct map_session_data *)ARGENTITY(x))
+#define ARGNPC(x) ((struct map_session_data *)ARGENTITY(x))
+#define ARGMOB(x) ((struct map_session_data *)ARGENTITY(x))
+
+#define ARG_MAY_BE_AREA(x) (TY(x) == TY_AREA || TY(x) == TY_LOCATION)
+
+#endif /* !defined(MAGIC_EXPR_EVAL) */
diff --git a/src/map/magic-expr.c b/src/map/magic-expr.c
new file mode 100644
index 0000000..2fbaa4f
--- /dev/null
+++ b/src/map/magic-expr.c
@@ -0,0 +1,1286 @@
+#include "magic-expr.h"
+#include "magic-expr-eval.h"
+#include "itemdb.h"
+#include <math.h>
+
+#define IS_SOLID(c) ((c) == 1 || (c) == 5)
+
+int
+map_is_solid(int m, int x, int y)
+{
+ return (IS_SOLID(map_getcell(m, x, y)));
+}
+
+#undef IS_SOLID
+
+static void
+free_area(area_t *area)
+{
+ if (!area)
+ return;
+
+ switch (area->ty) {
+ case AREA_UNION:
+ free_area(area->a.a_union[0]);
+ free_area(area->a.a_union[1]);
+ break;
+ default: break;
+ }
+
+ free(area);
+}
+
+static area_t *
+dup_area(area_t *area)
+{
+ area_t *retval = malloc(sizeof(area_t));
+ *retval = *area;
+
+ switch (area->ty) {
+ case AREA_UNION:
+ retval->a.a_union[0] = dup_area(retval->a.a_union[0]);
+ retval->a.a_union[1] = dup_area(retval->a.a_union[1]);
+ break;
+ default: break;
+ }
+
+ return retval;
+}
+
+void
+magic_copy_var(val_t *dest, val_t *src)
+{
+ *dest = *src;
+
+ switch (dest->ty) {
+ case TY_STRING: dest->v.v_string = strdup(dest->v.v_string);
+ break;
+ case TY_AREA: dest->v.v_area = dup_area(dest->v.v_area);
+ break;
+ default: break;
+ }
+
+}
+
+void
+magic_clear_var(val_t *v)
+{
+ switch (v->ty) {
+ case TY_STRING: free(v->v.v_string);
+ break;
+ case TY_AREA: free_area(v->v.v_area);
+ break;
+ default: break;
+ }
+}
+
+static char *
+show_entity(entity_t *entity)
+{
+ switch (entity->type) {
+ case BL_PC:
+ return ((struct map_session_data *)entity)->status.name;
+ case BL_NPC:
+ return ((struct npc_data *)entity)->name;
+ case BL_MOB:
+ return ((struct mob_data *)entity)->name;
+ case BL_ITEM:
+ /* Sorry about this one... */
+ return ((struct item_data *)(&((struct flooritem_data *)entity)->item_data))->name;
+ case BL_SKILL:
+ return "%skill";
+ case BL_PET:
+ return ((struct pet_data *)entity)->name;
+ case BL_SPELL:
+ return "%invocation(ERROR:this-should-not-be-an-entity)";
+ default:
+ return "%unknown-entity";
+ }
+}
+
+static void
+stringify(val_t *v, int within_op)
+{
+ static char *dirs[8] = {"S", "SW", "W", "NW", "N", "NE", "E", "SE"};
+ char *buf;
+
+ switch (v->ty) {
+ case TY_UNDEF:
+ buf = strdup("UNDEF");
+ break;
+
+ case TY_INT:
+ buf = malloc(32);
+ sprintf(buf, "%i", v->v.v_int);
+ break;
+
+ case TY_STRING:
+ return;
+
+ case TY_DIR:
+ buf = strdup(dirs[v->v.v_int]);
+ break;
+
+ case TY_ENTITY:
+ buf = strdup(show_entity(v->v.v_entity));
+ break;
+
+ case TY_LOCATION:
+ buf = malloc(128);
+ sprintf(buf, "<\"%s\", %d, %d>", map[v->v.v_location.m].name,
+ v->v.v_location.x, v->v.v_location.y);
+ break;
+
+ case TY_AREA:
+ buf = strdup("%area");
+ free_area(v->v.v_area);
+ break;
+
+ case TY_SPELL:
+ buf = strdup(v->v.v_spell->name);
+ break;
+
+ case TY_INVOCATION: {
+ invocation_t *invocation = within_op
+ ? v->v.v_invocation
+ : (invocation_t *) map_id2bl(v->v.v_int);
+ buf = strdup(invocation->spell->name);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "[magic] INTERNAL ERROR: Cannot stringify %d\n", v->ty);
+ return;
+ }
+
+ v->v.v_string = buf;
+ v->ty = TY_STRING;
+}
+
+static void
+intify(val_t *v)
+{
+ if (v->ty == TY_INT)
+ return;
+
+ magic_clear_var(v);
+ v->ty = TY_INT;
+ v->v.v_int = 1;
+}
+
+
+area_t *
+area_new(int ty)
+{
+ area_t *retval = (area_t *)aCalloc(sizeof(area_t), 1);
+ retval->ty = ty;
+ return retval;
+}
+
+area_t *
+area_union(area_t *area, area_t *other_area)
+{
+ area_t *retval = area_new(AREA_UNION);
+ retval->a.a_union[0] = area;
+ retval->a.a_union[1] = other_area;
+ retval->size = area->size + other_area->size; /* Assume no overlap */
+ return retval;
+}
+
+/**
+ * Turns location into area, leaves other types untouched
+ */
+static void
+make_area(val_t *v)
+{
+ if (v->ty == TY_LOCATION) {
+ area_t *a = malloc(sizeof (area_t));
+ v->ty = TY_AREA;
+ a->ty = AREA_LOCATION;
+ a->a.a_loc = v->v.v_location;
+ v->v.v_area = a;
+ }
+}
+
+static void
+make_location(val_t *v)
+{
+ if (v->ty == TY_AREA &&
+ v->v.v_area->ty == AREA_LOCATION) {
+ location_t location = v->v.v_area->a.a_loc;
+ free_area(v->v.v_area);
+ v->ty = TY_LOCATION;
+ v->v.v_location = location;
+ }
+}
+
+static int
+fun_add(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (TY(0) == TY_INT && TY(1) == TY_INT) {
+ /* Integer addition */
+ RESULTINT = ARGINT(0) + ARGINT(1);
+ result->ty = TY_INT;
+ } else if (ARG_MAY_BE_AREA(0) && ARG_MAY_BE_AREA(1)) {
+ /* Area union */
+ make_area(&args[0]);
+ make_area(&args[1]);
+ RESULTAREA = area_union(ARGAREA(0), ARGAREA(1));
+ ARGAREA(0) = NULL;
+ ARGAREA(1) = NULL;
+ result->ty = TY_AREA;
+ } else {
+ /* Anything else -> string concatenation */
+ stringify(&args[0], 1);
+ stringify(&args[1], 1);
+ /* Yes, we could speed this up. */
+ RESULTSTR = (char *) malloc(1 + strlen(ARGSTR(0)) + strlen(ARGSTR(1)));
+ strcpy(RESULTSTR, ARGSTR(0));
+ strcat(RESULTSTR, ARGSTR(1));
+ result->ty = TY_STRING;
+ }
+ return 0;
+}
+
+static int
+fun_sub(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) - ARGINT(1);
+ return 0;
+}
+
+
+static int
+fun_mul(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) * ARGINT(1);
+ return 0;
+}
+
+
+static int
+fun_div(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (!ARGINT(1))
+ return 1; /* division by zero */
+ RESULTINT = ARGINT(0) / ARGINT(1);
+ return 0;
+}
+
+static int
+fun_mod(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (!ARGINT(1))
+ return 1; /* division by zero */
+ RESULTINT = ARGINT(0) % ARGINT(1);
+ return 0;
+}
+
+static int
+fun_or(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) || ARGINT(1);
+ return 0;
+}
+
+static int
+fun_and(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) && ARGINT(1);
+ return 0;
+}
+
+static int
+fun_not(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = !ARGINT(0);
+ return 0;
+}
+
+static int
+fun_gte(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (TY(0) == TY_STRING || TY(1) == TY_STRING) {
+ stringify(&args[0], 1);
+ stringify(&args[1], 1);
+ RESULTINT = strcmp(ARGSTR(0), ARGSTR(1)) >= 0;
+ } else {
+ intify(&args[0]);
+ intify(&args[1]);
+ RESULTINT = ARGINT(0) >= ARGINT(1);
+ }
+ return 0;
+}
+
+static int
+fun_gt(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (TY(0) == TY_STRING || TY(1) == TY_STRING) {
+ stringify(&args[0], 1);
+ stringify(&args[1], 1);
+ RESULTINT = strcmp(ARGSTR(0), ARGSTR(1)) > 0;
+ } else {
+ intify(&args[0]);
+ intify(&args[1]);
+ RESULTINT = ARGINT(0) > ARGINT(1);
+ }
+ return 0;
+}
+
+
+static int
+fun_eq(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (TY(0) == TY_STRING || TY(1) == TY_STRING) {
+ stringify(&args[0], 1);
+ stringify(&args[1], 1);
+ RESULTINT = strcmp(ARGSTR(0), ARGSTR(1)) == 0;
+ } else if (TY(0) == TY_DIR && TY(1) == TY_DIR)
+ RESULTINT = ARGDIR(0) == ARGDIR(1);
+ else if (TY(0) == TY_ENTITY && TY(1) == TY_ENTITY)
+ RESULTINT = ARGENTITY(0) == ARGENTITY(1);
+ else if (TY(0) == TY_LOCATION && TY(1) == TY_LOCATION)
+ RESULTINT = (ARGLOCATION(0).x == ARGLOCATION(1).x
+ && ARGLOCATION(0).y == ARGLOCATION(1).y
+ && ARGLOCATION(0).m == ARGLOCATION(1).m);
+ else if (TY(0) == TY_AREA && TY(1) == TY_AREA)
+ RESULTINT = ARGAREA(0) == ARGAREA(1); /* Probably not that great an idea... */
+ else if (TY(0) == TY_SPELL && TY(1) == TY_SPELL)
+ RESULTINT = ARGSPELL(0) == ARGSPELL(1);
+ else if (TY(0) == TY_INVOCATION && TY(1) == TY_INVOCATION)
+ RESULTINT = ARGINVOCATION(0) == ARGINVOCATION(1);
+ else {
+ intify(&args[0]);
+ intify(&args[1]);
+ RESULTINT = ARGINT(0) == ARGINT(1);
+ }
+ return 0;
+}
+
+static int
+fun_bitand(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) & ARGINT(1);
+ return 0;
+}
+
+static int
+fun_bitor(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) | ARGINT(1);
+ return 0;
+}
+
+static int
+fun_bitxor(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) ^ ARGINT(1);
+ return 0;
+}
+
+static int
+fun_bitshl(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) << ARGINT(1);
+ return 0;
+}
+
+static int
+fun_bitshr(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGINT(0) >> ARGINT(1);
+ return 0;
+}
+
+static int
+fun_max(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = MAX(ARGINT(0), ARGINT(1));
+ return 0;
+}
+
+static int
+fun_min(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = MIN(ARGINT(0), ARGINT(1));
+ return 0;
+}
+
+static int
+fun_if_then_else(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ARGINT(0))
+ magic_copy_var(result, &args[1]);
+ else
+ magic_copy_var(result, &args[2]);
+ return 0;
+}
+
+void
+magic_area_rect(int *m, int *x, int *y, int *width, int *height, area_t *area)
+{
+ switch (area->ty) {
+ case AREA_UNION: break;
+
+ case AREA_LOCATION:
+ *m = area->a.a_loc.m;
+ *x = area->a.a_loc.x;
+ *y = area->a.a_loc.y;
+ *width = 1;
+ *height = 1;
+ break;
+
+ case AREA_RECT:
+ *m = area->a.a_rect.loc.m;
+ *x = area->a.a_rect.loc.x;
+ *y = area->a.a_rect.loc.y;
+ *width = area->a.a_rect.width;
+ *height = area->a.a_rect.height;
+ break;
+
+ case AREA_BAR: {
+ int tx = area->a.a_bar.loc.x;
+ int ty = area->a.a_bar.loc.y;
+ int twidth = area->a.a_bar.width;
+ int tdepth = area->a.a_bar.width;
+ *m = area->a.a_bar.loc.m;
+
+ switch (area->a.a_bar.dir) {
+ case DIR_S:
+ *x = tx - twidth;
+ *y = ty;
+ *width = twidth * 2 + 1;
+ *height = tdepth;
+ break;
+
+ case DIR_W:
+ *x = tx - tdepth;
+ *y = ty - twidth;
+ *width = tdepth;
+ *height = twidth * 2 + 1;
+ break;
+
+ case DIR_N:
+ *x = tx - twidth;
+ *y = ty - tdepth;
+ *width = twidth * 2 + 1;
+ *height = tdepth;
+ break;
+
+ case DIR_E:
+ *x = tx;
+ *y = ty - twidth;
+ *width = tdepth;
+ *height = twidth * 2 + 1;
+ break;
+
+ default:
+ fprintf(stderr, "Error: Trying to compute area of NE/SE/NW/SW-facing bar");
+ *x = tx; *y = ty; *width = *height = 1;
+ }
+ break;
+ }
+ }
+}
+
+static int
+location_in_area(int m, int x, int y, area_t *area)
+{
+ switch (area->ty) {
+ case AREA_UNION:
+ return location_in_area(m, x, y, area->a.a_union[0])
+ || location_in_area(m, x, y, area->a.a_union[1]);
+ case AREA_LOCATION:
+ case AREA_RECT:
+ case AREA_BAR: {
+ int am;
+ int ax, ay, awidth, aheight;
+ magic_area_rect(&am, &ax, &ay, &awidth, &aheight, area);
+ return (am == m
+ && (x >= ax) && (y >= ay)
+ && (x < ax + awidth) && (y < ay + aheight));
+ }
+ default:
+ fprintf(stderr, "INTERNAL ERROR: Invalid area\n");
+ return 0;
+ }
+}
+
+
+static int
+fun_is_in(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = location_in_area(ARGLOCATION(0).m,
+ ARGLOCATION(0).x,
+ ARGLOCATION(0).y,
+ ARGAREA(1));
+ return 0;
+}
+
+static int
+fun_skill(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ETY(0) != BL_PC
+ || ARGINT(1) < 0
+ || ARGINT(1) >= MAX_SKILL
+ || ARGPC(0)->status.skill[ARGINT(1)].id != ARGINT(1))
+ RESULTINT = 0;
+ else
+ RESULTINT = ARGPC(0)->status.skill[ARGINT(1)].lv;
+ return 0;
+}
+
+static int
+fun_has_shroud(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = (ETY(0) == BL_PC
+ && ARGPC(0)->state.shroud_active);
+ return 0;
+}
+
+#define BATTLE_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { RESULTINT = battle_get_##name(ARGENTITY(0)); return 0; }
+
+BATTLE_GETTER(str);
+BATTLE_GETTER(agi);
+BATTLE_GETTER(vit);
+BATTLE_GETTER(dex);
+BATTLE_GETTER(luk);
+BATTLE_GETTER(int);
+BATTLE_GETTER(lv);
+BATTLE_GETTER(hp);
+BATTLE_GETTER(max_hp);
+BATTLE_GETTER(dir);
+
+#define MMO_GETTER(name) static int fun_get_##name(env_t *env, int args_nr, val_t *result, val_t *args) { \
+ if (ETY(0) == BL_PC) \
+ RESULTINT = ARGPC(0)->status.name; \
+ else \
+ RESULTINT = 0; \
+ return 0; }
+
+MMO_GETTER(sp);
+MMO_GETTER(max_sp);
+
+static int
+fun_name_of(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (TY(0) == TY_ENTITY) {
+ RESULTSTR = strdup(show_entity(ARGENTITY(0)));
+ return 0;
+ } else if (TY(0) == TY_SPELL) {
+ RESULTSTR = strdup(ARGSPELL(0)->name);
+ return 0;
+ } else if (TY(0) == TY_INVOCATION) {
+ RESULTSTR = strdup(ARGINVOCATION(0)->spell->name);
+ return 0;
+ }
+ return 1;
+}
+
+#define COPY_LOCATION(dest, src) (dest).x = (src).x; (dest).y = (src).y; (dest).m = (src).m;
+
+static int
+fun_location(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ COPY_LOCATION(RESULTLOCATION, *(ARGENTITY(0)));
+ return 0;
+}
+
+/* Recall that glibc's rand() isnt' too bad in the lower bits */
+
+static int
+fun_random(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ int delta = ARGINT(0);
+ if (delta < 0)
+ delta = -delta;
+ if (delta == 0) {
+ RESULTINT = 0;
+ return 0;
+ }
+ RESULTINT = rand() % delta;
+ if (ARGINT(0) < 0)
+ RESULTINT = -RESULTINT;
+ return 0;
+}
+
+static int
+fun_random_dir(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ARGINT(0))
+ RESULTDIR = rand() & 0x7;
+ else
+ RESULTDIR = (rand() & 0x3) * 2;
+ return 0;
+}
+
+static int
+fun_hash_entity(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = ARGENTITY(0)->id;
+ return 0;
+}
+
+static int
+fun_count_item(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ character_t *chr = (ETY(0) == BL_PC) ? ARGPC(0) : NULL;
+ int item_id;
+
+ if (TY(1) == TY_INT)
+ item_id = ARGINT(1);
+ else if (TY(1) == TY_STRING) {
+ struct item_data *bitem = itemdb_searchname(ARGSTR(1));
+ if (!bitem) {
+ fprintf(stderr, "Unknown item `%s' used in spell\n", ARGSTR(1));
+ return 1;
+ } else
+ item_id = bitem->nameid;
+ } else
+ return 0;
+
+ if (!chr)
+ return 1;
+
+ RESULTINT = pc_count_all_items(chr, item_id);
+ return 0;
+}
+
+
+static int
+fun_is_married(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = (ETY(0) == BL_PC
+ && ARGPC(0)->status.partner_id);
+ return 0;
+}
+
+static int
+fun_partner(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ETY(0) == BL_PC
+ && ARGPC(0)->status.partner_id) {
+ RESULTENTITY = (entity_t *) map_nick2sd(map_charid2nick(ARGPC(0)->status.partner_id));
+ return 0;
+ } else
+ return 1;
+}
+
+static int
+fun_awayfrom(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ location_t *loc = &ARGLOCATION(0);
+ int dx = heading_x[ARGDIR(1)];
+ int dy = heading_y[ARGDIR(1)];
+ int distance = ARGINT(2);
+ while (distance-- && !map_is_solid(loc->m, loc->x, loc->y)) {
+ loc->x += dx;
+ loc->y += dy;
+ }
+
+ RESULTLOCATION = *loc;
+ return 0;
+}
+
+static int
+fun_failed(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = TY(0) == TY_FAIL;
+ return 0;
+}
+
+static int
+fun_npc(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTENTITY = (entity_t *)npc_name2id(ARGSTR(0));
+ return RESULTENTITY == NULL;
+}
+
+static int
+fun_pc(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTENTITY = (entity_t *)map_nick2sd(ARGSTR(0));
+ return RESULTENTITY == NULL;
+}
+
+static int
+fun_distance(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ARGLOCATION(0).m != ARGLOCATION(1).m)
+ RESULTINT = INT_MAX;
+ else
+ RESULTINT = MAX(abs(ARGLOCATION(0).x - ARGLOCATION(1).x),
+ abs(ARGLOCATION(0).y - ARGLOCATION(1).y));
+ return 0;
+}
+
+
+static int
+fun_rdistance(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ARGLOCATION(0).m != ARGLOCATION(1).m)
+ RESULTINT = INT_MAX;
+ else {
+ int dx = ARGLOCATION(0).x - ARGLOCATION(1).x;
+ int dy = ARGLOCATION(0).y - ARGLOCATION(1).y;
+ RESULTINT = (int) (sqrt((dx*dx) + (dy*dy)));
+ }
+ return 0;
+}
+
+
+static int
+fun_anchor(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ teleport_anchor_t *anchor = magic_find_anchor(ARGSTR(0));
+
+ if (!anchor)
+ return 1;
+
+ magic_eval(env, result, anchor->location);
+
+ make_area(result);
+ if (result->ty != TY_AREA) {
+ magic_clear_var(result);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+static int
+fun_line_of_sight(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ entity_t e1, e2;
+
+ COPY_LOCATION(e1, ARGLOCATION(0));
+ COPY_LOCATION(e2, ARGLOCATION(1));
+
+ RESULTINT = battle_check_range(&e1, &e2, 0);
+
+ return 0;
+}
+
+void
+magic_random_location(location_t *dest, area_t *area)
+{
+ switch (area->ty) {
+ case AREA_UNION: {
+ int rv = rand() % area->size;
+ if (rv < area->a.a_union[0]->size)
+ magic_random_location(dest, area->a.a_union[0]);
+ else
+ magic_random_location(dest, area->a.a_union[1]);
+ break;
+ }
+
+ case AREA_LOCATION:
+ case AREA_RECT:
+ case AREA_BAR: {
+ int m, x, y, w, h;
+ magic_area_rect(&m, &x, &y, &w, &h, area);
+
+ if (w <= 1)
+ w = 1;
+
+ if (h <= 1)
+ h = 1;
+
+ x += rand() % w;
+ y += rand() % h;
+
+ if (!map_is_solid(m, x, y)) {
+ int start_x = x;
+ int start_y = y;
+ int i;
+ int initial_dir = rand() & 0x7;
+ int dir = initial_dir;
+
+ /* try all directions, up to a distance to 10, for a free slot */
+ do {
+ x = start_x;
+ y = start_y;
+
+ for (i = 0; i < 10 && map_is_solid(m, x, y); i++) {
+ x += heading_x[dir];
+ y += heading_y[dir];
+ }
+
+ dir = (dir + 1) & 0x7;
+ } while (map_is_solid(m, x, y) && dir != initial_dir);
+
+ }
+ /* We've tried our best. If the map is still solid, the engine will automatically randomise the target location if we try to warp. */
+
+ dest->m = m;
+ dest->x = x;
+ dest->y = y;
+ break;
+ }
+
+ default:
+ fprintf(stderr, "Unknown area type %d\n", area->ty);
+ }
+}
+
+static int
+fun_pick_location(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ magic_random_location(&result->v.v_location, ARGAREA(0));
+ return 0;
+}
+
+static int
+fun_read_script_int(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ entity_t *subject_p = ARGENTITY(0);
+ char *var_name = ARGSTR(1);
+
+ if (subject_p->type != BL_PC)
+ return 1;
+
+ RESULTINT = pc_readglobalreg((character_t *) subject_p, var_name);
+ return 0;
+}
+
+static int
+fun_rbox(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ location_t loc = ARGLOCATION(0);
+ int radius = ARGINT(1);
+
+ RESULTAREA = area_new(AREA_RECT);
+ RESULTAREA->a.a_rect.loc.m = loc.m;
+ RESULTAREA->a.a_rect.loc.x = loc.x - radius;
+ RESULTAREA->a.a_rect.loc.y = loc.y - radius;
+ RESULTAREA->a.a_rect.width = radius * 2 + 1;
+ RESULTAREA->a.a_rect.height = radius * 2 + 1;
+
+ return 0;
+}
+
+static int
+fun_running_status_update(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ if (ETY(0) != BL_PC)
+ return 1;
+
+ RESULTINT = battle_get_sc_data(ARGENTITY(0))[ARGINT(1)].timer != -1;
+ return 0;
+}
+
+static int
+fun_element(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = battle_get_element(ARGENTITY(0)) % 10;
+ return 0;
+}
+
+static int
+fun_element_level(env_t *env, int args_nr, val_t *result, val_t *args)
+{
+ RESULTINT = battle_get_element(ARGENTITY(0)) / 10;
+ return 0;
+}
+
+#define BATTLE_RECORD2(sname, name) { sname, "e", 'i', fun_get_##name }
+#define BATTLE_RECORD(name) BATTLE_RECORD2(#name, name)
+static fun_t functions[] = {
+ { "+", "..", '.', fun_add },
+ { "-", "ii", 'i', fun_sub },
+ { "*", "ii", 'i', fun_mul },
+ { "/", "ii", 'i', fun_div },
+ { "%", "ii", 'i', fun_mod },
+ { "||", "ii", 'i', fun_or },
+ { "&&", "ii", 'i', fun_and },
+ { ">", "..", 'i', fun_gt },
+ { ">=", "..", 'i', fun_gte },
+ { "=", "..", 'i', fun_eq },
+ { "|", "..", 'i', fun_bitor },
+ { "&", "ii", 'i', fun_bitand },
+ { "^", "ii", 'i', fun_bitxor },
+ { "<<", "ii", 'i', fun_bitshl },
+ { ">>", "ii", 'i', fun_bitshr },
+ { "not", "i", 'i', fun_not },
+ { "max", "ii", 'i', fun_max },
+ { "min", "ii", 'i', fun_min },
+ { "is_in", "la", 'i', fun_is_in },
+ { "if_then_else", "i__", '_', fun_if_then_else },
+ { "skill", "ei", 'i', fun_skill },
+ BATTLE_RECORD(str),
+ BATTLE_RECORD(agi),
+ BATTLE_RECORD(vit),
+ BATTLE_RECORD(dex),
+ BATTLE_RECORD(luk),
+ BATTLE_RECORD(int),
+ BATTLE_RECORD2("level", lv),
+ BATTLE_RECORD(hp),
+ BATTLE_RECORD(max_hp),
+ BATTLE_RECORD(sp),
+ BATTLE_RECORD(max_sp),
+ { "dir", "e", 'd', fun_get_dir },
+ { "name_of", ".", 's', fun_name_of },
+ { "location", "e", 'l', fun_location },
+ { "random", "i", 'i', fun_random },
+ { "random_dir", "i", 'd', fun_random_dir },
+ { "hash_entity", "e", 'i', fun_hash_entity },
+ { "is_married", "e", 'i', fun_is_married },
+ { "partner", "e", 'e', fun_partner },
+ { "awayfrom", "ldi", 'l', fun_awayfrom },
+ { "failed", "_", 'i', fun_failed },
+ { "pc", "s", 'e', fun_pc },
+ { "npc", "s", 'e', fun_npc },
+ { "distance", "ll", 'i', fun_distance },
+ { "rdistance", "ll", 'i', fun_rdistance },
+ { "anchor", "s", 'a', fun_anchor },
+ { "random_location", "a", 'l', fun_pick_location },
+ { "script_int", "es", 'i', fun_read_script_int },
+ { "rbox", "li", 'a', fun_rbox },
+ { "count_item", "e.", 'i', fun_count_item },
+ { "line_of_sight", "ll", 'i', fun_line_of_sight },
+ { "running_status_update", "ei", 'i', fun_running_status_update },
+ { "element", "e", 'i', fun_element },
+ { "element_level", "e", 'i', fun_element_level },
+ { "has_shroud", "e", 'i', fun_has_shroud },
+ { NULL, NULL, '.', NULL }
+};
+
+static int functions_are_sorted = 0;
+
+int
+compare_fun(const void *lhs, const void *rhs)
+{
+ return strcmp(((fun_t *)lhs)->name,
+ ((fun_t *)rhs)->name);
+}
+
+fun_t *
+magic_get_fun(char *name, int *index)
+{
+ static int functions_nr;
+ fun_t *result;
+ fun_t key;
+
+ if (!functions_are_sorted) {
+ fun_t *it = functions;
+
+ while (it->name) ++it;
+ functions_nr = it - functions;
+
+ qsort(functions, functions_nr, sizeof(fun_t),
+ compare_fun);
+ functions_are_sorted = 1;
+ }
+
+ key.name = name;
+ result = (fun_t *) bsearch(&key, functions, functions_nr, sizeof(fun_t),
+ compare_fun);
+
+ if (result && index)
+ *index = result - functions;
+
+ return result;
+}
+
+static int // 1 on failure
+eval_location(env_t *env, location_t *dest, e_location_t *expr)
+{
+ val_t m, x, y;
+ magic_eval(env, &m, expr->m);
+ magic_eval(env, &x, expr->x);
+ magic_eval(env, &y, expr->y);
+
+ if (CHECK_TYPE(&m, TY_STRING)
+ && CHECK_TYPE(&x, TY_INT)
+ && CHECK_TYPE(&y, TY_INT)) {
+ int map_id = map_mapname2mapid(m.v.v_string);
+ magic_clear_var(&m);
+ if (map_id < 0)
+ return 1;
+ dest->m = map_id;
+ dest->x = x.v.v_int;
+ dest->y = y.v.v_int;
+ return 0;
+ } else {
+ magic_clear_var(&m);
+ magic_clear_var(&x);
+ magic_clear_var(&y);
+ return 1;
+ }
+}
+
+static area_t *
+eval_area(env_t *env, e_area_t *expr)
+{
+ area_t *area = malloc(sizeof(area_t));
+ area->ty = expr->ty;
+
+ switch (expr->ty) {
+ case AREA_LOCATION:
+ area->size = 1;
+ if (eval_location(env, &area->a.a_loc, &expr->a.a_loc)) {
+ free(area);
+ return NULL;
+ } else
+ return area;
+
+ case AREA_UNION: {
+ int i, fail = 0;
+ for (i = 0; i < 2; i++) {
+ area->a.a_union[i] = eval_area(env, expr->a.a_union[i]);
+ if (!area->a.a_union[i])
+ fail = 1;
+ }
+
+ if (fail) {
+ for (i = 0; i < 2; i++) {
+ if (area->a.a_union[i])
+ free_area(area->a.a_union[i]);
+ }
+ free(area);
+ return NULL;
+ }
+ area->size = area->a.a_union[0]->size + area->a.a_union[1]->size;
+ return area;
+ }
+
+ case AREA_RECT: {
+ val_t width, height;
+ magic_eval(env, &width, expr->a.a_rect.width);
+ magic_eval(env, &height, expr->a.a_rect.height);
+
+ area->a.a_rect.width = width.v.v_int;
+ area->a.a_rect.height = height.v.v_int;
+
+ if (CHECK_TYPE(&width, TY_INT)
+ && CHECK_TYPE(&height, TY_INT)
+ && !eval_location (env, &(area->a.a_rect.loc), &expr->a.a_rect.loc)) {
+ area->size = area->a.a_rect.width * area->a.a_rect.height;
+ magic_clear_var(&width);
+ magic_clear_var(&height);
+ return area;
+ } else {
+ free(area);
+ magic_clear_var(&width);
+ magic_clear_var(&height);
+ return NULL;
+ }
+ }
+
+ case AREA_BAR: {
+ val_t width, depth, dir;
+ magic_eval(env, &width, expr->a.a_bar.width);
+ magic_eval(env, &depth, expr->a.a_bar.depth);
+ magic_eval(env, &dir, expr->a.a_bar.dir);
+
+ area->a.a_bar.width = width.v.v_int;
+ area->a.a_bar.depth = depth.v.v_int;
+ area->a.a_bar.dir = dir.v.v_int;
+
+ if (CHECK_TYPE(&width, TY_INT)
+ && CHECK_TYPE(&depth, TY_INT)
+ && CHECK_TYPE(&dir, TY_DIR)
+ && !eval_location (env, &area->a.a_bar.loc, &expr->a.a_bar.loc)) {
+ area->size = (area->a.a_bar.width * 2 + 1) * area->a.a_bar.depth;
+ magic_clear_var(&width);
+ magic_clear_var(&depth);
+ magic_clear_var(&dir);
+ return area;
+ } else {
+ free(area);
+ magic_clear_var(&width);
+ magic_clear_var(&depth);
+ magic_clear_var(&dir);
+ return NULL;
+ }
+ }
+
+ default:
+ fprintf(stderr, "INTERNAL ERROR: Unknown area type %d\n", area->ty);
+ free(area);
+ return NULL;
+ }
+}
+
+static int
+type_key(char ty_key)
+{
+ switch (ty_key) {
+ case 'i': return TY_INT;
+ case 'd': return TY_DIR;
+ case 's': return TY_STRING;
+ case 'e': return TY_ENTITY;
+ case 'l': return TY_LOCATION;
+ case 'a': return TY_AREA;
+ case 'S': return TY_SPELL;
+ case 'I': return TY_INVOCATION;
+ default: return -1;
+ }
+}
+
+int
+magic_signature_check(char *opname, char *funname, char *signature, int args_nr, val_t *args, int line, int column)
+{
+ int i;
+ for (i = 0; i < args_nr; i++) {
+ val_t *arg = &args[i];
+ char ty_key = signature[i];
+ int ty = arg->ty;
+ int desired_ty = type_key(ty_key);
+
+ if (ty == TY_ENTITY) {
+ /* Dereference entities in preparation for calling function */
+ arg->v.v_entity = map_id2bl(arg->v.v_int);
+ if (!arg->v.v_entity)
+ ty = arg->ty = TY_FAIL;
+ } else if (ty == TY_INVOCATION) {
+ arg->v.v_invocation = (invocation_t *) map_id2bl(arg->v.v_int);
+ if (!arg->v.v_entity)
+ ty = arg->ty = TY_FAIL;
+ }
+
+ if (!ty_key) {
+ fprintf(stderr, "[magic-eval]: L%d:%d: Too many arguments (%d) to %s `%s'\n",
+ line, column, args_nr, opname, funname);
+ return 1;
+ }
+
+ if (ty == TY_FAIL
+ && ty_key != '_')
+ return 1; /* Fail `in a sane way': This is a perfectly permissible error */
+
+ if (ty == desired_ty
+ || desired_ty < 0 /* `dontcare' */)
+ continue;
+
+ if (ty == TY_UNDEF) {
+ fprintf(stderr, "[magic-eval]: L%d:%d: Argument #%d to %s `%s' undefined\n",
+ line, column, i + 1, opname, funname);
+ return 1;
+ }
+
+ /* If we are here, we have a type mismatch but no failure _yet_. Try to coerce. */
+ switch (desired_ty) {
+ case TY_INT: intify(arg); break; /* 100% success rate */
+ case TY_STRING: stringify(arg, 1); break; /* 100% success rate */
+ case TY_AREA: make_area(arg); break; /* Only works for locations */
+ case TY_LOCATION: make_location(arg); break; /* Only works for some areas */
+ default: break; /* We'll fail right below */
+ }
+
+ ty = arg->ty;
+ if (ty != desired_ty) { /* Coercion failed? */
+ if (ty != TY_FAIL)
+ fprintf(stderr, "[magic-eval]: L%d:%d: Argument #%d to %s `%s' of incorrect type (%d)\n",
+ line, column, i + 1, opname, funname, ty);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+magic_eval(env_t *env, val_t *dest, expr_t *expr)
+{
+ switch (expr->ty) {
+ case EXPR_VAL:
+ magic_copy_var(dest, &expr->e.e_val);
+ break;
+
+ case EXPR_LOCATION:
+ if (eval_location(env, &dest->v.v_location, &expr->e.e_location))
+ dest->ty = TY_FAIL;
+ else
+ dest->ty = TY_LOCATION;
+ break;
+
+ case EXPR_AREA:
+ if ((dest->v.v_area = eval_area(env, &expr->e.e_area)))
+ dest->ty = TY_AREA;
+ else
+ dest->ty = TY_FAIL;
+ break;
+
+ case EXPR_FUNAPP: {
+ val_t arguments[MAX_ARGS];
+ int args_nr = expr->e.e_funapp.args_nr;
+ int i;
+ fun_t *f = functions + expr->e.e_funapp.id;
+
+ for (i = 0; i < args_nr; ++i)
+ magic_eval(env, &arguments[i], expr->e.e_funapp.args[i]);
+ if (magic_signature_check("function", f->name, f->signature, args_nr, arguments,
+ expr->e.e_funapp.line_nr, expr->e.e_funapp.column)
+ || f->fun(env, args_nr, dest, arguments))
+ dest->ty = TY_FAIL;
+ else {
+ int dest_ty = type_key(f->ret_ty);
+ if (dest_ty != -1)
+ dest->ty = dest_ty;
+
+ /* translate entity back into persistent int */
+ if (dest->ty == TY_ENTITY) {
+ if (dest->v.v_entity)
+ dest->v.v_int = dest->v.v_entity->id;
+ else
+ dest->ty = TY_FAIL;
+ }
+ }
+
+ for (i = 0; i < args_nr; ++i)
+ magic_clear_var(&arguments[i]);
+ break;
+ }
+
+ case EXPR_ID: {
+ val_t v = VAR(expr->e.e_id);
+ magic_copy_var(dest, &v);
+ break;
+ }
+
+ default:
+ fprintf(stderr, "INTERNAL ERROR: Unknown expression type %d\n", expr->ty);
+ break;
+ }
+}
+
+int
+magic_eval_int(env_t *env, expr_t *expr)
+{
+ val_t result;
+ magic_eval(env, &result, expr);
+
+ if (result.ty == TY_FAIL
+ || result.ty == TY_UNDEF)
+ return 0;
+
+ intify(&result);
+
+ return result.v.v_int;
+}
+
+char *
+magic_eval_str(env_t *env, expr_t *expr)
+{
+ val_t result;
+ magic_eval(env, &result, expr);
+
+ if (result.ty == TY_FAIL
+ || result.ty == TY_UNDEF)
+ return strdup("?");
+
+ stringify(&result, 0);
+
+ return result.v.v_string;
+}
+
+expr_t *
+magic_new_expr(int ty)
+{
+ expr_t *expr = (expr_t *)malloc(sizeof(expr_t));
+ expr->ty = ty;
+ return expr;
+}
diff --git a/src/map/magic-expr.h b/src/map/magic-expr.h
new file mode 100644
index 0000000..5f58d3c
--- /dev/null
+++ b/src/map/magic-expr.h
@@ -0,0 +1,92 @@
+#ifndef MAGIC_EXPR_H_
+#define MAGIC_EXPR_H_
+#include "magic-interpreter.h"
+#include "magic-interpreter-aux.h"
+
+#ifndef MAX
+# define MAX(x,y) (((x)>(y)) ? (x) : (y))
+#endif
+#ifndef MIN
+# define MIN(x,y) (((x)<(y)) ? (x) : (y))
+#endif
+
+#ifndef INT_MAX
+# define INT_MAX (1<<30) // It's more than that, but this is quite sufficient for our purposes.
+#endif
+
+/*
+ * Argument types:
+ * i : int
+ * d : dir
+ * s : string
+ * e : entity
+ * l : location
+ * a : area
+ * S : spell
+ * I : invocation
+ * . : any, except for fail/undef
+ * _ : any, including fail, but not undef
+ */
+typedef struct fun {
+ char *name;
+ char *signature;
+ char ret_ty;
+ int (*fun)(env_t *env, int args_nr, val_t *result, val_t *args);
+} fun_t;
+
+typedef struct op {
+ char *name;
+ char *signature;
+ int (*op)(env_t *env, int args_nr, val_t *args);
+} op_t;
+
+/**
+ * Retrieves a function by name
+ * @param name The name to look up
+ * @return A function of that name, or NULL, and a function index
+ */
+fun_t *
+magic_get_fun(char *name, int *index);
+
+/**
+ * Retrieves an operation by name
+ * @param name The name to look up
+ * @return An operation of that name, or NULL, and a function index
+ */
+op_t *
+magic_get_op(char *name, int *index);
+
+/**
+ * Evaluates an expression and stores the result in `dest'
+ */
+void
+magic_eval(env_t *env, val_t *dest, expr_t *expr);
+
+/**
+ * Evaluates an expression and coerces the result into an integer
+ */
+int
+magic_eval_int(env_t *env, expr_t *expr);
+
+/**
+ * Evaluates an expression and coerces the result into a string
+ */
+char *
+magic_eval_str(env_t *env, expr_t *expr);
+
+int
+map_is_solid(int m, int x, int y);
+
+expr_t *
+magic_new_expr(int ty);
+
+void
+magic_clear_var(val_t *v);
+
+void
+magic_copy_var(val_t *dest, val_t *src);
+
+void
+magic_random_location(location_t *dest, area_t *area);
+
+#endif /* !defined(MAGIC_EXPR_H_) */ \ No newline at end of file
diff --git a/src/map/magic-interpreter-aux.h b/src/map/magic-interpreter-aux.h
new file mode 100644
index 0000000..975e502
--- /dev/null
+++ b/src/map/magic-interpreter-aux.h
@@ -0,0 +1,8 @@
+#ifndef MAGIC_INTERPRETER_AUX_H_
+#define MAGIC_INTERPRETER_AUX_H_
+
+#define CHECK_TYPE(v, t) ((v)->ty == t)
+
+#define VAR(i) ((!env->vars || env->vars[i].ty == TY_UNDEF)? env->base_env->vars[i] : env->vars[i])
+
+#endif /* !defined(MAGIC_INTERPRETER_AUX_H_) */
diff --git a/src/map/magic-interpreter-base.c b/src/map/magic-interpreter-base.c
new file mode 100644
index 0000000..9be6f81
--- /dev/null
+++ b/src/map/magic-interpreter-base.c
@@ -0,0 +1,540 @@
+#include "magic.h"
+#include "magic-interpreter.h"
+#include "magic-expr.h"
+#include "magic-interpreter-aux.h"
+
+static void
+set_int_p(val_t *v, int i, int t)
+{
+ v->ty = t;
+ v->v.v_int = i;
+}
+#define set_int(v, i) set_int_p(v, i, TY_INT)
+#define set_dir(v, i) set_int_p(v, i, TY_DIR)
+
+#define SETTER(tty, dyn_ty, field) (val_t *v, tty x) { v->ty = dyn_ty; v->v.field = x; }
+
+static void
+set_string SETTER(char *, TY_STRING, v_string);
+
+static void
+set_entity(val_t *v, entity_t *e)
+{
+ v->ty = TY_ENTITY;
+ v->v.v_int = e->id;
+}
+
+static void
+set_invocation(val_t *v, invocation_t *i)
+{
+ v->ty = TY_INVOCATION;
+ v->v.v_int = i->bl.id;
+}
+
+static void
+set_spell SETTER(spell_t *, TY_SPELL, v_spell);
+
+#define setenv(f, v, x) f(&(env->vars[v]), x)
+
+#define set_env_int(v, x) setenv(set_int, v, x)
+#define set_env_dir(v, x) setenv(set_dir, v, x)
+#define set_env_string(v, x) setenv(set_string, v, x)
+#define set_env_entity(v, x) setenv(set_entity, v, x)
+#define set_env_location(v, x) setenv(set_location, v, x)
+#define set_env_area(v, x) setenv(set_area, v, x)
+#define set_env_invocation(v, x) setenv(set_invocation, v, x)
+#define set_env_spell(v, x) setenv(set_spell, v, x)
+
+
+
+magic_conf_t magic_conf; /* Global magic conf */
+env_t magic_default_env = { &magic_conf, NULL };
+
+static int spells_sorted = 0;
+
+char *
+magic_find_invocation(char *spellname)
+{
+ int i;
+
+ for (i = 0; i < abs(magic_conf.spells_nr); i++)
+ if (!strcmp(magic_conf.spells[i]->name, spellname))
+ return magic_conf.spells[i]->invocation;
+
+ return NULL;
+}
+
+static int
+spell_compare(const void *lhs, const void *rhs)
+{
+ return strcmp((*((spell_t **)lhs))->invocation,
+ (*((spell_t **)rhs))->invocation);
+}
+
+spell_t *
+magic_find_spell(char *invocation)
+{
+ spell_t key;
+ spell_t *keyp = &key;
+ spell_t **retval;
+
+ if (!spells_sorted) {
+ qsort(magic_conf.spells, magic_conf.spells_nr, sizeof(spell_t *), spell_compare);
+ spells_sorted = 1;
+ }
+
+ key.invocation = invocation;
+
+ retval = ((spell_t **)bsearch(&keyp, magic_conf.spells, magic_conf.spells_nr, sizeof(spell_t *), spell_compare));
+
+ if (!retval)
+ return NULL;
+ else
+ return *retval;
+}
+
+
+/* -------------------------------------------------------------------------------- */
+/* Spell anchors */
+/* -------------------------------------------------------------------------------- */
+
+static int
+compare_teleport_anchor(const void * lhs, const void *rhs)
+{
+ return strcmp((*((teleport_anchor_t **)lhs))->invocation,
+ (*((teleport_anchor_t **)rhs))->invocation);
+}
+
+char *
+magic_find_anchor_invocation(char *anchor_name)
+{
+ int i;
+
+ for (i = 0; i < abs(magic_conf.anchors_nr); i++)
+ if (!strcmp(magic_conf.anchors[i]->name, anchor_name))
+ return magic_conf.anchors[i]->invocation;
+
+ return NULL;
+}
+
+
+teleport_anchor_t *
+magic_find_anchor(char *name)
+{
+ teleport_anchor_t key;
+ teleport_anchor_t *keyp = &key;
+ teleport_anchor_t **retval;
+
+ if (magic_conf.anchors_nr > 0) { /* unsorted */
+ qsort(magic_conf.anchors, magic_conf.anchors_nr, sizeof(teleport_anchor_t *),
+ compare_teleport_anchor);
+ magic_conf.anchors_nr = -magic_conf.anchors_nr;
+ }
+
+ key.invocation = name;
+
+ retval = (teleport_anchor_t **) bsearch(&keyp,
+ magic_conf.anchors, -magic_conf.anchors_nr, sizeof(teleport_anchor_t *),
+ compare_teleport_anchor);
+
+ if (!retval)
+ return NULL;
+ else
+ return *retval;
+}
+
+
+/* -------------------------------------------------------------------------------- */
+/* Spell guard checks */
+/* -------------------------------------------------------------------------------- */
+
+static env_t *
+alloc_env(magic_conf_t *conf)
+{
+ env_t *env = (env_t*) aCalloc(sizeof (env_t), 1);
+ env->vars = (val_t *) aCalloc(sizeof (val_t), conf->vars_nr);
+ env->base_env = conf;
+ return env;
+}
+
+static env_t *
+clone_env(env_t *src)
+{
+ env_t *retval = alloc_env(src->base_env);
+ int i;
+
+ for (i = 0; i < src->base_env->vars_nr; i++)
+ magic_copy_var(&retval->vars[i],
+ &src->vars[i]);
+
+ return retval;
+}
+
+void
+magic_free_env(env_t *env)
+{
+ int i;
+ for (i = 0; i < env->base_env->vars_nr; i++)
+ magic_clear_var(&env->vars[i]);
+ free(env);
+}
+
+env_t *
+spell_create_env(magic_conf_t *conf, spell_t *spell, character_t *caster, int spellpower, char *param)
+{
+ env_t *env = alloc_env(conf);
+
+ switch (spell->spellarg_ty) {
+
+ case SPELLARG_STRING:
+ set_env_string(spell->arg, param);
+ break;
+
+ case SPELLARG_PC:{
+ character_t *subject = map_nick2sd(param);
+ if (!subject)
+ subject = caster;
+ set_env_entity(spell->arg, &subject->bl);
+ free(param);
+ break;
+ }
+
+ case SPELLARG_NONE:
+ break;
+ default:
+ fprintf(stderr, "Unexpected spellarg type %d\n", spell->spellarg_ty);
+ }
+
+ set_env_entity(VAR_CASTER, &caster->bl);
+ set_env_int(VAR_SPELLPOWER, spellpower);
+ set_env_spell(VAR_SPELL, spell);
+
+ return env;
+}
+
+
+
+static void
+free_components(component_t **component_holder)
+{
+ if (*component_holder == NULL)
+ return;
+ free_components(&(*component_holder)->next);
+ free(*component_holder);
+ *component_holder = NULL;
+}
+
+void
+magic_add_component(component_t **component_holder, int id, int count)
+{
+ if (count <= 0)
+ return;
+
+ if (*component_holder == NULL) {
+ component_t *component = (component_t *)malloc(sizeof(component_t));
+ component->next = NULL;
+ component->item_id = id;
+ component->count = count;
+ *component_holder = component;
+ } else {
+ component_t *component = *component_holder;
+ if (component->item_id == id) {
+ component->count += count;
+ return;
+ } else magic_add_component(&component->next, id, count);
+ /* Tail-recurse; gcc can optimise this. Not that it matters. */
+ }
+}
+
+static void
+copy_components(component_t **component_holder, component_t *component)
+{
+ if (component == NULL)
+ return;
+
+ magic_add_component(component_holder, component->item_id, component->count);
+ copy_components(component_holder, component->next);
+}
+
+
+typedef struct spellguard_check {
+ component_t *catalysts, *components;
+ int mana, casttime;
+} spellguard_check_t;
+
+
+static int
+check_prerequisites(character_t *caster, component_t *component)
+{
+ while (component) {
+ if (pc_count_all_items(caster, component->item_id)
+ < component->count)
+ return 0; /* insufficient */
+
+ component = component->next;
+ }
+
+ return 1;
+}
+
+static void
+consume_components(character_t *caster, component_t *component)
+{
+ while (component) {
+ pc_remove_items(caster, component->item_id, component->count);
+ component = component->next;
+ }
+}
+
+
+static int
+spellguard_can_satisfy(spellguard_check_t *check, character_t *caster, env_t *env)
+{
+ int tick = gettick();
+
+ int retval = (caster->cast_tick <= tick /* Hasn't cast a spell too recently */
+ && check->mana <= caster->status.sp
+ && check_prerequisites(caster, check->catalysts)
+ && check_prerequisites(caster, check->components));
+
+ if (retval) {
+ int casttime = check->casttime;
+
+ if (VAR(VAR_MIN_CASTTIME).ty == TY_INT)
+ casttime = MAX(casttime, VAR(VAR_MIN_CASTTIME).v.v_int);
+
+ caster->cast_tick = tick + casttime; /* Make sure not to cast too frequently */
+
+ consume_components(caster, check->components);
+ pc_heal(caster, 0, -check->mana);
+ }
+
+ return retval;
+}
+
+static effect_set_t *
+spellguard_check_sub(spellguard_check_t *check, spellguard_t *guard, character_t *caster, env_t *env)
+{
+ if (guard == NULL)
+ return NULL;
+
+ switch (guard->ty) {
+ case SPELLGUARD_CONDITION:
+ if (!magic_eval_int(env, guard->s.s_condition))
+ return NULL;
+ break;
+
+ case SPELLGUARD_COMPONENTS:
+ copy_components(&check->components, guard->s.s_components);
+ break;
+
+ case SPELLGUARD_CATALYSTS:
+ copy_components(&check->catalysts, guard->s.s_catalysts);
+ break;
+
+ case SPELLGUARD_CHOICE: {
+ spellguard_check_t altcheck = *check;
+ effect_set_t *retval;
+
+ altcheck.components = NULL;
+ altcheck.catalysts = NULL;
+
+ copy_components(&altcheck.catalysts, check->catalysts);
+ copy_components(&altcheck.components, check->components);
+
+ retval = spellguard_check_sub(&altcheck, guard->next, caster, env);
+ free_components(&altcheck.catalysts);
+ free_components(&altcheck.components);
+ if (retval)
+ return retval;
+ else
+ return spellguard_check_sub(check, guard->s.s_alt, caster, env);
+ }
+
+ case SPELLGUARD_MANA:
+ check->mana += magic_eval_int(env, guard->s.s_mana);
+ break;
+
+ case SPELLGUARD_CASTTIME:
+ check->casttime += magic_eval_int(env, guard->s.s_mana);
+ break;
+
+ case SPELLGUARD_EFFECT:
+ if (spellguard_can_satisfy(check, caster, env))
+ return &guard->s.s_effect;
+ else
+ return NULL;
+
+ default:
+ fprintf(stderr, "Unexpected spellguard type %d\n", guard->ty);
+ return NULL;
+ }
+
+ return spellguard_check_sub(check, guard->next, caster, env);
+}
+
+static effect_set_t *
+check_spellguard(spellguard_t *guard, character_t *caster, env_t *env)
+{
+ spellguard_check_t check;
+ effect_set_t *retval;
+ check.catalysts = NULL;
+ check.components = NULL;
+ check.mana = check.casttime = 0;
+
+ retval = spellguard_check_sub(&check, guard, caster, env);
+
+ free_components(&check.catalysts);
+ free_components(&check.components);
+
+ return retval;
+}
+
+/* -------------------------------------------------------------------------------- */
+/* Public API */
+/* -------------------------------------------------------------------------------- */
+
+
+effect_set_t *
+spell_trigger(spell_t *spell, character_t *caster, env_t *env)
+{
+ int i;
+ spellguard_t *guard = spell->spellguard;
+
+ for (i = 0; i < spell->letdefs_nr; i++)
+ magic_eval(env,
+ &env->vars[spell->letdefs[i].id],
+ spell->letdefs[i].expr);
+
+ return check_spellguard(guard, caster, env);
+}
+
+static void
+spell_set_location(invocation_t *invocation, entity_t *entity)
+{
+ magic_clear_var(&invocation->env->vars[VAR_LOCATION]);
+ invocation->env->vars[VAR_LOCATION].ty = TY_LOCATION;
+ invocation->env->vars[VAR_LOCATION].v.v_location.m = entity->m;
+ invocation->env->vars[VAR_LOCATION].v.v_location.x = entity->x;
+ invocation->env->vars[VAR_LOCATION].v.v_location.y = entity->y;
+}
+
+void
+spell_update_location(invocation_t *invocation)
+{
+ if (invocation->spell->flags & SPELL_FLAG_LOCAL)
+ return;
+ else {
+ character_t *owner = (character_t *)map_id2bl(invocation->subject);
+ if (!owner)
+ return;
+
+ spell_set_location(invocation, (entity_t *) owner);
+ }
+}
+
+invocation_t *
+spell_instantiate(effect_set_t *effect_set, env_t *env)
+{
+ invocation_t *retval = (invocation_t *)aCalloc(sizeof(invocation_t), 1);
+ entity_t *caster;
+
+ retval->env = env;
+
+ retval->caster = VAR(VAR_CASTER).v.v_int;
+ retval->spell = VAR(VAR_SPELL).v.v_spell;
+ retval->stack_size = 0;
+ retval->current_effect = effect_set->effect;
+ retval->trigger_effect = effect_set->at_trigger;
+ retval->end_effect = effect_set->at_end;
+
+ caster = map_id2bl(retval->caster); // must still exist
+ retval->bl.id = map_addobject(&retval->bl);
+ retval->bl.type = BL_SPELL;
+ retval->bl.m = caster->m;
+ retval->bl.x = caster->x;
+ retval->bl.y = caster->y;
+
+ map_addblock(&retval->bl);
+ set_env_invocation(VAR_INVOCATION, retval);
+
+ return retval;
+}
+
+invocation_t *
+spell_clone_effect(invocation_t *base)
+{
+ invocation_t *retval = (invocation_t *)malloc(sizeof(invocation_t));
+ env_t *env;
+
+ memcpy(retval, base, sizeof(invocation_t));
+
+ retval->env = clone_env(retval->env);
+ env = retval->env;
+ retval->current_effect = retval->trigger_effect;
+ retval->next_invocation = NULL;
+ retval->end_effect = NULL;
+ retval->script_pos = 0;
+ retval->stack_size = 0;
+ retval->timer = 0;
+ retval->subject = 0;
+ retval->status_change_refs_nr = 0;
+ retval->status_change_refs = NULL;
+ retval->flags = 0;
+
+ retval->bl.id = 0;
+ retval->bl.prev = NULL;
+ retval->bl.next = NULL;
+
+ retval->bl.id = map_addobject(&retval->bl);
+ set_env_invocation(VAR_INVOCATION, retval);
+
+ return retval;
+}
+
+
+void
+spell_bind(character_t *subject, invocation_t *invocation)
+{
+ /* Only bind nonlocal spells */
+
+ if (!(invocation->spell->flags & SPELL_FLAG_LOCAL)) {
+ if (invocation->flags & INVOCATION_FLAG_BOUND
+ || invocation->subject
+ || invocation->next_invocation) {
+ int *i = NULL;
+ fprintf(stderr, "[magic] INTERNAL ERROR: Attempt to re-bind spell invocation `%s'\n", invocation->spell->name);
+ *i = 1;
+ return;
+ }
+
+ invocation->next_invocation = subject->active_spells;
+ subject->active_spells = invocation;
+ invocation->flags |= INVOCATION_FLAG_BOUND;
+ invocation->subject = subject->bl.id;
+ }
+
+ spell_set_location(invocation, (entity_t*) subject);
+}
+
+int
+spell_unbind(character_t *subject, invocation_t *invocation)
+{
+ invocation_t **seeker = &subject->active_spells;
+
+ while (*seeker) {
+ if (*seeker == invocation) {
+ *seeker = invocation->next_invocation;
+
+ invocation->flags &= ~INVOCATION_FLAG_BOUND;
+ invocation->next_invocation = NULL;
+ invocation->subject = 0;
+
+ return 0;
+ }
+ seeker = &((*seeker)->next_invocation);
+ }
+
+ return 1;
+}
+
diff --git a/src/map/magic-interpreter-lexer.c b/src/map/magic-interpreter-lexer.c
new file mode 100644
index 0000000..b5a42a0
--- /dev/null
+++ b/src/map/magic-interpreter-lexer.c
@@ -0,0 +1,2370 @@
+#line 2 "magic-interpreter-lexer.c"
+
+#line 4 "magic-interpreter-lexer.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer magic_frontend__create_buffer
+#define yy_delete_buffer magic_frontend__delete_buffer
+#define yy_flex_debug magic_frontend__flex_debug
+#define yy_init_buffer magic_frontend__init_buffer
+#define yy_flush_buffer magic_frontend__flush_buffer
+#define yy_load_buffer_state magic_frontend__load_buffer_state
+#define yy_switch_to_buffer magic_frontend__switch_to_buffer
+#define yyin magic_frontend_in
+#define yyleng magic_frontend_leng
+#define yylex magic_frontend_lex
+#define yylineno magic_frontend_lineno
+#define yyout magic_frontend_out
+#define yyrestart magic_frontend_restart
+#define yytext magic_frontend_text
+#define yywrap magic_frontend_wrap
+#define yyalloc magic_frontend_alloc
+#define yyrealloc magic_frontend_realloc
+#define yyfree magic_frontend_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE magic_frontend_restart(magic_frontend_in )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int magic_frontend_leng;
+
+extern FILE *magic_frontend_in, *magic_frontend_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE magic_frontend_lex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-magic_frontend_lineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < magic_frontend_leng; ++yyl )\
+ if ( magic_frontend_text[yyl] == '\n' )\
+ --magic_frontend_lineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up magic_frontend_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up magic_frontend_text again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via magic_frontend_restart()), so that the user can continue scanning by
+ * just pointing magic_frontend_in at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when magic_frontend_text is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int magic_frontend_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow magic_frontend_wrap()'s to do buffer switches
+ * instead of setting up a fresh magic_frontend_in. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void magic_frontend_restart (FILE *input_file );
+void magic_frontend__switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE magic_frontend__create_buffer (FILE *file,int size );
+void magic_frontend__delete_buffer (YY_BUFFER_STATE b );
+void magic_frontend__flush_buffer (YY_BUFFER_STATE b );
+void magic_frontend_push_buffer_state (YY_BUFFER_STATE new_buffer );
+void magic_frontend_pop_buffer_state (void );
+
+static void magic_frontend_ensure_buffer_stack (void );
+static void magic_frontend__load_buffer_state (void );
+static void magic_frontend__init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER magic_frontend__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE magic_frontend__scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE magic_frontend__scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE magic_frontend__scan_bytes (yyconst char *bytes,int len );
+
+void *magic_frontend_alloc (yy_size_t );
+void *magic_frontend_realloc (void *,yy_size_t );
+void magic_frontend_free (void * );
+
+#define yy_new_buffer magic_frontend__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ magic_frontend_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ magic_frontend__create_buffer(magic_frontend_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ magic_frontend_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ magic_frontend__create_buffer(magic_frontend_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define magic_frontend_wrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *magic_frontend_in = (FILE *) 0, *magic_frontend_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int magic_frontend_lineno;
+
+int magic_frontend_lineno = 1;
+
+extern char *magic_frontend_text;
+#define yytext_ptr magic_frontend_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up magic_frontend_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ magic_frontend_leng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 83
+#define YY_END_OF_BUFFER 84
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[230] =
+ { 0,
+ 0, 0, 84, 82, 81, 81, 82, 82, 82, 23,
+ 33, 17, 18, 21, 19, 28, 20, 22, 76, 76,
+ 27, 26, 14, 9, 13, 29, 78, 78, 78, 78,
+ 7, 78, 78, 78, 78, 78, 5, 78, 78, 78,
+ 1, 78, 3, 31, 32, 34, 82, 30, 12, 0,
+ 75, 0, 0, 79, 24, 76, 0, 0, 35, 16,
+ 11, 10, 49, 15, 36, 78, 78, 78, 78, 78,
+ 78, 72, 78, 78, 78, 78, 67, 47, 78, 78,
+ 78, 78, 6, 4, 39, 63, 78, 78, 8, 78,
+ 78, 78, 78, 2, 78, 78, 78, 40, 78, 0,
+
+ 0, 74, 25, 0, 75, 0, 0, 80, 77, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 48, 78, 71, 46, 78, 78, 64, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78, 0, 0,
+ 74, 78, 78, 78, 78, 38, 78, 78, 78, 78,
+ 78, 69, 78, 78, 78, 54, 78, 78, 78, 56,
+ 78, 78, 78, 78, 68, 78, 73, 57, 60, 78,
+ 58, 78, 78, 78, 62, 78, 78, 78, 44, 78,
+ 78, 78, 45, 78, 78, 78, 78, 78, 78, 78,
+ 78, 59, 65, 78, 78, 78, 43, 50, 66, 78,
+
+ 78, 78, 78, 78, 78, 70, 78, 51, 78, 41,
+ 78, 55, 78, 78, 78, 78, 61, 52, 78, 37,
+ 78, 53, 78, 78, 78, 78, 78, 42, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 1, 7, 8, 1, 9,
+ 10, 11, 12, 13, 14, 1, 15, 16, 17, 17,
+ 17, 17, 17, 17, 17, 17, 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, 34, 46, 34, 47, 34,
+ 48, 49, 50, 51, 23, 1, 52, 52, 52, 52,
+
+ 52, 52, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 53,
+ 34, 34, 54, 55, 56, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[57] =
+ { 0,
+ 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 2, 1, 3, 3, 1, 1, 1,
+ 1, 1, 2, 1, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
+ 1, 3, 2, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[238] =
+ { 0,
+ 0, 0, 279, 280, 280, 280, 257, 52, 274, 280,
+ 268, 280, 280, 280, 280, 280, 42, 260, 44, 46,
+ 280, 280, 44, 46, 48, 280, 45, 232, 47, 234,
+ 43, 233, 0, 44, 46, 51, 48, 229, 51, 241,
+ 63, 79, 244, 280, 280, 280, 31, 213, 280, 78,
+ 280, 97, 264, 280, 280, 89, 263, 0, 280, 280,
+ 280, 280, 280, 280, 280, 0, 226, 55, 235, 76,
+ 76, 0, 233, 219, 82, 219, 0, 0, 216, 232,
+ 220, 231, 0, 0, 0, 0, 217, 214, 0, 218,
+ 220, 223, 209, 0, 208, 213, 219, 201, 213, 39,
+
+ 88, 280, 280, 110, 111, 118, 242, 280, 0, 202,
+ 205, 200, 216, 204, 195, 213, 197, 193, 206, 205,
+ 0, 200, 203, 0, 206, 205, 0, 202, 183, 198,
+ 186, 189, 191, 192, 193, 183, 195, 175, 68, 119,
+ 76, 174, 189, 183, 180, 0, 170, 177, 173, 167,
+ 183, 0, 165, 183, 171, 0, 177, 172, 166, 0,
+ 167, 164, 172, 160, 0, 157, 0, 0, 0, 167,
+ 0, 164, 149, 136, 0, 129, 125, 144, 0, 142,
+ 127, 122, 0, 134, 120, 124, 134, 130, 121, 114,
+ 127, 0, 0, 123, 109, 124, 0, 0, 0, 110,
+
+ 108, 121, 120, 104, 109, 0, 103, 0, 99, 0,
+ 100, 0, 98, 96, 110, 124, 0, 0, 93, 0,
+ 110, 0, 96, 106, 99, 91, 87, 0, 280, 175,
+ 178, 180, 183, 186, 189, 125, 192
+ } ;
+
+static yyconst flex_int16_t yy_def[238] =
+ { 0,
+ 229, 1, 229, 229, 229, 229, 229, 230, 231, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 229, 229, 229, 233, 229, 229, 230,
+ 229, 234, 231, 229, 229, 229, 235, 236, 229, 229,
+ 229, 229, 229, 229, 229, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 233,
+
+ 237, 229, 229, 230, 230, 234, 235, 229, 236, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 233, 237,
+ 233, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+ 232, 232, 232, 232, 232, 232, 232, 232, 0, 229,
+ 229, 229, 229, 229, 229, 229, 229
+ } ;
+
+static yyconst flex_int16_t yy_nxt[337] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 4, 26, 27, 28, 29, 30, 31, 32,
+ 33, 33, 34, 33, 33, 35, 36, 37, 38, 39,
+ 33, 40, 41, 42, 33, 43, 33, 44, 4, 45,
+ 46, 33, 33, 47, 48, 4, 51, 56, 56, 56,
+ 56, 56, 56, 59, 60, 61, 62, 63, 64, 65,
+ 67, 70, 73, 77, 79, 81, 83, 86, 74, 101,
+ 75, 78, 51, 111, 80, 71, 102, 101, 68, 82,
+ 100, 89, 87, 84, 102, 90, 58, 91, 112, 50,
+
+ 52, 105, 92, 95, 56, 56, 93, 96, 94, 121,
+ 97, 114, 117, 118, 51, 51, 101, 98, 115, 116,
+ 50, 100, 105, 102, 101, 122, 52, 109, 228, 227,
+ 226, 102, 225, 224, 223, 222, 140, 221, 220, 219,
+ 218, 217, 216, 141, 215, 106, 214, 213, 212, 211,
+ 210, 209, 208, 207, 206, 205, 204, 203, 52, 52,
+ 202, 201, 200, 199, 198, 197, 106, 140, 196, 195,
+ 194, 193, 192, 191, 141, 50, 50, 50, 53, 53,
+ 53, 66, 66, 100, 100, 100, 104, 104, 104, 107,
+ 107, 107, 139, 139, 139, 190, 189, 188, 187, 186,
+
+ 185, 184, 183, 182, 181, 180, 179, 178, 177, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168, 167, 166,
+ 165, 164, 163, 162, 161, 160, 159, 158, 157, 156,
+ 155, 154, 153, 152, 151, 150, 149, 148, 147, 146,
+ 145, 144, 143, 142, 108, 138, 137, 136, 135, 134,
+ 133, 132, 131, 130, 129, 128, 127, 126, 125, 124,
+ 123, 120, 119, 113, 110, 108, 54, 103, 99, 88,
+ 85, 76, 72, 69, 57, 55, 54, 49, 229, 3,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229
+ } ;
+
+static yyconst flex_int16_t yy_chk[337] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 8, 17, 17, 19,
+ 19, 20, 20, 23, 23, 23, 24, 24, 25, 25,
+ 27, 29, 31, 34, 35, 36, 37, 39, 31, 47,
+ 31, 34, 50, 68, 35, 29, 47, 100, 27, 36,
+ 101, 41, 39, 37, 100, 41, 19, 41, 68, 52,
+
+ 8, 52, 41, 42, 56, 56, 41, 42, 41, 75,
+ 42, 70, 71, 71, 104, 105, 139, 42, 70, 70,
+ 106, 140, 106, 139, 141, 75, 50, 236, 227, 226,
+ 225, 141, 224, 223, 221, 219, 101, 216, 215, 214,
+ 213, 211, 209, 101, 207, 52, 205, 204, 203, 202,
+ 201, 200, 196, 195, 194, 191, 190, 189, 104, 105,
+ 188, 187, 186, 185, 184, 182, 106, 140, 181, 180,
+ 178, 177, 176, 174, 140, 230, 230, 230, 231, 231,
+ 231, 232, 232, 233, 233, 233, 234, 234, 234, 235,
+ 235, 235, 237, 237, 237, 173, 172, 170, 166, 164,
+
+ 163, 162, 161, 159, 158, 157, 155, 154, 153, 151,
+ 150, 149, 148, 147, 145, 144, 143, 142, 138, 137,
+ 136, 135, 134, 133, 132, 131, 130, 129, 128, 126,
+ 125, 123, 122, 120, 119, 118, 117, 116, 115, 114,
+ 113, 112, 111, 110, 107, 99, 98, 97, 96, 95,
+ 93, 92, 91, 90, 88, 87, 82, 81, 80, 79,
+ 76, 74, 73, 69, 67, 57, 53, 48, 43, 40,
+ 38, 32, 30, 28, 18, 11, 9, 7, 3, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[84] =
+ { 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, };
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int magic_frontend__flex_debug;
+int magic_frontend__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *magic_frontend_text;
+#line 1 "magic-interpreter.l"
+#line 2 "magic-interpreter.l"
+#include "magic-interpreter.h"
+#include "magic-interpreter-parser.h"
+
+#ifdef HEADING
+# undef HEADING
+#endif
+
+#define FIXLOC magic_frontend_lloc.first_line = magic_frontend_lineno
+
+#define HEADING(dir) { magic_frontend_lval.i = dir; FIXLOC; return DIR; }
+
+#define YY_NO_INPUT 1
+#line 658 "magic-interpreter-lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int magic_frontend_lex_destroy (void );
+
+int magic_frontend_get_debug (void );
+
+void magic_frontend_set_debug (int debug_flag );
+
+YY_EXTRA_TYPE magic_frontend_get_extra (void );
+
+void magic_frontend_set_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *magic_frontend_get_in (void );
+
+void magic_frontend_set_in (FILE * in_str );
+
+FILE *magic_frontend_get_out (void );
+
+void magic_frontend_set_out (FILE * out_str );
+
+int magic_frontend_get_leng (void );
+
+char *magic_frontend_get_text (void );
+
+int magic_frontend_get_lineno (void );
+
+void magic_frontend_set_lineno (int line_number );
+
+YYSTYPE * magic_frontend_get_lval (void );
+
+void magic_frontend_set_lval (YYSTYPE * yylval_param );
+
+ YYLTYPE *magic_frontend_get_lloc (void );
+
+ void magic_frontend_set_lloc (YYLTYPE * yylloc_param );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int magic_frontend_wrap (void );
+#else
+extern int magic_frontend_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( magic_frontend_text, magic_frontend_leng, 1, magic_frontend_out )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( magic_frontend_in )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( magic_frontend_in ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, magic_frontend_in))==0 && ferror(magic_frontend_in)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(magic_frontend_in); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int magic_frontend_lex \
+ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param );
+
+#define YY_DECL int magic_frontend_lex \
+ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param )
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after magic_frontend_text and magic_frontend_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+ YYSTYPE * yylval;
+
+ YYLTYPE * yylloc;
+
+#line 22 "magic-interpreter.l"
+
+
+#line 855 "magic-interpreter-lexer.c"
+
+ yylval = yylval_param;
+
+ yylloc = yylloc_param;
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! magic_frontend_in )
+ magic_frontend_in = stdin;
+
+ if ( ! magic_frontend_out )
+ magic_frontend_out = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ magic_frontend_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ magic_frontend__create_buffer(magic_frontend_in,YY_BUF_SIZE );
+ }
+
+ magic_frontend__load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of magic_frontend_text. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 230 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 280 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < magic_frontend_leng; ++yyl )
+ if ( magic_frontend_text[yyl] == '\n' )
+
+ magic_frontend_lineno++;
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 24 "magic-interpreter.l"
+HEADING(0);
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 25 "magic-interpreter.l"
+HEADING(1);
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 26 "magic-interpreter.l"
+HEADING(2);
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 27 "magic-interpreter.l"
+HEADING(3);
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 28 "magic-interpreter.l"
+HEADING(4);
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 29 "magic-interpreter.l"
+HEADING(5);
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 30 "magic-interpreter.l"
+HEADING(6);
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 31 "magic-interpreter.l"
+HEADING(7);
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 32 "magic-interpreter.l"
+{FIXLOC; return '=';}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 33 "magic-interpreter.l"
+{FIXLOC; return EQ;}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 34 "magic-interpreter.l"
+{FIXLOC; return NEQ;}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 35 "magic-interpreter.l"
+{FIXLOC; return NEQ;}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 36 "magic-interpreter.l"
+{FIXLOC; return '>';}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 37 "magic-interpreter.l"
+{FIXLOC; return '<';}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 38 "magic-interpreter.l"
+{FIXLOC; return GTE;}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 39 "magic-interpreter.l"
+{FIXLOC; return LTE;}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 40 "magic-interpreter.l"
+{FIXLOC; return '(';}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 41 "magic-interpreter.l"
+{FIXLOC; return ')';}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 42 "magic-interpreter.l"
+{FIXLOC; return '+';}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 43 "magic-interpreter.l"
+{FIXLOC; return '-';}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 44 "magic-interpreter.l"
+{FIXLOC; return '*';}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 45 "magic-interpreter.l"
+{FIXLOC; return '/';}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 46 "magic-interpreter.l"
+{FIXLOC; return '%';}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 47 "magic-interpreter.l"
+{FIXLOC; return ANDAND;}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 48 "magic-interpreter.l"
+{FIXLOC; return OROR;}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 49 "magic-interpreter.l"
+{FIXLOC; return ';';}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 50 "magic-interpreter.l"
+{FIXLOC; return ':';}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 51 "magic-interpreter.l"
+{FIXLOC; return ',';}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 52 "magic-interpreter.l"
+{FIXLOC; return '@';}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 53 "magic-interpreter.l"
+{FIXLOC; return '|';}
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 54 "magic-interpreter.l"
+{FIXLOC; return '[';}
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 55 "magic-interpreter.l"
+{FIXLOC; return ']';}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 56 "magic-interpreter.l"
+{FIXLOC; return '&';}
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 57 "magic-interpreter.l"
+{FIXLOC; return '^';}
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 58 "magic-interpreter.l"
+{FIXLOC; return SHL;}
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 59 "magic-interpreter.l"
+{FIXLOC; return SHR;}
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 60 "magic-interpreter.l"
+{FIXLOC; return PROCEDURE;}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 61 "magic-interpreter.l"
+{FIXLOC; return CALL;}
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 62 "magic-interpreter.l"
+{FIXLOC; return OR;}
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 63 "magic-interpreter.l"
+{FIXLOC; return TO;}
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 64 "magic-interpreter.l"
+{FIXLOC; return TOWARDS;}
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 65 "magic-interpreter.l"
+{FIXLOC; return TELEPORT_ANCHOR;}
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 66 "magic-interpreter.l"
+{FIXLOC; return SILENT;}
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 67 "magic-interpreter.l"
+{FIXLOC; return LOCAL;}
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 68 "magic-interpreter.l"
+{FIXLOC; return SPELL;}
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 69 "magic-interpreter.l"
+{FIXLOC; return LET;}
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 70 "magic-interpreter.l"
+{FIXLOC; return IN;}
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 71 "magic-interpreter.l"
+{FIXLOC; return END;}
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 72 "magic-interpreter.l"
+{FIXLOC; return DARROW;}
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 73 "magic-interpreter.l"
+{FIXLOC; return STRING_TY;}
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 74 "magic-interpreter.l"
+{FIXLOC; return REQUIRE;}
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 75 "magic-interpreter.l"
+{FIXLOC; return CATALYSTS;}
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 76 "magic-interpreter.l"
+{FIXLOC; return COMPONENTS;}
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 77 "magic-interpreter.l"
+{FIXLOC; return MANA;}
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 78 "magic-interpreter.l"
+{FIXLOC; return CASTTIME;}
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 79 "magic-interpreter.l"
+{FIXLOC; return SKIP;}
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 80 "magic-interpreter.l"
+{FIXLOC; return ABORT;}
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 81 "magic-interpreter.l"
+{FIXLOC; return BREAK;}
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 82 "magic-interpreter.l"
+{FIXLOC; return EFFECT;}
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 83 "magic-interpreter.l"
+{FIXLOC; return ATEND;}
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 84 "magic-interpreter.l"
+{FIXLOC; return ATTRIGGER;}
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 85 "magic-interpreter.l"
+{FIXLOC; return CONST;}
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 86 "magic-interpreter.l"
+{FIXLOC; return PC_F;}
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 87 "magic-interpreter.l"
+{FIXLOC; return MOB_F;}
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 88 "magic-interpreter.l"
+{FIXLOC; return ENTITY_F;}
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 89 "magic-interpreter.l"
+{FIXLOC; return TARGET_F;}
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 90 "magic-interpreter.l"
+{FIXLOC; return IF;}
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 91 "magic-interpreter.l"
+{FIXLOC; return THEN;}
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 92 "magic-interpreter.l"
+{FIXLOC; return ELSE;}
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 93 "magic-interpreter.l"
+{FIXLOC; return FOREACH;}
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 94 "magic-interpreter.l"
+{FIXLOC; return FOR;}
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 95 "magic-interpreter.l"
+{FIXLOC; return DO;}
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 96 "magic-interpreter.l"
+{FIXLOC; return SLEEP;}
+ YY_BREAK
+case 74:
+/* rule 74 can match eol */
+YY_RULE_SETUP
+#line 98 "magic-interpreter.l"
+{ char *string = strdup(magic_frontend_text);
+ magic_frontend_lval.s = string;
+ FIXLOC;
+ return SCRIPT_DATA;
+ }
+ YY_BREAK
+case 75:
+/* rule 75 can match eol */
+YY_RULE_SETUP
+#line 104 "magic-interpreter.l"
+{ char *string = strdup(magic_frontend_text + 1);
+ char *src = string;
+ char *dst = string;
+ while (*src && *src != '"')
+ if (*src == '\\') {
+ *dst++ = src[1];
+ src += 2;
+ } else
+ *dst++ = *src++;
+ *dst = '\0'; /* terminate */
+ magic_frontend_lval.s = string;
+ FIXLOC;
+ return STRING;
+ }
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 119 "magic-interpreter.l"
+{ magic_frontend_lval.i = atoi(magic_frontend_text);
+ FIXLOC;
+ return INT; }
+ YY_BREAK
+case 77:
+YY_RULE_SETUP
+#line 123 "magic-interpreter.l"
+{ magic_frontend_lval.i = strtol(magic_frontend_text + 2, NULL, 16);
+ FIXLOC;
+ return INT; }
+ YY_BREAK
+case 78:
+YY_RULE_SETUP
+#line 127 "magic-interpreter.l"
+{ magic_frontend_lval.s = strdup(magic_frontend_text);
+ FIXLOC;
+ return ID; }
+ YY_BREAK
+case 79:
+*yy_cp = (yy_hold_char); /* undo effects of setting up magic_frontend_text */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up magic_frontend_text again */
+YY_RULE_SETUP
+#line 131 "magic-interpreter.l"
+/* Ignore comments */
+ YY_BREAK
+case 80:
+*yy_cp = (yy_hold_char); /* undo effects of setting up magic_frontend_text */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up magic_frontend_text again */
+YY_RULE_SETUP
+#line 132 "magic-interpreter.l"
+/* Ignore comments */
+ YY_BREAK
+case 81:
+/* rule 81 can match eol */
+YY_RULE_SETUP
+#line 133 "magic-interpreter.l"
+/* ignore whitespace */
+ YY_BREAK
+case 82:
+YY_RULE_SETUP
+#line 134 "magic-interpreter.l"
+fprintf(stderr, "%s: Unexpected character in line %d\n", MAGIC_CONFIG_FILE, magic_frontend_lineno);
+ YY_BREAK
+case 83:
+YY_RULE_SETUP
+#line 137 "magic-interpreter.l"
+ECHO;
+ YY_BREAK
+#line 1399 "magic-interpreter-lexer.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed magic_frontend_in at a new source and called
+ * magic_frontend_lex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = magic_frontend_in;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( magic_frontend_wrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * magic_frontend_text, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of magic_frontend_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ magic_frontend_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ magic_frontend_restart(magic_frontend_in );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) magic_frontend_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 230 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 230 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 229);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ magic_frontend_restart(magic_frontend_in );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( magic_frontend_wrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve magic_frontend_text */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ if ( c == '\n' )
+
+ magic_frontend_lineno++;
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void magic_frontend_restart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ magic_frontend_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ magic_frontend__create_buffer(magic_frontend_in,YY_BUF_SIZE );
+ }
+
+ magic_frontend__init_buffer(YY_CURRENT_BUFFER,input_file );
+ magic_frontend__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void magic_frontend__switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * magic_frontend_pop_buffer_state();
+ * magic_frontend_push_buffer_state(new_buffer);
+ */
+ magic_frontend_ensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ magic_frontend__load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (magic_frontend_wrap()) processing, but the only time this flag
+ * is looked at is after magic_frontend_wrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void magic_frontend__load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ magic_frontend_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE magic_frontend__create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) magic_frontend_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend__create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) magic_frontend_alloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend__create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ magic_frontend__init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with magic_frontend__create_buffer()
+ *
+ */
+ void magic_frontend__delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ magic_frontend_free((void *) b->yy_ch_buf );
+
+ magic_frontend_free((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a magic_frontend_restart() or at EOF.
+ */
+ static void magic_frontend__init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ magic_frontend__flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then magic_frontend__init_buffer was _probably_
+ * called from magic_frontend_restart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void magic_frontend__flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ magic_frontend__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void magic_frontend_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ magic_frontend_ensure_buffer_stack();
+
+ /* This block is copied from magic_frontend__switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from magic_frontend__switch_to_buffer. */
+ magic_frontend__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void magic_frontend_pop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ magic_frontend__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ magic_frontend__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void magic_frontend_ensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)magic_frontend_alloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend_ensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)magic_frontend_realloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend_ensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE magic_frontend__scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) magic_frontend_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend__scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ magic_frontend__switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to magic_frontend_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * magic_frontend__scan_bytes() instead.
+ */
+YY_BUFFER_STATE magic_frontend__scan_string (yyconst char * yystr )
+{
+
+ return magic_frontend__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to magic_frontend_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE magic_frontend__scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) magic_frontend_alloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in magic_frontend__scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = magic_frontend__scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in magic_frontend__scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up magic_frontend_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ magic_frontend_text[magic_frontend_leng] = (yy_hold_char); \
+ (yy_c_buf_p) = magic_frontend_text + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ magic_frontend_leng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int magic_frontend_get_lineno (void)
+{
+
+ return magic_frontend_lineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *magic_frontend_get_in (void)
+{
+ return magic_frontend_in;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *magic_frontend_get_out (void)
+{
+ return magic_frontend_out;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int magic_frontend_get_leng (void)
+{
+ return magic_frontend_leng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *magic_frontend_get_text (void)
+{
+ return magic_frontend_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void magic_frontend_set_lineno (int line_number )
+{
+
+ magic_frontend_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see magic_frontend__switch_to_buffer
+ */
+void magic_frontend_set_in (FILE * in_str )
+{
+ magic_frontend_in = in_str ;
+}
+
+void magic_frontend_set_out (FILE * out_str )
+{
+ magic_frontend_out = out_str ;
+}
+
+int magic_frontend_get_debug (void)
+{
+ return magic_frontend__flex_debug;
+}
+
+void magic_frontend_set_debug (int bdebug )
+{
+ magic_frontend__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from magic_frontend_lex_destroy(), so don't allocate here.
+ */
+
+ /* We do not touch magic_frontend_lineno unless the option is enabled. */
+ magic_frontend_lineno = 1;
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ magic_frontend_in = stdin;
+ magic_frontend_out = stdout;
+#else
+ magic_frontend_in = (FILE *) 0;
+ magic_frontend_out = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * magic_frontend_lex_init()
+ */
+ return 0;
+}
+
+/* magic_frontend_lex_destroy is for both reentrant and non-reentrant scanners. */
+int magic_frontend_lex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ magic_frontend__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ magic_frontend_pop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ magic_frontend_free((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * magic_frontend_lex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *magic_frontend_alloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *magic_frontend_realloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void magic_frontend_free (void * ptr )
+{
+ free( (char *) ptr ); /* see magic_frontend_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 137 "magic-interpreter.l"
+
+
+
diff --git a/src/map/magic-interpreter-parser.c b/src/map/magic-interpreter-parser.c
new file mode 100644
index 0000000..09ddce2
--- /dev/null
+++ b/src/map/magic-interpreter-parser.c
@@ -0,0 +1,3198 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names. */
+#define yyparse magic_frontend_parse
+#define yylex magic_frontend_lex
+#define yyerror magic_frontend_error
+#define yylval magic_frontend_lval
+#define yychar magic_frontend_char
+#define yydebug magic_frontend_debug
+#define yynerrs magic_frontend_nerrs
+#define yylloc magic_frontend_lloc
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ INT = 258,
+ STRING = 259,
+ ID = 260,
+ DIR = 261,
+ CONST = 262,
+ PROCEDURE = 263,
+ CALL = 264,
+ SILENT = 265,
+ LOCAL = 266,
+ SHL = 267,
+ SHR = 268,
+ EQ = 269,
+ NEQ = 270,
+ GTE = 271,
+ LTE = 272,
+ ANDAND = 273,
+ OROR = 274,
+ SCRIPT_DATA = 275,
+ TO = 276,
+ TOWARDS = 277,
+ TELEPORT_ANCHOR = 278,
+ SPELL = 279,
+ LET = 280,
+ IN = 281,
+ END = 282,
+ DARROW = 283,
+ STRING_TY = 284,
+ REQUIRE = 285,
+ CATALYSTS = 286,
+ COMPONENTS = 287,
+ MANA = 288,
+ CASTTIME = 289,
+ SKIP = 290,
+ ABORT = 291,
+ BREAK = 292,
+ EFFECT = 293,
+ ATEND = 294,
+ ATTRIGGER = 295,
+ PC_F = 296,
+ MOB_F = 297,
+ ENTITY_F = 298,
+ TARGET_F = 299,
+ IF = 300,
+ THEN = 301,
+ ELSE = 302,
+ FOREACH = 303,
+ FOR = 304,
+ DO = 305,
+ SLEEP = 306,
+ OR = 307
+ };
+#endif
+/* Tokens. */
+#define INT 258
+#define STRING 259
+#define ID 260
+#define DIR 261
+#define CONST 262
+#define PROCEDURE 263
+#define CALL 264
+#define SILENT 265
+#define LOCAL 266
+#define SHL 267
+#define SHR 268
+#define EQ 269
+#define NEQ 270
+#define GTE 271
+#define LTE 272
+#define ANDAND 273
+#define OROR 274
+#define SCRIPT_DATA 275
+#define TO 276
+#define TOWARDS 277
+#define TELEPORT_ANCHOR 278
+#define SPELL 279
+#define LET 280
+#define IN 281
+#define END 282
+#define DARROW 283
+#define STRING_TY 284
+#define REQUIRE 285
+#define CATALYSTS 286
+#define COMPONENTS 287
+#define MANA 288
+#define CASTTIME 289
+#define SKIP 290
+#define ABORT 291
+#define BREAK 292
+#define EFFECT 293
+#define ATEND 294
+#define ATTRIGGER 295
+#define PC_F 296
+#define MOB_F 297
+#define ENTITY_F 298
+#define TARGET_F 299
+#define IF 300
+#define THEN 301
+#define ELSE 302
+#define FOREACH 303
+#define FOR 304
+#define DO 305
+#define SLEEP 306
+#define OR 307
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "magic-interpreter-parser.y"
+
+#include "magic-interpreter.h"
+#include "magic-expr.h"
+#include <stdarg.h>
+
+magic_conf_t magic_conf;
+
+static int
+intern_id(char *id_name);
+
+
+static expr_t *
+fun_expr(char *name, int args_nr, expr_t **args, int line, int column);
+
+#define BIN_EXPR(x, name, arg1, arg2, line, column) { expr_t *e[2]; e[0] = arg1; e[1] = arg2; x = fun_expr(name, 2, e, line, column); }
+
+static int failed_flag = 0;
+
+static void
+magic_frontend_error(const char *msg);
+
+static void
+fail(int line, int column, char *fmt, ...);
+
+static spell_t *
+new_spell(spellguard_t *guard);
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b);
+
+static spellguard_t *
+new_spellguard(int ty);
+
+static effect_t *
+new_effect(int ty);
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation);
+
+static void
+add_spell(spell_t *spell, int line_nr);
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr);
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column);
+
+int
+magic_frontend_lex();
+
+static void
+install_proc(proc_t *proc);
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column);
+
+static void
+bind_constant(char *name, val_t *val, int line_nr);
+
+static val_t *
+find_constant(char *name);
+
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 71 "magic-interpreter-parser.y"
+{
+ int i;
+ char *s;
+ int op;
+ magic_conf_t *magic_conf;
+ val_t value;
+ expr_t *expr;
+ e_location_t location;
+ e_area_t area;
+ args_rec_t arg_list;
+ struct { int letdefs_nr; letdef_t *letdefs; } letdefs;
+ spell_t *spell;
+ struct { int id, ty; } spellarg_def;
+ letdef_t vardef;
+ spellguard_t *spellguard;
+ component_t *components;
+ struct {int id, count; } component;
+ effect_t *effect;
+ proc_t *proc;
+}
+/* Line 187 of yacc.c. */
+#line 295 "magic-interpreter-parser.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 320 "magic-interpreter-parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 16
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 920
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 72
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 32
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 108
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 247
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 307
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 14, 22, 2,
+ 70, 71, 12, 10, 16, 11, 2, 13, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 17, 18,
+ 8, 7, 9, 2, 15, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 20, 2, 21, 23, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 19, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 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
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 4, 8, 9, 12, 13, 15, 17,
+ 21, 25, 30, 37, 45, 54, 55, 58, 61, 62,
+ 68, 70, 72, 74, 76, 78, 80, 82, 84, 88,
+ 92, 96, 100, 104, 108, 112, 116, 120, 124, 128,
+ 132, 136, 140, 144, 148, 152, 156, 160, 165, 169,
+ 170, 172, 174, 178, 187, 189, 198, 208, 210, 215,
+ 217, 221, 225, 227, 231, 235, 239, 244, 245, 248,
+ 249, 252, 254, 258, 262, 264, 268, 271, 274, 277,
+ 280, 283, 287, 289, 293, 297, 299, 301, 303, 305,
+ 307, 309, 311, 313, 317, 320, 323, 326, 329, 334,
+ 342, 351, 358, 363, 367, 373, 375, 382, 383
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 73, 0, -1, -1, 77, 74, 73, -1, -1, 74,
+ 18, -1, -1, 76, -1, 5, -1, 76, 16, 5,
+ -1, 5, 7, 82, -1, 24, 5, 7, 82, -1,
+ 40, 5, 17, 82, 7, 82, -1, 25, 5, 70,
+ 75, 71, 7, 103, -1, 78, 41, 5, 79, 17,
+ 82, 7, 87, -1, -1, 28, 78, -1, 27, 78,
+ -1, -1, 70, 5, 17, 80, 71, -1, 58, -1,
+ 46, -1, 6, -1, 3, -1, 4, -1, 81, -1,
+ 5, -1, 86, -1, 82, 10, 82, -1, 82, 11,
+ 82, -1, 82, 12, 82, -1, 82, 14, 82, -1,
+ 82, 13, 82, -1, 82, 8, 82, -1, 82, 9,
+ 82, -1, 82, 22, 82, -1, 82, 23, 82, -1,
+ 82, 19, 82, -1, 82, 29, 82, -1, 82, 30,
+ 82, -1, 82, 34, 82, -1, 82, 33, 82, -1,
+ 82, 35, 82, -1, 82, 36, 82, -1, 82, 31,
+ 82, -1, 82, 7, 82, -1, 82, 32, 82, -1,
+ 5, 70, 83, 71, -1, 70, 82, 71, -1, -1,
+ 84, -1, 82, -1, 84, 16, 82, -1, 15, 70,
+ 82, 16, 82, 16, 82, 71, -1, 85, -1, 85,
+ 15, 10, 70, 82, 16, 82, 71, -1, 85, 39,
+ 82, 17, 70, 82, 16, 82, 71, -1, 90, -1,
+ 42, 88, 43, 90, -1, 74, -1, 88, 89, 74,
+ -1, 5, 7, 82, -1, 91, -1, 91, 19, 90,
+ -1, 94, 45, 91, -1, 70, 90, 71, -1, 55,
+ 103, 92, 93, -1, -1, 57, 103, -1, -1, 56,
+ 103, -1, 96, -1, 94, 69, 94, -1, 70, 95,
+ 71, -1, 94, -1, 94, 16, 95, -1, 47, 82,
+ -1, 48, 97, -1, 49, 97, -1, 50, 82, -1,
+ 51, 82, -1, 20, 98, 21, -1, 99, -1, 98,
+ 16, 99, -1, 3, 12, 100, -1, 100, -1, 4,
+ -1, 3, -1, 58, -1, 59, -1, 60, -1, 41,
+ -1, 61, -1, 70, 103, 71, -1, 52, 18, -1,
+ 53, 18, -1, 44, 18, -1, 54, 18, -1, 5,
+ 7, 82, 18, -1, 65, 101, 5, 43, 82, 67,
+ 102, -1, 66, 5, 7, 82, 38, 82, 67, 102,
+ -1, 62, 82, 63, 102, 64, 102, -1, 62, 82,
+ 63, 102, -1, 68, 82, 18, -1, 5, 70, 83,
+ 71, 18, -1, 37, -1, 26, 5, 70, 83, 71,
+ 18, -1, -1, 102, 74, 103, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 206, 206, 207, 213, 214, 220, 221, 225, 231,
+ 238, 249, 255, 266, 275, 288, 289, 294, 302, 303,
+ 309, 311, 316, 319, 322, 328, 331, 342, 345, 347,
+ 349, 351, 353, 355, 357, 359, 361, 363, 365, 367,
+ 369, 371, 373, 375, 377, 379, 381, 384, 389, 394,
+ 395, 400, 405, 412, 416, 420, 426, 436, 438, 447,
+ 451, 460, 473, 475, 484, 486, 488, 499, 500, 506,
+ 507, 512, 514, 520, 525, 527, 532, 536, 540, 544,
+ 548, 555, 560, 564, 571, 573, 578, 587, 592, 594,
+ 596, 598, 600, 605, 607, 609, 611, 613, 615, 626,
+ 633, 640, 646, 652, 656, 660, 667, 674, 675
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "INT", "STRING", "ID", "DIR", "'='",
+ "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "'@'", "','", "':'",
+ "';'", "'|'", "'['", "']'", "'&'", "'^'", "CONST", "PROCEDURE", "CALL",
+ "SILENT", "LOCAL", "SHL", "SHR", "EQ", "NEQ", "GTE", "LTE", "ANDAND",
+ "OROR", "SCRIPT_DATA", "TO", "TOWARDS", "TELEPORT_ANCHOR", "SPELL",
+ "LET", "IN", "END", "DARROW", "STRING_TY", "REQUIRE", "CATALYSTS",
+ "COMPONENTS", "MANA", "CASTTIME", "SKIP", "ABORT", "BREAK", "EFFECT",
+ "ATEND", "ATTRIGGER", "PC_F", "MOB_F", "ENTITY_F", "TARGET_F", "IF",
+ "THEN", "ELSE", "FOREACH", "FOR", "DO", "SLEEP", "OR", "'('", "')'",
+ "$accept", "spellconf", "semicolons", "proc_formals_list",
+ "proc_formals_list_ne", "spellconf_option", "spell_flags", "argopt",
+ "arg_ty", "value", "expr", "arg_list", "arg_list_ne", "location", "area",
+ "spelldef", "defs", "def", "spellbody_list", "spellbody",
+ "maybe_trigger", "maybe_end", "spellguard", "spellguard_list", "prereq",
+ "items", "item_list", "item", "item_name", "selection", "effect",
+ "effect_list", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 61, 60, 62,
+ 43, 45, 42, 47, 37, 64, 44, 58, 59, 124,
+ 91, 93, 38, 94, 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,
+ 40, 41
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 72, 73, 73, 74, 74, 75, 75, 76, 76,
+ 77, 77, 77, 77, 77, 78, 78, 78, 79, 79,
+ 80, 80, 81, 81, 81, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 83,
+ 83, 84, 84, 85, 86, 86, 86, 87, 87, 88,
+ 88, 89, 90, 90, 91, 91, 91, 92, 92, 93,
+ 93, 94, 94, 94, 95, 95, 96, 96, 96, 96,
+ 96, 97, 98, 98, 99, 99, 100, 100, 101, 101,
+ 101, 101, 101, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 103, 103
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 3, 0, 2, 0, 1, 1, 3,
+ 3, 4, 6, 7, 8, 0, 2, 2, 0, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 4, 3, 0,
+ 1, 1, 3, 8, 1, 8, 9, 1, 4, 1,
+ 3, 3, 1, 3, 3, 3, 4, 0, 2, 0,
+ 2, 1, 3, 3, 1, 3, 2, 2, 2, 2,
+ 2, 3, 1, 3, 3, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 2, 2, 2, 2, 4, 7,
+ 8, 6, 4, 3, 5, 1, 6, 0, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 0, 0, 15, 15, 0, 0, 4, 0,
+ 0, 0, 0, 17, 16, 0, 1, 2, 0, 23,
+ 24, 26, 22, 0, 0, 25, 10, 54, 27, 0,
+ 6, 0, 5, 3, 18, 49, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
+ 8, 0, 7, 0, 0, 0, 51, 0, 50, 0,
+ 48, 45, 33, 34, 28, 29, 30, 32, 31, 37,
+ 35, 36, 38, 39, 44, 46, 41, 40, 42, 43,
+ 0, 0, 0, 0, 0, 0, 0, 47, 0, 0,
+ 0, 0, 107, 9, 45, 0, 0, 52, 0, 0,
+ 0, 0, 0, 105, 0, 0, 0, 0, 0, 0,
+ 0, 0, 107, 4, 13, 21, 20, 0, 0, 0,
+ 0, 0, 0, 49, 0, 96, 94, 95, 97, 0,
+ 91, 88, 89, 90, 92, 0, 0, 0, 0, 107,
+ 19, 4, 0, 0, 0, 0, 0, 107, 0, 14,
+ 57, 62, 0, 71, 0, 0, 0, 0, 0, 49,
+ 0, 0, 0, 103, 93, 108, 59, 0, 76, 0,
+ 77, 78, 79, 80, 67, 0, 74, 0, 0, 0,
+ 0, 53, 55, 0, 98, 0, 0, 102, 0, 0,
+ 0, 0, 4, 87, 86, 0, 82, 85, 107, 69,
+ 65, 0, 73, 0, 63, 64, 0, 72, 56, 104,
+ 0, 0, 0, 0, 0, 58, 60, 0, 0, 81,
+ 68, 107, 66, 74, 75, 106, 101, 0, 0, 61,
+ 87, 84, 83, 70, 99, 0, 100
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 7, 17, 61, 62, 8, 9, 65, 127, 25,
+ 66, 67, 68, 27, 28, 159, 177, 202, 185, 161,
+ 209, 232, 162, 187, 163, 180, 205, 206, 207, 145,
+ 123, 124
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -170
+static const yytype_int16 yypact[] =
+{
+ 100, 5, 18, 53, -18, -18, 56, 16, -170, 26,
+ 0, 67, 10, -170, -170, 64, -170, 150, 80, -170,
+ -170, 17, -170, 27, 0, -170, 730, 12, -170, 0,
+ 91, 0, -170, -170, 28, 0, 0, 238, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 89, 0, 730,
+ -170, 31, 93, 760, 99, 90, 730, 39, 101, 520,
+ -170, 109, 877, 877, 890, 890, 236, 236, 236, 109,
+ 109, 109, 109, 109, 877, 877, 877, 877, 849, 820,
+ 51, 550, 119, 118, 0, 114, 0, -170, 0, 0,
+ 0, 63, 174, -170, 288, 11, 790, 730, 580, 610,
+ 0, 1, 127, -170, 126, 129, 135, 136, 0, 54,
+ 152, 0, 174, -170, -170, -170, -170, 98, 88, 0,
+ 0, 640, 0, 0, 102, -170, -170, -170, -170, 453,
+ -170, -170, -170, -170, -170, 154, 163, 670, 105, 155,
+ -170, -170, 0, 160, 160, 0, 0, 174, 146, -170,
+ -170, 115, 14, -170, 268, 311, 0, 700, 111, 0,
+ 174, 140, 0, -170, -170, -170, 166, 6, 730, 60,
+ -170, -170, 730, 730, 128, 131, 9, 132, 116, 116,
+ 183, -170, -170, 341, -170, 169, 133, 125, 0, 488,
+ 191, 116, -170, 193, -170, -3, -170, -170, 174, 156,
+ -170, 183, -170, 116, -170, -170, 183, -170, -170, -170,
+ 188, 174, 384, 0, 0, -170, 166, 62, 60, -170,
+ -170, 174, -170, -9, -170, -170, -170, 174, 414, 730,
+ -170, -170, -170, -170, -170, 174, -170
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -170, 198, -101, -170, -170, -170, 68, -170, -170, -170,
+ -10, -116, -170, -170, -170, -170, -170, -170, -126, 21,
+ -170, -170, -134, 8, -170, 70, -170, -6, 2, -170,
+ -169, -102
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -16
+static const yytype_int16 yytable[] =
+{
+ 26, 197, 160, 19, 20, 21, 22, 211, 132, 4,
+ 5, 200, 10, 228, 37, 23, 16, 168, 229, 59,
+ 148, 63, 149, 11, 186, 211, 69, 57, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 175, 91, 201,
+ 176, 58, 236, 196, 189, 184, 217, 125, 12, 189,
+ 190, 15, 214, 203, 204, 240, 204, 18, 244, 126,
+ 24, 133, 13, 14, 29, 225, 246, 233, 190, 186,
+ 30, 31, 233, 190, 104, 34, 106, 35, 107, 108,
+ 109, 19, 20, 21, 22, 140, 60, 36, 64, 90,
+ 131, 226, 92, 23, 95, 1, 230, 96, 139, 93,
+ 97, 147, 141, 142, 143, 144, 38, 98, 71, 164,
+ 165, 100, 167, 103, 2, 3, 102, 4, 5, 243,
+ 151, 105, 134, 110, 188, 152, 153, 154, 155, 156,
+ 6, -15, 178, 157, 135, 182, 183, 136, 37, 19,
+ 20, 21, 22, 137, 138, 1, 193, 146, 158, 171,
+ 111, 23, 199, 152, 153, 154, 155, 156, 32, 150,
+ 172, 157, 169, 32, 2, 3, 174, 4, 5, 111,
+ 179, 112, 195, 198, 32, 208, 213, 219, 222, 221,
+ 6, -15, 113, 152, 153, 154, 155, 156, 224, 114,
+ 112, 157, 210, 212, 220, 227, 235, 115, 116, 117,
+ 215, 113, 231, 238, 239, 33, 158, 118, 114, 234,
+ 119, 120, 242, 121, 181, 122, 115, 116, 117, 241,
+ 152, 153, 154, 155, 156, 0, 118, 0, 0, 119,
+ 120, 0, 121, 38, 122, 38, 39, 40, 41, 42,
+ 43, 44, 45, 216, 0, 46, 0, 46, 47, 48,
+ 47, 48, 0, 0, 0, 49, 50, 49, 50, 51,
+ 52, 53, 54, 55, 56, 38, 39, 40, 41, 42,
+ 43, 44, 45, 0, 0, 0, 0, 46, -12, 0,
+ 47, 48, 0, -12, 0, 38, 0, 49, 50, 51,
+ 52, 53, 54, 55, 56, 0, -12, 0, 0, 70,
+ 0, 0, -12, -12, 0, -12, -12, 0, 38, 39,
+ 40, 41, 42, 43, 44, 45, 0, 0, -12, -12,
+ 46, 0, 0, 47, 48, 0, 0, 0, 0, 191,
+ 49, 50, 51, 52, 53, 54, 55, 56, 38, 39,
+ 40, 41, 42, 43, 44, 45, 0, 0, 0, 0,
+ 46, 0, 0, 47, 48, 0, 0, 0, 0, 0,
+ 49, 50, 51, 52, 53, 54, 55, 56, 0, 0,
+ 0, 0, 192, 0, 0, 0, 0, 0, 0, 0,
+ 0, 38, 39, 40, 41, 42, 43, 44, 45, 0,
+ 0, 0, 0, 46, 0, 0, 47, 48, 0, 0,
+ 0, 0, 218, 49, 50, 51, 52, 53, 54, 55,
+ 56, 38, 39, 40, 41, 42, 43, 44, 45, 0,
+ 0, 0, 0, 46, 0, 0, 47, 48, 0, 0,
+ 0, 0, 0, 49, 50, 51, 52, 53, 54, 55,
+ 56, 237, 0, 0, 0, 0, 0, 0, 0, 0,
+ 38, 39, 40, 41, 42, 43, 44, 45, 0, 0,
+ 0, 0, 46, 0, 0, 47, 48, 0, 0, 0,
+ 0, 245, 49, 50, 51, 52, 53, 54, 55, 56,
+ 0, 0, 0, 0, 0, 38, 39, 40, 41, 42,
+ 43, 44, 45, 0, 0, 0, 0, 46, 0, 0,
+ 47, 48, 0, 0, 0, 0, 170, 49, 50, 51,
+ 52, 53, 54, 55, 56, 0, 223, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 99, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 101, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 129, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 130, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 166, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 173, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 194, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 94, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 128, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 56, 38, 39, 40,
+ 41, 42, 43, 44, 45, 0, 0, 0, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50, 51, 52, 53, 54, 55, 38, 39, 40, 41,
+ 42, 43, 44, 45, 0, 0, 0, 0, 46, 0,
+ 0, 47, 48, 0, 0, 0, 0, 0, 49, 50,
+ 51, 52, 53, 54, 38, 0, 0, 41, 42, 43,
+ 44, 45, 0, 0, 0, 0, 46, 38, 0, 47,
+ 48, 0, 43, 44, 45, 0, 49, 50, 0, 46,
+ 0, 0, 47, 48, 0, 0, 0, 0, 0, 49,
+ 50
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 10, 170, 128, 3, 4, 5, 6, 16, 7, 27,
+ 28, 5, 7, 16, 24, 15, 0, 133, 21, 29,
+ 122, 31, 123, 5, 158, 16, 36, 15, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 149, 58, 43,
+ 151, 39, 221, 169, 45, 157, 190, 46, 5, 45,
+ 69, 5, 188, 3, 4, 3, 4, 41, 237, 58,
+ 70, 70, 4, 5, 7, 201, 245, 211, 69, 213,
+ 70, 17, 216, 69, 94, 5, 96, 70, 98, 99,
+ 100, 3, 4, 5, 6, 41, 5, 70, 70, 10,
+ 110, 202, 71, 15, 5, 5, 208, 17, 118, 16,
+ 71, 121, 58, 59, 60, 61, 7, 16, 128, 129,
+ 130, 70, 132, 5, 24, 25, 7, 27, 28, 231,
+ 42, 17, 5, 70, 19, 47, 48, 49, 50, 51,
+ 40, 41, 152, 55, 18, 155, 156, 18, 158, 3,
+ 4, 5, 6, 18, 18, 5, 166, 5, 70, 5,
+ 5, 15, 172, 47, 48, 49, 50, 51, 18, 71,
+ 7, 55, 70, 18, 24, 25, 71, 27, 28, 5,
+ 20, 26, 71, 43, 18, 57, 70, 18, 198, 64,
+ 40, 41, 37, 47, 48, 49, 50, 51, 7, 44,
+ 26, 55, 71, 71, 71, 12, 18, 52, 53, 54,
+ 189, 37, 56, 223, 224, 17, 70, 62, 44, 211,
+ 65, 66, 228, 68, 154, 70, 52, 53, 54, 227,
+ 47, 48, 49, 50, 51, -1, 62, -1, -1, 65,
+ 66, -1, 68, 7, 70, 7, 8, 9, 10, 11,
+ 12, 13, 14, 70, -1, 19, -1, 19, 22, 23,
+ 22, 23, -1, -1, -1, 29, 30, 29, 30, 31,
+ 32, 33, 34, 35, 36, 7, 8, 9, 10, 11,
+ 12, 13, 14, -1, -1, -1, -1, 19, 0, -1,
+ 22, 23, -1, 5, -1, 7, -1, 29, 30, 31,
+ 32, 33, 34, 35, 36, -1, 18, -1, -1, 71,
+ -1, -1, 24, 25, -1, 27, 28, -1, 7, 8,
+ 9, 10, 11, 12, 13, 14, -1, -1, 40, 41,
+ 19, -1, -1, 22, 23, -1, -1, -1, -1, 71,
+ 29, 30, 31, 32, 33, 34, 35, 36, 7, 8,
+ 9, 10, 11, 12, 13, 14, -1, -1, -1, -1,
+ 19, -1, -1, 22, 23, -1, -1, -1, -1, -1,
+ 29, 30, 31, 32, 33, 34, 35, 36, -1, -1,
+ -1, -1, 71, -1, -1, -1, -1, -1, -1, -1,
+ -1, 7, 8, 9, 10, 11, 12, 13, 14, -1,
+ -1, -1, -1, 19, -1, -1, 22, 23, -1, -1,
+ -1, -1, 71, 29, 30, 31, 32, 33, 34, 35,
+ 36, 7, 8, 9, 10, 11, 12, 13, 14, -1,
+ -1, -1, -1, 19, -1, -1, 22, 23, -1, -1,
+ -1, -1, -1, 29, 30, 31, 32, 33, 34, 35,
+ 36, 67, -1, -1, -1, -1, -1, -1, -1, -1,
+ 7, 8, 9, 10, 11, 12, 13, 14, -1, -1,
+ -1, -1, 19, -1, -1, 22, 23, -1, -1, -1,
+ -1, 67, 29, 30, 31, 32, 33, 34, 35, 36,
+ -1, -1, -1, -1, -1, 7, 8, 9, 10, 11,
+ 12, 13, 14, -1, -1, -1, -1, 19, -1, -1,
+ 22, 23, -1, -1, -1, -1, 63, 29, 30, 31,
+ 32, 33, 34, 35, 36, -1, 38, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, 17, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, 18, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, 18, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 36, 7, 8, 9,
+ 10, 11, 12, 13, 14, -1, -1, -1, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, 35, 7, 8, 9, 10,
+ 11, 12, 13, 14, -1, -1, -1, -1, 19, -1,
+ -1, 22, 23, -1, -1, -1, -1, -1, 29, 30,
+ 31, 32, 33, 34, 7, -1, -1, 10, 11, 12,
+ 13, 14, -1, -1, -1, -1, 19, 7, -1, 22,
+ 23, -1, 12, 13, 14, -1, 29, 30, -1, 19,
+ -1, -1, 22, 23, -1, -1, -1, -1, -1, 29,
+ 30
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 5, 24, 25, 27, 28, 40, 73, 77, 78,
+ 7, 5, 5, 78, 78, 5, 0, 74, 41, 3,
+ 4, 5, 6, 15, 70, 81, 82, 85, 86, 7,
+ 70, 17, 18, 73, 5, 70, 70, 82, 7, 8,
+ 9, 10, 11, 12, 13, 14, 19, 22, 23, 29,
+ 30, 31, 32, 33, 34, 35, 36, 15, 39, 82,
+ 5, 75, 76, 82, 70, 79, 82, 83, 84, 82,
+ 71, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 10, 82, 71, 16, 7, 5, 17, 71, 16, 16,
+ 70, 17, 7, 5, 82, 17, 82, 82, 82, 82,
+ 70, 5, 26, 37, 44, 52, 53, 54, 62, 65,
+ 66, 68, 70, 102, 103, 46, 58, 80, 7, 16,
+ 16, 82, 7, 70, 5, 18, 18, 18, 18, 82,
+ 41, 58, 59, 60, 61, 101, 5, 82, 103, 74,
+ 71, 42, 47, 48, 49, 50, 51, 55, 70, 87,
+ 90, 91, 94, 96, 82, 82, 16, 82, 83, 70,
+ 63, 5, 7, 18, 71, 103, 74, 88, 82, 20,
+ 97, 97, 82, 82, 103, 90, 94, 95, 19, 45,
+ 69, 71, 71, 82, 18, 71, 83, 102, 43, 82,
+ 5, 43, 89, 3, 4, 98, 99, 100, 57, 92,
+ 71, 16, 71, 70, 90, 91, 70, 94, 71, 18,
+ 71, 64, 82, 38, 7, 90, 74, 12, 16, 21,
+ 103, 56, 93, 94, 95, 18, 102, 67, 82, 82,
+ 3, 100, 99, 103, 102, 67, 102
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, Location); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ YYLTYPE const * const yylocationp;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (yylocationp);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ YYLTYPE const * const yylocationp;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule)
+ YYSTYPE *yyvsp;
+ YYLTYPE *yylsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , &(yylsp[(yyi + 1) - (yynrhs)]) );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, yylsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (yylocationp);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+/* Location data for the look-ahead symbol. */
+YYLTYPE yylloc;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+ /* Initialize the default location before parsing starts. */
+ yylloc.first_line = yylloc.last_line = 1;
+ yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+ YYSTACK_RELOCATE (yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+ *++yylsp = yylloc;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 206 "magic-interpreter-parser.y"
+ {;}
+ break;
+
+ case 3:
+#line 208 "magic-interpreter-parser.y"
+ {;}
+ break;
+
+ case 4:
+#line 213 "magic-interpreter-parser.y"
+ {;}
+ break;
+
+ case 5:
+#line 215 "magic-interpreter-parser.y"
+ {;}
+ break;
+
+ case 6:
+#line 220 "magic-interpreter-parser.y"
+ { (yyval.proc) = aCalloc(sizeof(proc_t), 1); ;}
+ break;
+
+ case 7:
+#line 222 "magic-interpreter-parser.y"
+ { (yyval.proc) = (yyvsp[(1) - (1)].proc); ;}
+ break;
+
+ case 8:
+#line 226 "magic-interpreter-parser.y"
+ { (yyval.proc) = aCalloc(sizeof(proc_t), 1);
+ (yyval.proc)->args_nr = 1;
+ (yyval.proc)->args = malloc(sizeof(int));
+ (yyval.proc)->args[0] = intern_id((yyvsp[(1) - (1)].s));
+ ;}
+ break;
+
+ case 9:
+#line 232 "magic-interpreter-parser.y"
+ { (yyval.proc) = (yyvsp[(1) - (3)].proc);
+ (yyval.proc)->args = realloc((yyval.proc)->args, sizeof(int) * (1 + (yyval.proc)->args_nr));
+ (yyval.proc)->args[(yyval.proc)->args_nr++] = intern_id((yyvsp[(3) - (3)].s));
+ ;}
+ break;
+
+ case 10:
+#line 239 "magic-interpreter-parser.y"
+ {
+ int var_id;
+ if (find_constant((yyvsp[(1) - (3)].s))) {
+ fail((yylsp[(1) - (3)]).first_line, 0, "Attempt to redefine constant `%s' as global\n", (yyvsp[(1) - (3)].s));
+ free((yyvsp[(1) - (3)].s));
+ } else {
+ var_id = intern_id((yyvsp[(1) - (3)].s));
+ magic_eval(&magic_default_env, &magic_conf.vars[var_id], (yyvsp[(3) - (3)].expr));
+ }
+ ;}
+ break;
+
+ case 11:
+#line 250 "magic-interpreter-parser.y"
+ {
+ val_t var;
+ magic_eval(&magic_default_env, &var, (yyvsp[(4) - (4)].expr));
+ bind_constant((yyvsp[(2) - (4)].s), &var, (yylsp[(1) - (4)]).first_line);
+ ;}
+ break;
+
+ case 12:
+#line 256 "magic-interpreter-parser.y"
+ {
+ teleport_anchor_t *anchor = calloc(sizeof(teleport_anchor_t), 1);
+ anchor->name = (yyvsp[(2) - (6)].s);
+ anchor->invocation = magic_eval_str(&magic_default_env, (yyvsp[(4) - (6)].expr));
+ anchor->location = (yyvsp[(6) - (6)].expr);
+
+ if (!failed_flag)
+ add_teleport_anchor(anchor, (yylsp[(1) - (6)]).first_line);
+ failed_flag = 0;
+ ;}
+ break;
+
+ case 13:
+#line 267 "magic-interpreter-parser.y"
+ {
+ proc_t *proc = (yyvsp[(4) - (7)].proc);
+ proc->name = (yyvsp[(2) - (7)].s);
+ proc->body = (yyvsp[(7) - (7)].effect);
+ if (!failed_flag)
+ install_proc(proc);
+ failed_flag = 0;
+ ;}
+ break;
+
+ case 14:
+#line 276 "magic-interpreter-parser.y"
+ { spell_t *spell = (yyvsp[(8) - (8)].spell);
+ spell->name = (yyvsp[(3) - (8)].s);
+ spell->invocation = magic_eval_str(&magic_default_env, (yyvsp[(6) - (8)].expr));
+ spell->arg = (yyvsp[(4) - (8)].spellarg_def).id;
+ spell->spellarg_ty = (yyvsp[(4) - (8)].spellarg_def).ty;
+ spell->flags = (yyvsp[(1) - (8)].i);
+ if (!failed_flag)
+ add_spell(spell, (yylsp[(1) - (8)]).first_line);
+ failed_flag = 0;
+ ;}
+ break;
+
+ case 15:
+#line 288 "magic-interpreter-parser.y"
+ { (yyval.i) = 0; ;}
+ break;
+
+ case 16:
+#line 290 "magic-interpreter-parser.y"
+ { if ((yyvsp[(2) - (2)].i) & SPELL_FLAG_LOCAL)
+ fail((yylsp[(1) - (2)]).first_line, (yylsp[(1) - (2)]).first_column, "`LOCAL' specified more than once");
+ (yyval.i) = (yyvsp[(2) - (2)].i) | SPELL_FLAG_LOCAL;
+ ;}
+ break;
+
+ case 17:
+#line 295 "magic-interpreter-parser.y"
+ { if ((yyvsp[(2) - (2)].i) & SPELL_FLAG_SILENT)
+ fail((yylsp[(1) - (2)]).first_line, (yylsp[(1) - (2)]).first_column, "`SILENT' specified more than once");
+ (yyval.i) = (yyvsp[(2) - (2)].i) | SPELL_FLAG_SILENT;
+ ;}
+ break;
+
+ case 18:
+#line 302 "magic-interpreter-parser.y"
+ { (yyval.spellarg_def).ty = SPELLARG_NONE; ;}
+ break;
+
+ case 19:
+#line 304 "magic-interpreter-parser.y"
+ { (yyval.spellarg_def).id = intern_id((yyvsp[(2) - (5)].s));
+ (yyval.spellarg_def).ty = (yyvsp[(4) - (5)].i); ;}
+ break;
+
+ case 20:
+#line 310 "magic-interpreter-parser.y"
+ { (yyval.i) = SPELLARG_PC; ;}
+ break;
+
+ case 21:
+#line 312 "magic-interpreter-parser.y"
+ { (yyval.i) = SPELLARG_STRING; ;}
+ break;
+
+ case 22:
+#line 317 "magic-interpreter-parser.y"
+ { (yyval.value).ty = TY_DIR;
+ (yyval.value).v.v_int = (yyvsp[(1) - (1)].i); ;}
+ break;
+
+ case 23:
+#line 320 "magic-interpreter-parser.y"
+ { (yyval.value).ty = TY_INT;
+ (yyval.value).v.v_int = (yyvsp[(1) - (1)].i); ;}
+ break;
+
+ case 24:
+#line 323 "magic-interpreter-parser.y"
+ { (yyval.value).ty = TY_STRING;
+ (yyval.value).v.v_string = (yyvsp[(1) - (1)].s); ;}
+ break;
+
+ case 25:
+#line 329 "magic-interpreter-parser.y"
+ { (yyval.expr) = magic_new_expr(EXPR_VAL);
+ (yyval.expr)->e.e_val = (yyvsp[(1) - (1)].value); ;}
+ break;
+
+ case 26:
+#line 332 "magic-interpreter-parser.y"
+ {
+ val_t *val;
+ if ((val = find_constant((yyvsp[(1) - (1)].s)))) {
+ (yyval.expr) = magic_new_expr(EXPR_VAL);
+ (yyval.expr)->e.e_val = *val;
+ } else {
+ (yyval.expr) = magic_new_expr(EXPR_ID);
+ (yyval.expr)->e.e_id = intern_id((yyvsp[(1) - (1)].s));
+ }
+ ;}
+ break;
+
+ case 27:
+#line 343 "magic-interpreter-parser.y"
+ { (yyval.expr) = magic_new_expr(EXPR_AREA);
+ (yyval.expr)->e.e_area = (yyvsp[(1) - (1)].area); ;}
+ break;
+
+ case 28:
+#line 346 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "+", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 29:
+#line 348 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "-", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 30:
+#line 350 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "*", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 31:
+#line 352 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "%", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 32:
+#line 354 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "/", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 33:
+#line 356 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), ">", (yyvsp[(3) - (3)].expr), (yyvsp[(1) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 34:
+#line 358 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), ">", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 35:
+#line 360 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "&", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 36:
+#line 362 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "^", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 37:
+#line 364 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "|", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 38:
+#line 366 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "<<", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 39:
+#line 368 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), ">>", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 40:
+#line 370 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), ">=", (yyvsp[(3) - (3)].expr), (yyvsp[(1) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 41:
+#line 372 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), ">=", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 42:
+#line 374 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "&&", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 43:
+#line 376 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "||", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 44:
+#line 378 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "=", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 45:
+#line 380 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "=", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 46:
+#line 382 "magic-interpreter-parser.y"
+ { BIN_EXPR((yyval.expr), "=", (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column);
+ (yyval.expr) = fun_expr("not", 1, &(yyval.expr), (yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column); ;}
+ break;
+
+ case 47:
+#line 385 "magic-interpreter-parser.y"
+ { (yyval.expr) = fun_expr((yyvsp[(1) - (4)].s), (yyvsp[(3) - (4)].arg_list).args_nr, (yyvsp[(3) - (4)].arg_list).args, (yylsp[(1) - (4)]).first_line, (yylsp[(1) - (4)]).first_column);
+ if ((yyvsp[(3) - (4)].arg_list).args)
+ free((yyvsp[(3) - (4)].arg_list).args);
+ free((yyvsp[(1) - (4)].s)); ;}
+ break;
+
+ case 48:
+#line 390 "magic-interpreter-parser.y"
+ { (yyval.expr) = (yyvsp[(2) - (3)].expr); ;}
+ break;
+
+ case 49:
+#line 394 "magic-interpreter-parser.y"
+ { (yyval.arg_list).args_nr = 0; ;}
+ break;
+
+ case 50:
+#line 396 "magic-interpreter-parser.y"
+ { (yyval.arg_list) = (yyvsp[(1) - (1)].arg_list) ;}
+ break;
+
+ case 51:
+#line 401 "magic-interpreter-parser.y"
+ { (yyval.arg_list).args = aCalloc(sizeof(expr_t *), 1);
+ (yyval.arg_list).args_nr = 1;
+ (yyval.arg_list).args[0] = (yyvsp[(1) - (1)].expr);
+ ;}
+ break;
+
+ case 52:
+#line 406 "magic-interpreter-parser.y"
+ { (yyval.arg_list).args = realloc((yyval.arg_list).args, (1 + (yyval.arg_list).args_nr) * sizeof(expr_t *));
+ (yyval.arg_list).args[(yyval.arg_list).args_nr++] = (yyvsp[(3) - (3)].expr);
+ ;}
+ break;
+
+ case 53:
+#line 413 "magic-interpreter-parser.y"
+ { (yyval.location).m = (yyvsp[(3) - (8)].expr); (yyval.location).x = (yyvsp[(5) - (8)].expr); (yyval.location).y = (yyvsp[(7) - (8)].expr); ;}
+ break;
+
+ case 54:
+#line 417 "magic-interpreter-parser.y"
+ { (yyval.area).ty = AREA_LOCATION;
+ (yyval.area).a.a_loc = (yyvsp[(1) - (1)].location);
+ ;}
+ break;
+
+ case 55:
+#line 421 "magic-interpreter-parser.y"
+ { (yyval.area).ty = AREA_RECT;
+ (yyval.area).a.a_rect.loc = (yyvsp[(1) - (8)].location);
+ (yyval.area).a.a_rect.width = (yyvsp[(5) - (8)].expr);
+ (yyval.area).a.a_rect.height = (yyvsp[(7) - (8)].expr);
+ ;}
+ break;
+
+ case 56:
+#line 427 "magic-interpreter-parser.y"
+ { (yyval.area).ty = AREA_BAR;
+ (yyval.area).a.a_bar.loc = (yyvsp[(1) - (9)].location);
+ (yyval.area).a.a_bar.width = (yyvsp[(6) - (9)].expr);
+ (yyval.area).a.a_bar.depth = (yyvsp[(8) - (9)].expr);
+ (yyval.area).a.a_bar.dir = (yyvsp[(3) - (9)].expr);
+ ;}
+ break;
+
+ case 57:
+#line 437 "magic-interpreter-parser.y"
+ { (yyval.spell) = new_spell((yyvsp[(1) - (1)].spellguard)); ;}
+ break;
+
+ case 58:
+#line 439 "magic-interpreter-parser.y"
+ { (yyval.spell) = new_spell((yyvsp[(4) - (4)].spellguard));
+ (yyval.spell)->letdefs_nr = (yyvsp[(2) - (4)].letdefs).letdefs_nr;
+ (yyval.spell)->letdefs = (yyvsp[(2) - (4)].letdefs).letdefs;
+ (yyval.spell)->spellguard = (yyvsp[(4) - (4)].spellguard);
+ ;}
+ break;
+
+ case 59:
+#line 448 "magic-interpreter-parser.y"
+ { (yyval.letdefs).letdefs_nr = 0;
+ (yyval.letdefs).letdefs = (letdef_t *) malloc(1);
+ ;}
+ break;
+
+ case 60:
+#line 452 "magic-interpreter-parser.y"
+ { (yyval.letdefs) = (yyvsp[(1) - (3)].letdefs);
+ (yyval.letdefs).letdefs_nr++;
+ (yyval.letdefs).letdefs = realloc((yyval.letdefs).letdefs, sizeof(letdef_t) * (yyval.letdefs).letdefs_nr);
+ (yyval.letdefs).letdefs[(yyvsp[(1) - (3)].letdefs).letdefs_nr] = (yyvsp[(2) - (3)].vardef);
+ ;}
+ break;
+
+ case 61:
+#line 461 "magic-interpreter-parser.y"
+ {
+ if (find_constant((yyvsp[(1) - (3)].s))) {
+ fail((yylsp[(1) - (3)]).first_line, (yylsp[(1) - (3)]).first_column, "Attempt to re-define constant `%s' as LET-bound variable.\n", (yyvsp[(1) - (3)].s));
+ free((yyvsp[(1) - (3)].s));
+ } else {
+ (yyval.vardef).id = intern_id((yyvsp[(1) - (3)].s));
+ (yyval.vardef).expr = (yyvsp[(3) - (3)].expr);
+ }
+ ;}
+ break;
+
+ case 62:
+#line 474 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = (yyvsp[(1) - (1)].spellguard); ;}
+ break;
+
+ case 63:
+#line 476 "magic-interpreter-parser.y"
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = (yyvsp[(1) - (3)].spellguard);
+ sg->s.s_alt = (yyvsp[(3) - (3)].spellguard);
+ (yyval.spellguard) = sg;
+ ;}
+ break;
+
+ case 64:
+#line 485 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = spellguard_implication((yyvsp[(1) - (3)].spellguard), (yyvsp[(3) - (3)].spellguard)); ;}
+ break;
+
+ case 65:
+#line 487 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = (yyvsp[(2) - (3)].spellguard); ;}
+ break;
+
+ case 66:
+#line 489 "magic-interpreter-parser.y"
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_EFFECT);
+ sg->s.s_effect.effect = (yyvsp[(2) - (4)].effect);
+ sg->s.s_effect.at_trigger = (yyvsp[(3) - (4)].effect);
+ sg->s.s_effect.at_end = (yyvsp[(4) - (4)].effect);
+ (yyval.spellguard) = sg;
+ ;}
+ break;
+
+ case 67:
+#line 499 "magic-interpreter-parser.y"
+ { (yyval.effect) = NULL; ;}
+ break;
+
+ case 68:
+#line 501 "magic-interpreter-parser.y"
+ { (yyval.effect) = (yyvsp[(2) - (2)].effect); ;}
+ break;
+
+ case 69:
+#line 506 "magic-interpreter-parser.y"
+ { (yyval.effect) = NULL; ;}
+ break;
+
+ case 70:
+#line 508 "magic-interpreter-parser.y"
+ { (yyval.effect) = (yyvsp[(2) - (2)].effect); ;}
+ break;
+
+ case 71:
+#line 513 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = (yyvsp[(1) - (1)].spellguard); ;}
+ break;
+
+ case 72:
+#line 515 "magic-interpreter-parser.y"
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = (yyvsp[(1) - (3)].spellguard);
+ sg->s.s_alt = (yyvsp[(3) - (3)].spellguard);
+ (yyval.spellguard) = sg;
+ ;}
+ break;
+
+ case 73:
+#line 521 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = (yyvsp[(2) - (3)].spellguard); ;}
+ break;
+
+ case 74:
+#line 526 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = (yyvsp[(1) - (1)].spellguard); ;}
+ break;
+
+ case 75:
+#line 528 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = spellguard_implication ((yyvsp[(1) - (3)].spellguard), (yyvsp[(3) - (3)].spellguard)); ;}
+ break;
+
+ case 76:
+#line 533 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = new_spellguard(SPELLGUARD_CONDITION);
+ (yyval.spellguard)->s.s_condition = (yyvsp[(2) - (2)].expr);
+ ;}
+ break;
+
+ case 77:
+#line 537 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = new_spellguard(SPELLGUARD_CATALYSTS);
+ (yyval.spellguard)->s.s_catalysts = (yyvsp[(2) - (2)].components);
+ ;}
+ break;
+
+ case 78:
+#line 541 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = new_spellguard(SPELLGUARD_COMPONENTS);
+ (yyval.spellguard)->s.s_components = (yyvsp[(2) - (2)].components);
+ ;}
+ break;
+
+ case 79:
+#line 545 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = new_spellguard(SPELLGUARD_MANA);
+ (yyval.spellguard)->s.s_mana = (yyvsp[(2) - (2)].expr);
+ ;}
+ break;
+
+ case 80:
+#line 549 "magic-interpreter-parser.y"
+ { (yyval.spellguard) = new_spellguard(SPELLGUARD_CASTTIME);
+ (yyval.spellguard)->s.s_casttime = (yyvsp[(2) - (2)].expr);
+ ;}
+ break;
+
+ case 81:
+#line 556 "magic-interpreter-parser.y"
+ { (yyval.components) = (yyvsp[(2) - (3)].components); ;}
+ break;
+
+ case 82:
+#line 561 "magic-interpreter-parser.y"
+ { (yyval.components) = NULL;
+ magic_add_component(&(yyval.components), (yyvsp[(1) - (1)].component).id, (yyvsp[(1) - (1)].component).count);
+ ;}
+ break;
+
+ case 83:
+#line 565 "magic-interpreter-parser.y"
+ { (yyval.components) = (yyvsp[(1) - (3)].components);
+ magic_add_component(&(yyval.components), (yyvsp[(3) - (3)].component).id, (yyvsp[(3) - (3)].component).count);
+ ;}
+ break;
+
+ case 84:
+#line 572 "magic-interpreter-parser.y"
+ { (yyval.component).id = (yyvsp[(3) - (3)].i); (yyval.component).count = (yyvsp[(1) - (3)].i); ;}
+ break;
+
+ case 85:
+#line 574 "magic-interpreter-parser.y"
+ { (yyval.component).id = (yyvsp[(1) - (1)].i); (yyval.component).count = 1; ;}
+ break;
+
+ case 86:
+#line 579 "magic-interpreter-parser.y"
+ { struct item_data *item = itemdb_searchname((yyvsp[(1) - (1)].s));
+ if (!item) {
+ fail ((yylsp[(1) - (1)]).first_line, (yylsp[(1) - (1)]).first_column, "Unknown item `%s'\n", (yyvsp[(1) - (1)].s));
+ (yyval.i) = 0;
+ } else
+ (yyval.i) = item->nameid;
+ free ((yyvsp[(1) - (1)].s));
+ ;}
+ break;
+
+ case 87:
+#line 588 "magic-interpreter-parser.y"
+ { (yyval.i) = (yyvsp[(1) - (1)].i); ;}
+ break;
+
+ case 88:
+#line 593 "magic-interpreter-parser.y"
+ { (yyval.i) = FOREACH_FILTER_PC; ;}
+ break;
+
+ case 89:
+#line 595 "magic-interpreter-parser.y"
+ { (yyval.i) = FOREACH_FILTER_MOB; ;}
+ break;
+
+ case 90:
+#line 597 "magic-interpreter-parser.y"
+ { (yyval.i) = FOREACH_FILTER_ENTITY; ;}
+ break;
+
+ case 91:
+#line 599 "magic-interpreter-parser.y"
+ { (yyval.i) = FOREACH_FILTER_SPELL; ;}
+ break;
+
+ case 92:
+#line 601 "magic-interpreter-parser.y"
+ { (yyval.i) = FOREACH_FILTER_TARGET; ;}
+ break;
+
+ case 93:
+#line 606 "magic-interpreter-parser.y"
+ { (yyval.effect) = (yyvsp[(2) - (3)].effect); ;}
+ break;
+
+ case 94:
+#line 608 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_SKIP); ;}
+ break;
+
+ case 95:
+#line 610 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_ABORT); ;}
+ break;
+
+ case 96:
+#line 612 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_END); ;}
+ break;
+
+ case 97:
+#line 614 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_BREAK); ;}
+ break;
+
+ case 98:
+#line 616 "magic-interpreter-parser.y"
+ {
+ if (find_constant((yyvsp[(1) - (4)].s))) {
+ fail((yylsp[(1) - (4)]).first_line, (yylsp[(1) - (4)]).first_column, "Attempt to re-define constant `%s' in assignment.", (yyvsp[(1) - (4)].s));
+ free((yyvsp[(1) - (4)].s));
+ } else {
+ (yyval.effect) = new_effect(EFFECT_ASSIGN);
+ (yyval.effect)->e.e_assign.id = intern_id((yyvsp[(1) - (4)].s));
+ (yyval.effect)->e.e_assign.expr = (yyvsp[(3) - (4)].expr);
+ }
+ ;}
+ break;
+
+ case 99:
+#line 627 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_FOREACH);
+ (yyval.effect)->e.e_foreach.id = intern_id((yyvsp[(3) - (7)].s));
+ (yyval.effect)->e.e_foreach.area = (yyvsp[(5) - (7)].expr);
+ (yyval.effect)->e.e_foreach.body = (yyvsp[(7) - (7)].effect);
+ (yyval.effect)->e.e_foreach.filter = (yyvsp[(2) - (7)].i);
+ ;}
+ break;
+
+ case 100:
+#line 634 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_FOR);
+ (yyval.effect)->e.e_for.id = intern_id((yyvsp[(2) - (8)].s));
+ (yyval.effect)->e.e_for.start = (yyvsp[(4) - (8)].expr);
+ (yyval.effect)->e.e_for.stop = (yyvsp[(6) - (8)].expr);
+ (yyval.effect)->e.e_for.body = (yyvsp[(8) - (8)].effect);
+ ;}
+ break;
+
+ case 101:
+#line 641 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_IF);
+ (yyval.effect)->e.e_if.cond = (yyvsp[(2) - (6)].expr);
+ (yyval.effect)->e.e_if.true_branch = (yyvsp[(4) - (6)].effect);
+ (yyval.effect)->e.e_if.false_branch = (yyvsp[(6) - (6)].effect);
+ ;}
+ break;
+
+ case 102:
+#line 647 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_IF);
+ (yyval.effect)->e.e_if.cond = (yyvsp[(2) - (4)].expr);
+ (yyval.effect)->e.e_if.true_branch = (yyvsp[(4) - (4)].effect);
+ (yyval.effect)->e.e_if.false_branch = new_effect(EFFECT_SKIP);
+ ;}
+ break;
+
+ case 103:
+#line 653 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_SLEEP);
+ (yyval.effect)->e.e_sleep = (yyvsp[(2) - (3)].expr);
+ ;}
+ break;
+
+ case 104:
+#line 657 "magic-interpreter-parser.y"
+ { (yyval.effect) = op_effect((yyvsp[(1) - (5)].s), (yyvsp[(3) - (5)].arg_list).args_nr, (yyvsp[(3) - (5)].arg_list).args, (yylsp[(1) - (5)]).first_line, (yylsp[(1) - (5)]).first_column);
+ free((yyvsp[(1) - (5)].s));
+ ;}
+ break;
+
+ case 105:
+#line 661 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_SCRIPT);
+ (yyval.effect)->e.e_script = parse_script((unsigned char *) (yyvsp[(1) - (1)].s), (yylsp[(1) - (1)]).first_line);
+ free((yyvsp[(1) - (1)].s));
+ if ((yyval.effect)->e.e_script == NULL)
+ fail((yylsp[(1) - (1)]).first_line, (yylsp[(1) - (1)]).first_column, "Failed to compile script\n");
+ ;}
+ break;
+
+ case 106:
+#line 668 "magic-interpreter-parser.y"
+ { (yyval.effect) = call_proc((yyvsp[(2) - (6)].s), (yyvsp[(4) - (6)].arg_list).args_nr, (yyvsp[(4) - (6)].arg_list).args, (yylsp[(1) - (6)]).first_line, (yylsp[(1) - (6)]).first_column);
+ free((yyvsp[(2) - (6)].s));
+ ;}
+ break;
+
+ case 107:
+#line 674 "magic-interpreter-parser.y"
+ { (yyval.effect) = new_effect(EFFECT_SKIP); ;}
+ break;
+
+ case 108:
+#line 676 "magic-interpreter-parser.y"
+ { (yyval.effect) = set_effect_continuation((yyvsp[(1) - (3)].effect), (yyvsp[(3) - (3)].effect)); ;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 2600 "magic-interpreter-parser.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+ yyerror_range[0] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, &yylloc);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ yyerror_range[0] = yylsp[1-yylen];
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[0] = *yylsp;
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+ yyerror_range[1] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the look-ahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 680 "magic-interpreter-parser.y"
+
+
+/* We do incremental realloc here to store our results. Since this happens only once
+ * during startup for a relatively manageable set of configs, it should be fine. */
+
+static int
+intern_id(char *id_name)
+{
+ int i;
+
+ for (i = 0; i < magic_conf.vars_nr; i++)
+ if (!strcmp(id_name, magic_conf.var_name[i])) {
+ free(id_name);
+ return i;
+ }
+
+ /* Must add new */
+ i = magic_conf.vars_nr++;
+ magic_conf.var_name = realloc(magic_conf.var_name, magic_conf.vars_nr * sizeof(char *));
+ magic_conf.var_name[i] = id_name;
+ magic_conf.vars = realloc(magic_conf.vars, magic_conf.vars_nr * sizeof(val_t));
+ magic_conf.vars[i].ty = TY_UNDEF;
+
+ return i;
+}
+
+static void
+add_spell(spell_t *spell, int line_nr)
+{
+ int index = magic_conf.spells_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.spells[i]->name, spell->name)) {
+ fail(line_nr, 0, "Attempt to redefine spell `%s'\n", spell->name);
+ return;
+ }
+ if (!strcmp(magic_conf.spells[i]->invocation, spell->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine spell invocation `%s' between spells `%s' and `%s'\n",
+ spell->invocation, magic_conf.spells[i]->name, spell->name);
+ return;
+ }
+ }
+ magic_conf.spells_nr++;
+
+ magic_conf.spells = realloc(magic_conf.spells, magic_conf.spells_nr * sizeof (spell_t*));
+ magic_conf.spells[index] = spell;
+
+
+}
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr)
+{
+ int index = magic_conf.anchors_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.anchors[i]->name, anchor->name)) {
+ fail(line_nr, 0, "Attempt to redefine teleport anchor `%s'\n", anchor->name);
+ return;
+ }
+ if (!strcmp(magic_conf.anchors[i]->invocation, anchor->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine anchor invocation `%s' between anchors `%s' and `%s'\n",
+ anchor->invocation, magic_conf.anchors[i]->name, anchor->name);
+ return;
+ }
+ }
+ magic_conf.anchors_nr++;
+
+ magic_conf.anchors = realloc(magic_conf.anchors, magic_conf.anchors_nr * sizeof (teleport_anchor_t*));
+ magic_conf.anchors[index] = anchor;
+}
+
+
+static void
+fail(int line, int column, char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "[magic-init] L%d:%d: ", line, column);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ failed_flag = 1;
+}
+
+static expr_t *
+fun_expr(char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ expr_t *expr;
+ fun_t *fun = magic_get_fun(name, &id);
+
+ if (!fun) {
+ fail(line, column, "Unknown function `%s'\n", name);
+ } else if (strlen(fun->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to function `%s': Expected %d, found %d\n", name, strlen(fun->signature), args_nr);
+ fun = NULL;
+ }
+
+ if (fun) {
+ int i;
+
+ expr = magic_new_expr(EXPR_FUNAPP);
+ expr->e.e_funapp.line_nr = line;
+ expr->e.e_funapp.column = column;
+ expr->e.e_funapp.id = id;
+ expr->e.e_funapp.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ expr->e.e_funapp.args[i] = args[i];
+ } else { /* failure */
+ expr = magic_new_expr(EXPR_VAL);
+ expr->e.e_val.ty = TY_FAIL;
+ }
+
+ return expr;
+}
+
+static spell_t *
+new_spell(spellguard_t *guard)
+{
+ spell_t *retval = calloc(1, sizeof(spell_t));
+ retval->spellguard = guard;
+ return retval;
+}
+
+static spellguard_t *
+new_spellguard(int ty)
+{
+ spellguard_t *retval = calloc(1, sizeof(spellguard_t));
+ retval->ty = ty;
+ return retval;
+}
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b)
+{
+ spellguard_t *retval = a;
+
+ if (a == b) /* This can happen due to reference sharing:
+ * e.g.,
+ * (R0 -> (R1 | R2)) => (R3)
+ * yields
+ * (R0 -> (R1 -> R3 | R2 -> R3))
+ *
+ * So if we now add => R4 to that, we want
+ * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4))
+ *
+ * but we only need to add it once, because the R3 reference is shared.
+ */
+ return retval;
+
+ /* If the premise is a disjunction, b is the continuation of _all_ branches */
+ if (a->ty == SPELLGUARD_CHOICE)
+ spellguard_implication(a->s.s_alt, b);
+ if (a->next)
+ spellguard_implication(a->next, b);
+ else
+ a->next = b;
+
+
+ return retval;
+}
+
+static effect_t *
+new_effect(int ty)
+{
+ effect_t *effect = (effect_t *) calloc(1, sizeof(effect_t));
+ effect->ty = ty;
+ return effect;
+}
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation)
+{
+ effect_t *retval = src;
+ /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */
+
+ if (src == continuation)
+ return retval;
+
+ /* For FOR and FOREACH, we use special stack handlers and thus don't have to set
+ * the continuation. It's only IF that we need to handle in this fashion. */
+ if (src->ty == EFFECT_IF) {
+ set_effect_continuation(src->e.e_if.true_branch, continuation);
+ set_effect_continuation(src->e.e_if.false_branch, continuation);
+ }
+ if (src->next)
+ set_effect_continuation(src->next, continuation);
+ else
+ src->next = continuation;
+
+ return retval;
+}
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ effect_t *effect;
+ op_t *op = magic_get_op(name, &id);
+
+ if (!op)
+ fail(line, column, "Unknown operation `%s'\n", name);
+ else if (strlen(op->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to operation `%s': Expected %d, found %d\n", name, strlen(op->signature), args_nr);
+ op = NULL;
+ }
+
+ if (op) {
+ int i;
+
+ effect = new_effect(EFFECT_OP);
+ effect->e.e_op.line_nr = line;
+ effect->e.e_op.column = column;
+ effect->e.e_op.id = id;
+ effect->e.e_op.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ effect->e.e_op.args[i] = args[i];
+ } else /* failure */
+ effect = new_effect(EFFECT_SKIP);
+
+ return effect;
+}
+
+
+proc_t *procs = NULL;
+int procs_nr = 0;
+
+
+static void
+install_proc(proc_t *proc)
+{
+ if (!procs) {
+ procs = proc;
+ procs_nr = 1;
+ } else {
+ procs = realloc(procs, sizeof(proc_t) * (1 + procs_nr));
+ procs[procs_nr++] = *proc;
+ }
+}
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column)
+{
+ proc_t *p = NULL;
+ int i;
+ effect_t *retval;
+
+ for (i = 0; i < procs_nr; i++)
+ if (!strcmp(procs[i].name, name)) {
+ p = &procs[i];
+ break;
+ }
+
+ if (!p) {
+ fail(line_nr, column, "Unknown procedure `%s'\n", name);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ if (p->args_nr != args_nr) {
+ fail(line_nr, column, "Procedure %s/%d invoked with %d parameters\n", name, p->args_nr, args_nr);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ retval = new_effect(EFFECT_CALL);
+ retval->e.e_call.body = p->body;
+ retval->e.e_call.args_nr = args_nr;
+ retval->e.e_call.formals = p->args;
+ retval->e.e_call.actuals = args;
+ return retval;
+}
+
+struct const_def_rec {
+ char *name;
+ val_t val;
+} *const_defs = NULL;
+
+int const_defs_nr = 0;
+
+static void
+bind_constant(char *name, val_t *val, int line_nr)
+{
+ if (find_constant(name)) {
+ fail(line_nr, 0, "Redefinition of constant `%s'\n", name);
+ return;
+ }
+
+ if (!const_defs)
+ const_defs = (struct const_def_rec *)malloc(sizeof(struct const_def_rec));
+ else
+ const_defs = (struct const_def_rec *)realloc(const_defs,
+ (const_defs_nr + 1) * sizeof(struct const_def_rec));
+
+ const_defs[const_defs_nr].name = name;
+ const_defs[const_defs_nr].val = *val;
+ ++const_defs_nr;
+}
+
+static val_t *
+find_constant(char *name)
+{
+ int i;
+ for (i = 0; i < const_defs_nr; i++) {
+ if (!strcmp(const_defs[i].name, name)) {
+ free(name);
+ return &const_defs[i].val;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+#define INTERN_ASSERT(name, id) { int zid = intern_id(name); if (zid != id) fprintf(stderr, "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n", name, zid, id); error_flag = 1; }
+
+extern FILE *magic_frontend_in;
+
+int
+magic_init(char *conffile) // must be called after itemdb initialisation
+{
+ int error_flag = 0;
+
+ magic_conf.vars_nr = 0;
+ magic_conf.var_name = (char **)malloc(1);
+ magic_conf.vars = (val_t *)malloc(1);
+
+ magic_conf.obscure_chance = 95;
+ magic_conf.min_casttime = 100;
+
+ magic_conf.spells_nr = 0;
+ magic_conf.spells = (spell_t **)malloc(1);
+
+ magic_conf.anchors_nr = 0;
+ magic_conf.anchors = (teleport_anchor_t **)malloc(1);
+
+ INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME);
+ INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE);
+ INTERN_ASSERT("caster", VAR_CASTER);
+ INTERN_ASSERT("spellpower", VAR_SPELLPOWER);
+ INTERN_ASSERT("self_spell", VAR_SPELL);
+ INTERN_ASSERT("self_invocation", VAR_INVOCATION);
+ INTERN_ASSERT("target", VAR_TARGET);
+ INTERN_ASSERT("script_target", VAR_SCRIPTTARGET);
+ INTERN_ASSERT("location", VAR_LOCATION);
+
+ magic_frontend_in = fopen(conffile, "r");
+ if (!magic_frontend_in) {
+ fprintf(stderr, "[magic-conf] Magic configuration file `%s' not found -> no magic.\n", conffile);
+ return 0;
+ }
+ magic_frontend_parse();
+
+ if (magic_conf.vars[VAR_MIN_CASTTIME].ty == TY_INT)
+ magic_conf.min_casttime = magic_conf.vars[VAR_MIN_CASTTIME].v.v_int;
+
+ if (magic_conf.vars[VAR_OBSCURE_CHANCE].ty == TY_INT)
+ magic_conf.obscure_chance = magic_conf.vars[VAR_OBSCURE_CHANCE].v.v_int;
+
+ printf("[magic-conf] Magic initialised; obscure at %d%%. %d spells, %d teleport anchors.\n",
+ magic_conf.obscure_chance, magic_conf.spells_nr, magic_conf.anchors_nr);
+
+ if (procs)
+ free(procs);
+ return error_flag;
+}
+
+extern int magic_frontend_lineno;
+
+static void
+magic_frontend_error(const char *msg)
+{
+ fprintf(stderr, "[magic-conf] Parse error: %s at line %d\n", msg, magic_frontend_lineno);
+ failed_flag = 1;
+}
+
diff --git a/src/map/magic-interpreter-parser.h b/src/map/magic-interpreter-parser.h
new file mode 100644
index 0000000..f327d78
--- /dev/null
+++ b/src/map/magic-interpreter-parser.h
@@ -0,0 +1,195 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ INT = 258,
+ STRING = 259,
+ ID = 260,
+ DIR = 261,
+ CONST = 262,
+ PROCEDURE = 263,
+ CALL = 264,
+ SILENT = 265,
+ LOCAL = 266,
+ SHL = 267,
+ SHR = 268,
+ EQ = 269,
+ NEQ = 270,
+ GTE = 271,
+ LTE = 272,
+ ANDAND = 273,
+ OROR = 274,
+ SCRIPT_DATA = 275,
+ TO = 276,
+ TOWARDS = 277,
+ TELEPORT_ANCHOR = 278,
+ SPELL = 279,
+ LET = 280,
+ IN = 281,
+ END = 282,
+ DARROW = 283,
+ STRING_TY = 284,
+ REQUIRE = 285,
+ CATALYSTS = 286,
+ COMPONENTS = 287,
+ MANA = 288,
+ CASTTIME = 289,
+ SKIP = 290,
+ ABORT = 291,
+ BREAK = 292,
+ EFFECT = 293,
+ ATEND = 294,
+ ATTRIGGER = 295,
+ PC_F = 296,
+ MOB_F = 297,
+ ENTITY_F = 298,
+ TARGET_F = 299,
+ IF = 300,
+ THEN = 301,
+ ELSE = 302,
+ FOREACH = 303,
+ FOR = 304,
+ DO = 305,
+ SLEEP = 306,
+ OR = 307
+ };
+#endif
+/* Tokens. */
+#define INT 258
+#define STRING 259
+#define ID 260
+#define DIR 261
+#define CONST 262
+#define PROCEDURE 263
+#define CALL 264
+#define SILENT 265
+#define LOCAL 266
+#define SHL 267
+#define SHR 268
+#define EQ 269
+#define NEQ 270
+#define GTE 271
+#define LTE 272
+#define ANDAND 273
+#define OROR 274
+#define SCRIPT_DATA 275
+#define TO 276
+#define TOWARDS 277
+#define TELEPORT_ANCHOR 278
+#define SPELL 279
+#define LET 280
+#define IN 281
+#define END 282
+#define DARROW 283
+#define STRING_TY 284
+#define REQUIRE 285
+#define CATALYSTS 286
+#define COMPONENTS 287
+#define MANA 288
+#define CASTTIME 289
+#define SKIP 290
+#define ABORT 291
+#define BREAK 292
+#define EFFECT 293
+#define ATEND 294
+#define ATTRIGGER 295
+#define PC_F 296
+#define MOB_F 297
+#define ENTITY_F 298
+#define TARGET_F 299
+#define IF 300
+#define THEN 301
+#define ELSE 302
+#define FOREACH 303
+#define FOR 304
+#define DO 305
+#define SLEEP 306
+#define OR 307
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 71 "magic-interpreter-parser.y"
+{
+ int i;
+ char *s;
+ int op;
+ magic_conf_t *magic_conf;
+ val_t value;
+ expr_t *expr;
+ e_location_t location;
+ e_area_t area;
+ args_rec_t arg_list;
+ struct { int letdefs_nr; letdef_t *letdefs; } letdefs;
+ spell_t *spell;
+ struct { int id, ty; } spellarg_def;
+ letdef_t vardef;
+ spellguard_t *spellguard;
+ component_t *components;
+ struct {int id, count; } component;
+ effect_t *effect;
+ proc_t *proc;
+}
+/* Line 1489 of yacc.c. */
+#line 174 "magic-interpreter-parser.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE magic_frontend_lval;
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYLTYPE magic_frontend_lloc;
diff --git a/src/map/magic-interpreter-parser.y b/src/map/magic-interpreter-parser.y
new file mode 100644
index 0000000..6d310bd
--- /dev/null
+++ b/src/map/magic-interpreter-parser.y
@@ -0,0 +1,1057 @@
+%{
+#include "magic-interpreter.h"
+#include "magic-expr.h"
+#include <stdarg.h>
+
+magic_conf_t magic_conf;
+
+static int
+intern_id(char *id_name);
+
+
+static expr_t *
+fun_expr(char *name, int args_nr, expr_t **args, int line, int column);
+
+#define BIN_EXPR(x, name, arg1, arg2, line, column) { expr_t *e[2]; e[0] = arg1; e[1] = arg2; x = fun_expr(name, 2, e, line, column); }
+
+static int failed_flag = 0;
+
+static void
+magic_frontend_error(const char *msg);
+
+static void
+fail(int line, int column, char *fmt, ...);
+
+static spell_t *
+new_spell(spellguard_t *guard);
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b);
+
+static spellguard_t *
+new_spellguard(int ty);
+
+static effect_t *
+new_effect(int ty);
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation);
+
+static void
+add_spell(spell_t *spell, int line_nr);
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr);
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column);
+
+int
+magic_frontend_lex();
+
+static void
+install_proc(proc_t *proc);
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column);
+
+static void
+bind_constant(char *name, val_t *val, int line_nr);
+
+static val_t *
+find_constant(char *name);
+
+
+%}
+
+%name-prefix="magic_frontend_"
+
+%locations
+
+%union {
+ int i;
+ char *s;
+ int op;
+ magic_conf_t *magic_conf;
+ val_t value;
+ expr_t *expr;
+ e_location_t location;
+ e_area_t area;
+ args_rec_t arg_list;
+ struct { int letdefs_nr; letdef_t *letdefs; } letdefs;
+ spell_t *spell;
+ struct { int id, ty; } spellarg_def;
+ letdef_t vardef;
+ spellguard_t *spellguard;
+ component_t *components;
+ struct {int id, count; } component;
+ effect_t *effect;
+ proc_t *proc;
+};
+
+%expect 7
+
+%token <i> INT
+%token <s> STRING
+%token <s> ID
+%token <i> DIR
+
+%token '='
+%token '<'
+%token '>'
+%token '+'
+%token '-'
+%token '*'
+%token '/'
+%token '%'
+%token '@'
+%token ','
+%token ':'
+%token ';'
+%token '|'
+%token '['
+%token ']'
+%token '&'
+%token '^'
+
+%token CONST
+%token PROCEDURE
+%token CALL
+%token SILENT
+%token LOCAL
+%token SHL
+%token SHR
+%token EQ
+%token NEQ
+%token GTE
+%token LTE
+%token ANDAND
+%token OROR
+%token <s> SCRIPT_DATA
+%token TO
+%token TOWARDS
+%token TELEPORT_ANCHOR
+%token SPELL
+%token LET
+%token IN
+%token END
+%token DARROW
+%token STRING_TY
+%token REQUIRE
+%token CATALYSTS
+%token COMPONENTS
+%token MANA
+%token CASTTIME
+%token SKIP
+%token ABORT
+%token BREAK
+%token EFFECT
+%token ATEND
+%token ATTRIGGER
+%token PC_F
+%token MOB_F
+%token ENTITY_F
+%token TARGET_F
+%token IF
+%token THEN
+%token ELSE
+%token FOREACH
+%token FOR
+%token DO
+%token SLEEP
+
+%type <value> value
+%type <location> location
+%type <area> area
+%type <arg_list> arg_list
+%type <arg_list> arg_list_ne
+%type <letdefs> defs
+%type <spell> spelldef
+%type <spellarg_def> argopt
+%type <vardef> def
+%type <spellguard> spellbody_list
+%type <spellguard> spellbody
+%type <spellguard> spellguard
+%type <spellguard> spellguard_list
+%type <spellguard> prereq
+%type <component> item
+%type <components> items
+%type <components> item_list
+%type <i> item_name
+%type <i> selection;
+%type <effect> effect
+%type <effect> effect_list
+%type <effect> maybe_trigger
+%type <effect> maybe_end
+%type <i> spell_flags;
+
+%type <expr> expr
+%type <i> arg_ty
+%type <proc> proc_formals_list
+%type <proc> proc_formals_list_ne
+
+%left OROR
+%left ANDAND
+%left '<' '>' GTE LTE NEQ EQ
+%left '+' '-'
+%left '*' '/' '%'
+%left SHL SHR '&' '^' '|'
+%right '='
+%left OR
+%left DARROW
+
+%%
+
+spellconf : /* empty */
+ {}
+ | spellconf_option semicolons spellconf
+ {}
+ ;
+
+
+semicolons : /* empty */
+ {}
+ | semicolons ';'
+ {}
+ ;
+
+
+proc_formals_list : /* empty */
+ { $$ = aCalloc(sizeof(proc_t), 1); }
+ | proc_formals_list_ne
+ { $$ = $1; }
+ ;
+
+proc_formals_list_ne : ID
+ { $$ = aCalloc(sizeof(proc_t), 1);
+ $$->args_nr = 1;
+ $$->args = malloc(sizeof(int));
+ $$->args[0] = intern_id($1);
+ }
+ | proc_formals_list_ne ',' ID
+ { $$ = $1;
+ $$->args = realloc($$->args, sizeof(int) * (1 + $$->args_nr));
+ $$->args[$$->args_nr++] = intern_id($3);
+ }
+ ;
+
+spellconf_option : ID '=' expr
+ {
+ int var_id;
+ if (find_constant($1)) {
+ fail(@1.first_line, 0, "Attempt to redefine constant `%s' as global\n", $1);
+ free($1);
+ } else {
+ var_id = intern_id($1);
+ magic_eval(&magic_default_env, &magic_conf.vars[var_id], $3);
+ }
+ }
+ | CONST ID '=' expr
+ {
+ val_t var;
+ magic_eval(&magic_default_env, &var, $4);
+ bind_constant($2, &var, @1.first_line);
+ }
+ | TELEPORT_ANCHOR ID ':' expr '=' expr
+ {
+ teleport_anchor_t *anchor = calloc(sizeof(teleport_anchor_t), 1);
+ anchor->name = $2;
+ anchor->invocation = magic_eval_str(&magic_default_env, $4);
+ anchor->location = $6;
+
+ if (!failed_flag)
+ add_teleport_anchor(anchor, @1.first_line);
+ failed_flag = 0;
+ }
+ | PROCEDURE ID '(' proc_formals_list ')' '=' effect_list
+ {
+ proc_t *proc = $4;
+ proc->name = $2;
+ proc->body = $7;
+ if (!failed_flag)
+ install_proc(proc);
+ failed_flag = 0;
+ }
+ | spell_flags SPELL ID argopt ':' expr '=' spelldef
+ { spell_t *spell = $8;
+ spell->name = $3;
+ spell->invocation = magic_eval_str(&magic_default_env, $6);
+ spell->arg = $4.id;
+ spell->spellarg_ty = $4.ty;
+ spell->flags = $1;
+ if (!failed_flag)
+ add_spell(spell, @1.first_line);
+ failed_flag = 0;
+ }
+
+spell_flags : /* empty */
+ { $$ = 0; }
+ | LOCAL spell_flags
+ { if ($2 & SPELL_FLAG_LOCAL)
+ fail(@1.first_line, @1.first_column, "`LOCAL' specified more than once");
+ $$ = $2 | SPELL_FLAG_LOCAL;
+ }
+ | SILENT spell_flags
+ { if ($2 & SPELL_FLAG_SILENT)
+ fail(@1.first_line, @1.first_column, "`SILENT' specified more than once");
+ $$ = $2 | SPELL_FLAG_SILENT;
+ }
+
+
+argopt : /* empty */
+ { $$.ty = SPELLARG_NONE; }
+ | '(' ID ':' arg_ty ')'
+ { $$.id = intern_id($2);
+ $$.ty = $4; }
+ ;
+
+
+arg_ty : PC_F
+ { $$ = SPELLARG_PC; }
+ | STRING_TY
+ { $$ = SPELLARG_STRING; }
+ ;
+
+
+value : DIR
+ { $$.ty = TY_DIR;
+ $$.v.v_int = $1; }
+ | INT
+ { $$.ty = TY_INT;
+ $$.v.v_int = $1; }
+ | STRING
+ { $$.ty = TY_STRING;
+ $$.v.v_string = $1; }
+ ;
+
+
+expr : value
+ { $$ = magic_new_expr(EXPR_VAL);
+ $$->e.e_val = $1; }
+ | ID
+ {
+ val_t *val;
+ if ((val = find_constant($1))) {
+ $$ = magic_new_expr(EXPR_VAL);
+ $$->e.e_val = *val;
+ } else {
+ $$ = magic_new_expr(EXPR_ID);
+ $$->e.e_id = intern_id($1);
+ }
+ }
+ | area
+ { $$ = magic_new_expr(EXPR_AREA);
+ $$->e.e_area = $1; }
+ | expr '+' expr
+ { BIN_EXPR($$, "+", $1, $3, @1.first_line, @1.first_column); }
+ | expr '-' expr
+ { BIN_EXPR($$, "-", $1, $3, @1.first_line, @1.first_column); }
+ | expr '*' expr
+ { BIN_EXPR($$, "*", $1, $3, @1.first_line, @1.first_column); }
+ | expr '%' expr
+ { BIN_EXPR($$, "%", $1, $3, @1.first_line, @1.first_column); }
+ | expr '/' expr
+ { BIN_EXPR($$, "/", $1, $3, @1.first_line, @1.first_column); }
+ | expr '<' expr
+ { BIN_EXPR($$, ">", $3, $1, @1.first_line, @1.first_column); }
+ | expr '>' expr
+ { BIN_EXPR($$, ">", $1, $3, @1.first_line, @1.first_column); }
+ | expr '&' expr
+ { BIN_EXPR($$, "&", $1, $3, @1.first_line, @1.first_column); }
+ | expr '^' expr
+ { BIN_EXPR($$, "^", $1, $3, @1.first_line, @1.first_column); }
+ | expr '|' expr
+ { BIN_EXPR($$, "|", $1, $3, @1.first_line, @1.first_column); }
+ | expr SHL expr
+ { BIN_EXPR($$, "<<", $1, $3, @1.first_line, @1.first_column); }
+ | expr SHR expr
+ { BIN_EXPR($$, ">>", $1, $3, @1.first_line, @1.first_column); }
+ | expr LTE expr
+ { BIN_EXPR($$, ">=", $3, $1, @1.first_line, @1.first_column); }
+ | expr GTE expr
+ { BIN_EXPR($$, ">=", $1, $3, @1.first_line, @1.first_column); }
+ | expr ANDAND expr
+ { BIN_EXPR($$, "&&", $1, $3, @1.first_line, @1.first_column); }
+ | expr OROR expr
+ { BIN_EXPR($$, "||", $1, $3, @1.first_line, @1.first_column); }
+ | expr EQ expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); }
+ | expr '=' expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); }
+ | expr NEQ expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column);
+ $$ = fun_expr("not", 1, &$$, @1.first_line, @1.first_column); }
+ | ID '(' arg_list ')'
+ { $$ = fun_expr($1, $3.args_nr, $3.args, @1.first_line, @1.first_column);
+ if ($3.args)
+ free($3.args);
+ free($1); }
+ | '(' expr ')'
+ { $$ = $2; }
+ ;
+
+arg_list : /* empty */
+ { $$.args_nr = 0; }
+ | arg_list_ne
+ { $$ = $1 }
+ ;
+
+
+arg_list_ne : expr
+ { $$.args = aCalloc(sizeof(expr_t *), 1);
+ $$.args_nr = 1;
+ $$.args[0] = $1;
+ }
+ | arg_list_ne ',' expr
+ { $$.args = realloc($$.args, (1 + $$.args_nr) * sizeof(expr_t *));
+ $$.args[$$.args_nr++] = $3;
+ }
+ ;
+
+
+location : '@' '(' expr ',' expr ',' expr ')'
+ { $$.m = $3; $$.x = $5; $$.y = $7; }
+ ;
+
+area : location
+ { $$.ty = AREA_LOCATION;
+ $$.a.a_loc = $1;
+ }
+ | location '@' '+' '(' expr ',' expr ')'
+ { $$.ty = AREA_RECT;
+ $$.a.a_rect.loc = $1;
+ $$.a.a_rect.width = $5;
+ $$.a.a_rect.height = $7;
+ }
+ | location TOWARDS expr ':' '(' expr ',' expr ')'
+ { $$.ty = AREA_BAR;
+ $$.a.a_bar.loc = $1;
+ $$.a.a_bar.width = $6;
+ $$.a.a_bar.depth = $8;
+ $$.a.a_bar.dir = $3;
+ }
+ ;
+
+
+spelldef : spellbody_list
+ { $$ = new_spell($1); }
+ | LET defs IN spellbody_list
+ { $$ = new_spell($4);
+ $$->letdefs_nr = $2.letdefs_nr;
+ $$->letdefs = $2.letdefs;
+ $$->spellguard = $4;
+ }
+ ;
+
+
+defs : semicolons
+ { $$.letdefs_nr = 0;
+ $$.letdefs = (letdef_t *) malloc(1);
+ }
+ | defs def semicolons
+ { $$ = $1;
+ $$.letdefs_nr++;
+ $$.letdefs = realloc($$.letdefs, sizeof(letdef_t) * $$.letdefs_nr);
+ $$.letdefs[$1.letdefs_nr] = $2;
+ }
+ ;
+
+
+def : ID '=' expr
+ {
+ if (find_constant($1)) {
+ fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' as LET-bound variable.\n", $1);
+ free($1);
+ } else {
+ $$.id = intern_id($1);
+ $$.expr = $3;
+ }
+ }
+ ;
+
+
+spellbody_list : spellbody
+ { $$ = $1; }
+ | spellbody '|' spellbody_list
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = $1;
+ sg->s.s_alt = $3;
+ $$ = sg;
+ }
+ ;
+
+
+spellbody : spellguard DARROW spellbody
+ { $$ = spellguard_implication($1, $3); }
+ | '(' spellbody_list ')'
+ { $$ = $2; }
+ | EFFECT effect_list maybe_trigger maybe_end
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_EFFECT);
+ sg->s.s_effect.effect = $2;
+ sg->s.s_effect.at_trigger = $3;
+ sg->s.s_effect.at_end = $4;
+ $$ = sg;
+ }
+ ;
+
+
+maybe_trigger : /* empty */
+ { $$ = NULL; }
+ | ATTRIGGER effect_list
+ { $$ = $2; }
+ ;
+
+
+maybe_end : /* empty */
+ { $$ = NULL; }
+ | ATEND effect_list
+ { $$ = $2; }
+ ;
+
+
+spellguard : prereq
+ { $$ = $1; }
+ | spellguard OR spellguard
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = $1;
+ sg->s.s_alt = $3;
+ $$ = sg;
+ }
+ | '(' spellguard_list ')'
+ { $$ = $2; }
+ ;
+
+
+spellguard_list : spellguard
+ { $$ = $1; }
+ | spellguard ',' spellguard_list
+ { $$ = spellguard_implication ($1, $3); }
+ ;
+
+
+prereq : REQUIRE expr
+ { $$ = new_spellguard(SPELLGUARD_CONDITION);
+ $$->s.s_condition = $2;
+ }
+ | CATALYSTS items
+ { $$ = new_spellguard(SPELLGUARD_CATALYSTS);
+ $$->s.s_catalysts = $2;
+ }
+ | COMPONENTS items
+ { $$ = new_spellguard(SPELLGUARD_COMPONENTS);
+ $$->s.s_components = $2;
+ }
+ | MANA expr
+ { $$ = new_spellguard(SPELLGUARD_MANA);
+ $$->s.s_mana = $2;
+ }
+ | CASTTIME expr
+ { $$ = new_spellguard(SPELLGUARD_CASTTIME);
+ $$->s.s_casttime = $2;
+ }
+ ;
+
+
+items : '[' item_list ']'
+ { $$ = $2; }
+ ;
+
+
+item_list : item
+ { $$ = NULL;
+ magic_add_component(&$$, $1.id, $1.count);
+ }
+ | item_list ',' item
+ { $$ = $1;
+ magic_add_component(&$$, $3.id, $3.count);
+ }
+ ;
+
+
+item : INT '*' item_name
+ { $$.id = $3; $$.count = $1; }
+ | item_name
+ { $$.id = $1; $$.count = 1; }
+ ;
+
+
+item_name : STRING
+ { struct item_data *item = itemdb_searchname($1);
+ if (!item) {
+ fail (@1.first_line, @1.first_column, "Unknown item `%s'\n", $1);
+ $$ = 0;
+ } else
+ $$ = item->nameid;
+ free ($1);
+ }
+ | INT
+ { $$ = $1; }
+ ;
+
+
+selection : PC_F
+ { $$ = FOREACH_FILTER_PC; }
+ | MOB_F
+ { $$ = FOREACH_FILTER_MOB; }
+ | ENTITY_F
+ { $$ = FOREACH_FILTER_ENTITY; }
+ | SPELL
+ { $$ = FOREACH_FILTER_SPELL; }
+ | TARGET_F
+ { $$ = FOREACH_FILTER_TARGET; }
+ ;
+
+
+effect : '(' effect_list ')'
+ { $$ = $2; }
+ | SKIP ';'
+ { $$ = new_effect(EFFECT_SKIP); }
+ | ABORT ';'
+ { $$ = new_effect(EFFECT_ABORT); }
+ | END ';'
+ { $$ = new_effect(EFFECT_END); }
+ | BREAK ';'
+ { $$ = new_effect(EFFECT_BREAK); }
+ | ID '=' expr ';'
+ {
+ if (find_constant($1)) {
+ fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' in assignment.", $1);
+ free($1);
+ } else {
+ $$ = new_effect(EFFECT_ASSIGN);
+ $$->e.e_assign.id = intern_id($1);
+ $$->e.e_assign.expr = $3;
+ }
+ }
+ | FOREACH selection ID IN expr DO effect
+ { $$ = new_effect(EFFECT_FOREACH);
+ $$->e.e_foreach.id = intern_id($3);
+ $$->e.e_foreach.area = $5;
+ $$->e.e_foreach.body = $7;
+ $$->e.e_foreach.filter = $2;
+ }
+ | FOR ID '=' expr TO expr DO effect
+ { $$ = new_effect(EFFECT_FOR);
+ $$->e.e_for.id = intern_id($2);
+ $$->e.e_for.start = $4;
+ $$->e.e_for.stop = $6;
+ $$->e.e_for.body = $8;
+ }
+ | IF expr THEN effect ELSE effect
+ { $$ = new_effect(EFFECT_IF);
+ $$->e.e_if.cond = $2;
+ $$->e.e_if.true_branch = $4;
+ $$->e.e_if.false_branch = $6;
+ }
+ | IF expr THEN effect
+ { $$ = new_effect(EFFECT_IF);
+ $$->e.e_if.cond = $2;
+ $$->e.e_if.true_branch = $4;
+ $$->e.e_if.false_branch = new_effect(EFFECT_SKIP);
+ }
+ | SLEEP expr ';'
+ { $$ = new_effect(EFFECT_SLEEP);
+ $$->e.e_sleep = $2;
+ }
+ | ID '(' arg_list ')' ';'
+ { $$ = op_effect($1, $3.args_nr, $3.args, @1.first_line, @1.first_column);
+ free($1);
+ }
+ | SCRIPT_DATA
+ { $$ = new_effect(EFFECT_SCRIPT);
+ $$->e.e_script = parse_script((unsigned char *) $1, @1.first_line);
+ free($1);
+ if ($$->e.e_script == NULL)
+ fail(@1.first_line, @1.first_column, "Failed to compile script\n");
+ }
+ | CALL ID '(' arg_list ')' ';'
+ { $$ = call_proc($2, $4.args_nr, $4.args, @1.first_line, @1.first_column);
+ free($2);
+ }
+ ;
+
+effect_list : /* empty */
+ { $$ = new_effect(EFFECT_SKIP); }
+ | effect semicolons effect_list
+ { $$ = set_effect_continuation($1, $3); }
+ ;
+
+
+%%
+
+/* We do incremental realloc here to store our results. Since this happens only once
+ * during startup for a relatively manageable set of configs, it should be fine. */
+
+static int
+intern_id(char *id_name)
+{
+ int i;
+
+ for (i = 0; i < magic_conf.vars_nr; i++)
+ if (!strcmp(id_name, magic_conf.var_name[i])) {
+ free(id_name);
+ return i;
+ }
+
+ /* Must add new */
+ i = magic_conf.vars_nr++;
+ magic_conf.var_name = realloc(magic_conf.var_name, magic_conf.vars_nr * sizeof(char *));
+ magic_conf.var_name[i] = id_name;
+ magic_conf.vars = realloc(magic_conf.vars, magic_conf.vars_nr * sizeof(val_t));
+ magic_conf.vars[i].ty = TY_UNDEF;
+
+ return i;
+}
+
+static void
+add_spell(spell_t *spell, int line_nr)
+{
+ int index = magic_conf.spells_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.spells[i]->name, spell->name)) {
+ fail(line_nr, 0, "Attempt to redefine spell `%s'\n", spell->name);
+ return;
+ }
+ if (!strcmp(magic_conf.spells[i]->invocation, spell->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine spell invocation `%s' between spells `%s' and `%s'\n",
+ spell->invocation, magic_conf.spells[i]->name, spell->name);
+ return;
+ }
+ }
+ magic_conf.spells_nr++;
+
+ magic_conf.spells = realloc(magic_conf.spells, magic_conf.spells_nr * sizeof (spell_t*));
+ magic_conf.spells[index] = spell;
+
+
+}
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr)
+{
+ int index = magic_conf.anchors_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.anchors[i]->name, anchor->name)) {
+ fail(line_nr, 0, "Attempt to redefine teleport anchor `%s'\n", anchor->name);
+ return;
+ }
+ if (!strcmp(magic_conf.anchors[i]->invocation, anchor->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine anchor invocation `%s' between anchors `%s' and `%s'\n",
+ anchor->invocation, magic_conf.anchors[i]->name, anchor->name);
+ return;
+ }
+ }
+ magic_conf.anchors_nr++;
+
+ magic_conf.anchors = realloc(magic_conf.anchors, magic_conf.anchors_nr * sizeof (teleport_anchor_t*));
+ magic_conf.anchors[index] = anchor;
+}
+
+
+static void
+fail(int line, int column, char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "[magic-init] L%d:%d: ", line, column);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ failed_flag = 1;
+}
+
+static expr_t *
+fun_expr(char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ expr_t *expr;
+ fun_t *fun = magic_get_fun(name, &id);
+
+ if (!fun) {
+ fail(line, column, "Unknown function `%s'\n", name);
+ } else if (strlen(fun->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to function `%s': Expected %d, found %d\n", name, strlen(fun->signature), args_nr);
+ fun = NULL;
+ }
+
+ if (fun) {
+ int i;
+
+ expr = magic_new_expr(EXPR_FUNAPP);
+ expr->e.e_funapp.line_nr = line;
+ expr->e.e_funapp.column = column;
+ expr->e.e_funapp.id = id;
+ expr->e.e_funapp.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ expr->e.e_funapp.args[i] = args[i];
+ } else { /* failure */
+ expr = magic_new_expr(EXPR_VAL);
+ expr->e.e_val.ty = TY_FAIL;
+ }
+
+ return expr;
+}
+
+static spell_t *
+new_spell(spellguard_t *guard)
+{
+ spell_t *retval = calloc(1, sizeof(spell_t));
+ retval->spellguard = guard;
+ return retval;
+}
+
+static spellguard_t *
+new_spellguard(int ty)
+{
+ spellguard_t *retval = calloc(1, sizeof(spellguard_t));
+ retval->ty = ty;
+ return retval;
+}
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b)
+{
+ spellguard_t *retval = a;
+
+ if (a == b) /* This can happen due to reference sharing:
+ * e.g.,
+ * (R0 -> (R1 | R2)) => (R3)
+ * yields
+ * (R0 -> (R1 -> R3 | R2 -> R3))
+ *
+ * So if we now add => R4 to that, we want
+ * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4))
+ *
+ * but we only need to add it once, because the R3 reference is shared.
+ */
+ return retval;
+
+ /* If the premise is a disjunction, b is the continuation of _all_ branches */
+ if (a->ty == SPELLGUARD_CHOICE)
+ spellguard_implication(a->s.s_alt, b);
+ if (a->next)
+ spellguard_implication(a->next, b);
+ else
+ a->next = b;
+
+
+ return retval;
+}
+
+static effect_t *
+new_effect(int ty)
+{
+ effect_t *effect = (effect_t *) calloc(1, sizeof(effect_t));
+ effect->ty = ty;
+ return effect;
+}
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation)
+{
+ effect_t *retval = src;
+ /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */
+
+ if (src == continuation)
+ return retval;
+
+ /* For FOR and FOREACH, we use special stack handlers and thus don't have to set
+ * the continuation. It's only IF that we need to handle in this fashion. */
+ if (src->ty == EFFECT_IF) {
+ set_effect_continuation(src->e.e_if.true_branch, continuation);
+ set_effect_continuation(src->e.e_if.false_branch, continuation);
+ }
+ if (src->next)
+ set_effect_continuation(src->next, continuation);
+ else
+ src->next = continuation;
+
+ return retval;
+}
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ effect_t *effect;
+ op_t *op = magic_get_op(name, &id);
+
+ if (!op)
+ fail(line, column, "Unknown operation `%s'\n", name);
+ else if (strlen(op->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to operation `%s': Expected %d, found %d\n", name, strlen(op->signature), args_nr);
+ op = NULL;
+ }
+
+ if (op) {
+ int i;
+
+ effect = new_effect(EFFECT_OP);
+ effect->e.e_op.line_nr = line;
+ effect->e.e_op.column = column;
+ effect->e.e_op.id = id;
+ effect->e.e_op.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ effect->e.e_op.args[i] = args[i];
+ } else /* failure */
+ effect = new_effect(EFFECT_SKIP);
+
+ return effect;
+}
+
+
+proc_t *procs = NULL;
+int procs_nr = 0;
+
+
+static void
+install_proc(proc_t *proc)
+{
+ if (!procs) {
+ procs = proc;
+ procs_nr = 1;
+ } else {
+ procs = realloc(procs, sizeof(proc_t) * (1 + procs_nr));
+ procs[procs_nr++] = *proc;
+ }
+}
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column)
+{
+ proc_t *p = NULL;
+ int i;
+ effect_t *retval;
+
+ for (i = 0; i < procs_nr; i++)
+ if (!strcmp(procs[i].name, name)) {
+ p = &procs[i];
+ break;
+ }
+
+ if (!p) {
+ fail(line_nr, column, "Unknown procedure `%s'\n", name);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ if (p->args_nr != args_nr) {
+ fail(line_nr, column, "Procedure %s/%d invoked with %d parameters\n", name, p->args_nr, args_nr);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ retval = new_effect(EFFECT_CALL);
+ retval->e.e_call.body = p->body;
+ retval->e.e_call.args_nr = args_nr;
+ retval->e.e_call.formals = p->args;
+ retval->e.e_call.actuals = args;
+ return retval;
+}
+
+struct const_def_rec {
+ char *name;
+ val_t val;
+} *const_defs = NULL;
+
+int const_defs_nr = 0;
+
+static void
+bind_constant(char *name, val_t *val, int line_nr)
+{
+ if (find_constant(name)) {
+ fail(line_nr, 0, "Redefinition of constant `%s'\n", name);
+ return;
+ }
+
+ if (!const_defs)
+ const_defs = (struct const_def_rec *)malloc(sizeof(struct const_def_rec));
+ else
+ const_defs = (struct const_def_rec *)realloc(const_defs,
+ (const_defs_nr + 1) * sizeof(struct const_def_rec));
+
+ const_defs[const_defs_nr].name = name;
+ const_defs[const_defs_nr].val = *val;
+ ++const_defs_nr;
+}
+
+static val_t *
+find_constant(char *name)
+{
+ int i;
+ for (i = 0; i < const_defs_nr; i++) {
+ if (!strcmp(const_defs[i].name, name)) {
+ free(name);
+ return &const_defs[i].val;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+#define INTERN_ASSERT(name, id) { int zid = intern_id(name); if (zid != id) fprintf(stderr, "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n", name, zid, id); error_flag = 1; }
+
+extern FILE *magic_frontend_in;
+
+int
+magic_init(char *conffile) // must be called after itemdb initialisation
+{
+ int error_flag = 0;
+
+ magic_conf.vars_nr = 0;
+ magic_conf.var_name = (char **)malloc(1);
+ magic_conf.vars = (val_t *)malloc(1);
+
+ magic_conf.obscure_chance = 95;
+ magic_conf.min_casttime = 100;
+
+ magic_conf.spells_nr = 0;
+ magic_conf.spells = (spell_t **)malloc(1);
+
+ magic_conf.anchors_nr = 0;
+ magic_conf.anchors = (teleport_anchor_t **)malloc(1);
+
+ INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME);
+ INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE);
+ INTERN_ASSERT("caster", VAR_CASTER);
+ INTERN_ASSERT("spellpower", VAR_SPELLPOWER);
+ INTERN_ASSERT("self_spell", VAR_SPELL);
+ INTERN_ASSERT("self_invocation", VAR_INVOCATION);
+ INTERN_ASSERT("target", VAR_TARGET);
+ INTERN_ASSERT("script_target", VAR_SCRIPTTARGET);
+ INTERN_ASSERT("location", VAR_LOCATION);
+
+ magic_frontend_in = fopen(conffile, "r");
+ if (!magic_frontend_in) {
+ fprintf(stderr, "[magic-conf] Magic configuration file `%s' not found -> no magic.\n", conffile);
+ return 0;
+ }
+ magic_frontend_parse();
+
+ if (magic_conf.vars[VAR_MIN_CASTTIME].ty == TY_INT)
+ magic_conf.min_casttime = magic_conf.vars[VAR_MIN_CASTTIME].v.v_int;
+
+ if (magic_conf.vars[VAR_OBSCURE_CHANCE].ty == TY_INT)
+ magic_conf.obscure_chance = magic_conf.vars[VAR_OBSCURE_CHANCE].v.v_int;
+
+ printf("[magic-conf] Magic initialised; obscure at %d%%. %d spells, %d teleport anchors.\n",
+ magic_conf.obscure_chance, magic_conf.spells_nr, magic_conf.anchors_nr);
+
+ if (procs)
+ free(procs);
+ return error_flag;
+}
+
+extern int magic_frontend_lineno;
+
+static void
+magic_frontend_error(const char *msg)
+{
+ fprintf(stderr, "[magic-conf] Parse error: %s at line %d\n", msg, magic_frontend_lineno);
+ failed_flag = 1;
+}
diff --git a/src/map/magic-interpreter.h b/src/map/magic-interpreter.h
new file mode 100644
index 0000000..94c32bc
--- /dev/null
+++ b/src/map/magic-interpreter.h
@@ -0,0 +1,418 @@
+/* Magic interpreter */
+
+#ifndef MAGIC_INTERPRETER_H
+#define MAGIC_INTERPRETER_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "magic.h"
+#include "malloc.h"
+#include "map.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "npc.h"
+#include "mob.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "script.h"
+#include "battle.h"
+#include "skill.h"
+#include "party.h"
+#include "guild.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "vending.h"
+#include "nullpo.h"
+
+#include "../common/timer.h"
+
+#define SPELLARG_NONE 0 /* No spell parameter */
+#define SPELLARG_PC 1 /* Spell parameter describes pc (defaults to self) */
+#define SPELLARG_STRING 2 /* Spell parameter describes pc (defaults to self) */
+
+
+
+/* ------ */
+/* Values */
+/* ------ */
+
+#define TY_UNDEF 0
+#define TY_INT 1
+#define TY_DIR 2
+#define TY_STRING 3
+#define TY_ENTITY 5
+#define TY_LOCATION 6
+#define TY_AREA 7
+#define TY_SPELL 8
+#define TY_INVOCATION 9
+#define TY_FAIL 127
+
+#define DIR_S 0
+#define DIR_SW 1
+#define DIR_W 2
+#define DIR_NW 3
+#define DIR_N 4
+#define DIR_NE 5
+#define DIR_E 6
+#define DIR_SE 7
+
+struct expr;
+struct val;
+struct location;
+struct area;
+struct spell;
+struct invocation;
+
+typedef struct location {
+ int m;
+ int x, y;
+} location_t;
+
+#define AREA_LOCATION 0
+#define AREA_UNION 1
+#define AREA_RECT 2
+#define AREA_BAR 3
+
+typedef struct area {
+ union a {
+ location_t a_loc;
+ struct { location_t loc; int width, depth, dir; } a_bar;
+ struct { location_t loc; int width, height; } a_rect;
+ struct area * a_union[2];
+ } a;
+ int size;
+ unsigned char ty;
+} area_t;
+
+typedef struct val {
+ union v {
+ int v_int;
+ char *v_string;
+ entity_t *v_entity; /* Used ONLY during operation/function invocation; otherwise we use v_int */
+ area_t *v_area;
+ location_t v_location;
+ struct invocation *v_invocation; /* Used ONLY during operation/function invocation; otherwise we use v_int */
+ struct spell *v_spell;
+ } v;
+ unsigned char ty;
+} val_t;
+
+
+/* ----------- */
+/* Expressions */
+/* ----------- */
+
+#define MAX_ARGS 7 /* Max. # of args used in builtin primitive functions */
+
+#define EXPR_VAL 0
+#define EXPR_LOCATION 1
+#define EXPR_AREA 2
+#define EXPR_FUNAPP 3
+#define EXPR_ID 4
+
+typedef struct e_location {
+ struct expr *m, *x, *y;
+} e_location_t;
+
+typedef struct e_area {
+ union a0 {
+ e_location_t a_loc;
+ struct { e_location_t loc; struct expr *width, *depth, *dir; } a_bar;
+ struct { e_location_t loc; struct expr *width, *height; } a_rect;
+ struct e_area* a_union[2];
+ } a;
+ unsigned char ty;
+} e_area_t;
+
+typedef struct expr {
+ union e {
+ val_t e_val;
+ e_location_t e_location;
+ e_area_t e_area;
+ struct { int id, line_nr, column; int args_nr; struct expr *args[MAX_ARGS]; } e_funapp;
+ int e_id;
+ } e;
+ unsigned char ty;
+} expr_t;
+
+
+/* ------- */
+/* Effects */
+/* ------- */
+
+#define EFFECT_SKIP 0
+#define EFFECT_ABORT 1
+#define EFFECT_ASSIGN 2
+#define EFFECT_FOREACH 3
+#define EFFECT_FOR 4
+#define EFFECT_IF 5
+#define EFFECT_SLEEP 6
+#define EFFECT_SCRIPT 7
+#define EFFECT_BREAK 8
+#define EFFECT_OP 9
+#define EFFECT_END 10
+#define EFFECT_CALL 11
+
+#define FOREACH_FILTER_MOB 1
+#define FOREACH_FILTER_PC 2
+#define FOREACH_FILTER_ENTITY 3
+#define FOREACH_FILTER_TARGET 4
+#define FOREACH_FILTER_SPELL 5
+
+typedef struct effect {
+ struct effect* next;
+ union e0 {
+ struct { int id; expr_t *expr; } e_assign;
+ struct { int id;
+ expr_t *area;
+ struct effect* body;
+ unsigned char filter; } e_foreach;
+ struct { int id;
+ expr_t *start, *stop;
+ struct effect *body; } e_for;
+ struct { expr_t *cond; struct effect *true_branch, *false_branch; } e_if;
+ expr_t *e_sleep; /* sleep time */
+ unsigned char *e_script;
+ struct { int id; int args_nr; int line_nr, column; expr_t *args[MAX_ARGS]; } e_op;
+ struct { int args_nr, *formals; expr_t **actuals; struct effect *body; } e_call;
+ } e;
+ unsigned char ty;
+} effect_t;
+
+
+/* ---------- */
+/* Components */
+/* ---------- */
+
+typedef struct component {
+ struct component *next;
+ int item_id;
+ int count;
+} component_t;
+
+
+/* ----------- */
+/* Spellguards */
+/* ----------- */
+
+#define SPELLGUARD_CONDITION 0
+#define SPELLGUARD_COMPONENTS 1
+#define SPELLGUARD_CATALYSTS 2
+#define SPELLGUARD_CHOICE 3
+#define SPELLGUARD_MANA 4
+#define SPELLGUARD_CASTTIME 5
+#define SPELLGUARD_EFFECT 6
+
+typedef struct effect_set {
+ effect_t *effect, *at_trigger, *at_end;
+} effect_set_t;
+
+typedef struct spellguard {
+ struct spellguard *next;
+ union s {
+ expr_t *s_condition;
+ expr_t *s_mana;
+ expr_t *s_casttime;
+ component_t *s_components;
+ component_t *s_catalysts;
+ struct spellguard *s_alt; /* either `next' or `s.s_alt' */
+ effect_set_t s_effect;
+ } s;
+ unsigned char ty;
+} spellguard_t;
+
+
+/* ------ */
+/* Spells */
+/* ------ */
+
+typedef struct letdef {
+ int id;
+ expr_t *expr;
+} letdef_t;
+
+#define SPELL_FLAG_LOCAL (1 << 0) // spell associated not with caster but with place
+#define SPELL_FLAG_SILENT (1 << 1) // spell invocation never uttered
+
+typedef struct spell {
+ char *name;
+ char *invocation;
+ int flags;
+ int arg;
+ int spellarg_ty;
+
+ int letdefs_nr;
+ letdef_t *letdefs;
+
+ spellguard_t *spellguard;
+} spell_t;
+
+/* ------- */
+/* Anchors */
+/* ------- */
+
+typedef struct teleport_anchor {
+ char *name;
+ char *invocation;
+ expr_t *location;
+} teleport_anchor_t;
+
+/* ------------------- */
+/* The big config blob */
+/* ------------------- */
+
+typedef struct {
+ int vars_nr;
+ char **var_name;
+ val_t *vars; /* Initial assignments, if any, or NULL */
+
+ int obscure_chance;
+ int min_casttime;
+
+ int spells_nr;
+ spell_t **spells;
+
+ int anchors_nr; /* NEGATIVE iff we have sorted the anchors */
+ teleport_anchor_t **anchors;
+} magic_conf_t;
+
+
+/* Execution environment */
+
+#define VAR_MIN_CASTTIME 0
+#define VAR_OBSCURE_CHANCE 1
+#define VAR_CASTER 2
+#define VAR_SPELLPOWER 3
+#define VAR_SPELL 4
+#define VAR_INVOCATION 5
+#define VAR_TARGET 6
+#define VAR_SCRIPTTARGET 7
+#define VAR_LOCATION 8
+
+struct magic_config;
+
+typedef struct env {
+ magic_conf_t *base_env;
+ val_t *vars;
+} env_t;
+
+#define MAX_STACK_SIZE 32
+
+#define CONT_STACK_FOREACH 0
+#define CONT_STACK_FOR 1
+#define CONT_STACK_PROC 2
+
+typedef struct cont_activation_record {
+ effect_t *return_location;
+ union c {
+ struct { int id; effect_t *body; int entities_nr; int *entities; int index; } c_foreach;
+ struct { int id; effect_t *body; int current; int stop; } c_for;
+ struct { int args_nr, *formals; val_t *old_actuals; } c_proc;
+ } c;
+ unsigned char ty;
+} cont_activation_record_t;
+
+typedef struct status_change_ref {
+ int sc_type;
+ int bl_id;
+} status_change_ref_t;
+
+#define INVOCATION_FLAG_BOUND (1 << 0) /* Bound directly to the caster (i.e., ignore its location) */
+#define INVOCATION_FLAG_ABORTED (1 << 1) /* Used `abort' to terminate */
+
+typedef struct invocation {
+ struct block_list bl;
+
+ struct invocation *next_invocation; /* used for spells directly associated with a caster: they form a singly-linked list */
+ int flags;
+
+ env_t *env;
+ spell_t *spell;
+ int caster; /* this is the person who originally invoked the spell */
+ int subject; /* when this person dies, the spell dies with it */
+
+ int timer; /* spell timer, if any */
+
+ int stack_size;
+ cont_activation_record_t stack[MAX_STACK_SIZE];
+
+ int script_pos; /* Script position; if nonzero, resume the script we were running. */
+ effect_t *current_effect;
+ effect_t *trigger_effect; /* If non-NULL, this is used to spawn a cloned effect based on the same environment */
+ effect_t *end_effect; /* If non-NULL, this is executed when the spell terminates naturally, e.g. when all status changes have run out or all delays are over. */
+
+ /* Status change references: for status change updates, keep track of whom we updated where */
+ int status_change_refs_nr;
+ status_change_ref_t *status_change_refs;
+
+} invocation_t;
+
+
+extern magic_conf_t magic_conf; /* Global magic conf */
+extern env_t magic_default_env; /* Fake default environment */
+
+
+/**
+ * Adds a component selection to a component holder (which may initially be NULL)
+ */
+void
+magic_add_component(component_t **component_holder, int id, int count);
+
+
+teleport_anchor_t *
+magic_find_anchor(char *name);
+
+/**
+ * The parameter `param' must have been dynamically allocated; ownership is transferred to the resultant env_t.
+ */
+env_t *
+spell_create_env(magic_conf_t *conf, spell_t *spell, character_t *caster, int spellpower, char *param);
+
+void
+magic_free_env(env_t *env);
+
+effect_set_t *
+spell_trigger(spell_t *spell, character_t *caster, env_t *env);
+
+invocation_t *
+spell_instantiate(effect_set_t *effect, env_t *env);
+
+/**
+ * Bind a spell to a subject (this is a no-op for `local' spells).
+ */
+void
+spell_bind(character_t *subject, invocation_t *invocation);
+
+int // 1 on failure
+spell_unbind(character_t *subject, invocation_t *invocation);
+
+
+/**
+ * Clones a spell to run the at_effect field
+ */
+invocation_t *
+spell_clone_effect(invocation_t *source);
+
+spell_t *
+magic_find_spell(char *invocation);
+
+/* The following is used only by the parser: */
+typedef struct args_rec {
+ int args_nr;
+ expr_t **args;
+} args_rec_t;
+
+typedef struct {
+ char *name;
+ int args_nr;
+ int *args;
+ effect_t *body;
+} proc_t;
+
+#endif /* !defined (MAGIC_INTERPRETER_H) */
diff --git a/src/map/magic-interpreter.l b/src/map/magic-interpreter.l
new file mode 100644
index 0000000..ce71320
--- /dev/null
+++ b/src/map/magic-interpreter.l
@@ -0,0 +1,137 @@
+%{
+#include "magic-interpreter.h"
+#include "magic-interpreter-parser.h"
+
+#ifdef HEADING
+# undef HEADING
+#endif
+
+#define FIXLOC magic_frontend_lloc.first_line = magic_frontend_lineno
+
+#define HEADING(dir) { magic_frontend_lval.i = dir; FIXLOC; return DIR; }
+
+%}
+
+%option yylineno
+%option noyywrap
+%option prefix="magic_frontend_"
+%option nounput
+%option noinput
+%option bison-bridge bison-locations
+
+%%
+
+"S" HEADING(0);
+"SW" HEADING(1);
+"W" HEADING(2);
+"NW" HEADING(3);
+"N" HEADING(4);
+"NE" HEADING(5);
+"E" HEADING(6);
+"SE" HEADING(7);
+"=" {FIXLOC; return '=';}
+"==" {FIXLOC; return EQ;}
+"<>" {FIXLOC; return NEQ;}
+"!=" {FIXLOC; return NEQ;}
+">" {FIXLOC; return '>';}
+"<" {FIXLOC; return '<';}
+">=" {FIXLOC; return GTE;}
+"<=" {FIXLOC; return LTE;}
+"(" {FIXLOC; return '(';}
+")" {FIXLOC; return ')';}
+"+" {FIXLOC; return '+';}
+"-" {FIXLOC; return '-';}
+"*" {FIXLOC; return '*';}
+"/" {FIXLOC; return '/';}
+"%" {FIXLOC; return '%';}
+"&&" {FIXLOC; return ANDAND;}
+"||" {FIXLOC; return OROR;}
+";" {FIXLOC; return ';';}
+":" {FIXLOC; return ':';}
+"," {FIXLOC; return ',';}
+"@" {FIXLOC; return '@';}
+"|" {FIXLOC; return '|';}
+"[" {FIXLOC; return '[';}
+"]" {FIXLOC; return ']';}
+"&" {FIXLOC; return '&';}
+"^" {FIXLOC; return '^';}
+"<<" {FIXLOC; return SHL;}
+">>" {FIXLOC; return SHR;}
+"PROCEDURE" {FIXLOC; return PROCEDURE;}
+"CALL" {FIXLOC; return CALL;}
+"OR" {FIXLOC; return OR;}
+"TO" {FIXLOC; return TO;}
+"TOWARDS" {FIXLOC; return TOWARDS;}
+"TELEPORT-ANCHOR" {FIXLOC; return TELEPORT_ANCHOR;}
+"SILENT" {FIXLOC; return SILENT;}
+"LOCAL" {FIXLOC; return LOCAL;}
+"SPELL" {FIXLOC; return SPELL;}
+"LET" {FIXLOC; return LET;}
+"IN" {FIXLOC; return IN;}
+"END" {FIXLOC; return END;}
+"=>" {FIXLOC; return DARROW;}
+"STRING" {FIXLOC; return STRING_TY;}
+"REQUIRE" {FIXLOC; return REQUIRE;}
+"CATALYSTS" {FIXLOC; return CATALYSTS;}
+"COMPONENTS" {FIXLOC; return COMPONENTS;}
+"MANA" {FIXLOC; return MANA;}
+"CASTTIME" {FIXLOC; return CASTTIME;}
+"SKIP" {FIXLOC; return SKIP;}
+"ABORT" {FIXLOC; return ABORT;}
+"BREAK" {FIXLOC; return BREAK;}
+"EFFECT" {FIXLOC; return EFFECT;}
+"ATEND" {FIXLOC; return ATEND;}
+"ATTRIGGER" {FIXLOC; return ATTRIGGER;}
+"CONST" {FIXLOC; return CONST;}
+"PC" {FIXLOC; return PC_F;}
+"MOB" {FIXLOC; return MOB_F;}
+"ENTITY" {FIXLOC; return ENTITY_F;}
+"TARGET" {FIXLOC; return TARGET_F;}
+"IF" {FIXLOC; return IF;}
+"THEN" {FIXLOC; return THEN;}
+"ELSE" {FIXLOC; return ELSE;}
+"FOREACH" {FIXLOC; return FOREACH;}
+"FOR" {FIXLOC; return FOR;}
+"DO" {FIXLOC; return DO;}
+"WAIT" {FIXLOC; return SLEEP;}
+
+\{([^\}]|\\.)*\} { char *string = strdup(yytext);
+ magic_frontend_lval.s = string;
+ FIXLOC;
+ return SCRIPT_DATA;
+ }
+
+\"([^\"]|\\.)*\" { char *string = strdup(yytext + 1);
+ char *src = string;
+ char *dst = string;
+ while (*src && *src != '"')
+ if (*src == '\\') {
+ *dst++ = src[1];
+ src += 2;
+ } else
+ *dst++ = *src++;
+ *dst = '\0'; /* terminate */
+ magic_frontend_lval.s = string;
+ FIXLOC;
+ return STRING;
+ }
+
+"-"?[0-9]+ { magic_frontend_lval.i = atoi(yytext);
+ FIXLOC;
+ return INT; }
+
+"0x"[0-9a-fA-F]+ { magic_frontend_lval.i = strtol(yytext + 2, NULL, 16);
+ FIXLOC;
+ return INT; }
+
+[a-zA-Z][-_a-zA-Z0-9?!]* { magic_frontend_lval.s = strdup(yytext);
+ FIXLOC;
+ return ID; }
+
+"#".*$ /* Ignore comments */
+"//".*$ /* Ignore comments */
+[ \n\t\r] /* ignore whitespace */
+. fprintf(stderr, "%s: Unexpected character in line %d\n", MAGIC_CONFIG_FILE, magic_frontend_lineno);
+
+
+%%
diff --git a/src/map/magic-stmt.c b/src/map/magic-stmt.c
new file mode 100644
index 0000000..49c75b1
--- /dev/null
+++ b/src/map/magic-stmt.c
@@ -0,0 +1,1323 @@
+#include "magic-interpreter.h"
+#include "magic-expr.h"
+#include "magic-expr-eval.h"
+#include "magic-interpreter-aux.h"
+
+int
+clif_spawn_fake_npc_for_player(struct map_session_data *sd, int fake_npc_id);
+
+#define INVISIBLE_NPC 127 /* used for local spell effects */
+
+//#define DEBUG
+
+static void
+print_val(val_t *v)
+{
+ switch (v->ty) {
+ case TY_UNDEF: fprintf(stderr, "UNDEF"); break;
+ case TY_INT: fprintf(stderr, "%d", v->v.v_int); break;
+ case TY_DIR: fprintf(stderr, "dir%d", v->v.v_int); break;
+ case TY_STRING: fprintf(stderr, "`%s'", v->v.v_string); break;
+ default: fprintf(stderr, "ty%d", v->ty); break;
+ }
+}
+
+static void
+dump_env(env_t *env)
+{
+ int i;
+ for (i = 0; i < env->base_env->vars_nr; i++) {
+ val_t *v = &env->vars[i];
+ val_t *bv = &env->base_env->vars[i];
+
+ fprintf(stderr, "%02x %30s ", i, env->base_env->var_name[i]);
+ print_val(v);
+ fprintf(stderr, "\t(");
+ print_val(bv);
+ fprintf(stderr, ")\n");
+ }
+}
+
+static void
+clear_activation_record(cont_activation_record_t *ar)
+{
+ switch (ar->ty) {
+ case CONT_STACK_FOREACH:
+ free(ar->c.c_foreach.entities);
+ break;
+ case CONT_STACK_PROC:
+ free(ar->c.c_proc.old_actuals);
+ break;
+ }
+}
+
+static int
+invocation_timer_callback(int _, unsigned int __, int id, int data)
+{
+ invocation_t *invocation = (invocation_t *) map_id2bl(id);
+
+ if (invocation) {
+ invocation->timer = 0;
+ spell_execute(invocation);
+ }
+ return 0;
+}
+
+static void
+clear_stack(invocation_t *invocation)
+{
+ int i;
+
+ for (i = 0; i < invocation->stack_size; i++)
+ clear_activation_record(&invocation->stack[i]);
+
+ invocation->stack_size = 0;
+}
+
+static void
+free_invocation(invocation_t *invocation)
+{
+ if (invocation->status_change_refs)
+ free(invocation->status_change_refs);
+
+ if (invocation->flags & INVOCATION_FLAG_BOUND) {
+ entity_t *e = map_id2bl(invocation->subject);
+ if (e && e->type == BL_PC)
+ spell_unbind((character_t *) e, invocation);
+ }
+
+ clear_stack(invocation);
+
+ if (invocation->timer)
+ delete_timer(invocation->timer, invocation_timer_callback);
+
+ magic_free_env(invocation->env);
+
+ map_delblock(&invocation->bl);
+ map_delobject(invocation->bl.id); // also frees the object
+// free(invocation);
+}
+
+void
+magic_stop_completely(character_t *c)
+{
+ while (c->active_spells)
+ free_invocation(c->active_spells);
+}
+
+/* Spell execution has finished normally or we have been notified by a finished skill timer */
+static void
+try_to_finish_invocation(invocation_t *invocation)
+{
+ if (invocation->status_change_refs_nr == 0 && !invocation->current_effect) {
+ if (invocation->end_effect) {
+ clear_stack(invocation);
+ invocation->current_effect = invocation->end_effect;
+ invocation->end_effect = NULL;
+ spell_execute(invocation);
+ } else
+ free_invocation(invocation);
+ }
+}
+
+static void
+char_set_attack_info(character_t *subject, int speed, int range)
+{
+ subject->attack_spell_delay = speed;
+ subject->attack_spell_range = range;
+
+ if (speed == 0) {
+ pc_calcstatus(subject, 1);
+ clif_updatestatus(subject, SP_ASPD);
+ clif_updatestatus(subject, SP_ATTACKRANGE);
+ } else {
+ subject->aspd = speed;
+ clif_updatestatus(subject, SP_ASPD);
+ clif_updatestatus(subject, SP_ATTACKRANGE);
+ }
+}
+
+static void
+char_set_weapon_icon(character_t *subject, int count, int icon, int look)
+{
+// const int magic_item_inventory_index = -1;
+// const int weapon_position = 4;
+
+ // The icon isn't working at the moment.
+
+ subject->attack_spell_icon_override = icon;
+ subject->attack_spell_look_override = look;
+
+ clif_fixpcpos(subject);
+ if (count) {
+// /* Set it to `override' */
+// clif_additem(subject, magic_item_inventory_index, count, 0, icon);
+ clif_changelook(&subject->bl, LOOK_WEAPON, look);
+// clif_equipitemack(subject, magic_item_inventory_index, weapon_position, 1);
+ } else {
+ /* Set it to `normal' */
+ clif_changelook(&subject->bl, LOOK_WEAPON, subject->status.weapon);
+
+// if (subject->equip_index[weapon_position] == -1)
+// clif_equipitemack(subject, 0, weapon_position, 1);
+// else
+// clif_equipitemack(subject, subject->equip_index[weapon_position], weapon_position, 1);
+ }
+}
+
+static int
+trigger_spell(int subject, int spell)
+{
+ invocation_t *invocation = (invocation_t *)map_id2bl(spell);
+
+ if (!invocation)
+ return 0;
+
+ invocation = spell_clone_effect(invocation);
+
+ spell_bind((character_t *)map_id2bl(subject), invocation);
+ magic_clear_var(&invocation->env->vars[VAR_CASTER]);
+ invocation->env->vars[VAR_CASTER].ty = TY_ENTITY;
+ invocation->env->vars[VAR_CASTER].v.v_int = subject;
+
+ return invocation->bl.id;
+}
+
+static void
+entity_warp(entity_t *target, int destm, int destx, int desty);
+
+static void
+char_update(character_t *character)
+{
+ entity_warp((entity_t *)character, character->bl.m, character->bl.x, character->bl.y);
+}
+
+static int
+timer_callback_effect(int _, unsigned int __, int id, int data)
+{
+ clif_misceffect(map_id2bl(id), data);
+ return 0;
+}
+
+static void
+entity_effect(entity_t *entity, int effect_nr, int delay)
+{
+ add_timer(gettick() + delay,
+ &timer_callback_effect,
+ entity->id,
+ effect_nr);
+}
+
+void
+magic_unshroud(character_t *other_char)
+{
+ other_char->state.shroud_active = 0;
+ // Now warp the caster out of and back into here to refresh everyone's display
+ char_update(other_char);
+ clif_displaymessage(other_char->fd, "Your shroud has been dispelled!");
+// entity_effect(&other_char->bl, MAGIC_EFFECT_REVEAL);
+}
+
+
+static int
+timer_callback_effect_npc_delete(int timer_id, unsigned int odelay, int npc_id, int _)
+{
+ struct npc_data *effect_npc = (struct npc_data *)map_id2bl(npc_id);
+ npc_free(effect_npc);
+
+ return 0;
+}
+
+static struct npc_data *
+local_spell_effect(int m, int x, int y, int effect, int tdelay)
+{
+ int delay = 30000; /* 1 minute should be enough for all interesting spell effects, I hope */
+ struct npc_data *effect_npc = npc_spawn_text(m, x, y,
+ INVISIBLE_NPC, "", NULL);
+ int effect_npc_id = effect_npc->bl.id;
+
+ entity_effect(&effect_npc->bl, effect, tdelay);
+ add_timer(gettick() + delay,
+ timer_callback_effect_npc_delete,
+ effect_npc_id, 0);
+
+ return effect_npc;
+}
+
+
+static int
+op_sfx(env_t *env, int args_nr, val_t *args)
+{
+ int delay = ARGINT(2);
+
+ if (TY(0) == TY_ENTITY) {
+ entity_effect(ARGENTITY(0), ARGINT(1), delay);
+ } else if (TY(0) == TY_LOCATION) {
+ local_spell_effect(ARGLOCATION(0).m,
+ ARGLOCATION(0).x,
+ ARGLOCATION(0).y,
+ ARGINT(1), delay);
+ } else return 1;
+
+ return 0;
+}
+
+
+static int
+op_instaheal(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *caster = (VAR(VAR_CASTER).ty == TY_ENTITY)
+ ? map_id2bl(VAR(VAR_CASTER).v.v_int)
+ : NULL;
+ if (!caster)
+ caster = ARGENTITY(0);
+
+ battle_heal(caster, ARGENTITY(0), ARGINT(1), ARGINT(2), 0);
+ return 0;
+}
+
+
+static int
+op_itemheal(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *subject = ARGENTITY(0);
+ if (subject->type == BL_PC) {
+ pc_itemheal((struct map_session_data *) subject,
+ ARGINT(1),
+ ARGINT(2));
+ } else return op_instaheal(env, args_nr, args);
+
+ return 0;
+}
+
+
+#define SHROUD_HIDE_NAME_TALKING_FLAG (1 << 0)
+#define SHROUD_DISAPPEAR_ON_PICKUP_FLAG (1 << 1)
+#define SHROUD_DISAPPEAR_ON_TALK_FLAG (1 << 2)
+
+#define ARGCHAR(n) (ARGENTITY(n)->type == BL_PC) ? (character_t *)(ARGENTITY(n)) : NULL
+
+static int
+op_shroud(env_t *env, int args_nr, val_t *args)
+{
+ character_t *subject = ARGCHAR(0);
+ int arg = ARGINT(1);
+
+ if (!subject)
+ return 0;
+
+ subject->state.shroud_active = 1;
+ subject->state.shroud_hides_name_talking = (arg & SHROUD_HIDE_NAME_TALKING_FLAG) != 0;
+ subject->state.shroud_disappears_on_pickup = (arg & SHROUD_DISAPPEAR_ON_PICKUP_FLAG) != 0;
+ subject->state.shroud_disappears_on_talk = (arg & SHROUD_DISAPPEAR_ON_TALK_FLAG) != 0;
+ return 0;
+}
+
+static int
+op_reveal(env_t *env, int args_nr, val_t *args)
+{
+ character_t *subject = ARGCHAR(0);
+
+ if (subject && subject->state.shroud_active)
+ magic_unshroud(subject);
+
+ return 0;
+}
+
+static int
+op_message(env_t *env, int args_nr, val_t *args)
+{
+ character_t *subject = ARGCHAR(0);
+
+ if (subject)
+ clif_displaymessage(subject->fd, ARGSTR(1));
+
+ return 0;
+}
+
+static int
+timer_callback_kill_npc(int timer_id, unsigned int odelay, int npc_id, int data)
+{
+ struct npc_data *npc = (struct npc_data *) map_id2bl(npc_id);
+ if (npc)
+ npc_free(npc);
+
+ return 0;
+}
+
+static int
+op_messenger_npc(env_t *env, int args_nr, val_t *args)
+{
+ struct npc_data *npc;
+ location_t *loc = &ARGLOCATION(0);
+
+ npc = npc_spawn_text(loc->m, loc->x, loc->y,
+ ARGINT(1), ARGSTR(2), ARGSTR(3));
+
+ add_timer(gettick() + ARGINT(4),
+ &timer_callback_kill_npc,
+ npc->bl.id,
+ 0);
+
+ return 0;
+}
+
+static void
+entity_warp(entity_t *target, int destm, int destx, int desty)
+{
+ if (target->type == BL_PC
+ || target->type == BL_MOB) {
+
+ switch (target->type) {
+ case BL_PC: {
+ character_t *character = (character_t *) target;
+ clif_clearchar_area(&character->bl, 3);
+ map_delblock(&character->bl);
+ character->bl.x = destx;
+ character->bl.y = desty;
+ character->bl.m = destm;
+
+ pc_touch_all_relevant_npcs(character);
+
+ // Note that touching NPCs may have triggered warping and thereby updated x and y:
+ clif_changemap(character, map[character->bl.m].name, character->bl.x, character->bl.y);
+
+ break;
+ }
+ case BL_MOB:
+ target->x = destx;
+ target->y = desty;
+ target->m = destm;
+ clif_fixmobpos((struct mob_data *) target);
+ break;
+ }
+ }
+}
+
+static int
+op_move(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *subject = ARGENTITY(0);
+ int dir = ARGDIR(1);
+
+ int newx = subject->x + heading_x[dir];
+ int newy = subject->y + heading_y[dir];
+
+ if (!map_is_solid(subject->m, newx, newy))
+ entity_warp(subject, subject->m, newx, newy);
+
+ return 0;
+}
+
+static int
+op_warp(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *subject = ARGENTITY(0);
+ location_t *loc = &ARGLOCATION(1);
+
+ entity_warp(subject, loc->m, loc->x, loc->y);
+
+ return 0;
+}
+
+static int
+op_banish(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *subject = ARGENTITY(0);
+
+ if (subject->type == BL_MOB) {
+ struct mob_data *mob = (struct mob_data *) subject;
+
+ if (mob->mode & MOB_MODE_SUMMONED)
+ mob_catch_delete(mob, 3);
+ }
+
+ return 0;
+}
+
+static void
+record_status_change(invocation_t *invocation, int bl_id, int sc_id)
+{
+ int index = invocation->status_change_refs_nr++;
+ status_change_ref_t *cr;
+
+ if (invocation->status_change_refs)
+ invocation->status_change_refs = realloc(invocation->status_change_refs,
+ sizeof(status_change_ref_t)
+ * invocation->status_change_refs_nr);
+ else
+ invocation->status_change_refs = malloc(sizeof(status_change_ref_t));
+
+ cr = &invocation->status_change_refs[index];
+
+ cr->sc_type = sc_id;
+ cr->bl_id = bl_id;
+}
+
+static int
+op_status_change(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *subject = ARGENTITY(0);
+ int invocation_id = VAR(VAR_INVOCATION).ty == TY_INVOCATION
+ ? VAR(VAR_INVOCATION).v.v_int
+ : 0;
+ invocation_t *invocation = (invocation_t *)map_id2bl(invocation_id);
+
+ skill_status_effect(subject, ARGINT(1), ARGINT(2), ARGINT(3), ARGINT(4), ARGINT(5), ARGINT(6), 0, invocation_id);
+
+ if (invocation)
+ record_status_change(invocation, subject->id, ARGINT(1));
+
+ return 0;
+}
+
+static int
+op_override_attack(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *psubject = ARGENTITY(0);
+ int charges = ARGINT(1);
+ int attack_delay = ARGINT(2);
+ int attack_range = ARGINT(3);
+ int icon = ARGINT(4);
+ int look = ARGINT(5);
+ character_t *subject;
+
+ if (psubject->type != BL_PC)
+ return 0;
+
+ subject = (character_t *) psubject;
+
+ if (subject->attack_spell_override) {
+ invocation_t *old_invocation = (invocation_t *)map_id2bl(subject->attack_spell_override);
+ if (old_invocation)
+ free_invocation(old_invocation);
+ }
+
+ subject->attack_spell_override = trigger_spell(subject->bl.id, VAR(VAR_INVOCATION).v.v_int);
+ subject->attack_spell_charges = charges;
+
+ if (subject->attack_spell_override) {
+ char_set_weapon_icon(subject, charges, icon, look);
+ char_set_attack_info(subject, attack_delay, attack_range);
+ }
+
+ return 0;
+}
+
+
+static int
+op_create_item(env_t *env, int args_nr, val_t *args)
+{
+ struct item_data *item_data;
+ struct item item;
+ entity_t *entity = ARGENTITY(0);
+ character_t *subject;
+ int must_add_sequentially;
+ int count = ARGINT(2);
+ if (count <= 0)
+ return 0;
+
+ if (entity->type == BL_PC)
+ subject = (character_t *) entity;
+ else
+ return 0;
+
+ if (TY(1) == TY_INT)
+ item_data = itemdb_exists(ARGINT(1));
+ else if (TY(1) == TY_STRING)
+ item_data = itemdb_searchname(ARGSTR(1));
+ else
+ return 1;
+
+ if (!item_data)
+ return 0;
+
+ must_add_sequentially = (item_data->type == 4
+ || item_data->type == 5
+ || item_data->type == 7
+ || item_data->type == 8); /* Very elegant. */
+
+ memset(&item, 0, sizeof(struct item));
+ item.nameid = item_data->nameid;
+ item.identify = 1;
+
+ if (must_add_sequentially)
+ while (count--)
+ pc_additem(subject, &item, 1);
+ else
+ pc_additem(subject, &item, count);
+
+ return 0;
+}
+
+
+#define AGGRAVATION_MODE_ATTACKS_CASTER(n) ((n) == 0 || (n) == 2)
+#define AGGRAVATION_MODE_MAKES_AGGRESSIVE(n) ((n) > 0)
+
+static int
+op_aggravate(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *victim = ARGENTITY(2);
+ int mode = ARGINT(1);
+ entity_t *target = ARGENTITY(0);
+ struct mob_data *other;
+
+ if (target->type == BL_MOB)
+ other = (struct mob_data *) target;
+ else
+ return 0;
+
+ mob_target(other, victim, battle_get_range(victim));
+
+ if (AGGRAVATION_MODE_MAKES_AGGRESSIVE(mode))
+ other->mode = 0x85 | (other->mode & MOB_SENSIBLE_MASK); /* war */
+
+ if (AGGRAVATION_MODE_ATTACKS_CASTER(mode)) {
+ other->target_id = victim->id;
+ other->attacked_id = victim->id;
+ }
+
+ return 0;
+}
+
+#define MONSTER_ATTITUDE_HOSTILE 0
+#define MONSTER_ATTITUDE_FRIENDLY 1
+#define MONSTER_ATTITUDE_SERVANT 2
+
+static int
+op_spawn(env_t *env, int args_nr, val_t *args)
+{
+ area_t *area = ARGAREA(0);
+ entity_t *owner_e = ARGENTITY(1);
+ int monster_id = ARGINT(2);
+ int monster_attitude = ARGINT(3);
+ int monster_count = ARGINT(4);
+ int monster_lifetime = ARGINT(5);
+ int i;
+
+ character_t *owner = (monster_attitude == MONSTER_ATTITUDE_SERVANT && owner_e->type == BL_PC)
+ ? (character_t *) owner_e
+ : NULL;
+
+ for (i = 0; i < monster_count; i++) {
+ location_t loc;
+ magic_random_location(&loc, area);
+
+ int mob_id;
+ struct mob_data *mob;
+
+ mob_id = mob_once_spawn(owner, map[loc.m].name, loc.x, loc.y, "--ja--", // Is that needed?
+ monster_id, 1, "");
+
+ mob = (struct mob_data *)map_id2bl(mob_id);
+
+ if (mob) {
+ mob->mode = mob_db[monster_id].mode;
+
+ switch (monster_attitude) {
+
+ case MONSTER_ATTITUDE_SERVANT:
+ mob->state.special_mob_ai = 1;
+ mob->mode |= 0x04;
+ break;
+
+ case MONSTER_ATTITUDE_FRIENDLY:
+ mob->mode = 0x81;
+ break;
+
+ case MONSTER_ATTITUDE_HOSTILE:
+ mob->mode = 0x85;
+ if (owner) {
+ mob->target_id = owner->bl.id;
+ mob->attacked_id = owner->bl.id;
+ }
+ break;
+ }
+
+ mob->mode |= MOB_MODE_SUMMONED | MOB_MODE_TURNS_AGAINST_BAD_MASTER;
+
+ mob->deletetimer = add_timer(gettick() + monster_lifetime,
+ mob_timer_delete,
+ mob_id, 0);
+
+ if (owner) {
+ mob->master_id = owner->bl.id;
+ mob->master_dist = 6;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+op_injure(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *caster = ARGENTITY(0);
+ entity_t *target = ARGENTITY(1);
+ int damage_caused = ARGINT(2);
+ int mp_damage = ARGINT(3);
+ int target_hp = battle_get_hp(target);
+ int mdef = battle_get_mdef(target);
+
+ if (target->type == BL_PC
+ && !map[target->m].flag.pvp)
+ return 0; /* Cannot damage other players outside of pvp */
+
+ if (target != caster) {
+ /* Not protected against own spells */
+ damage_caused = (damage_caused * (100 - mdef)) / 100;
+ mp_damage = (mp_damage * (100 - mdef)) / 100;
+ }
+
+ damage_caused = (damage_caused > target_hp) ? target_hp : damage_caused;
+
+ if (damage_caused < 0)
+ damage_caused = 0;
+
+ if (damage_caused || mp_damage) {
+ battle_damage(caster, target, damage_caused, mp_damage);
+ clif_damage(caster, target, gettick(),
+ 0, 0, damage_caused, 0, 0, 0);
+ }
+
+ return 0;
+}
+
+static int
+op_emote(env_t *env, int args_nr, val_t *args)
+{
+ entity_t *victim = ARGENTITY(0);
+ int emotion = ARGINT(1);
+ clif_emotion(victim, emotion);
+
+ return 0;
+}
+
+static int
+op_set_script_variable(env_t *env, int args_nr, val_t *args)
+{
+ character_t *c = (ETY(0) == BL_PC)? ARGPC(0) : NULL;
+
+ if (!c)
+ return 1;
+
+ pc_setglobalreg(c, ARGSTR(1), ARGINT(2));
+
+ return 0;
+}
+
+
+static op_t operations[] =
+{
+ { "sfx", ".ii", op_sfx },
+ { "instaheal", "eii", op_instaheal },
+ { "itemheal", "eii", op_itemheal },
+ { "shroud", "ei", op_shroud },
+ { "unshroud", "e", op_reveal },
+ { "message", "es", op_message },
+ { "messenger_npc", "lissi", op_messenger_npc },
+ { "move", "ed", op_move },
+ { "warp", "el", op_warp },
+ { "banish", "e", op_banish },
+ { "status_change", "eiiiiii", op_status_change },
+ { "override_attack", "eiiiii", op_override_attack },
+ { "create_item", "e.i", op_create_item },
+ { "aggravate", "eie", op_aggravate },
+ { "spawn", "aeiiii", op_spawn },
+ { "injure", "eeii", op_injure },
+ { "emote", "ei", op_emote },
+ { "set_script_variable", "esi", op_set_script_variable },
+ { NULL, NULL, NULL }
+};
+
+static int operations_sorted = 0;
+static int operation_count;
+
+
+int
+compare_operations(const void *lhs, const void *rhs)
+{
+ return strcmp(((op_t *)lhs)->name, ((op_t *)rhs)->name);
+}
+
+
+op_t *
+magic_get_op(char *name, int *index)
+{
+ op_t key;
+
+ if (!operations_sorted) {
+ op_t *opc = operations;
+
+ while (opc->name)
+ ++opc;
+
+ operation_count = opc - operations;
+
+ qsort(operations, operation_count, sizeof(op_t), compare_operations);
+ operations_sorted = 1;
+ }
+
+ key.name = name;
+ op_t *op = bsearch(&key, operations, operation_count, sizeof(op_t), compare_operations);
+
+ if (op && index)
+ *index = op - operations;
+
+ return op;
+}
+
+
+void
+spell_effect_report_termination(int invocation_id, int bl_id, int sc_id, int supplanted)
+{
+ int i;
+ int index = -1;
+ invocation_t *invocation = (invocation_t *) map_id2bl(invocation_id);
+
+ if (!invocation)
+ return;
+
+ for (i = 0; i < invocation->status_change_refs_nr; i++) {
+ status_change_ref_t *cr = &invocation->status_change_refs[i];
+ if (cr->sc_type == sc_id && cr->bl_id == bl_id) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ fprintf(stderr, "[magic] INTERNAL ERROR: spell-effect-report-termination: tried to terminate on unexpected bl %d, sc %d\n", bl_id, sc_id);
+ return;
+ }
+
+ if (index == invocation->status_change_refs_nr -1)
+ invocation->status_change_refs_nr--;
+ else /* Copy last change ref to the one we are deleting */
+ invocation->status_change_refs[index] = invocation->status_change_refs[--invocation->status_change_refs_nr];
+
+ try_to_finish_invocation(invocation);
+}
+
+static effect_t *
+return_to_stack(invocation_t *invocation)
+{
+ if (!invocation->stack_size)
+ return NULL;
+ else {
+ cont_activation_record_t *ar = invocation->stack + (invocation->stack_size - 1);
+ switch (ar->ty) {
+
+ case CONT_STACK_PROC: {
+ effect_t *ret = ar->return_location;
+ int i;
+
+ for (i = 0; i < ar->c.c_proc.args_nr; i++) {
+ val_t *var = &invocation->env->vars[ar->c.c_proc.formals[i]];
+ magic_clear_var(var);
+ *var = ar->c.c_proc.old_actuals[i];
+ }
+
+ clear_activation_record(ar);
+ --invocation->stack_size;
+
+ return ret;
+ }
+
+ case CONT_STACK_FOREACH: {
+ int entity_id;
+ val_t *var = &invocation->env->vars[ar->c.c_foreach.id];
+
+ do {
+ if (ar->c.c_foreach.index >= ar->c.c_foreach.entities_nr) {
+ effect_t *ret = ar->return_location;
+ clear_activation_record(ar);
+ --invocation->stack_size;
+ return ret;
+ }
+
+ entity_id = ar->c.c_foreach.entities[ar->c.c_foreach.index++];
+ } while (!entity_id || !map_id2bl(entity_id));
+
+ magic_clear_var(var);
+ var->ty = TY_ENTITY;
+ var->v.v_int = entity_id;
+
+ return ar->c.c_foreach.body;
+ }
+
+ case CONT_STACK_FOR:
+ if (ar->c.c_for.current > ar->c.c_for.stop) {
+ effect_t *ret = ar->return_location;
+ clear_activation_record(ar);
+ --invocation->stack_size;
+ return ret;
+ }
+
+ magic_clear_var(&invocation->env->vars[ar->c.c_for.id]);
+ invocation->env->vars[ar->c.c_for.id].ty = TY_INT;
+ invocation->env->vars[ar->c.c_for.id].v.v_int = ar->c.c_for.current++;
+
+ return ar->c.c_for.body;
+
+ default:
+ fprintf(stderr, "[magic] INTERNAL ERROR: While executing spell `%s': stack corruption\n", invocation->spell->name);
+ return NULL;
+ }
+ }
+}
+
+static cont_activation_record_t *
+add_stack_entry(invocation_t *invocation, int ty, effect_t *return_location)
+{
+ cont_activation_record_t *ar = invocation->stack + invocation->stack_size++;
+ if (invocation->stack_size >= MAX_STACK_SIZE) {
+ fprintf(stderr, "[magic] Execution stack size exceeded in spell `%s'; truncating effect\n", invocation->spell->name);
+ invocation->stack_size--;
+ return NULL;
+ }
+
+ ar->ty = ty;
+ ar->return_location = return_location;
+ return ar;
+}
+
+static int
+find_entities_in_area_c(entity_t *target, va_list va)
+{
+ int *entities_allocd_p = va_arg(va, int *);
+ int *entities_nr_p = va_arg(va, int *);
+ int **entities_p = va_arg(va, int **);
+ int filter = va_arg(va, int);
+
+ switch (target->type) {
+
+ case BL_PC:
+ if (filter == FOREACH_FILTER_PC
+ || filter == FOREACH_FILTER_ENTITY
+ || (filter == FOREACH_FILTER_TARGET
+ && map[target->m].flag.pvp))
+ break;
+ else
+ return 0;
+
+ case BL_MOB:
+ if (filter == FOREACH_FILTER_MOB
+ || filter == FOREACH_FILTER_ENTITY
+ || filter == FOREACH_FILTER_TARGET)
+ break;
+ else
+ return 0;
+
+ default:
+ return 0;
+ }
+
+ if (*entities_nr_p == *entities_allocd_p) {
+ /* Need more space */
+ (*entities_allocd_p) += 32;
+ *entities_p = realloc(*entities_p, sizeof(int) * (*entities_allocd_p));
+ }
+
+ (*entities_p)[(*entities_nr_p)++] = target->id;
+
+ return 0;
+}
+
+static void
+find_entities_in_area(area_t *area, int *entities_allocd_p, int *entities_nr_p, int **entities_p, int filter)
+{
+ switch (area->ty) {
+ case AREA_UNION:
+ find_entities_in_area(area->a.a_union[0], entities_allocd_p, entities_nr_p, entities_p, filter);
+ find_entities_in_area(area->a.a_union[1], entities_allocd_p, entities_nr_p, entities_p, filter);
+ break;
+
+ default: {
+ int m, x, y, width, height;
+ magic_area_rect(&m, &x, &y, &width, &height, area);
+ map_foreachinarea(find_entities_in_area_c,
+ m, x, y, x+width, y+height,
+ 0 /* filter elsewhere */,
+ entities_allocd_p, entities_nr_p, entities_p, filter);
+ }
+ }
+}
+
+static effect_t *
+run_foreach(invocation_t *invocation, effect_t *foreach, effect_t *return_location)
+{
+ val_t area;
+ int filter = foreach->e.e_foreach.filter;
+ int id = foreach->e.e_foreach.id;
+ effect_t *body = foreach->e.e_foreach.body;
+
+ magic_eval(invocation->env, &area, foreach->e.e_foreach.area);
+
+ if (area.ty != TY_AREA) {
+ magic_clear_var(&area);
+ fprintf(stderr, "[magic] Error in spell `%s': FOREACH loop over non-area\n", invocation->spell->name);
+ return return_location;
+ } else {
+ cont_activation_record_t *ar = add_stack_entry(invocation, CONT_STACK_FOREACH, return_location);
+ int entities_allocd = 64;
+ int *entities_collect = malloc(entities_allocd * sizeof(int));
+ int *entities;
+ int *shuffle_board;
+ int entities_nr = 0;
+ int i;
+
+ if (!ar)
+ return return_location;
+
+ find_entities_in_area(area.v.v_area, &entities_allocd, &entities_nr, &entities_collect, filter);
+
+ /* Now shuffle */
+ shuffle_board = malloc((sizeof(int) * (1 + entities_nr))); // +1: to avoid spurious warnings in memory profilers
+ entities = malloc((sizeof(int) * (1 + entities_nr))); // +1: to avoid spurious warnings in memory profilers
+ for (i = 0; i < entities_nr; i++)
+ shuffle_board[i] = i;
+
+ for (i = entities_nr - 1 ; i >= 0; i--) {
+ int random_index = rand() % (i + 1);
+ entities[i] = entities_collect[shuffle_board[random_index]];
+ shuffle_board[random_index] = shuffle_board[i]; // thus, we are guaranteed only to use unused indices
+ }
+
+ free(entities_collect);
+ free(shuffle_board);
+ /* Done shuffling */
+
+ ar->c.c_foreach.id = id;
+ ar->c.c_foreach.body = body;
+ ar->c.c_foreach.index = 0;
+ ar->c.c_foreach.entities_nr = entities_nr;
+ ar->c.c_foreach.entities = entities;
+
+ magic_clear_var(&area);
+
+ return return_to_stack(invocation);
+ }
+}
+
+static effect_t *
+run_for(invocation_t *invocation, effect_t *for_, effect_t *return_location)
+{
+ cont_activation_record_t *ar;
+ int id = for_->e.e_for.id;
+ val_t start;
+ val_t stop;
+
+ magic_eval(invocation->env, &start, for_->e.e_for.start);
+ magic_eval(invocation->env, &stop, for_->e.e_for.stop);
+
+ if (start.ty != TY_INT || stop.ty != TY_INT) {
+ magic_clear_var(&start);
+ magic_clear_var(&stop);
+ fprintf(stderr, "[magic] Error in spell `%s': FOR loop start or stop point is not an integer\n", invocation->spell->name);
+ return return_location;
+ }
+
+ ar = add_stack_entry(invocation, CONT_STACK_FOR, return_location);
+
+ if (!ar)
+ return return_location;
+
+ ar->c.c_for.id = id;
+ ar->c.c_for.current = start.v.v_int;
+ ar->c.c_for.stop = stop.v.v_int;
+ ar->c.c_for.body = for_->e.e_for.body;
+
+ return return_to_stack(invocation);
+}
+
+static effect_t *
+run_call(invocation_t *invocation, effect_t *return_location)
+{
+ effect_t *current = invocation->current_effect;
+ cont_activation_record_t *ar;
+ int args_nr = current->e.e_call.args_nr;
+ int *formals = current->e.e_call.formals;
+ val_t *old_actuals = aCalloc(sizeof(val_t), args_nr);
+ int i;
+
+ ar = add_stack_entry(invocation, CONT_STACK_PROC, return_location);
+ ar->c.c_proc.args_nr = args_nr;
+ ar->c.c_proc.formals = formals;
+ ar->c.c_proc.old_actuals = old_actuals;
+ for (i = 0; i < args_nr; i++) {
+ val_t *env_val = &invocation->env->vars[formals[i]];
+ val_t result;
+ magic_copy_var(&old_actuals[i], env_val);
+ magic_eval(invocation->env, &result, current->e.e_call.actuals[i]);
+ *env_val = result;
+ }
+
+ return current->e.e_call.body;
+}
+
+static void
+print_cfg(int i, effect_t *e)
+{
+ int j;
+ for (j = 0; j < i; j++)
+ printf(" ");
+
+ printf("%p: ", e);
+
+ if (!e) {
+ puts(" -- end --");
+ return;
+ }
+
+ switch (e->ty) {
+ case EFFECT_SKIP:
+ puts("SKIP"); break;
+ case EFFECT_END:
+ puts("END"); break;
+ case EFFECT_ABORT:
+ puts("ABORT"); break;
+ case EFFECT_ASSIGN:
+ puts("ASSIGN"); break;
+ case EFFECT_FOREACH:
+ puts("FOREACH");
+ print_cfg(i+1, e->e.e_foreach.body);
+ break;
+ case EFFECT_FOR:
+ puts("FOR");
+ print_cfg(i+1, e->e.e_for.body);
+ break;
+ case EFFECT_IF:
+ puts("IF");
+ for (j = 0; j < i; j++)
+ printf(" ");
+ puts("THEN");
+ print_cfg(i+1, e->e.e_if.true_branch);
+ for (j = 0; j < i; j++)
+ printf(" ");
+ puts("ELSE");
+ print_cfg(i+1, e->e.e_if.false_branch);
+ break;
+ case EFFECT_SLEEP:
+ puts("SLEEP"); break;
+ case EFFECT_SCRIPT:
+ puts("SCRIPT"); break;
+ case EFFECT_BREAK:
+ puts("BREAK"); break;
+ case EFFECT_OP:
+ puts("OP"); break;
+ }
+ print_cfg(i, e->next);
+}
+
+
+/**
+ * Execute a spell invocation until we abort, finish, or hit the next `sleep'.
+ *
+ * Use spell_execute() to automate handling of timers
+ *
+ * Returns: 0 if finished (all memory is freed implicitly)
+ * >1 if we hit `sleep'; the result is the number of ticks we should sleep for.
+ * -1 if we paused to wait for a user action (via script interaction)
+ */
+static int
+spell_run(invocation_t *invocation, int allow_delete)
+{
+#ifdef DEBUG
+ fprintf(stderr, "Resuming execution: invocation of `%s'\n", invocation->spell->name);
+ print_cfg(1, invocation->current_effect);
+#endif
+ while (invocation->current_effect) {
+ effect_t *e = invocation->current_effect;
+ effect_t *next = e->next;
+ int i;
+
+#ifdef DEBUG
+ fprintf(stderr, "Next step of type %d\n", e->ty);
+ dump_env(invocation->env);
+#endif
+
+ switch (e->ty) {
+ case EFFECT_SKIP:
+ break;
+
+ case EFFECT_ABORT:
+ invocation->flags |= INVOCATION_FLAG_ABORTED;
+ invocation->end_effect = NULL;
+ case EFFECT_END:
+ clear_stack(invocation);
+ next = NULL;
+ break;
+
+ case EFFECT_ASSIGN:
+ magic_eval(invocation->env, &invocation->env->vars[e->e.e_assign.id], e->e.e_assign.expr);
+ break;
+
+ case EFFECT_FOREACH:
+ next = run_foreach(invocation, e, next);
+ break;
+
+ case EFFECT_FOR:
+ next = run_for(invocation, e, next);
+ break;
+
+ case EFFECT_IF:
+ if (magic_eval_int(invocation->env, e->e.e_if.cond))
+ next = e->e.e_if.true_branch;
+ else
+ next = e->e.e_if.false_branch;
+ break;
+
+ case EFFECT_SLEEP: {
+ int sleeptime = magic_eval_int(invocation->env, e->e.e_sleep);
+ invocation->current_effect = next;
+ if (sleeptime > 0)
+ return sleeptime;
+ break;
+ }
+
+ case EFFECT_SCRIPT: {
+ character_t *caster = (character_t *) map_id2bl(invocation->caster);
+ if (caster) {
+ env_t *env = invocation->env;
+ character_t *caster = (character_t *)map_id2bl(invocation->caster);
+ argrec_t arg[] = { {"@target", .v.i = VAR(VAR_TARGET).ty == TY_ENTITY ? 0 : VAR(VAR_TARGET).v.v_int },
+ {"@caster", .v.i = invocation->caster },
+ {"@caster_name$", .v.s = caster ? caster->status.name : "" }};
+ int message_recipient = VAR(VAR_SCRIPTTARGET).ty == TY_ENTITY
+ ? VAR(VAR_SCRIPTTARGET).v.v_int
+ : invocation->caster;
+ character_t *recipient = (character_t *) map_id2bl(message_recipient);
+
+ if (recipient->npc_id && recipient->npc_id != invocation->bl.id)
+ break; /* Don't send multiple message boxes at once */
+
+ if (!invocation->script_pos) // first time running this script?
+ clif_spawn_fake_npc_for_player(recipient,
+ invocation->bl.id);
+ // We have to do this or otherwise the client won't think that it's
+ // dealing with an NPC
+
+ int newpos = run_script_l(e->e.e_script,
+ invocation->script_pos,
+ message_recipient,
+ invocation->bl.id,
+ 3, arg);
+ /* Returns the new script position, or -1 once the script is finished */
+ if (newpos != -1) {
+ /* Must set up for continuation */
+ recipient->npc_id = invocation->bl.id;
+ recipient->npc_pos = invocation->script_pos = newpos;
+ return -1; /* Signal `wait for script' */
+ } else
+ invocation->script_pos = 0;
+ clif_clearchar_id(invocation->bl.id, 1, caster->fd);
+ }
+ break;
+ }
+
+ case EFFECT_BREAK:
+ next = return_to_stack(invocation);
+ break;
+
+ case EFFECT_OP: {
+ op_t *op = &operations[e->e.e_op.id];
+ val_t args[MAX_ARGS];
+
+ for (i = 0; i < e->e.e_op.args_nr; i++)
+ magic_eval(invocation->env, &args[i], e->e.e_op.args[i]);
+
+ if (!magic_signature_check("effect", op->name, op->signature,
+ e->e.e_op.args_nr, args,
+ e->e.e_op.line_nr, e->e.e_op.column))
+ op->op(invocation->env, e->e.e_op.args_nr, args);
+
+ for (i = 0; i < e->e.e_op.args_nr; i++)
+ magic_clear_var(&args[i]);
+ break;
+ }
+
+ case EFFECT_CALL:
+ next = run_call(invocation, next);
+ break;
+
+ default:
+ fprintf(stderr, "[magic] INTERNAL ERROR: Unknown effect %d\n", e->ty);
+ }
+
+
+ if (!next)
+ next = return_to_stack(invocation);
+
+ invocation->current_effect = next;
+ }
+
+ if (allow_delete)
+ try_to_finish_invocation(invocation);
+ return 0;
+}
+
+
+extern void
+spell_update_location(invocation_t *invocation);
+
+void
+spell_execute_d(invocation_t *invocation, int allow_deletion)
+{
+ int delta;
+
+ spell_update_location(invocation);
+ delta = spell_run(invocation, allow_deletion);
+
+ if (delta > 0)
+ invocation->timer = add_timer(gettick() + delta,
+ &invocation_timer_callback,
+ invocation->bl.id, 0);
+
+ /* If 0, the script cleaned itself. If -1 (wait-for-script), we must wait for the user. */
+}
+
+void
+spell_execute(invocation_t *invocation)
+{
+ spell_execute_d(invocation, 1);
+}
+
+int
+spell_attack(int caster_id, int target_id)
+{
+ character_t *caster = (character_t *)map_id2bl(caster_id);
+ invocation_t *invocation = (invocation_t *)map_id2bl(caster->attack_spell_override);
+
+ if (invocation
+ && caster->attack_spell_charges > 0) {
+ magic_clear_var(&invocation->env->vars[VAR_TARGET]);
+ invocation->env->vars[VAR_TARGET].ty = TY_ENTITY;
+ invocation->env->vars[VAR_TARGET].v.v_int = target_id;
+
+ invocation->current_effect = invocation->trigger_effect;
+ invocation->flags &= ~INVOCATION_FLAG_ABORTED;
+ spell_execute_d(invocation, 0 /* don't delete the invocation if done */);
+
+ if (!(invocation->flags & INVOCATION_FLAG_ABORTED)) // If we didn't abort:
+ caster->attack_spell_charges--;
+ }
+
+ if (invocation
+ && caster->attack_spell_override != invocation->bl.id) {
+ /* Attack spell changed / was refreshed */
+ free_invocation(invocation);
+ } else if (!invocation
+ || caster->attack_spell_charges <= 0) {
+ caster->attack_spell_override = 0;
+ char_set_weapon_icon(caster, 0, 0, 0);
+ char_set_attack_info(caster, 0, 0);
+
+ if (invocation)
+ free_invocation(invocation);
+ }
+
+ return 1;
+}
diff --git a/src/map/magic.c b/src/map/magic.c
new file mode 100644
index 0000000..114f74d
--- /dev/null
+++ b/src/map/magic.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "magic-interpreter.h"
+
+#undef DEBUG
+
+static char *
+magic_preprocess_message(character_t *character, char *start, char *end)
+{
+ if (character->state.shroud_active
+ && character->state.shroud_disappears_on_talk)
+ magic_unshroud(character);
+
+ if (character->state.shroud_active
+ && character->state.shroud_hides_name_talking) {
+ int len = strlen(end);
+ strcpy(start, "? ");
+ memmove(start + 2, end, len + 1);
+ return start + 4;
+ } else
+ return end + 2; /* step past blank */
+}
+
+#define ISBLANK(c) ((c) == ' ')
+
+/* Returns a dynamically allocated copy of `src'.
+ * `*parameter' may point within that copy or be NULL. */
+static char *
+magic_tokenise(char *src, char **parameter)
+{
+ char *retval = strdup(src);
+ char *seeker = retval;
+
+ while (*seeker && !ISBLANK(*seeker))
+ ++seeker;
+
+ if (!*seeker)
+ *parameter = NULL;
+ else {
+ *seeker = 0; /* Terminate invocation */
+ ++seeker;
+
+ while (ISBLANK (*seeker))
+ ++seeker;
+
+ *parameter = seeker;
+ }
+
+ return retval;
+}
+
+int
+magic_message(character_t *caster,
+ char *spell_, size_t spell_len)
+{
+ int power = caster->status.base_level + caster->status.int_;
+ char *invocation_base = spell_ + 8;
+ char *source_invocation = strchr(invocation_base, ':');
+ spell_t *spell;
+ char *parameter;
+ char *spell_invocation;
+
+ if (!source_invocation)
+ return 0;
+
+ /* Pre-message filter in case some spell alters output */
+ source_invocation = magic_preprocess_message(caster, invocation_base, source_invocation);
+
+ spell_invocation = magic_tokenise(source_invocation, &parameter);
+ parameter = parameter ? strdup(parameter) : strdup("");
+
+ spell = magic_find_spell(spell_invocation);
+ free(spell_invocation);
+
+ if (spell) {
+ env_t *env = spell_create_env(&magic_conf, spell, caster, power, parameter);
+ effect_set_t *effects = spell_trigger(spell, caster, env);
+
+#ifdef DEBUG
+ fprintf(stderr, "Found spell `%s', triggered = %d\n", spell_, effects != NULL);
+#endif
+
+ if (effects) {
+ invocation_t *invocation = spell_instantiate(effects, env);
+
+ /* We have a proper spell effect-- obscure the invocation! */
+ while (*source_invocation) {
+ if (((rand() * 100.0) / (RAND_MAX * 1.0)) < magic_conf.obscure_chance)
+ *source_invocation = '*';
+ ++source_invocation;
+ }
+
+ spell_bind(caster, invocation);
+ spell_execute(invocation);
+
+ return (spell->flags & SPELL_FLAG_SILENT)? -1 : 1;
+ } else {
+ magic_free_env(env);
+ }
+ return 0;
+ }
+
+ return 0; /* Not a spell */
+}
+
+int
+magic_init(char *conffile); // must be called after itemdb initialisation
+
+void
+do_init_magic()
+{
+ magic_init(MAGIC_CONFIG_FILE);
+}
diff --git a/src/map/magic.h b/src/map/magic.h
new file mode 100644
index 0000000..9ca47bb
--- /dev/null
+++ b/src/map/magic.h
@@ -0,0 +1,95 @@
+#ifndef MAGIC_H_
+#define MAGIC_H_
+
+#include "clif.h"
+#include "intif.h"
+
+#define MAGIC_CONFIG_FILE "conf/magic.conf"
+
+
+typedef struct map_session_data character_t;
+typedef struct block_list entity_t;
+
+struct invocation; /* Spell invocation */
+
+
+/**
+ * Try to cast magic.
+ *
+ * As an intended side effect, the magic message may be distorted (text only).
+ *
+ * \param caster Player attempting to cast magic
+ * \param spell The prospective incantation
+ * \param spell_len Number of characters in the incantation
+ * \return 1 or -1 if the input message was magic and was handled by this function, 0 otherwise. -1 is returned when the
+ * message should not be repeated.
+ */
+int
+magic_message(character_t *caster,
+ char *spell, size_t spell_len);
+
+/**
+ * Removes the shroud from a character
+ *
+ * \param character The character to remove the shroud from
+ */
+void
+magic_unshroud(character_t *character);
+
+/**
+ * Notifies a running spell that a status_change timer triggered by the spell has expired
+ *
+ * \param invocation The invocation to notify
+ * \param bl_id ID of the PC for whom this happened
+ * \param type sc_id ID of the status change entry that finished
+ * \param supplanted Whether the status_change finished normally (0) or was supplanted by a new status_change (1)
+ */
+void
+spell_effect_report_termination(int invocation, int bl_id, int sc_id, int supplanted);
+
+
+/**
+ * Initialise all spells, read config data
+ */
+void
+do_init_magic();
+
+/**
+ * Identifies the invocation used to trigger a spell
+ *
+ * Returns NULL if not found
+ */
+char *
+magic_find_invocation(char *spellame);
+
+/**
+ * Identifies the invocation used to denote a teleport location
+ *
+ * Returns NULL if not found
+ */
+char *
+magic_find_anchor_invocation(char *teleport_location);
+
+
+/**
+ * Execute a spell invocation and sets up timers to finish
+ */
+void
+spell_execute(struct invocation *invocation);
+
+/**
+ * Stops all magic bound to the specified character
+ *
+ */
+void
+magic_stop_completely(character_t *c);
+
+/**
+ * Attacks with a magical spell charged to the character
+ *
+ * Returns 0 if there is no charged spell or the spell is depleted.
+ */
+int
+spell_attack(int caster, int target);
+
+#endif /* !defined(MAGIC_H_) */
diff --git a/src/map/map.c b/src/map/map.c
index 040e180..079da90 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -35,6 +35,7 @@
#include "atcommand.h"
#include "nullpo.h"
#include "socket.h"
+#include "magic.h"
#ifdef MEMWATCH
#include "memwatch.h"
@@ -284,6 +285,7 @@ int map_delblock(struct block_list *bl)
if(bl->type==BL_PC)
map[bl->m].users--;
+
if(bl->next) bl->next->prev = bl->prev;
if(bl->prev==&bl_head){
// リストの頭なので、map[]のblock_listを更新する
@@ -655,7 +657,10 @@ int map_delobject(int id) {
if(obj==NULL)
return 0;
- map_delobjectnofree(id);
+ map_delobjectnofree(id);
+ if (obj->type == BL_PC) // [Fate] Not sure where else to put this... I'm not sure where delobject for PCs is called from
+ pc_cleanup((struct map_session_data *)obj);
+
map_freeblock(obj);
return 0;
@@ -1077,7 +1082,7 @@ struct map_session_data * map_nick2sd(char *nick) {
nicklen = strlen(nick);
for (i = 0; i < fd_max; i++) {
- if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth)
+ if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) {
// Without case sensitive check (increase the number of similar character names found)
if (strnicmp(pl_sd->status.name, nick, nicklen) == 0) {
// Strict comparison (if found, we finish the function immediatly with correct value)
@@ -1086,6 +1091,7 @@ struct map_session_data * map_nick2sd(char *nick) {
quantity++;
sd = pl_sd;
}
+ }
}
// Here, the exact character name is not found
// We return the found index of a similar account ONLY if there is 1 similar character
@@ -1991,6 +1997,7 @@ int do_init(int argc, char *argv[]) {
do_init_storage();
do_init_skill();
do_init_pet();
+ do_init_magic();
#ifndef TXT_ONLY /* mail system [Valaris] */
if(battle_config.mail_system)
@@ -2007,3 +2014,20 @@ int do_init(int argc, char *argv[]) {
return 0;
}
+int
+map_scriptcont(struct map_session_data *sd, int id)
+{
+ struct block_list *bl = map_id2bl(id);
+
+ if (!bl)
+ return 0;
+
+ switch (bl->type) {
+ case BL_NPC: return npc_scriptcont(sd, id);
+ case BL_SPELL:
+ spell_execute((struct invocation *) bl);
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/map/map.h b/src/map/map.h
index 3c7e2de..28d6758 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -5,6 +5,13 @@
#include <stdarg.h>
#include "mmo.h"
+#ifndef MAX
+# define MAX(x,y) (((x)>(y)) ? (x) : (y))
+#endif
+#ifndef MIN
+# define MIN(x,y) (((x)<(y)) ? (x) : (y))
+#endif
+
#define MAX_PC_CLASS (1+6+6+1+6+1+1+1+1+4023)
#define PC_CLASS_BASE 0
#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001)
@@ -36,8 +43,8 @@
#define OPTION_HIDE 0x40
-enum { BL_NUL, BL_PC, BL_NPC, BL_MOB, BL_ITEM, BL_CHAT, BL_SKILL , BL_PET };
-enum { WARP, SHOP, SCRIPT, MONS };
+enum { BL_NUL, BL_PC, BL_NPC, BL_MOB, BL_ITEM, BL_CHAT, BL_SKILL, BL_PET, BL_SPELL };
+enum { WARP, SHOP, SCRIPT, MONS, MESSAGE };
struct block_list {
struct block_list *next,*prev;
int id;
@@ -61,6 +68,7 @@ struct script_regstr {
struct status_change {
int timer;
int val1,val2,val3,val4;
+ int spell_invocation; /* [Fate] If triggered by a spell, record here */
};
struct vending {
short index;
@@ -68,6 +76,8 @@ struct vending {
int value;
};
+struct invocation;
+
struct skill_unit_group;
struct skill_unit {
struct block_list bl;
@@ -115,26 +125,36 @@ struct pet_db;
struct item_data;
struct square;
+struct quick_regeneration { // [Fate]
+ int amount; // Amount of HP/SP left to regenerate
+ unsigned char speed; // less is faster (number of half-second ticks to wait between updates)
+ unsigned char tickdelay; // number of ticks to next update
+};
+
struct map_session_data {
struct block_list bl;
struct {
- unsigned auth : 1;
- unsigned change_walk_target : 1;
- unsigned attack_continue : 1;
- unsigned menu_or_input : 1;
- unsigned dead_sit : 2;
- unsigned skillcastcancel : 1;
- unsigned waitingdisconnect : 1;
- unsigned lr_flag : 2;
- unsigned connect_new : 1;
- unsigned arrow_atk : 1;
- unsigned attack_type : 3;
- unsigned skill_flag : 1;
- unsigned gangsterparadise : 1;
- unsigned produce_flag : 1;
- unsigned make_arrow_flag : 1;
- unsigned potionpitcher_flag : 1;
- unsigned storage_flag : 1;
+ unsigned auth : 1;
+ unsigned change_walk_target : 1;
+ unsigned attack_continue : 1;
+ unsigned menu_or_input : 1;
+ unsigned dead_sit : 2;
+ unsigned skillcastcancel : 1;
+ unsigned waitingdisconnect : 1;
+ unsigned lr_flag : 2;
+ unsigned connect_new : 1;
+ unsigned arrow_atk : 1;
+ unsigned attack_type : 3;
+ unsigned skill_flag : 1;
+ unsigned gangsterparadise : 1;
+ unsigned produce_flag : 1;
+ unsigned make_arrow_flag : 1;
+ unsigned potionpitcher_flag : 1;
+ unsigned storage_flag : 1;
+ unsigned shroud_active : 1;
+ unsigned shroud_hides_name_talking : 1;
+ unsigned shroud_disappears_on_pickup : 1;
+ unsigned shroud_disappears_on_talk : 1;
} state;
struct {
unsigned killer : 1;
@@ -185,6 +205,16 @@ struct map_session_data {
int followtimer; // [MouseJstr]
int followtarget;
+ int cast_tick; // [Fate] Next tick at which spellcasting is allowed
+ struct invocation *active_spells; // [Fate] Singly-linked list of active spells linked to this PC
+ int attack_spell_override; // [Fate] When an attack spell is active for this player, they trigger it
+ // like a weapon. Check pc_attack_timer() for details.
+ short attack_spell_icon_override; // Weapon equipment slot (slot 4) item override
+ short attack_spell_look_override; // Weapon `look' (attack animation) override
+ short attack_spell_charges; // [Fate] Remaining number of charges for the attack spell
+ short attack_spell_delay; // [Fate] ms delay after spell attack
+ short attack_spell_range; // [Fate] spell range
+
short attackrange,attackrange_;
int skilltimer;
int skilltarget;
@@ -199,6 +229,9 @@ struct map_session_data {
int cloneskill_id,cloneskill_lv;
int potion_hp,potion_sp,potion_per_hp,potion_per_sp;
+ // [Fate] Used for gradual healing; amount of enqueued regeneration
+ struct quick_regeneration quick_regeneration_hp, quick_regeneration_sp;
+
int invincible_timer;
unsigned int canact_tick;
unsigned int canmove_tick;
@@ -358,6 +391,7 @@ struct npc_data {
short x,y;
char name[16];
} warp;
+ char *message; // for MESSAGE: only send this message
} u;
// ここにメンバを追加してはならない(shop_itemが可変長の為)
@@ -365,6 +399,12 @@ struct npc_data {
int eventtimer[MAX_EVENTTIMER];
short arenaflag;
};
+
+#define MOB_MODE_SUMMONED 0x1000
+#define MOB_MODE_TURNS_AGAINST_BAD_MASTER 0x2000
+
+#define MOB_SENSIBLE_MASK 0xf000 // fate: mob mode flags that I actually understand
+
struct mob_data {
struct block_list bl;
short n;
@@ -650,6 +690,7 @@ void map_addiddb(struct block_list *);
void map_deliddb(struct block_list *bl);
int map_foreachiddb(int (*)(void*,void*,va_list),...);
void map_addnickdb(struct map_session_data *);
+int map_scriptcont(struct map_session_data *sd,int id); /* Continues a script either on a spell or on an NPC */
struct map_session_data * map_nick2sd(char*);
// gat関連
diff --git a/src/map/mob.c b/src/map/mob.c
index 1304a41..d53cbfe 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -1881,7 +1881,7 @@ static int mob_delay_item_drop(int tid,unsigned int tick,int id,int data)
if(battle_config.item_auto_get==1){
if(ditem->first_sd && (flag = pc_additem(ditem->first_sd,&temp_item,ditem->amount))){
- clif_additem(ditem->first_sd,0,0,flag);
+ clif_additem(ditem->first_sd,0,0,flag);
map_addflooritem(&temp_item,1,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0);
}
free(ditem);
@@ -2021,6 +2021,15 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type)
nullpo_retr(0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック
+ if (src->id == md->master_id
+ && md->mode & MOB_MODE_TURNS_AGAINST_BAD_MASTER) {
+ /* If the master hits a monster, have the monster turn against him */
+ md->master_id = 0;
+ md->mode = 0x85; /* Regular war mode */
+ md->target_id = src->id;
+ md->attacked_id = src->id;
+ }
+
max_hp = battle_get_max_hp(&md->bl);
if(src && src->type == BL_PC) {
diff --git a/src/map/npc.c b/src/map/npc.c
index cdc0fe5..a82fe12 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -717,7 +717,7 @@ int npc_event(struct map_session_data *sd,const char *eventname,int mob_kill)
npc_event_dequeue(sd);
return 0;
}
-
+
sd->npc_id=nd->bl.id;
sd->npc_pos=run_script(nd->u.scr.script,ev->pos,sd->bl.id,nd->bl.id);
return 0;
@@ -773,6 +773,7 @@ int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
xs=map[m].npc[i]->u.warp.xs;
ys=map[m].npc[i]->u.warp.ys;
break;
+ case MESSAGE:
case SCRIPT:
xs=map[m].npc[i]->u.scr.xs;
ys=map[m].npc[i]->u.scr.ys;
@@ -796,6 +797,7 @@ int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
skill_stop_dancing(&sd->bl,0);
pc_setpos(sd,map[m].npc[i]->u.warp.name,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0);
break;
+ case MESSAGE:
case SCRIPT:
{
char *name=(char *)aCalloc(50,sizeof(char));
@@ -875,6 +877,10 @@ int npc_click(struct map_session_data *sd,int id)
case SCRIPT:
sd->npc_pos=run_script(nd->u.scr.script,0,sd->bl.id,id);
break;
+ case MESSAGE:
+ if (nd->u.message)
+ clif_scriptmes(sd, id, nd->u.message);
+ break;
}
return 0;
@@ -897,6 +903,12 @@ int npc_scriptcont(struct map_session_data *sd,int id)
nd=(struct npc_data *)map_id2bl(id);
+ if (!nd /* NPC was disposed? */ || nd->bl.subtype == MESSAGE) {
+ clif_scriptclose(sd, id);
+ npc_event_dequeue(sd);
+ return 0;
+ }
+
sd->npc_pos=run_script(nd->u.scr.script,sd->npc_pos,sd->bl.id,id);
return 0;
@@ -1591,7 +1603,6 @@ static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line
nd->u.scr.nexttimer=-1;
nd->u.scr.timerid=-1;
-
return 0;
}
@@ -1889,6 +1900,88 @@ static int npcname_db_final(void *key,void *data,va_list ap)
{
return 0;
}
+
+struct npc_data *
+npc_spawn_text(int m, int x, int y,
+ int class,
+ char *name,
+ char *message)
+{
+ struct npc_data *retval = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
+ retval->bl.id = npc_get_new_npc_id();
+ retval->bl.x = x;
+ retval->bl.y = y;
+ retval->bl.m = m;
+ retval->bl.type = BL_NPC;
+ retval->bl.subtype = MESSAGE;
+
+ strncpy(retval->name, name, 23);
+ strncpy(retval->exname, name, 23);
+ retval->name[15] = 0;
+ retval->exname[15] = 0;
+ retval->u.message = message? strdup(message) : NULL;
+
+ retval->class = class;
+ retval->speed = 200;
+
+ clif_spawnnpc(retval);
+ map_addblock(&retval->bl);
+ map_addiddb(&retval->bl);
+ if (retval->name && retval->name[0])
+ strdb_insert(npcname_db, retval->name, retval);
+
+ return retval;
+}
+
+static void
+npc_free_internal(struct npc_data *nd)
+{
+ struct chat_data *cd;
+
+ if(nd->chat_id && (cd=(struct chat_data*)map_id2bl(nd->chat_id))) {
+ free(cd);
+ cd = NULL;
+ }
+ if(nd->bl.subtype == SCRIPT) {
+ if(nd->u.scr.timer_event)
+ free(nd->u.scr.timer_event);
+ if(nd->u.scr.src_id==0){
+ if(nd->u.scr.script){
+ free(nd->u.scr.script);
+ nd->u.scr.script=NULL;
+ }
+ if(nd->u.scr.label_list){
+ free(nd->u.scr.label_list);
+ nd->u.scr.label_list = NULL;
+ }
+ }
+ } else if (nd->bl.subtype == MESSAGE
+ && nd->u.message) {
+ free(nd->u.message);
+ }
+ free(nd);
+}
+
+void
+npc_propagate_update(struct npc_data *nd)
+{
+ map_foreachinarea(npc_enable_sub,
+ nd->bl.m,
+ nd->bl.x - nd->u.scr.xs, nd->bl.y - nd->u.scr.ys,
+ nd->bl.x + nd->u.scr.xs, nd->bl.y + nd->u.scr.ys,
+ BL_PC, nd);
+}
+
+void
+npc_free(struct npc_data *nd)
+{
+ clif_clearchar(&nd->bl, 0);
+ npc_propagate_update(nd);
+ map_deliddb(&nd->bl);
+ map_delblock(&nd->bl);
+ npc_free_internal(nd);
+}
+
/*==========================================
* 終了
*------------------------------------------
@@ -1899,7 +1992,6 @@ int do_final_npc(void)
struct block_list *bl;
struct npc_data *nd;
struct mob_data *md;
- struct chat_data *cd;
struct pet_data *pd;
if(ev_db)
@@ -1909,28 +2001,9 @@ int do_final_npc(void)
for(i=START_NPC_NUM;i<npc_id;i++){
if((bl=map_id2bl(i))){
- if(bl->type == BL_NPC && (nd = (struct npc_data *)bl)){
- if(nd->chat_id && (cd=(struct chat_data*)map_id2bl(nd->chat_id))){
- free(cd);
- cd = NULL;
- }
- if(nd->bl.subtype == SCRIPT){
- if(nd->u.scr.timer_event)
- free(nd->u.scr.timer_event);
- if(nd->u.scr.src_id==0){
- if(nd->u.scr.script){
- free(nd->u.scr.script);
- nd->u.scr.script=NULL;
- }
- if(nd->u.scr.label_list){
- free(nd->u.scr.label_list);
- nd->u.scr.label_list = NULL;
- }
- }
- }
- free(nd);
- nd = NULL;
- }else if(bl->type == BL_MOB && (md = (struct mob_data *)bl)){
+ if(bl->type == BL_NPC && (nd = (struct npc_data *)bl))
+ npc_free_internal(nd);
+ else if(bl->type == BL_MOB && (md = (struct mob_data *)bl)){
if(md->lootitem){
free(md->lootitem);
md->lootitem = NULL;
diff --git a/src/map/npc.h b/src/map/npc.h
index 63d7765..c843b56 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -28,6 +28,21 @@ struct npc_data* npc_name2id(const char *name);
int npc_get_new_npc_id(void);
+/**
+ * Spawns and installs a talk-only NPC
+ *
+ * \param message The message to speak. If message is NULL, the NPC will not do anything at all.
+ */
+struct npc_data *npc_spawn_text(int m, int x, int y,
+ int class,
+ char *name,
+ char *message); // message is strdup'd within
+
+/**
+ * Uninstalls and frees an NPC
+ */
+void npc_free(struct npc_data *npc);
+
void npc_addsrcfile(char *);
void npc_delsrcfile(char *);
int do_final_npc(void);
diff --git a/src/map/pc.c b/src/map/pc.c
index e3b9357..435e053 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -701,11 +701,14 @@ int pc_authok(int id, int login_id2, time_t connect_until_time, struct mmo_chars
sd->inchealsptick = 0;
sd->hp_sub = 0;
sd->sp_sub = 0;
+ sd->quick_regeneration_hp.amount = 0;
+ sd->quick_regeneration_sp.amount = 0;
sd->inchealspirithptick = 0;
sd->inchealspiritsptick = 0;
sd->canact_tick = tick;
sd->canmove_tick = tick;
sd->attackabletime = tick;
+ sd->cast_tick = tick;
sd->doridori_counter = 0;
@@ -994,7 +997,8 @@ int pc_calc_skilltree(struct map_session_data *sd)
}
}
- for(i=0;i<MAX_SKILL;i++){
+ for(i=0;i<MAX_SKILL;i++)
+ if (i < TMW_MAGIC || i > TMW_MAGIC_END){ // [Fate] This hack gets TMW magic working and persisted without bothering about the skill tree.
if (sd->status.skill[i].flag != 13) sd->status.skill[i].id=0;
if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){ // cardスキルなら、
sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2; // 本当のlvに
@@ -1014,6 +1018,7 @@ int pc_calc_skilltree(struct map_session_data *sd)
for(i=355;i<MAX_SKILL;i++)
sd->status.skill[i].id=i;
}
+
}else{
// 通常の計算
do{
@@ -1049,7 +1054,9 @@ int pc_checkweighticon(struct map_session_data *sd)
nullpo_retr(0, sd);
- if(sd->weight*2 >= sd->max_weight)
+
+ if(sd->weight*2 >= sd->max_weight
+ && sd->sc_data[SC_FLYING_BACKPACK].timer == -1)
flag=1;
if(sd->weight*10 >= sd->max_weight*9)
flag=2;
@@ -1069,6 +1076,16 @@ int pc_checkweighticon(struct map_session_data *sd)
return 0;
}
+
+void
+pc_set_weapon_look(struct map_session_data* sd)
+{
+ if (sd->attack_spell_override)
+ clif_changelook(&sd->bl, LOOK_WEAPON, sd->attack_spell_look_override);
+ else
+ clif_changelook(&sd->bl, LOOK_WEAPON, sd->status.weapon);
+}
+
/*==========================================
* パラメータ計算
* first==0の時、計算対象のパラメータが呼び出し前から
@@ -1248,7 +1265,7 @@ int pc_calcstatus(struct map_session_data* sd,int first)
if(!sd->disguiseflag && sd->disguise) {
sd->disguise=0;
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ pc_set_weapon_look(sd);
clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom);
clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top);
@@ -1776,6 +1793,15 @@ int pc_calcstatus(struct map_session_data* sd,int first)
sd->sc_data[i=SC_SPEEDPOTION0].timer!=-1) // 増 速ポーション
aspd_rate -= sd->sc_data[i].val2;
+ if (sd->sc_data[SC_HASTE].timer != -1)
+ aspd_rate -= sd->sc_data[SC_HASTE].val1;
+
+
+ /* Slow down if protected */
+
+ if (sd->sc_data[SC_PHYS_SHIELD].timer != -1)
+ aspd_rate += sd->sc_data[SC_PHYS_SHIELD].val1;
+
// HIT/FLEE変化系
if(sd->sc_data[SC_WHISTLE].timer!=-1){ // 口笛
sd->flee += sd->flee * (sd->sc_data[SC_WHISTLE].val1
@@ -1890,6 +1916,10 @@ int pc_calcstatus(struct map_session_data* sd,int first)
sd->aspd = sd->aspd*aspd_rate/100;
if(pc_isriding(sd)) // 騎兵修練
sd->aspd = sd->aspd*(100 + 10*(5 - pc_checkskill(sd,KN_CAVALIERMASTERY)))/ 100;
+
+ if (sd->attack_spell_override)
+ sd->aspd = sd->attack_spell_delay;
+
if(sd->aspd < battle_config.max_aspd) sd->aspd = battle_config.max_aspd;
sd->amotion = sd->aspd;
sd->dmotion = 800-sd->paramc[1]*4;
@@ -1921,7 +1951,7 @@ int pc_calcstatus(struct map_session_data* sd,int first)
if(b_class != sd->view_class) {
clif_changelook(&sd->bl,LOOK_BASE,sd->view_class);
#if PACKETVER < 4
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ pc_set_weapon_look(sd);
clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
#else
clif_changelook(&sd->bl,LOOK_WEAPON,0);
@@ -2826,6 +2856,47 @@ int pc_search_inventory(struct map_session_data *sd,int item_id)
return -1;
}
+int
+pc_count_all_items(struct map_session_data *player, int item_id)
+{
+ int i;
+ int count = 0;
+
+ nullpo_retr(0, player);
+
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (player->status.inventory[i].nameid == item_id)
+ count += player->status.inventory[i].amount;
+ }
+
+ return count;
+}
+
+int
+pc_remove_items(struct map_session_data *player, int item_id, int count)
+{
+ int i;
+
+ nullpo_retr(0, player);
+
+ for (i = 0; i < MAX_INVENTORY && count; i++) {
+ if (player->status.inventory[i].nameid == item_id) {
+ int to_delete = count;
+ /* only delete as much as we have */
+ if (to_delete > player->status.inventory[i].amount)
+ to_delete = player->status.inventory[i].amount;
+
+ count -= to_delete;
+
+ pc_delitem(player, i, to_delete, 0 /* means `really delete and update status' */);
+
+ if (!count)
+ return 0;
+ }
+ }
+ return 0;
+}
+
/*==========================================
* アイテム追加。個数のみitem構造体の数字を無視
*------------------------------------------
@@ -3830,6 +3901,15 @@ int pc_stop_walking(struct map_session_data *sd,int type)
return 0;
}
+void
+pc_touch_all_relevant_npcs(struct map_session_data *sd)
+{
+ if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y)&0x80)
+ npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
+ else
+ sd->areanpc_id=0;
+}
+
/*==========================================
*
*------------------------------------------
@@ -3878,10 +3958,7 @@ int pc_movepos(struct map_session_data *sd,int dst_x,int dst_y)
skill_unit_move(&sd->bl,gettick(),dist+7); // スキルユニットの検査
- if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y)&0x80)
- npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
- else
- sd->areanpc_id=0;
+ pc_touch_all_relevant_npcs(sd);
return 0;
}
@@ -4035,6 +4112,7 @@ int pc_attack_timer(int tid,unsigned int tick,int id,int data)
short *opt;
int dist,skill,range;
+
sd=map_id2sd(id);
if(sd == NULL)
return 0;
@@ -4084,54 +4162,61 @@ int pc_attack_timer(int tid,unsigned int tick,int id,int data)
}
}
- dist = distance(sd->bl.x,sd->bl.y,bl->x,bl->y);
- range = sd->attackrange;
- if(sd->status.weapon != 11) range++;
- if( dist > range ){ // 届 かないので移動
- //if(pc_can_reach(sd,bl->x,bl->y))
- //clif_movetoattack(sd,bl);
- return 0;
- }
-
if (sd->attackabletime > tick)
return 0; // cannot attack yet
- if(dist <= range && !battle_check_range(&sd->bl,bl,range) ) {
- if(pc_can_reach(sd,bl->x,bl->y) && sd->canmove_tick < tick && (sd->sc_data[SC_ANKLE].timer == -1 || sd->sc_data[SC_SPIDERWEB].timer == -1))
- // TMW client doesn't support this
- //pc_walktoxy(sd,bl->x,bl->y);
- clif_movetoattack(sd, bl);
- sd->attackabletime = tick + (sd->aspd<<1);
- }
- else {
- if(battle_config.pc_attack_direction_change)
- sd->dir=sd->head_dir=map_calc_dir(&sd->bl, bl->x,bl->y ); // 向き設定
-
- if(sd->walktimer != -1)
- pc_stop_walking(sd,1);
+ if (sd->attack_spell_override // [Fate] If we have an active attack spell, use that
+ && spell_attack(id, sd->attacktarget)) {
+ // Return if the spell succeeded. If the spell had disspiated, spell_attack() may fail.
+ sd->attackabletime = tick + sd->attack_spell_delay;
+
+ } else {
+ dist = distance(sd->bl.x,sd->bl.y,bl->x,bl->y);
+ range = sd->attackrange;
+ if(sd->status.weapon != 11) range++;
+ if( dist > range ){ // 届 かないので移動
+ //if(pc_can_reach(sd,bl->x,bl->y))
+ //clif_movetoattack(sd,bl);
+ return 0;
+ }
- if(sd->sc_data[SC_COMBO].timer == -1) {
- map_freeblock_lock();
- pc_stop_walking(sd,0);
- sd->attacktarget_lv = battle_weapon_attack(&sd->bl,bl,tick,0);
- if(!(battle_config.pc_cloak_check_type&2) && sd->sc_data[SC_CLOAKING].timer != -1)
- skill_status_change_end(&sd->bl,SC_CLOAKING,-1);
- if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support)
- pet_target_check(sd,bl,0);
- map_freeblock_unlock();
- if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト
- sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
- else
- sd->attackabletime = tick + (sd->aspd<<1);
- }
- else if(sd->attackabletime <= tick) {
- if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト
- sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
- else
- sd->attackabletime = tick + (sd->aspd<<1);
- }
- if(sd->attackabletime <= tick) sd->attackabletime = tick + (battle_config.max_aspd<<1);
- }
+ if(dist <= range && !battle_check_range(&sd->bl,bl,range) ) {
+ if(pc_can_reach(sd,bl->x,bl->y) && sd->canmove_tick < tick && (sd->sc_data[SC_ANKLE].timer == -1 || sd->sc_data[SC_SPIDERWEB].timer == -1))
+ // TMW client doesn't support this
+ //pc_walktoxy(sd,bl->x,bl->y);
+ clif_movetoattack(sd, bl);
+ sd->attackabletime = tick + (sd->aspd<<1);
+ }
+ else {
+ if(battle_config.pc_attack_direction_change)
+ sd->dir=sd->head_dir=map_calc_dir(&sd->bl, bl->x,bl->y ); // 向き設定
+
+ if(sd->walktimer != -1)
+ pc_stop_walking(sd,1);
+
+ if(sd->sc_data[SC_COMBO].timer == -1) {
+ map_freeblock_lock();
+ pc_stop_walking(sd,0);
+ sd->attacktarget_lv = battle_weapon_attack(&sd->bl,bl,tick,0);
+ if(!(battle_config.pc_cloak_check_type&2) && sd->sc_data[SC_CLOAKING].timer != -1)
+ skill_status_change_end(&sd->bl,SC_CLOAKING,-1);
+ if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support)
+ pet_target_check(sd,bl,0);
+ map_freeblock_unlock();
+ if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト
+ sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
+ else
+ sd->attackabletime = tick + (sd->aspd<<1);
+ }
+ else if(sd->attackabletime <= tick) {
+ if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト
+ sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100);
+ else
+ sd->attackabletime = tick + (sd->aspd<<1);
+ }
+ if(sd->attackabletime <= tick) sd->attackabletime = tick + (battle_config.max_aspd<<1);
+ }
+ }
if(sd->state.attack_continue) {
sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0);
@@ -4658,7 +4743,8 @@ int pc_skillup(struct map_session_data *sd,int skill_num)
if( sd->status.skill_point>0 &&
sd->status.skill[skill_num].id!=0 &&
- sd->status.skill[skill_num].lv < skill_get_max(skill_num) )
+ sd->status.skill[skill_num].lv < skill_get_max(skill_num)
+ && (skill_num < TMW_MAGIC || skill_num > TMW_MAGIC_END)) // [Fate] Hack: Prevent exploit for raising magic levels
{
sd->status.skill[skill_num].lv++;
sd->status.skill_point--;
@@ -4967,6 +5053,9 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage)
skill_status_change_clear(&sd->bl,0); // ステータス異常を解除する
clif_updatestatus(sd,SP_HP);
pc_calcstatus(sd,0);
+ // [Fate] Reset magic
+ sd->cast_tick = gettick();
+ magic_stop_completely(sd);
for(i=0;i<5;i++)
if(sd->dev.val1[i]){
@@ -5365,8 +5454,72 @@ int pc_heal(struct map_session_data *sd,int hp,int sp)
* HP/SP回復
*------------------------------------------
*/
+static int pc_itemheal_effect(struct map_session_data *sd,int hp,int sp);
+
+static int // Compute how quickly we regenerate (less is faster) for that amount
+pc_heal_quick_speed(int amount)
+{
+ if (amount >= 200) {
+ if (amount >= 500)
+ return 0;
+ if (amount >= 350)
+ return 1;
+ return 2;
+ } else { // < 200
+ if (amount >= 100)
+ return 3;
+ if (amount >= 50)
+ return 4;
+ return 5;
+ }
+}
+
+static void
+pc_heal_quick_accumulate(int new_amount, struct quick_regeneration *quick_regen, int max)
+{
+ int current_amount = quick_regen->amount;
+ int current_speed = quick_regen->speed;
+ int new_speed = pc_heal_quick_speed(new_amount);
+
+ int average_speed = ((new_speed * new_amount) + (current_speed * current_amount)) / (current_amount + new_amount); // new_amount > 0, current_amount >= 0
+
+ quick_regen->speed = average_speed;
+ quick_regen->amount = MIN(current_amount + new_amount, max);
+
+ quick_regen->tickdelay = MIN(quick_regen->speed, quick_regen->tickdelay);
+}
+
int pc_itemheal(struct map_session_data *sd,int hp,int sp)
{
+ /* defer healing */
+ if (hp > 0) {
+ pc_heal_quick_accumulate(hp,
+ &sd->quick_regeneration_hp,
+ sd->status.max_hp - sd->status.hp);
+
+ hp = 0;
+ }
+ if (sp > 0) {
+ pc_heal_quick_accumulate(sp,
+ &sd->quick_regeneration_sp,
+ sd->status.max_sp - sd->status.sp);
+
+ sp = 0;
+ }
+
+ /* Hurt right away, if necessary */
+ if (hp < 0 || sp < 0)
+ pc_itemheal_effect(sd, hp, sp);
+
+ return 0;
+}
+
+
+/* pc_itemheal_effect is invoked once every 0.5s whenever the pc
+ * has health recovery queued up (cf. pc_natural_heal_sub).
+ */
+static int pc_itemheal_effect(struct map_session_data *sd,int hp,int sp)
+{
int bonus;
// if(battle_config.battle_log)
// printf("heal %d %d\n",hp,sp);
@@ -5578,7 +5731,7 @@ int pc_equiplookall(struct map_session_data *sd)
nullpo_retr(0, sd);
#if PACKETVER < 4
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ pc_set_weapon_look(sd);
clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
#else
clif_changelook(&sd->bl,LOOK_WEAPON,0);
@@ -6191,8 +6344,11 @@ int pc_equipitem(struct map_session_data *sd,int n,int pos)
clif_arrowequip(sd,n);
clif_arrow_fail(sd,3); // 3=矢が装備できました
}
- else
- clif_equipitemack(sd,n,pos,1);
+ else {
+ /* Don't update re-equipping if we're using a spell */
+ if (!(pos == 4 && sd->attack_spell_override))
+ clif_equipitemack(sd,n,pos,1);
+ }
for(i=0;i<11;i++) {
if(pos & equip_pos[i])
@@ -6206,7 +6362,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int pos)
else
sd->weapontype1 = 0;
pc_calcweapontype(sd);
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ pc_set_weapon_look(sd);
}
if(sd->status.inventory[n].equip & 0x0020) {
if(sd->inventory_data[n]) {
@@ -6302,7 +6458,7 @@ int pc_unequipitem(struct map_session_data *sd,int n,int type)
sd->weapontype1 = 0;
sd->status.weapon = sd->weapontype2;
pc_calcweapontype(sd);
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ pc_set_weapon_look(sd);
}
if(sd->status.inventory[n].equip & 0x0020) {
sd->status.shield = sd->weapontype2 = 0;
@@ -6821,6 +6977,7 @@ static int pc_natural_heal_sp(struct map_session_data *sd)
static int pc_spirit_heal_hp(struct map_session_data *sd,int level)
{
int bonus_hp,interval = battle_config.natural_heal_skill_interval;
+ struct status_change *sc_data = battle_get_sc_data(&sd->bl);
nullpo_retr(0, sd);
@@ -6831,7 +6988,8 @@ static int pc_spirit_heal_hp(struct map_session_data *sd,int level)
sd->inchealspirithptick += natural_heal_diff_tick;
- if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
+ if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate
+ && sc_data[SC_FLYING_BACKPACK].timer == -1)
interval += interval;
if(sd->inchealspirithptick >= interval) {
@@ -6872,7 +7030,7 @@ static int pc_spirit_heal_sp(struct map_session_data *sd,int level)
sd->inchealspiritsptick += natural_heal_diff_tick;
if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate)
- interval += interval;
+ interval += interval;
if(sd->inchealspiritsptick >= interval) {
bonus_sp = sd->nsshealsp;
@@ -6903,17 +7061,49 @@ static int pc_spirit_heal_sp(struct map_session_data *sd,int level)
* HP/SP 自然回復 各クライアント
*------------------------------------------
*/
+static int pc_itemheal_effect(struct map_session_data *sd, int hp, int sp);
+
+static int
+pc_quickregenerate_effect(struct quick_regeneration *quick_regen, int heal_speed)
+{
+ if (!(quick_regen->tickdelay--)) {
+ int bonus = MIN(heal_speed * battle_config.itemheal_regeneration_factor,
+ quick_regen->amount);
+
+ quick_regen->amount -= bonus;
+
+ quick_regen->tickdelay = quick_regen->speed;
+
+ return bonus;
+ }
+
+ return 0;
+}
static int pc_natural_heal_sub(struct map_session_data *sd,va_list ap) {
int skill;
nullpo_retr(0, sd);
+ if (sd->sc_data[SC_HALT_REGENERATE].timer != -1)
+ return 0;
+
+ if (sd->quick_regeneration_hp.amount || sd->quick_regeneration_sp.amount) {
+ int hp_bonus = pc_quickregenerate_effect(&sd->quick_regeneration_hp, sd->nhealhp);
+ int sp_bonus = pc_quickregenerate_effect(&sd->quick_regeneration_sp, sd->nhealsp);
+
+ pc_itemheal_effect(sd,
+ hp_bonus,
+ sp_bonus);
+ }
+
// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status)
- if ((battle_config.natural_heal_weight_rate > 100 || sd->weight*100/sd->max_weight < battle_config.natural_heal_weight_rate) &&
- !pc_isdead(sd) &&
- !pc_ishiding(sd) &&
- sd->sc_data[SC_POISON].timer == -1
+ if ((sd->sc_data[SC_FLYING_BACKPACK].timer != -1
+ || battle_config.natural_heal_weight_rate > 100
+ || sd->weight*100/sd->max_weight < battle_config.natural_heal_weight_rate) &&
+ !pc_isdead(sd) &&
+ !pc_ishiding(sd) &&
+ sd->sc_data[SC_POISON].timer == -1
) {
pc_natural_heal_hp(sd);
if( sd->sc_data && sd->sc_data[SC_EXTREMITYFIST].timer == -1 && //阿修羅状態ではSPが回復しない
@@ -7514,3 +7704,9 @@ int do_init_pc(void) {
return 0;
}
+
+void
+pc_cleanup(struct map_session_data *sd)
+{
+ magic_stop_completely(sd);
+}
diff --git a/src/map/pc.h b/src/map/pc.h
index 1919007..f424d57 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -23,6 +23,9 @@
#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight)
#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9)
+void pc_touch_all_relevant_npcs(struct map_session_data *sd); /* Checks all npcs/warps at the same location to see whether they
+ ** should do something with the specified player. */
+
int pc_isGM(struct map_session_data *sd);
int pc_iskiller(struct map_session_data *src, struct map_session_data *target); // [MouseJstr]
int pc_getrefinebonus(int lv,int type);
@@ -64,6 +67,8 @@ int pc_additem(struct map_session_data*,struct item*,int);
int pc_getzeny(struct map_session_data*,int);
int pc_delitem(struct map_session_data*,int,int,int);
int pc_checkitem(struct map_session_data*);
+int pc_count_all_items(struct map_session_data* player, int item_id);
+int pc_remove_items(struct map_session_data* player, int item_id, int count);
int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount);
int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type);
@@ -156,7 +161,7 @@ int pc_divorce(struct map_session_data *sd);
struct map_session_data *pc_get_partner(struct map_session_data *sd);
int pc_set_gm_level(int account_id, int level);
void pc_setstand(struct map_session_data *sd);
-
+void pc_cleanup(struct map_session_data *sd); // [Fate] Clean up after a logged-out PC
struct pc_base_job{
int job; //職業、ただし転生職や養子職の場合は元の職業を返す(廃プリ→プリ)
diff --git a/src/map/pet.c b/src/map/pet.c
index 6026b1e..22d2af7 100644
--- a/src/map/pet.c
+++ b/src/map/pet.c
@@ -629,7 +629,7 @@ int pet_return_egg(struct map_session_data *sd)
*((long *)(&tmp_item.card[1])) = sd->pet.pet_id;
tmp_item.card[3] = sd->pet.rename_flag;
if((flag = pc_additem(sd,&tmp_item,1))) {
- clif_additem(sd,0,0,flag);
+ clif_additem(sd,0,0,flag);
map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
}
if(battle_config.pet_status_support && sd->pet.intimate > 0) {
diff --git a/src/map/script.c b/src/map/script.c
index a9a171b..251ce01 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -249,6 +249,8 @@ int buildin_marriage(struct script_state *st);
int buildin_wedding_effect(struct script_state *st);
int buildin_divorce(struct script_state *st);
int buildin_getitemname(struct script_state *st);
+int buildin_getspellinvocation(struct script_state *st); // [Fate]
+int buildin_getanchorinvocation(struct script_state *st); // [Fate]
int buildin_makepet(struct script_state *st);
int buildin_getexp(struct script_state *st);
int buildin_getinventorylist(struct script_state *st);
@@ -283,6 +285,7 @@ int buildin_npctalk(struct script_state *st); // [Valaris]
int buildin_hasitems(struct script_state *st); // [Valaris]
int buildin_getlook(struct script_state *st); //Lorky [Lupus]
int buildin_getsavepoint(struct script_state *st); //Lorky [Lupus]
+int buildin_getpartnerid(struct script_state *st); // [Fate]
void push_val(struct script_stack *stack,int type,int val);
@@ -455,6 +458,9 @@ struct {
{buildin_wedding_effect,"wedding",""},
{buildin_divorce,"divorce",""},
{buildin_getitemname,"getitemname","i"},
+ {buildin_getspellinvocation,"getspellinvocation","s"},
+ {buildin_getanchorinvocation,"getanchorinvocation","s"},
+ {buildin_getpartnerid,"getpartnerid2","i"},
{buildin_makepet,"makepet","i"},
{buildin_getexp,"getexp","ii"},
{buildin_getinventorylist,"getinventorylist",""},
@@ -2301,7 +2307,7 @@ int buildin_getitem(struct script_state *st)
if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り
return 0;
if((flag = pc_additem(sd,&item_tmp,amount))) {
- clif_additem(sd,0,0,flag);
+ clif_additem(sd,0,0,flag);
map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
}
}
@@ -5257,6 +5263,8 @@ int buildin_marriage(struct script_state *st)
struct map_session_data *sd=script_rid2sd(st);
struct map_session_data *p_sd=map_nick2sd(partner);
+ fprintf(stderr, "0=%p (%d,%d), 1=%p (%d,%d)\n", sd, sd->bl.id, sd->status.partner_id,
+ p_sd, p_sd->bl.id, p_sd->status.partner_id);
if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
push_val(st->stack,C_INT,0);
return 0;
@@ -5391,6 +5399,46 @@ int buildin_getitemname(struct script_state *st)
return 0;
}
+
+int buildin_getspellinvocation(struct script_state *st)
+{
+ char *name;
+ char *invocation;
+
+ name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ invocation = magic_find_invocation(name);
+ if (!invocation)
+ invocation = "...";
+
+ push_str(st->stack, C_STR, strdup(invocation));
+ return 0;
+}
+
+int buildin_getanchorinvocation(struct script_state *st)
+{
+ char *name;
+ char *invocation;
+
+ name = conv_str(st,& (st->stack->stack_data[st->start+2]));
+
+ invocation = magic_find_anchor_invocation(name);
+ if (!invocation)
+ invocation = "...";
+
+ push_str(st->stack, C_STR, strdup(invocation));
+ return 0;
+}
+
+int buildin_getpartnerid(struct script_state *st)
+{
+ struct map_session_data *sd=script_rid2sd(st);
+
+ push_val(st->stack, C_INT, sd->status.partner_id);
+ return 0;
+}
+
+
/*==========================================
* petskillbonus [Valaris]
*------------------------------------------
@@ -6414,10 +6462,16 @@ int run_script_main(unsigned char *script,int pos,int rid,int oid,struct script_
*/
int run_script(unsigned char *script,int pos,int rid,int oid)
{
+ run_script_l(script, pos, rid, oid, 0, NULL);
+}
+
+int run_script_l(unsigned char *script,int pos,int rid,int oid, int args_nr, argrec_t *args)
+{
struct script_stack stack;
struct script_state st;
struct map_session_data *sd=map_id2sd(rid);
unsigned char *rootscript=script;
+ int i;
if(script==NULL || pos<0)
return -1;
@@ -6441,6 +6495,12 @@ int run_script(unsigned char *script,int pos,int rid,int oid)
st.pos=pos;
st.rid=rid;
st.oid=oid;
+ for (i = 0; i < args_nr; i++) {
+ if (args[i].name[strlen(args[i].name) - 1] == '$')
+ pc_setregstr(sd, add_str(args[i].name), args[i].v.s);
+ else
+ pc_setreg(sd, add_str(args[i].name), args[i].v.i);
+ }
run_script_main(script,pos,rid,oid,&st,rootscript);
free(stack.stack_data);
diff --git a/src/map/script.h b/src/map/script.h
index b50c466..3391643 100644
--- a/src/map/script.h
+++ b/src/map/script.h
@@ -24,6 +24,14 @@ struct script_state {
};
unsigned char * parse_script(unsigned char *,int);
+typedef struct {
+ char *name;
+ union {
+ int i;
+ char * s;
+ } v;
+} argrec_t;
+int run_script_l(unsigned char *,int,int,int,int,argrec_t *args);
int run_script(unsigned char *,int,int,int);
struct dbt* script_get_label_db();
diff --git a/src/map/skill.c b/src/map/skill.c
index 1e92b3f..cff56ba 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -9,6 +9,7 @@
#include "timer.h"
#include "nullpo.h"
#include "malloc.h"
+#include "magic.h"
#include "skill.h"
#include "map.h"
@@ -3519,7 +3520,7 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int
}
eflag = pc_additem(sd,&item_tmp,1);
if(eflag) {
- clif_additem(sd,0,0,eflag);
+ clif_additem(sd,0,0,eflag);
map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
}
}
@@ -7708,6 +7709,8 @@ int skill_status_change_end(struct block_list* bl, int type, int tid)
case SC_MATKPOT: /* magic attack potion [Valaris] */
case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか)
case SC_MELTDOWN: /* メルトダウン */
+ case SC_PHYS_SHIELD:
+ case SC_HASTE:
calc_flag = 1;
break;
case SC_BERSERK: /* バーサーク */
@@ -7913,6 +7916,11 @@ int skill_status_change_timer(int tid, unsigned int tick, int id, int data)
printf("skill_status_change_timer %d != %d\n",tid,sc_data[type].timer);
}
+ if (sc_data[type].spell_invocation) { // Must report termination
+ spell_effect_report_termination(sc_data[type].spell_invocation, bl->id, type, 0);
+ sc_data[type].spell_invocation = 0;
+ }
+
switch(type){ /* 特殊な処理になる場合 */
case SC_MAXIMIZEPOWER: /* マキシマイズパワー */
case SC_CLOAKING: /* クローキング */
@@ -8229,6 +8237,11 @@ int skill_status_change_timer(int tid, unsigned int tick, int id, int data)
return 0;
}
break;
+
+ case SC_FLYING_BACKPACK:
+ clif_updatestatus(sd, SP_WEIGHT);
+ break;
+
}
return skill_status_change_end( bl,type,tid );
@@ -8266,6 +8279,12 @@ int skill_encchant_eremental_end(struct block_list *bl,int type)
*/
int skill_status_change_start(struct block_list *bl, int type, int val1, int val2, int val3, int val4, int tick, int flag)
{
+ skill_status_effect(bl, type, val1, val2, val3, val4, tick, flag, 0);
+}
+
+
+int skill_status_effect(struct block_list *bl, int type, int val1, int val2, int val3, int val4, int tick, int flag, int spell_invocation)
+{
struct map_session_data *sd = NULL;
struct status_change* sc_data;
short *sc_count, *option, *opt1, *opt2, *opt3;
@@ -8663,7 +8682,7 @@ int skill_status_change_start(struct block_list *bl, int type, int val1, int val
case SC_SPEEDPOTION2:
calc_flag = 1;
tick = 1000 * tick;
- val2 = 5*(2+type-SC_SPEEDPOTION0);
+// val2 = 5*(2+type-SC_SPEEDPOTION0);
break;
/* atk & matk potions [Valaris] */
@@ -8921,8 +8940,16 @@ int skill_status_change_start(struct block_list *bl, int type, int val1, int val
val4 = (int)sg;
}
break;
+ case SC_HASTE:
+ calc_flag = 1;
case SC_SPLASHER: /* ベナムスプラッシャー */
+ case SC_PHYS_SHIELD:
+ case SC_MBARRIER:
+ case SC_HALT_REGENERATE:
break;
+ case SC_FLYING_BACKPACK:
+ updateflag = SP_WEIGHT;
+ break;
default:
if(battle_config.error_log)
printf("UnknownStatusChange [%d]\n", type);
@@ -9000,7 +9027,13 @@ int skill_status_change_start(struct block_list *bl, int type, int val1, int val
sc_data[type].val2 = val2;
sc_data[type].val3 = val3;
sc_data[type].val4 = val4;
+ if (sc_data[type].spell_invocation) // Supplant by newer spell
+ spell_effect_report_termination(sc_data[type].spell_invocation, bl->id, type, 1);
+
+ sc_data[type].spell_invocation = spell_invocation;
+
/* タイマー設定 */
+
sc_data[type].timer = add_timer(
gettick() + tick, skill_status_change_timer, bl->id, type);
diff --git a/src/map/skill.h b/src/map/skill.h
index 6cb3d88..b3e3f59 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -3,6 +3,7 @@
#define _SKILL_H_
#include "map.h"
+#include "magic.h"
#define MAX_SKILL_DB 450
#define MAX_SKILL_PRODUCE_DB 150
@@ -145,6 +146,7 @@ int skill_check_cloaking(struct block_list *bl);
int skill_is_danceskill(int id);
// ステータス異常
+int skill_status_effect(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag, int spell_invocation);
int skill_status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag);
int skill_status_change_timer(int tid, unsigned int tick, int id, int data);
int skill_encchant_eremental_end(struct block_list *bl, int type);
@@ -330,6 +332,13 @@ enum { // struct map_session_data の status_changeの番号テーブル
SC_MINDBREAKER =191,
SC_SPELLBREAKER =192,
+// Added for Fate's spells
+ SC_HALT_REGENERATE = 195, // Suspend regeneration
+ SC_FLYING_BACKPACK = 196, // Flying backpack
+ SC_MBARRIER = 197, // Magical barrier, magic resistance (val1 : power (%))
+ SC_HASTE = 198, // `Haste' spell (val1 : power)
+ SC_PHYS_SHIELD = 199, // `Protect' spell, reduce damage (val1: power)
+
// -- testing various SC effects
// SC_AURABLADE =81,
// SC_CONCENTRATION =83,
@@ -705,13 +714,22 @@ enum {
DC_FORTUNEKISS,
DC_SERVICEFORYOU,
+ NPC_SELFDESTRUCTION2 = 331,
+
WE_MALE = 334,
WE_FEMALE,
WE_CALLPARTNER,
- NPC_SELFDESTRUCTION2 = 331,
NPC_DARKCROSS = 338,
+ TMW_MAGIC = 340,
+ TMW_MAGIC_LIFE,
+ TMW_MAGIC_WAR,
+ TMW_MAGIC_TRANSMUTE,
+ TMW_MAGIC_NATURE,
+ TMW_MAGIC_ETHER,
+ TMW_MAGIC_END,
+
LK_AURABLADE = 355,
LK_PARRYING,
LK_CONCENTRATION,
diff --git a/src/map/storage.c b/src/map/storage.c
index 696a74e..ca7fa8b 100644
--- a/src/map/storage.c
+++ b/src/map/storage.c
@@ -234,7 +234,7 @@ int storage_storageget(struct map_session_data *sd,int index,int amount)
if((flag = pc_additem(sd,&stor->storage[index],amount)) == 0)
storage_delitem(sd,stor,index,amount);
else
- clif_additem(sd,0,0,flag);
+ clif_additem(sd,0,0,flag);
} // valid amount
}// valid index
}// storage open
diff --git a/src/map/trade.c b/src/map/trade.c
index 8bf2cb2..397654e 100644
--- a/src/map/trade.c
+++ b/src/map/trade.c
@@ -169,7 +169,7 @@ void trade_tradecancel(struct map_session_data *sd)
if((target_sd = map_id2sd(sd->trade_partner)) != NULL){
for(trade_i=0; trade_i<10;trade_i++) { //give items back (only virtual)
if(sd->deal_item_amount[trade_i] != 0) {
- clif_additem(sd,sd->deal_item_index[trade_i]-2,sd->deal_item_amount[trade_i],0);
+ clif_additem(sd,sd->deal_item_index[trade_i]-2,sd->deal_item_amount[trade_i],0);
sd->deal_item_index[trade_i] =0;
sd->deal_item_amount[trade_i]=0;
}