summaryrefslogtreecommitdiff
path: root/conf/magic.conf.tmw.template
diff options
context:
space:
mode:
Diffstat (limited to 'conf/magic.conf.tmw.template')
-rw-r--r--conf/magic.conf.tmw.template1617
1 files changed, 1617 insertions, 0 deletions
diff --git a/conf/magic.conf.tmw.template b/conf/magic.conf.tmw.template
new file mode 100644
index 00000000..931fb678
--- /dev/null
+++ b/conf/magic.conf.tmw.template
@@ -0,0 +1,1617 @@
+# Special-purpose globals
+obscure_chance = 95
+min_casttime = 200
+
+# Schools of magic
+CONST MAGIC = 340
+CONST LIFE = 341
+CONST WAR = 342
+CONST TRANSMUTE = 343
+CONST NATURE = 344
+CONST ASTRAL = 345
+
+# Elements
+CONST ELT_NEUTRAL = 0
+CONST ELT_WATER = 1
+CONST ELT_EARTH = 2
+CONST ELT_FIRE = 3
+CONST ELT_WIND = 4
+CONST ELT_POISON = 5
+CONST ELT_SHADOW = 6
+CONST ELT_HOLY = 7
+CONST ELT_GHOST = 8
+CONST ELT_UNDEAD = 9
+
+# Status effects
+CONST SC_POISON = 132
+CONST SC_SHEARED = 194 # This is the same as SC_HIDE, since mobs can't hide and shearing is only used for mobs. Feel free to fix!
+CONST SC_HIDE = 194
+CONST SC_HALT_REGENERATE = 195
+CONST SC_FLYING_BACKPACK = 196
+CONST SC_MBARRIER = 197
+CONST SC_HASTE = 198
+CONST SC_PHYS_SHIELD = 199
+
+CONST SO_GMINVISIBLE = 4096
+
+# Special effects
+CONST SFX_DEFAULT = 10
+CONST SFX_SUMMON_START = 21
+CONST SFX_SUMMON_FIRE = 22
+CONST SFX_TELEPORT = 24
+CONST SFX_RAIN = 25
+CONST SFX_HIT = 25
+CONST SFX_ARROW_HAIL = 27
+CONST SFX_BARRIER = 10
+CONST SFX_UNBARRIER = 10
+CONST SFX_HEAL = 3
+
+CONST MAX_RAIN_SPELL_RADIUS = 15
+
+CONST MAX_MAGIC_LEVEL = 2 # Increase up to 5 as each new magic level is completed.
+
+CONST MAGIC_FLAGS = "MAGIC_FLAGS"
+CONST MFLAG_MADE_CONC_POTION = 16384
+CONST MFLAG_MADE_CONC_POTION_SHIFT = 14
+
+CONST SCRIPT_XP = "MAGIC_EXPERIENCE"
+CONST SCRIPT_XP_MASK = 0xffff
+CONST SCRIPT_XP_SHIFT = 0
+CONST SCRIPT_LASTSPELL_MASK = 0xff
+CONST SCRIPT_LASTSPELL_SHIFT = 16
+CONST SCRIPT_HEALSPELL_MASK = 0xff
+CONST SCRIPT_HEALSPELL_SHIFT = 24
+CONST DEBUG = 0
+
+CONST ATTACK_ICON_GENERIC = 2000
+CONST ATTACK_ICON_SHEARING = 2001
+
+# Default sfx on caster
+PROCEDURE default_effect() =
+ sfx(caster, school - MAGIC + 2, 0);
+
+PROCEDURE sfx_generic(target) =
+ sfx(target, SFX_DEFAULT, 0);
+
+PROCEDURE set_var(name, mask, shift, value) =
+ set_script_variable(caster, name, script_int(caster, name) & (neg (mask << shift)) | ((value & mask) << shift));
+
+PROCEDURE gain_heal_xp(value, # How many HP we healed
+ gain, # how many life magic experience points we can potentially gain
+ heal_xp_value_divisor, # 1 for instaheal, 2 for slow heal
+ base_exp_factor) = # factor for how many base experience points (max) the player should be allowed to gain
+ last_heal_xp = (script_int(caster, SCRIPT_XP) >> SCRIPT_HEALSPELL_SHIFT) & SCRIPT_HEALSPELL_MASK;
+ IF (target <> caster
+ && (value / heal_xp_value_divisor) > (10 + last_heal_xp + random(last_heal_xp + 1) + random(last_heal_xp + 1)))
+ THEN (
+ heal_xp = last_heal_xp + gain;
+ IF (heal_xp > SCRIPT_HEALSPELL_MASK)
+ THEN heal_xp = SCRIPT_HEALSPELL_MASK;
+ CALL set_var(SCRIPT_XP, SCRIPT_HEALSPELL_MASK, SCRIPT_HEALSPELL_SHIFT, heal_xp);
+ )
+ IF target <> caster
+ THEN gain_experience(caster, base_exp_factor * extract_healer_experience(target, value), 0, 1);
+
+PROCEDURE gain_xp(gain) =
+ IF (level + 3 > skill(caster, MAGIC)) # Level 4 and 5 magic users don't gain anything from spell levels 0 resp. 0+1
+ THEN (
+ index = spell_index(self_spell);
+ last_index = (script_int(caster, SCRIPT_XP) >> SCRIPT_LASTSPELL_SHIFT) & SCRIPT_LASTSPELL_MASK;
+ last_xp = (script_int(caster, SCRIPT_XP) >> SCRIPT_XP_SHIFT) & SCRIPT_XP_MASK;
+ IF (index != last_index)
+ THEN ( # Some variation observed
+ xp = last_xp + gain;
+ IF (xp > SCRIPT_XP_MASK)
+ THEN xp = SCRIPT_XP_MASK;
+ CALL set_var(SCRIPT_XP, SCRIPT_XP_MASK, SCRIPT_XP_SHIFT, xp);
+ CALL set_var(SCRIPT_XP, SCRIPT_LASTSPELL_MASK, SCRIPT_LASTSPELL_SHIFT, index);
+ IF DEBUG THEN message(caster, "Spell xp = " + xp);
+ ) ELSE IF DEBUG THEN message(caster, "Re-cast same spell, xp remain at " + last_xp);
+ )
+
+PROCEDURE create_item(good_item, count, bad_item, difficulty) =
+ success = 1;
+ score = experience + random(min(spellpower, (experience / 3) + 1));
+ IF (score >= difficulty)
+ THEN create_item(caster, good_item, count);
+ ELSE (
+ success = 0;
+ score = score + random(luk(caster)) + random(luk(caster));
+ IF (score < difficulty / 3)
+ THEN (
+ message(caster, "Your spell backfires!");
+ IF (random(110) < (luk(caster)))
+ THEN itemheal(caster, 0 - ((level + 1) * (level + 2) * (3 + random(28))), 0);
+ ELSE itemheal(caster, 0 - (level + 1), 0);
+ ) ELSE IF (score < (difficulty * 2) / 3)
+ THEN (
+ IF random(5) = 0
+ THEN (message(caster, "Your spell solidifies into the shape of a mysterious object!");
+ create_item(caster, "Iten", 1);)
+ ELSE message(caster, "Your spell escapes!");
+ ) ELSE (
+ message(caster, "Your spell takes on a mind of its own!");
+ IF random(3) = 0
+ THEN create_item(caster, bad_item, 1);
+ )
+ )
+
+# Increase spellpower by school and general magic skill
+PROCEDURE adjust_spellpower(school) =
+ experience = (script_int(caster, SCRIPT_XP) >> SCRIPT_XP_SHIFT) & SCRIPT_XP_MASK;
+ spellpower = spellpower + (skill(caster, MAGIC) + skill(caster, school)) * 10;
+ # Below, we adjust by special items
+ IF (not(failed(target)) && (school = LIFE || school = NATURE))
+ THEN IF (target)
+ THEN IF (pc(target) = partner(caster))
+ THEN (spellpower = spellpower + 200;
+ IF is_equipped(caster, "WeddingRing")
+ THEN spellpower = spellpower + 50;
+ IF is_equipped(pc(target), "WeddingRing")
+ THEN spellpower = spellpower + 50;)
+
+PROCEDURE heal(target, max_heal) =
+ CALL default_effect();
+ IF caster <> target
+ THEN sfx(target, SFX_HEAL, 0);
+ power = spellpower + vit(caster);
+ power = min(max_heal, (max_heal * power) / 250);
+ itemheal(target, power, 0);
+
+# Goes through instaheal instead of itemheal
+PROCEDURE quickheal(target, power) =
+ CALL default_effect();
+ IF caster <> target
+ THEN sfx(target, SFX_HEAL, 0);
+ instaheal(target, power, 0);
+
+# Can attack the target? Imports attack_range from dynamic environment
+PROCEDURE attack_check(target) =
+ IF (not (line_of_sight(location(caster), location(target))))
+ THEN ABORT;
+ IF (not (rdistance(location(caster), location(target)) <= attack_range))
+ THEN ABORT;
+
+# Cause elemental damage. bonus_elt grants an attack bonus, malus_elt reduces the attack. `effect' is the sfx ID.
+PROCEDURE elt_damage(target, damage, dmgplus, bonus_elt, malus_elt, effect) =
+ d = damage + random(dmgplus);
+ IF (element(target) = malus_elt)
+ THEN d = d / 3;
+ IF (element(target) = bonus_elt)
+ THEN d = (d * (4 + element_level(target))) / 4;
+ #message(caster, "bonus=" + (element(target) = bonus_elt) + " malus=" + (element(target) = malus_elt) + " damage=" + damage + " + r(" + dmgplus + ") -> " + d);
+ sfx(target, effect, 0);
+ injure(caster, target, d, 0);
+
+PROCEDURE melee_damage(target, damage, dmgplus) =
+ CALL attack_check(target);
+ d = damage + random(dmgplus);
+ evade = level(target) + mdef(target);
+ IF (spellpower - random(100) < evade)
+ THEN d = 0;
+ injure(caster, target, d, 0);
+
+
+PROCEDURE install_attack_spell(charges, base_delay, range, attack_animation) =
+ CALL default_effect();
+ attack_range = range;
+ override_attack(caster, charges, ((200 - agi(caster)) * base_delay) / 200,
+ range, ATTACK_ICON_GENERIC, attack_animation, 0);
+
+PROCEDURE install_melee_spell(charges, base_delay, attack_animation) =
+ CALL install_attack_spell(charges, base_delay, 1, attack_animation);
+
+PROCEDURE summon_spell(mob_id, count, delay, lifetime, control_level) =
+ CALL default_effect();
+ sfx(location, SFX_SUMMON_START, 0);
+ WAIT delay;
+ sfx(location, SFX_SUMMON_FIRE, 0);
+ spawn(rbox(location, 2), caster, mob_id,
+ if_then_else(skill(caster, ASTRAL) >= control_level, 2, 1), # pets when level is high enough
+ count, lifetime);
+
+PROCEDURE abort_on_area_shield(pos) =
+ IF (is_exterior(pos) = 1 && (map_nr(pos) = 1 # Tulimshar
+ ||map_nr(pos) = 9 # Hurnscald
+ ||map_nr(pos) = 20)) # Nivalis
+ THEN (message(caster, "A powerful magic drains your spell just as it is beginning to take shape!");
+ ABORT;)
+
+
+#--------------------------------------------------------------------------------
+# Level 0 spells
+#--------------------------------------------------------------------------------
+
+SPELL ask-magic-exp : "#G01" =
+ LET level = 0
+ school = MAGIC
+ IN (MANA 1, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ level = skill (caster, MAGIC);
+ experience = (script_int(caster, SCRIPT_XP) >> SCRIPT_XP_SHIFT) & SCRIPT_XP_MASK;
+ IF (experience == SCRIPT_XP_MASK && level > 4)
+ THEN message (caster, "You are as proficient at magic as you can possibly be.");
+ ELSE (
+ # This duplicates the table in mana-seed.txt
+ IF level > 4
+ THEN max_experience = SCRIPT_XP_MASK;
+ ELSE IF level = 4
+ THEN max_experience = 40000;
+ ELSE IF level = 3
+ THEN max_experience = 8000;
+ ELSE IF level = 2
+ THEN max_experience = 1200;
+ ELSE max_experience = 100;
+
+ ratio = (10 * experience - random(max_experience / 30)) / max_experience; #Randomness: jitter a bit at the transitions to give more precise information if used frequently
+
+ IF ratio >= 45
+ THEN message(caster, "Magic flows naturally from you, readily and with ease. Your understanding of what you can currently control at present is flawless, far beyond your requirements to cast magic at this level." + if_then_else(level >= MAX_MAGIC_LEVEL,""," Surely the Mana Seed will more than readily offer more magic for such a proficient user."));
+ ELSE IF ratio >= 20
+ THEN message(caster, "You have perfect control of what you understand now, but there is now a distinct sensation of something more, something indescribable. If only the Mana Seed would give more magic to you...");
+ ELSE IF ratio >= 10
+ THEN message(caster, "You feel in perfect control of your magic" + if_then_else(level >= MAX_MAGIC_LEVEL,".",", and seem on the verge of something more... perhaps you should see the Mana Seed to ask for more magic?"));
+ ELSE IF ratio >= 9
+ THEN message(caster, "You feel in almost perfect control of your magic.");
+ ELSE IF ratio >= 8
+ THEN message(caster, "You feel that you have very good control of your magic.");
+ ELSE IF ratio >= 7
+ THEN message(caster, "You feel quite in control of your magic.");
+ ELSE IF ratio >= 6
+ THEN message(caster, "You feel mostly in control of your magic.");
+ ELSE IF ratio >= 5
+ THEN message(caster, "You feel somewhat in control of your magic.");
+ ELSE IF ratio >= 4
+ THEN message(caster, "You feel you still have a few difficulties in controlling your magic.");
+ ELSE IF ratio >= 3
+ THEN message(caster, "Trying to control your magic is still rather troublesome.");
+ ELSE IF ratio >= 2
+ THEN message(caster, "You feel that you have only the bare minimum of control over your magic.");
+ ELSE IF ratio >= 1
+ THEN message(caster, "You feel quite overwhelmed by your magic, but are beginning to see patterns.");
+ ELSE message (caster, "You feel completely overwhelmed by your magic.");
+ )
+
+# SPELL ask-life-magic-exp : "#G02" =
+# LET level = 0
+# school = MAGIC
+# IN (MANA 1, CASTTIME 1000,
+# REQUIRE skill(caster, MAGIC) > level)
+# => EFFECT CALL adjust_spellpower(school);
+# CALL default_effect();
+# message(caster, "You have " + ((script_int(caster, "MAGIC_EXPERIENCE") >> 24) & 0xff) + " Life Magic Experience points.");
+
+SPELL transmute-wood-to-figurine (name : STRING) : "#T00" =
+ LET level = 0
+ school = TRANSMUTE
+ IN (MANA 5, CASTTIME 4000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["RawLog"])
+ => ( REQUIRE name = "boo" =>
+ EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("MoubooFigurine", 1, "WarpedLog", 40);
+ CALL gain_xp(1);
+ | REQUIRE name = "lurk" =>
+ EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("WarpedLog", 1, "WarpedLog", 40);
+ message (caster, "You have no idea what a Skrytlurk looks like.");
+ )
+
+
+SPELL make-sulphur : "#T01" =
+ LET level = 0
+ school = TRANSMUTE
+ IN (MANA 4, CASTTIME 4000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["PileOfAsh"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("SulphurPowder", 1 + spellpower / 100 + (random(max(1, 800 - spellpower)) / 180), "PileOfAsh", 50);
+ CALL gain_xp(1);
+
+SPELL lesser-heal (target : STRING) : "#L00" =
+ LET level = 0
+ school = LIFE
+ IN (MANA 6, CASTTIME 500,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE if_then_else(failed(pc(target)), 1,
+ rdistance(location(caster), location(pc(target))) < 2 + (spellpower / 100)),
+ (COMPONENTS ["Lifestone"]))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ IF failed(pc(target))
+ THEN (IF ((target = "mouboo" || target = "Mouboo")
+ && rdistance(location(caster), location(npc("Mouboo"))) < 2 + (spellpower / 100))
+ THEN { mes "Your spell seems to have no effect on the mouboo."; close; }
+ ELSE target = caster;)
+ ELSE (target = pc(target);
+ IF is_dead(target)
+ THEN ABORT;)
+ CALL gain_heal_xp(min(200, max_hp(target) - hp(target)), 1, 2, 2); # report half values for non-instaheal
+ CALL heal(target, 200);
+ CALL gain_xp(1);
+
+
+SPELL flare-dart : "#W00" =
+ LET level = 0
+ school = WAR
+ IN (MANA 10, CASTTIME 500,
+ REQUIRE skill(caster, MAGIC) > level,
+ (REQUIRE skill(caster, school) > 2 OR COMPONENTS ["SulphurPowder"]))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ damage = 5 * sqrt(spellpower);
+ damage_bonus = 5 + level(caster) / 3;
+ CALL install_attack_spell(3 + spellpower / 50,
+ 1200,
+ 4, 31);
+ CALL gain_xp(1);
+ ATTRIGGER CALL attack_check(target);
+ CALL elt_damage (target, damage, damage_bonus, ELT_WATER, ELT_FIRE, 15);
+
+
+SPELL magic-blade : "#W01" =
+ LET level = 0
+ school = WAR
+ IN (MANA 9, CASTTIME 500,
+ REQUIRE skill(caster, MAGIC) > level)
+ => ( COMPONENTS ["SharpKnife"] =>
+ EFFECT CALL adjust_spellpower(WAR);
+ CALL default_effect();
+ CALL install_melee_spell(10 + spellpower / 15, 1200, 30);
+ CALL gain_xp(1);
+ ATTRIGGER CALL melee_damage(target, 60, 5 + str(caster));
+ | COMPONENTS ["Knife"] =>
+ EFFECT CALL adjust_spellpower(WAR);
+ CALL default_effect();
+ CALL install_melee_spell(10 + spellpower / 15, 1200, 30);
+ CALL gain_xp(1);
+ ATTRIGGER CALL melee_damage(target, 40, 5 + str(caster));
+ )
+
+SPELL aggravate : "#N00" =
+ LET level = 0
+ school = NATURE
+ IN (MANA 3, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ FOREACH MOB target IN rbox(location(caster), 2 + spellpower / 50) DO
+ IF line_of_sight(location(caster), location(target))
+ THEN (CALL sfx_generic(target);
+ aggravate(target, 0, caster);)
+
+SPELL grow-mauve : "#N01" =
+ LET level = 0
+ school = NATURE
+ IN (MANA 4, CASTTIME 2000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["MauveHerb", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ spawn(rbox(location, 2), caster, 1029, 1, skill(caster, school) / 2 + 1, 10000);
+
+SPELL grow-alizarin : "#N02" =
+ LET level = 0
+ school = NATURE
+ IN (MANA 4, CASTTIME 2000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["AlizarinHerb", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ spawn(rbox(location, 2), caster, 1032, 1, skill(caster, school) / 2 + 1, 10000);
+
+SPELL grow-gamboge : "#N03" =
+ LET level = 0
+ school = NATURE
+ IN (MANA 4, CASTTIME 2000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["GambogeHerb", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ spawn(rbox(location, 2), caster, 1031, 1, skill(caster, school) / 2 + 1, 10000);
+
+SPELL grow-cobalt : "#N04" =
+ LET level = 0
+ school = NATURE
+ IN (MANA 4, CASTTIME 2000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["CobaltHerb", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ spawn(rbox(location, 2), caster, 1030, 1, skill(caster, school) / 2 + 1, 10000);
+
+
+LOCAL SPELL summon-maggots : "#A00" =
+ LET level = 0
+ school = ASTRAL
+ IN (MANA 21, CASTTIME 20000,
+ REQUIRE skill(caster, MAGIC) > level,
+ COMPONENTS ["MaggotSlime", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL gain_xp(1);
+ CALL summon_spell(1002,
+ 1 + ((sqrt(spellpower) + (spellpower / 15)) / 5),
+ 5000 - (spellpower * 5),
+ 10000 + (spellpower * 50), 1);
+
+SPELL detect-magic : "#G00" =
+ LET level = 0
+ school = MAGIC
+ IN (MANA 3, CASTTIME 6000,
+ REQUIRE skill(caster, MAGIC) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ range = 1 + spellpower / 50;
+ FOREACH NPC n IN rbox(location(caster), range)
+ DO IF (contains_string(name_of(n), "#MAGIC") || contains_string(name_of(n), "#_M"))
+ THEN sfx(n, SFX_DEFAULT, 0);
+ FOREACH SPELL s IN rbox(location(caster), range)
+ DO IF (s <> self_invocation)
+ THEN sfx(s, SFX_DEFAULT, 0);
+
+
+#--------------------------------------------------------------------------------
+# Level 1 spells
+#--------------------------------------------------------------------------------
+
+SPELL make-arrows : "#T10" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 8, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["RawLog"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("Arrow", 1 + spellpower / 40 + (random(max(1, 800 - spellpower)) / 80), "WarpedLog", 500);
+ CALL gain_xp(1);
+
+SPELL make-shirt : "#T11" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 25, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS [5 * "CottonCloth"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("CottonShirt", 1, "CottonCloth", 425);
+ CALL gain_xp(2);
+
+SPELL make-tanktop : "#T12" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 25, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS [4 * "CottonCloth"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("TankTop", 1, "CottonCloth", 350);
+ CALL gain_xp(2);
+
+SPELL make-short-tanktop : "#T13" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 25, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS [3 * "CottonCloth"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("ShortTankTop", 1, "CottonCloth", 250);
+ CALL gain_xp(2);
+
+SPELL make-iron-powder : "#T14" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 8, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["IronOre"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("IronPowder", 1 + spellpower / 140 + (random(max(1, 900 - spellpower)) / 220), "IronOre", 700);
+ CALL gain_xp(3);
+
+SPELL make-concentration-potion : "#T15" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 8, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["BottleOfWater", 2 * "CobaltHerb", 2 * "PinkPetal"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item( if_then_else(random(2000 + experience) < experience, "ConcentrationPotion", "DarkConcentrationPotion"), 1,
+ if_then_else(random(2), 744, "DarkConcentrationPotion"),
+ 2000);
+ IF (success)
+ THEN CALL set_var(MAGIC_FLAGS, 1, MFLAG_MADE_CONC_POTION_SHIFT, 1);
+ CALL gain_xp(4);
+
+SPELL merge-concentration-potions : "#T16" =
+ LET level = 1
+ school = TRANSMUTE
+ IN (MANA 8, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["DarkConcentrationPotion", 744])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL create_item("ConcentrationPotion", 1, 744, 1000);
+ message (caster, "success = " + success);
+ IF (success)
+ THEN CALL set_var(MAGIC_FLAGS, 1, MFLAG_MADE_CONC_POTION_SHIFT, 1);
+ CALL gain_xp(4);
+
+
+SPELL lay-on-hands (target : STRING) : "#L10" =
+ LET level = 1
+ school = LIFE
+ IN (MANA 10, CASTTIME 500,
+ REQUIRE hp(caster) > max_hp(caster) / 20,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE if_then_else(failed(pc(target)), 1,
+ (pc(target) <> caster)
+ &&
+ (rdistance(location(caster),
+ location(pc(target))) < 2 + (spellpower / 50))
+ && not (running_status_update(pc(target), SC_HALT_REGENERATE))
+ ))
+ => EFFECT CALL adjust_spellpower(school);
+ IF (not (target)) THEN ABORT;
+ IF failed(pc(target))
+ THEN (IF ((target = "mouboo" || target = "Mouboo")
+ && (rdistance(location(caster), location(npc("Mouboo"))) < 2 + (spellpower / 100)))
+ THEN (needed = 1000;
+ {
+ set @spell, 1;
+ callfunc "QuestMoubooHeal";
+ })
+ ELSE ABORT;
+ )
+ ELSE (target = pc(target);
+ needed = max_hp(target) - hp(target);)
+
+ pay_fraction = max(80, 200 - (vit(caster) + (spellpower / 10))); # Pay at least 40%
+ payment = (needed * pay_fraction) / 200;
+ available = hp(caster) - (max_hp(caster) / 20);
+
+ IF payment < available
+ THEN power = needed;
+ ELSE (payment = available;
+ power = (available * 200) / pay_fraction;
+ )
+ CALL gain_heal_xp(power, 1, 1, 3);
+ CALL quickheal(target, power);
+ status_change(caster, SC_HALT_REGENERATE, 0, 0, 0, 0, 10000);
+ CALL gain_xp(min(4, payment / 100));
+
+SPELL lightning-strike : "#W10" =
+ LET level = 1
+ school = WAR
+ IN (MANA 20, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["IronPowder"]))
+ => EFFECT CALL adjust_spellpower(school);
+ damage = spellpower;
+ damage_bonus = 1 + spellpower / 2;
+ CALL install_attack_spell(1 + spellpower / 90,
+ 3000,
+ 8, 31);
+ CALL gain_xp(2);
+ ATTRIGGER CALL attack_check(target);
+ in_rain = 0;
+ area = location(caster);
+ FOREACH SPELL s IN rbox(location(caster), MAX_RAIN_SPELL_RADIUS + 1) DO
+ IF name_of(s) = "rain" THEN (
+ IF is_in (location(caster), s.area)
+ THEN (in_rain = in_rain | 1;
+ area = area + s.area;);
+ IF is_in (location(target), s.area)
+ THEN in_rain = in_rain | 2;
+ );
+ IF in_rain & 1
+ THEN (# caster standing in the rain? This is going to be fun.
+ used = 0;
+ FOREACH TARGET t IN area DO
+ IF (random(200) + luk(caster) > 175)
+ THEN (used = used + 1;
+ CALL elt_damage (t, damage / 6, 1 + (damage_bonus / 3), ELT_EARTH, ELT_WIND, 17 + random(3)););
+ IF (not(used) || (random(200) + luk(caster) < 150))
+ THEN (sfx(caster, 17 + random(3), 0);
+ itemheal(caster, 0 - damage - (random(damage_bonus)), 0););
+ ) ELSE
+ CALL elt_damage (target, damage, damage_bonus, ELT_EARTH, ELT_WIND, 17 + random(3));
+
+LOCAL SPELL arrow-hail : "#W11" =
+ LET level = 1
+ school = WAR
+ IN (MANA 25, CASTTIME 5000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE is_exterior(location(caster)),
+ (COMPONENTS [20 * "Arrow"] OR COMPONENTS [20 * "IronArrow"]),
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["SulphurPowder"]))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL abort_on_area_shield(location(caster));
+
+ range = 7;
+ area = rbox(awayfrom(location(caster), dir(caster), 1 + range), range);
+
+ FOREACH SPELL s IN rbox(awayfrom(location(caster), dir(caster), 1 + range), range * 2) DO
+ IF (s <> self_invocation) && (name_of(s) = "arrow-hail")
+ THEN (message (caster, "A nearby arrow hail spell absorbs your magic!");
+ ABORT;)
+
+ damage = 125;
+ damage_bonus = spellpower / 5;
+ CALL gain_xp(2);
+ FOR i = 0 TO spellpower / 8 DO (
+ FOR j = 0 TO 2 DO (
+ location = random_location(area);
+ sfx(location, SFX_ARROW_HAIL, 0);
+ done = 0;
+ FOREACH TARGET target IN rbox(location, 0) DO (
+ injure(caster, target, damage + random(damage_bonus) + random(damage_bonus), 0);
+ done = 1;
+ BREAK;
+ )
+ IF location(caster) = location && not(done)
+ THEN (itemheal(caster, 0 - (damage + random(damage_bonus) + random(damage_bonus)), 0);
+ sfx(caster, SFX_HIT, 0);
+ )
+ );
+ WAIT 250 + random(50) + random(50);
+ );
+
+SPELL magic-knuckles : "#W12" =
+ LET level = 1
+ school = WAR
+ IN (MANA 20, CASTTIME 500,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["Beer"]))
+ =>
+ EFFECT CALL adjust_spellpower(WAR);
+ str = str(caster);
+ CALL install_melee_spell(10 + spellpower / 10, 1300, 34);
+ ATTRIGGER CALL melee_damage(target, 30, 5 + (str * 2));
+
+SPELL flying-backpack (target : PC) : "#N10" =
+ LET level = 1
+ school = NATURE
+ IN (MANA 12, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["SilkCocoon"]),
+ REQUIRE rdistance(location(target), location(caster)) < 2 + spellpower / 30)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ IF (caster <> target)
+ THEN sfx(caster, 2, 0);
+ status_change(target, SC_FLYING_BACKPACK, 0, 0, 0, 0, 5000 + (spellpower * 500));
+ message (target, "Your backpack is lifted by a mystical force; you no longer feel it pressing on your back.");
+ CALL gain_xp(1);
+ ATEND message (target, "Your backpack is no longer levitating.");
+ sfx(target, 2, 0);
+
+SPELL protect (target : PC) : "#N11" =
+ LET level = 1
+ school = NATURE
+ IN (MANA 14, CASTTIME 1500,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["HardSpike"]),
+ REQUIRE rdistance(location(target), location(caster)) < 2 + spellpower / 30)
+ => EFFECT CALL adjust_spellpower(school);
+ sfx(target, 11, 0);
+ IF (caster <> target)
+ THEN CALL default_effect();
+ status_change(target, SC_PHYS_SHIELD, 5 + max(15, spellpower / 20), 0, 0, 0, 5000 + (spellpower * 1000));
+ message (target, "You feel more protected.");
+ CALL gain_xp(2);
+ ATEND message (target, "You feel less protected.");
+ sfx(target, 111, 0);
+
+SPELL happy-curse (target : PC) : "#N12" =
+ LET level = 1
+ school = NATURE
+ IN (MANA 13, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["GingerBreadMan"]),
+ REQUIRE rdistance(location(target), location(caster)) < 1 + spellpower / 100)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ FOR i = 0 TO (spellpower / 10) DO (emote(target, 3); WAIT 500;);
+ CALL gain_xp(1);
+
+LOCAL SPELL rain : "#N13" =
+ LET level = 1
+ school = NATURE
+ IN (MANA 17, CASTTIME 3000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE is_exterior(location(caster)),
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["BottleOfWater"]))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL abort_on_area_shield(location(caster));
+ FOREACH SPELL s IN rbox(location(caster), MAX_RAIN_SPELL_RADIUS + 1) DO
+ IF (s <> self_invocation) && (name_of(s) = "rain")
+ THEN (message (caster, "A nearby raincloud absorbs your magic.");
+ ABORT;)
+
+ CALL gain_xp(1);
+ range = min(MAX_RAIN_SPELL_RADIUS, 3 + min(spellpower, 200) / 30);
+ area = rbox(location(caster), range);
+ IF (is_in(location(npc("#DruidTree0#_M")), area)
+ || is_in(location(npc("#DruidTree1#_M")), area))
+ THEN {
+ set @flag, 1;
+ callfunc "QuestTreeTrigger";
+ };
+ FOR i = 0 TO (spellpower / 3) DO (
+ FOR j = 0 TO min(spellpower / 2, 200) / 100 DO (
+ location = random_location(area);
+ sfx(location, SFX_RAIN, 0);
+ FOREACH TARGET target IN rbox(location, 1) DO
+ IF element(target) = ELT_FIRE
+ THEN injure(caster, target, 2 + random(5 + spellpower / 15), 0);
+ )
+ WAIT 400 + random(100);
+ );
+
+PROCEDURE shear-drop(target, target2, item, prob) =
+ IF (target = name || target2 = name)
+ THEN (IF (score < prob)
+ THEN drop_item_for(place, item, 1, 60000, caster, 5000);)
+
+PROCEDURE shear-drop2(target, target2, item, prob, item2, prob2) =
+ IF (target = name || target2 = name)
+ THEN (IF (score < prob)
+ THEN drop_item_for(place, item, 1, 60000, caster, 5000);
+ ELSE CALL shear-drop(target, target2, item2, prob2 + prob);)
+
+PROCEDURE shear-drop3(target, target2, item, prob, item2, prob2, item3, prob3) =
+ IF (target = name || target2 = name)
+ THEN (IF (score < prob)
+ THEN drop_item_for(place, item, 1, 60000, caster, 5000);
+ ELSE CALL shear-drop2(target, target2, item2, prob2 + prob, item3, prob3 + prob);)
+
+
+SPELL shear : "#N14" =
+ LET level = 1
+ school = NATURE
+ IN (MANA 23, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ override_attack (caster, 1, 2000, 1, ATTACK_ICON_SHEARING, 30, 0);
+ ATTRIGGER IF not(is_pc(target))
+ THEN (score = random (1000 - (random (spellpower))); # lower score -> more valuable item
+ name = name_of(target);
+ place = random_location(rbox(location(target), 1));
+ IF running_status_update(target, SC_SHEARED)
+ THEN ABORT;
+ status_change(target, SC_SHEARED, 1, 1, 1, 1, 600000); # 10 minutes
+ CALL shear-drop("Fluffy", "Fluffy", "WhiteFur", 300);
+ CALL shear-drop("EasterFluffy", "Easter Fluffy", "WhiteFur", 300);
+ CALL shear-drop("SpikyMushroom", "Spiky Mushroom", "HardSpike", 250);
+ CALL shear-drop("Mouboo", "Mouboo", "CottonCloth", 175);
+ CALL shear-drop("Cobalt", "CobaltPlant", "CobaltHerb", 700);
+ CALL shear-drop("Alizarin", "AlizarinPlant", "AlizarinHerb", 700);
+ CALL shear-drop("Gamboge", "GambogePlant", "GambogeHerb", 700);
+ CALL shear-drop("Mauve", "MauvePlant", "MauveHerb", 700);
+ CALL shear-drop("SilkWorm", "Silkworm", "SilkCocoon", 300);
+ IF ((name = "Fluffy" || name = "Mouboo") && random(2))
+ THEN { set @value, 1; callfunc "QuestSagathaHappy"; };
+ )
+
+
+
+SPELL barrier (target : PC) : "#A10" =
+ LET level = 1
+ school = ASTRAL
+ IN (MANA 16, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["SmallMushroom"]),
+ REQUIRE rdistance(location(target), location(caster)) < 2 + spellpower / 30)
+ => EFFECT CALL adjust_spellpower(school);
+ sfx(target, SFX_BARRIER, 0);
+ IF (caster <> target)
+ THEN CALL default_effect();
+ status_change(target, SC_MBARRIER, 20 + max(30, spellpower / 8), 0, 0, 0, 2000 + (spellpower * 200));
+ message (target, "You are surrounded by a magical barrier.");
+ CALL gain_xp(3);
+ ATEND message (target, "Your magical barrier dissipates.");
+ sfx(target, SFX_UNBARRIER, 0);
+
+
+LOCAL SPELL summon-spiky-mushrooms : "#A11" =
+ LET level = 1
+ school = ASTRAL
+ IN (MANA 33, CASTTIME 20000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["HardSpike", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ CALL summon_spell(1019, 1 + spellpower / 120, 5000 - (spellpower * 9), spellpower * 400, 2);
+
+LOCAL SPELL summon-fluffies : "#A12" =
+ LET level = 1
+ school = ASTRAL
+ IN (MANA 39, CASTTIME 20000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["WhiteFur", "Root"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ CALL gain_xp(1);
+ CALL summon_spell(1020, 1 + spellpower / 170 + spellpower / 430, 5000 - (spellpower * 8), spellpower * 350, 3);
+
+
+SPELL detect-players : "#G10" =
+ LET level = 1
+ school = MAGIC
+ IN (MANA 7, CASTTIME 300,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ message = "";
+ FOREACH PC target IN rbox(location(caster), spellpower / 2)
+ DO IF ((target <> caster)
+ && not (running_status_update(pc(target), SC_HIDE))
+ && not (status_option(target, SO_GMINVISIBLE)))
+ THEN (IF message <> ""
+ THEN message = message + ", ";
+ message = message + name_of (target);
+ IF spellpower > 99
+ THEN message = message + "(" + level(target) + ")";
+ )
+ IF message = ""
+ THEN message(caster, "You sense no-one else nearby.");
+ ELSE message(caster, "You sense the following: " + message);
+
+SPELL reveal : "#G11" =
+ LET level = 1
+ school = MAGIC
+ IN (MANA 18, CASTTIME 3000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level)
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ FOREACH PC target IN rbox(location(caster), 1 + spellpower / 100)
+ DO IF has_shroud(target) && level(caster) * 2 > level(target)
+ THEN (unshroud(target);
+ sfx(target, SFX_DEFAULT, 500);)
+
+
+SPELL enchant-lifestone : "#G12" =
+ LET level = 1
+ school = MAGIC
+ IN (MANA 15, CASTTIME 4000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (COMPONENTS["BugLeg"] OR COMPONENTS ["MaggotSlime"]
+ OR COMPONENTS ["MauveHerb", "AlizarinHerb", "CobaltHerb", "GambogeHerb"]))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ create_item(caster, "Lifestone", 1);
+ CALL gain_xp(1);
+
+SPELL sense-spouse : "#G13" =
+ LET level = 1
+ school = MAGIC
+ IN (MANA 7, CASTTIME 400,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE is_married(caster),
+ REQUIRE is_equipped(caster, "WeddingRing"))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ IF (failed(partner(caster)) || not(is_equipped(partner(caster), "WeddingRing")))
+ THEN (message(caster, "You cannot sense your partner.");
+ ABORT;)
+ partner = partner(caster);
+ name = name_of(partner);
+ IF (is_dead(partner) || (map_nr(location(partner)) <> map_nr(location(caster))))
+ THEN (message(caster, "You cannot sense " + name + " nearby.");
+ ABORT;)
+ IF (map_level(location(partner)) > 2 && map_level(location(caster)) < map_level(location(partner)))
+ THEN (message(caster, "You sense " + name + " somewhere below.");
+ ABORT;)
+ IF (map_level(location(caster)) > 2 && map_level(location(partner)) < map_level(location(caster)))
+ THEN (message(caster, "You sense " + name + " somewhere above.");
+ ABORT;)
+ IF (map_level(location(caster)) <> map_level(location(partner)))
+ THEN message(caster, "You sense " + name + " somewhere in the vincinity.");
+ ELSE (distance = rdistance(location(caster), location(partner));
+ dir = dir_towards(location(caster), location(partner), 1);
+ IF (distance < 3)
+ THEN message(caster, "You sense " + name + " right next to you.");
+ ELSE IF (distance < 30)
+ THEN message(caster, "You sense " + name + " close by, towards the " + dir + ".");
+ ELSE IF (distance < 200)
+ THEN message(caster, "You sense " + name + " nearby, towards the " + dir + ".");
+ ELSE message(caster, "You sense " + name + " in the " + dir + ".");
+ )
+
+SPELL hide (target : PC) : "#A13" =
+ LET level = 1
+ school = ASTRAL
+ IN (MANA 11, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ (REQUIRE skill(caster, school) > 3 OR COMPONENTS ["CottonCloth"]),
+ REQUIRE rdistance(location(target), location(caster)) < 2 + spellpower / 30)
+ => EFFECT CALL adjust_spellpower(school);
+ sfx(target, SFX_DEFAULT, 0);
+ IF (caster <> target)
+ THEN CALL default_effect();
+ status_change(target, SC_HIDE, 0, 0, 0, 0, 5000 + (spellpower * 2500));
+ CALL gain_xp(2);
+ message(target, "You are hidden!");
+ IF (caster <> target) THEN message(caster, "You hid someone!");
+ ATEND message(target, "You are no longer hidden.");
+
+#--------------------------------------------------------------------------------
+# Level 2 spells
+#--------------------------------------------------------------------------------
+
+SPELL cure-poison (target : PC) : "#L20" =
+ LET level = 2
+ school = LIFE
+ IN (MANA 15, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE (rdistance(location(caster),
+ location(pc(target))) < 1 + (spellpower / 60)),
+ COMPONENTS ["GambogeHerb"])
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ IF (running_status_update (target, SC_POISON))
+ THEN (CALL gain_heal_xp(40, 1, 2, 2);
+ stop_status_change (target, SC_POISON);
+ CALL gain_xp(2);
+ IF caster <> target
+ THEN sfx(target, SFX_HEAL, 0););
+
+
+SPELL fire-ball : "#W22" =
+ LET level = 2
+ school = WAR
+ IN (MANA 30, CASTTIME 1000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ COMPONENTS ["PileOfAsh"])
+ => EFFECT CALL adjust_spellpower(school);
+ damage = min(50 + skill(caster, school) * 40,
+ 30 + ((spellpower * 3) / 2));
+ damage_bonus = level(caster) + spellpower * 2;
+ radius = 2 + spellpower / 50;
+ CALL install_attack_spell(1 + spellpower / 60,
+ 5000,
+ 10, 31);
+ ATTRIGGER CALL attack_check(target);
+ loc = location(target);
+ #WAIT 500;
+ sfx(loc, 16, 0);
+ FOREACH TARGET target IN rbox(loc, radius)
+ DO IF line_of_sight(loc, location(target))
+ THEN (divisor = (3 + rdistance(loc, location(target)));
+ CALL elt_damage (target, (damage * 3) / divisor, (damage_bonus * 3) / divisor, ELT_WATER, ELT_FIRE, 15);
+ )
+
+SPELL summon-partner : "#A23" =
+ LET level = 2
+ school = ASTRAL
+ IN (MANA 30, CASTTIME 2000,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level,
+ REQUIRE is_married(caster),
+ REQUIRE is_equipped(caster, "WeddingRing"))
+ => EFFECT CALL adjust_spellpower(school);
+ CALL default_effect();
+ IF (failed (partner (caster)))
+ THEN message (caster, "You call out for your partner, but there is no response.");
+ ELSE (message (caster, "You call out for " + (name_of (partner (caster))) + ".");
+ message (partner(caster), name_of(caster) + " is calling for your aid!");
+ sfx(partner(caster), 2, 0);
+ WAIT (max (5000, 30000 - (spellpower * 60)));
+ IF (failed (partner (caster)))
+ THEN message (caster, "Your partner has abandoned you.");
+ ELSE IF (is_dead (partner (caster)))
+ THEN message (caster, "Something seems to have happened to " + (name_of(partner(caster))) + ".");
+ ELSE (sfx(location(partner(caster)), SFX_TELEPORT, 0);
+ dest = awayfrom(location(caster), random_dir(1), 1);
+ warp(partner(caster), dest);
+ sfx(dest, SFX_TELEPORT, 0);
+ )
+ )
+
+
+
+#--------------------------------------------------------------------------------
+# Level 4 spells
+#--------------------------------------------------------------------------------
+
+SPELL shroud : "#N40" =
+ LET level = 4
+ school = NATURE
+ IN (MANA 40, CASTTIME 400,
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level)
+ => EFFECT CALL default_effect();
+ shroud(caster, 0x04);
+
+SPELL teleport (destination : STRING) : "#A40" =
+ LET level = 4
+ school = ASTRAL
+ IN (MANA 80, CASTTIME 400,
+ # COMPONENT ["EtherEssence"],
+ # CATALYST ["TeleportCrystal"],
+ REQUIRE skill(caster, MAGIC) > level,
+ REQUIRE skill(caster, school) > level)
+ => EFFECT CALL default_effect();
+ WAIT 1000 + (200000 / (spellpower + 10));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ warp(caster, random_location(anchor(destination)));
+ sfx(caster, SFX_TELEPORT, 200);
+
+#--------------------------------------------------------------------------------
+# Debug keywords
+#--------------------------------------------------------------------------------
+
+SPELL debug : "debug" =
+ REQUIRE DEBUG
+ => EFFECT message (caster, "FLAGS: "
+ + "drank=" + ((script_int(caster, "MAGIC_FLAGS") & 1) > 0) + ", "
+ + "Kmseed=" + ((script_int(caster, "MAGIC_FLAGS") & 2) > 0) + ", "
+ + "touched-mseed=" + ((script_int(caster, "MAGIC_FLAGS") & 4) > 0) + ", "
+ + "mseed-max=" + ((script_int(caster, "MAGIC_FLAGS") & 8) > 0) + ", "
+ + "Kauldsbel=" + ((script_int(caster, "MAGIC_FLAGS") & 16) > 0) + ", "
+ + "Kwyara=" + ((script_int(caster, "MAGIC_FLAGS") & 32) > 0) + ", "
+ + "Ksagatha=" + ((script_int(caster, "MAGIC_FLAGS") & 64) > 0) + ", "
+ + "Kmpotion=" + ((script_int(caster, "MAGIC_FLAGS") & 128) > 0) + ", "
+ + "mseed-rumour=" + ((script_int(caster, "MAGIC_FLAGS") & 256) > 0) + ", "
+ + "Kcuttree=" + ((script_int(caster, "MAGIC_FLAGS") & 512) > 0) + ", "
+ + "cut=" + ((script_int(caster, "MAGIC_FLAGS") & 1024) > 0) + ", "
+ + "Kdruidtree=" + ((script_int(caster, "MAGIC_FLAGS") & 2048) > 0) + ", "
+ + "Kimp=" + ((script_int(caster, "MAGIC_FLAGS") & 4096) > 0) + ", "
+ + "Koldwiz=" + ((script_int(caster, "MAGIC_FLAGS") & 8192) > 0) + ", "
+ + "made-conc=" + ((script_int(caster, "MAGIC_FLAGS") & 16384) > 0) + ", "
+ + "elanore-omar=" + ((script_int(caster, "MAGIC_FLAGS") & 32768) > 0));
+ message (caster, "EXP: " + (script_int(caster, "MAGIC_EXPERIENCE") & 0xffff)
+ + ", lastspell=" + ((script_int(caster, "MAGIC_EXPERIENCE") >> 16) & 0xff)
+ + ", healexp=" + ((script_int(caster, "MAGIC_EXPERIENCE") >> 24) & 0xff));
+ message (caster, "STATUS: "
+ + "auldsbel:" + (script_int(caster, "QUEST_MAGIC") & 0x1f) + "," + ((script_int(caster, "QUEST_MAGIC") >> 5) & 0x7) + ", "
+ + "dt/mb:" + ((script_int(caster, "QUEST_MAGIC") >> 8) & 0xf) + ", "
+ + "s-unhappy:" + ((script_int(caster, "QUEST_MAGIC") >> 12) & 0xf) + ", "
+ + "sagatha:" + ((script_int(caster, "QUEST_MAGIC") >> 16) & 0xff) + ", "
+ + "swords:" + ((script_int(caster, "QUEST_MAGIC") >> 24) & 0xff) + ", "
+ + "imp:" + ((script_int(caster, "QUEST_MAGIC2") >> 0) & 0xf) + ", "
+ + "elanore:" + ((script_int(caster, "QUEST_MAGIC2") >> 4) & 0xf) + ", "
+ + "elanore-sub:" + ((script_int(caster, "QUEST_MAGIC2") >> 12) & 0xf) + ", "
+ + "wyara:" + ((script_int(caster, "QUEST_MAGIC2") >> 8) & 0xf));
+
+PROCEDURE debug_xmod(name, mask, shift, gain) =
+ value = ((script_int(caster, name) >> shift) & mask) + gain;
+ IF (value < 0)
+ THEN value = 0;
+ IF (value > mask)
+ THEN value = mask;
+ CALL set_var(name, mask, shift, value);
+
+PROCEDURE debug_mod(name, delta) =
+ IF (name = "mexp") THEN CALL debug_xmod("MAGIC_EXPERIENCE", 0xffff, 0, delta);
+ ELSE IF (name = "lastspell") THEN CALL debug_xmod("MAGIC_EXPERIENCE", 0xff, 16, delta);
+ ELSE IF (name = "healexp") THEN CALL debug_xmod("MAGIC_EXPERIENCE", 0xff, 24, delta);
+ ELSE IF (name = "drank") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 0, delta);
+ ELSE IF (name = "Kmseed") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 1, delta);
+ ELSE IF (name = "touched-mseed") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 2, delta);
+ ELSE IF (name = "mseed-max") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 3, delta);
+ ELSE IF (name = "Kauldsbel") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 4, delta);
+ ELSE IF (name = "Kwyara") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 5, delta);
+ ELSE IF (name = "Ksagatha") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 6, delta);
+ ELSE IF (name = "Kmpotion") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 7, delta);
+ ELSE IF (name = "mseed-rumour") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 8, delta);
+ ELSE IF (name = "Kcuttree") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 9, delta);
+ ELSE IF (name = "cut") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 10, delta);
+ ELSE IF (name = "Kdruidtree") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 11, delta);
+ ELSE IF (name = "Kimp") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 12, delta);
+ ELSE IF (name = "oldwiz") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 13, delta);
+ ELSE IF (name = "made-conc") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 14, delta);
+ ELSE IF (name = "elanore-omar") THEN CALL debug_xmod("MAGIC_FLAGS", 0x1, 15, delta);
+ ELSE IF (name = "auldsbel") THEN CALL debug_xmod("QUEST_MAGIC", 0x1f, 0, delta);
+ ELSE IF (name = "Qauldsbel") THEN CALL debug_xmod("QUEST_MAGIC", 0x7, 5, delta);
+ ELSE IF (name = "dt") THEN CALL debug_xmod("QUEST_MAGIC", 0x3, 10, delta);
+ ELSE IF (name = "mb") THEN CALL debug_xmod("QUEST_MAGIC", 0x3, 8, delta);
+ ELSE IF (name = "s-unhappy") THEN CALL debug_xmod("QUEST_MAGIC", 0xff, 12, delta);
+ ELSE IF (name = "sagatha") THEN CALL debug_xmod("QUEST_MAGIC", 0xff, 16, delta);
+ ELSE IF (name = "swords") THEN CALL debug_xmod("QUEST_MAGIC", 0xff, 24, delta);
+ ELSE IF (name = "imp") THEN CALL debug_xmod("QUEST_MAGIC2", 0xf, 0, delta);
+ ELSE IF (name = "elanore") THEN CALL debug_xmod("QUEST_MAGIC2", 0xf, 4, delta);
+ ELSE IF (name = "wyara") THEN CALL debug_xmod("QUEST_MAGIC2", 0xf, 8, delta);
+ ELSE IF (name = "elanore-sub") THEN CALL debug_xmod("QUEST_MAGIC2", 0xf, 12, delta);
+ ELSE message(caster, "Unknown");
+
+SPELL debug-up1 (name : STRING) : "debug+1" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 1);
+
+SPELL debug-down1 (name : STRING) : "debug-1" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 0 - 1);
+
+SPELL debug-up16 (name : STRING) : "debug+16" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 16);
+
+SPELL debug-down16 (name : STRING) : "debug-16" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 0 - 16);
+
+SPELL debug-up256 (name : STRING) : "debug+256" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 256);
+
+SPELL debug-down256 (name : STRING) : "debug-256" =
+ REQUIRE DEBUG
+ => EFFECT CALL debug_mod(name, 0 - 256);
+
+SPELL debug-reset : "debug-reset" =
+ REQUIRE DEBUG
+ => EFFECT set_script_variable(caster, "QUEST_MAGIC", 0);
+ set_script_variable(caster, "QUEST_MAGIC2", 0);
+ set_script_variable(caster, "MAGIC_FLAGS", 0);
+ set_script_variable(caster, "MAGIC_EXP", 0);
+
+
+#--------------------------------------------------------------------------------
+# Special-purpose quasispells
+#--------------------------------------------------------------------------------
+
+CONST MIN_MARRY_LEVEL = 32;
+
+NONMAGIC SILENT SPELL marriage (target : PC) : "marry" =
+ (REQUIRE (is_in(location(caster), @("014-1.gat", 28,39) @+ (8, 6)) || is_in(location(caster), @("021-2.gat", 20,25) @+ (4, 4)) ))=>
+ EFFECT IF target == caster # no valid target or tried to marry self?
+ THEN ABORT;
+
+ IF (level(caster) < MIN_MARRY_LEVEL)
+ THEN (message(caster, "You must be level " + MIN_MARRY_LEVEL + " or higher to marry!"); ABORT;)
+
+ IF (level(target) < MIN_MARRY_LEVEL)
+ THEN (message(caster, "Your partner must be level " + MIN_MARRY_LEVEL + " or higher to marry!"); ABORT;)
+
+ IF partner(caster) = target
+ THEN (message(caster, "You and " + name_of(target) + " are already married."); ABORT;)
+
+ IF is_married(caster)
+ THEN (message(caster, "You are already married!"); ABORT;)
+
+ IF is_married(target)
+ THEN (message(caster, name_of(target) + " is already married."); ABORT;);
+
+ IF distance(location(caster), location(target)) <> 1
+ THEN (message(caster, "You need to stand next to each other."); ABORT;);
+
+ IF (count_item(caster, "WeddingRing") == 0 || count_item(target, "WeddingRing") == 0)
+ THEN (message(caster, "You must both be wearing your wedding rings!"); ABORT;)
+
+ script_target = target;
+ {
+ announce @caster_name$ + " is asking " + strcharinfo(0) + " for marriage.", 2;
+ mes @caster_name$ + " wishes to marry you.";
+ mes "Do you accept?";
+ next;
+ menu "Yes, I do!", L_yes,
+ "No.", -;
+ close;
+
+ L_yes:
+ if marriage(@caster_name$)
+ announce @caster_name$ + " and " + strcharinfo(0) + " are now married!", 0;
+ close;
+ }
+
+ IF not (is_married(caster))
+ THEN message(caster, name_of(target) + " turned down your marriage offer.");
+
+# SPELL change-hair-colour (colour : STRING) : "trapa" =
+# (MANA 20) => EFFECT IF colour = "nworbl"
+# THEN x = 0; # light brown
+# ELSE IF colour = "der"
+# THEN x = 1; # red
+# ELSE IF colour = "neerg"
+# THEN x = 2; # green
+# ELSE IF colour = "elprup"
+# THEN x = 3; # purple
+# ELSE IF colour = "yerg"
+# THEN x = 4; # grey
+# ELSE IF colour = "wolley"
+# THEN x = 5; # yellow
+# ELSE IF colour = "eulb"
+# THEN x = 6; # blue
+# ELSE IF colour = "nwrob"
+# THEN x = 7; # brown
+# ELSE IF colour = "elpropl"
+# THEN x = 8; # light purple
+# ELSE IF colour = "elpropd"
+# THEN x = 9; # dark purple
+# ELSE x = random(10);
+# sfx(caster, 2, 0);
+# set_hair_colour(caster, x);
+
+
+# SPELL trick-or-treat : "trick-or-treat" =
+# (CASTTIME 30000, MANA 20,
+# COMPONENTS [ "BugLeg" ]) => EFFECT IF (random(2))
+# THEN (sfx(caster, 2, 0);
+# FOR i = 0 TO random(10) DO
+# drop_item_for (random_location(rbox(location(caster), 5)),
+# "Candy", 1, 10000 + random(10000), caster, 3000);
+# FOR i = 0 TO random(10) DO
+# drop_item_for (random_location(rbox(location(caster), 5)),
+# "ChocolateBar", 1, 10000 + random(10000), caster, 3000);
+# )
+# ELSE (sfx(caster, 5, 0);
+# message(caster, "No treat for you!");
+# spawn(rbox(location(caster), 3), caster, 1010, 0, 1 + random(3), 10000 + random(20000));
+# FOREACH MOB target IN rbox(location(caster), 5) DO
+# (sfx(target, 5, 0);
+# aggravate(target, 0, caster);))
+
+
+LOCAL SPELL mouboo-groan : "#g" =
+ (MANA 1,
+ REQUIRE name_of(caster) = "MOUBOOTAUR") =>
+ EFFECT FOREACH PC p IN rbox(location(caster), 200) DO
+ distance = rdistance(location(caster), location(p));
+ IF (distance < 15)
+ THEN message(p, "The moubootaur's groaning rings in your ears!");
+ ELSE IF (distance < 70)
+ THEN message(p, "You hear a loud groaning noise, not far away...");
+ ELSE message(p, "You hear an odd groaning noise in the distance...");
+
+
+LOCAL SPELL mouboo-smell : "#s" =
+ (MANA 1,
+ REQUIRE name_of(caster) = "MOUBOOTAUR") =>
+ EFFECT WAIT 30000;
+ FOREACH PC p IN rbox(location(caster), 30) DO
+ message(p, "You notice a strange smell all around you.");
+
+
+CONST E10_FLAG_USED_FREE_WARP_SHIFT = 17
+
+#NONMAGIC SILENT SPELL easter (type : STRING) : "#m" =
+# (REQUIRE name_of(caster) == "Freeyorp") =>
+# EFFECT IF (type == "EE_RF") THEN (
+# {
+# doevent("Old Woman::OnReplenish");
+# }
+# ) ELSE IF (type == "EE_SUS") THEN (
+# {
+# doevent "Old Woman::OnManualStopTimer";
+# }
+# ) ELSE IF (type == "EE_INIT") THEN (
+# {
+# doevent "Old Woman::OnManualStartTimer";
+# }
+# ) ELSE IF (type == "BLOCKER_DISABLE") THEN (
+# {
+# set $Easter_2010_Npc_State2, ($Easter_2010_Npc_State2 & ~(E10_BARRIER_MASK << E10_BARRIER_SHIFT)) | (E10_BARRIER_OPEN << E10_BARRIER_SHIFT);
+# }
+# ) ELSE IF (type == "BLOCKER_ENABLE") THEN (
+# {
+# set $Easter_2010_Npc_State2, ($Easter_2010_Npc_State2 & ~(E10_BARRIER_MASK << E10_BARRIER_SHIFT)) | (E10_BARRIER_ACTIVE << E10_BARRIER_SHIFT);
+# }
+# ) ELSE message(caster, "Unknown action '" + type + "'!");
+
+# Invader spells
+
+#NONMAGIC SILENT SPELL easter-invader-init : "#i" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT warp(caster, @("028-1.gat", 139, 46));
+# {
+# gmcommand "@blvl -255";
+# gmcommand "@blvl 199";
+# gmcommand "@setmagic all 5 Xakelbael the Dark";
+# gmcommand "@allstats";
+# savepoint "028-1.gat", 130, 46;
+# }
+# create_item(caster, "DarkTalisman", 1);
+
+#NONMAGIC SILENT SPELL easter-invader-hide : "#h" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT { if (checkoption(4096)) goto L_toggle;
+# setoption(4096);
+# end;
+# L_toggle:
+# setoption(0);
+# end; };
+# warp(caster, location(caster));
+
+#NONMAGIC SILENT SPELL easter-invader-warp-taunt : "#o" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT warp(caster, @("009-1.gat", 48, 35));
+# WAIT 80;
+# sfx(caster, 32, 0);
+# WAIT 80;
+# FOREACH PC p IN rbox(location(caster), 50)
+# DO IF (p <> caster) THEN (
+# message(p, "A powerful explosion " + if_then_else(distance(location(caster), location(p)) < 10, "right around you nearly deafens you!", if_then_else(distance(location(caster), location(p)) < 30, "nearby shocks you!", "in the distance erupts... you're not sure of the source, but it will be something big...")));
+# itemheal(p, -1 - random(hp(p)), 0-(random(sp(p) >> 2)));
+# )
+# WAIT 1000;
+# FOREACH PC p IN rbox(location(caster), 15)
+# DO (
+# message(p, "As the smoke clears, you hear a distant, sickly laughing ringing in your ears...");
+# )
+
+#NONMAGIC SILENT SPELL easter-invader-warp-taunt-explosion : "#q" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT sfx(caster, 32, 0);
+# WAIT 100;
+# sfx(caster, 31, 0);
+# WAIT 30;
+# sfx(caster, 30, 0);
+# WAIT 30;
+# sfx(caster, 30, 0);
+# WAIT 20;
+# sfx(caster, 30, 0);
+# WAIT 10;
+# sfx(caster, 30, 0);
+# WAIT 10;
+
+#NONMAGIC SILENT SPELL easter-invader-warp-taunt-end : "#p" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT FOREACH PC p IN rbox(location(caster), 50) DO IF (p <> caster)
+# THEN ( script_target = p;
+# { gmcommand "@jump"; }
+# message(p, "A powerful force sends you flying!");
+# )
+# warp(caster, @("028-1.gat", 130, 46));
+
+
+#NONMAGIC SILENT SPELL easter-invader-attack-pulse : "#a" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT IF (script_int(caster, "InvState") == 1) THEN ( set_script_variable(caster, "InvState", 0); ABORT; );
+# set_script_variable(caster, "InvState", 1);
+# FOR i = 1 TO 500
+# DO ( sfx(caster, 31, 0);
+# WAIT 80;
+# FOREACH PC p IN rbox(location(caster), 4)
+# DO IF (p <> caster) THEN ( injure(caster, p, 30 - random(random(25)) - random(5) + random(i), 0); )
+# WAIT 420;
+# IF (script_int(caster, "InvState") == 0) THEN ABORT;
+# )
+# set_script_variable(caster, "InvState", 0);
+
+#NONMAGIC SILENT SPELL easter-invader-command : "#d" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT override_attack(caster, 3, ((200 - agi(caster)) * 5000) / 200,
+# 8, ATTACK_ICON_GENERIC, 31, 0);
+# ATTRIGGER CALL attack_check(target);
+# area = rbox(location(target), 8);
+# FOREACH MOB m IN area
+# DO (message(caster, "Class Id: " + mob_id(m));
+# IF (mob_id(m) = 1040 || mob_id(m) == 1042 || mob_id(m) == 1047) THEN (
+# CALL sfx_generic(m);
+# aggravate(m, 0, target);
+# )
+# )
+
+#NONMAGIC SILENT SPELL easter-invader-spike : "#w" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT FOR i = 1 TO 40
+# DO ( point = random_location(rbox(location(caster), sqrt(i) << 1));
+# IF (random(3))
+# THEN (sfx(point, 32, 0);
+# WAIT 50;
+# FOREACH PC p IN rbox(point, sqrt(i) >> 1)
+# DO IF (p <> caster)
+# THEN (injure(caster, p, i + random(i << 1), random(random(i)));)
+# ) ELSE (sfx(point, 31, 0);
+# WAIT 50;
+# FOREACH PC p IN rbox(point, sqrt(i))
+# DO IF (p <> caster)
+# THEN (injure(caster, p, i + random(i << 2), random(random(i)));)
+# )
+# WAIT 400 - (i << 3);
+# )
+
+#NONMAGIC SILENT SPELL easter-invader-jump (t : STRING) : "#j" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT IF not (status_option(caster, 4096)) THEN ( sfx(caster, 33, 0);
+# WAIT 200; );
+# IF (t == "r") THEN ( warp(caster, @("028-1.gat", 129, 46)); )
+# ELSE IF (t == "s") THEN ( warp(caster, @("028-1.gat", 69, 96)); )
+# ELSE IF (t == "c") THEN ( warp(caster, @("028-3.gat", 50, 58)); )
+# ELSE IF (t == "t") THEN ( warp(caster, @("009-1.gat", 48, 35)); )
+# ELSE { gmcommand "@jump"; };
+
+#NONMAGIC SILENT SPELL easter-invader-taunt-local (s : STRING) : "#l" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT FOREACH PC p IN rbox(location(caster), 20) DO message(p, s);
+
+#NONMAGIC SILENT SPELL easter-invader-taunt-global (s : STRING) : "#b" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT FOREACH PC p IN rbox(location(caster), 100) DO message(p, s);
+
+#NONMAGIC SILENT SPELL easter-invader-spawn : "#c" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT spawn(rbox(location(caster), 20), caster, 1040, random(random(5)), 1, 100000);
+# spawn(rbox(location(caster), 20), caster, 1042, random(random(5)), 1, 100000);
+# spawn(rbox(location(caster), 20), caster, 1047, random(random(5)), 1, 100000);
+
+NONMAGIC SILENT SPELL world-shift : "#S00" =
+ (REQUIRE (script_int(caster, "Easter_2010_QuestState") >> 16) & 1 == 1 && (script_int(caster, "Easter_2010_QuestState") >> 7) & 3 == 2) => # Travel only works when you have both helped the doctor at least thrice and have defeated the invader - top level requirement.
+ ( (REQUIRE count_item(caster, "DarkPetal")) # Allow unlimited travel with the petal
+ => ((REQUIRE rdistance(location(caster), @("028-1.gat", 63, 67)) < 5) =>
+ EFFECT sfx(location(caster), SFX_TELEPORT, 200);
+ WAIT 8000;
+ { savepoint "009-1", 52, 39; };
+ warp(caster, @("009-1.gat", 55, 37));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ |
+ (REQUIRE map_nr(location(caster)) != 28) =>
+ EFFECT inithp = hp(caster); // Store this value.
+ sfx(location(caster), SFX_TELEPORT, 200);
+ IF (map_nr(location(caster)) == 9 || map_nr(location(caster)) == 1 || map_nr(location(caster)) == 21 || map_nr(location(caster)) == 20) THEN WAIT 8000;
+ ELSE WAIT 20000;
+ IF (hp(caster) < inithp) THEN ABORT; // Cancel teleport if the player took damage during channel time (hacky, but for most purposes should work.)
+ { savepoint "028-1", 63, 68; };
+ warp(caster, @("028-1.gat", 63, 67));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ )
+ | (REQUIRE (rdistance(location(caster), @("028-1.gat", 63, 67)) < 5 )
+ => EFFECT set_script_variable(caster, "Easter_2010_QuestState", script_int(caster, "Easter_2010_QuestState") | (1 << 17));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ WAIT 8000;
+ { savepoint "009-1", 52, 39; };
+ warp(caster, @("009-1.gat", 55, 37));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ | (REQUIRE map_nr(location(caster)) != 28 && (script_int(caster, "Easter_2010_QuestState") >> 17) & 1 == 0) # Allow for one free warp home without the petal
+ => EFFECT inithp = hp(caster); // Store this value.
+ sfx(location(caster), SFX_TELEPORT, 200);
+ IF (map_nr(location(caster)) == 9 || map_nr(location(caster)) == 1 || map_nr(location(caster)) == 21 || map_nr(location(caster)) == 20) THEN WAIT 8000;
+ ELSE WAIT 20000;
+ IF (hp(caster) < inithp) THEN ABORT; // Cancel teleport if the player took damage during channel time (hacky, but for most purposes should work.)
+ set_script_variable(caster, "Easter_2010_QuestState", script_int(caster, "Easter_2010_QuestState") | (1 << 17));
+ { savepoint "028-1", 63, 68; };
+ warp(caster, @("028-1.gat", 63, 67));
+ sfx(location(caster), SFX_TELEPORT, 200);
+ )
+ )
+
+#NONMAGIC SILENT SPELL easter-invader-killer : "#z" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT { gmcommand "@killer"; }
+
+#NONMAGIC SILENT SPELL easter-invader-killable : "#x" =
+# (REQUIRE name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT { gmcommand "@killable"; }
+
+#NONMAGIC SILENT SPELL easter-debug-reset (s : STRING) : "#r" =
+# (REQUIRE name_of(caster) == "Freeyorp") =>
+# EFFECT set_script_variable(pc(s), "Easter_2010_QuestState", 0);
+
+#NONMAGIC SILENT SPELL easter-set-flag (s : PC) : "#k" =
+# (REQUIRE name_of(caster) == "Freeyorp" || name_of(caster) == "Xakelbael the Dark") =>
+# EFFECT IF (s == caster) THEN ABORT;
+# IF ((script_int(s, "Easter_2010_QuestState") >> 16) & 1) THEN ( message(caster, s + " already has the flag set!"); ABORT; )
+# script_target = s;
+# { set Easter_2010_QuestState, Easter_2010_QuestState | (1 << 16); // RETURN_READY flag
+# mes "As the smoke clears, you feel stronger, vivified.";
+# next;
+# mes "The world seems more vibrant. It looks the same as ever, but you get a sense of something more.";
+# next;
+# mes "You feel something familiar.";
+# next;
+# mes "This sensation... could it be home?";
+# next;
+# mes "A single word comes to your mind - " + getspellinvocation("world-shift") + ". While it sounds magical, you know with certainty that you will not need any magical power to use it.";
+# next;
+# mes "Could this be the way to move to and from the worlds? Either way, the dark petal the rose vanished into seems to be the key.";
+# next;
+# mes "Though you might be able to succeed in one trip without it, returning home.";
+# if ((Easter_2010_QuestState >> E10_STATE_ROSE_SHIFT) & E10_STATE_ROSE_MASK == E10_STATE_ROSE_ROSE_COMPLETE) close;
+# next;
+# mes "But the world is still not quite right. Perhaps there is someone you should help before the world releases its hold on you?";
+# close;
+# }
+
+NONMAGIC SILENT SPELL easter-get-debug (p : PC) : "#e" =
+ (REQUIRE name_of(caster) == "Freeyorp" || name_of(caster) == "Xakelbael the Dark") =>
+ EFFECT { message strcharinfo(0), "Global state egg1: " + $Easter_2010_Egg_Loc_State1;
+ message strcharinfo(0), "Global state egg2: " + $Easter_2010_Egg_Loc_State2;
+ message strcharinfo(0), "Global state egg3: " + $Easter_2010_Egg_Loc_State3;
+ message strcharinfo(0), "Global state egg4: " + $Easter_2010_Egg_Loc_State4;
+ message strcharinfo(0), "Global state egg5: " + $Easter_2010_Egg_Loc_State5;
+ message strcharinfo(0), "Global npc state1: " + $Easter_2010_Npc_State1;
+ message strcharinfo(0), "Global npc state2: " + $Easter_2010_Npc_State2;
+ };
+ message(caster, "Local state egg1: " + script_int(p, "Easter_2010_EggState1"));
+ message(caster, "Local state egg2: " + script_int(p, "Easter_2010_EggState2"));
+ message(caster, "Local state egg3: " + script_int(p, "Easter_2010_EggState3"));
+ message(caster, "Local state egg4: " + script_int(p, "Easter_2010_EggState4"));
+ message(caster, "Local state egg5: " + script_int(p, "Easter_2010_EggState5"));
+ message(caster, "Local state quest: " + script_int(p, "Easter_2010_QuestState"));
+
+
+
+
+PROCEDURE hug_tree(target) =
+ IF (target = ""
+ || target = "tree" || target = "tree*"
+ || target = "Tree" || target = "Tree*"
+ || target = "druid" || target = "druid*"
+ || target = "Druid" || target = "Druid*")
+ THEN {
+ set @flag, 2;
+ callfunc "QuestTreeTrigger";
+ };
+
+NONMAGIC SPELL hug0 (target : STRING) : "hug" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+NONMAGIC SPELL hug1 (target : STRING) : "*hug*" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+NONMAGIC SPELL hug2 (target : STRING) : "*hug" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+NONMAGIC SPELL hug3 (target : STRING) : "hugs" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+NONMAGIC SPELL hug4 (target : STRING) : "*hugs*" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+NONMAGIC SPELL hug5 (target : STRING) : "*hugs" = REQUIRE ((rdistance(location(caster), location(npc("#DruidTree0#_M"))) <= 1
+ || rdistance(location(caster), location(npc("#DruidTree1#_M"))) <= 1))
+ => EFFECT CALL hug_tree(target);
+
+
+#--------------------------------------------------------------------------------
+# Teleport anchors
+#--------------------------------------------------------------------------------
+
+TELEPORT-ANCHOR tulimshar : "tulimshar" = @("001-1.gat", 43, 66) @+ (3, 3);
+TELEPORT-ANCHOR hurnscald : "hurnscald" = @("009-1.gat", 55, 37) @+ (3, 3);
+TELEPORT-ANCHOR nivalis : "nivalis" = @("020-1.gat", 75, 63) @+ (19, 12);
+TELEPORT-ANCHOR wizardhut : "##00" = @("013-1.gat", 41, 92) @+ (3, 3);
+TELEPORT-ANCHOR pachua : "##01" = @("006-1.gat", 22, 101) @+ (3, 3);
+TELEPORT-ANCHOR desert : "##02" = @("005-1.gat", 160, 64) @+ (5, 5);
+TELEPORT-ANCHOR forest : "##03" = @("015-1.gat", 35, 35) @+ (40, 40);
+TELEPORT-ANCHOR snakecave : "##04" = @("011-4.gat", 50, 75) @+ (3, 3);
+TELEPORT-ANCHOR dimondscove : "##05" = @("010-2.gat", 23, 79) @+ (3, 3);
+
+