# 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
# Schools of magic
CONST SC_HALT_REGENERATE = 195
CONST SC_FLYING_BACKPACK = 196
CONST SC_MBARRIER = 197
CONST SC_HASTE = 198
CONST SC_PHYS_SHIELD = 199
CONST SFX_TELEPORT = 24
# Increase spellpower by school and general magic skill
PROCEDURE adjust_spellpower(school) =
spellpower = spellpower + (skill(caster, MAGIC) + skill(caster, school)) * 10;
IF ((school = LIFE) && (target = partner(caster)))
THEN spellpower = spellpower + 200; # Do something for wedding rings, too?
PROCEDURE heal(target, max_heal) =
sfx(caster, 4, 0);
IF caster <> target
THEN sfx(target, 4, 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) =
sfx(caster, 4, 0);
IF caster <> target
THEN sfx(target, 4, 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);
injure(caster, target, d, 0);
PROCEDURE install_attack_spell(charges, base_delay, range, attack_animation) =
sfx(caster, 5, 0);
attack_range = range;
override_attack(caster, charges, ((200 - agi(caster)) * base_delay) / 200,
range, 0, attack_animation);
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) =
sfx(caster, 2, 0);
sfx(location, 21, 0);
WAIT delay;
sfx(location, 22, 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);
#--------------------------------------------------------------------------------
# Level 0 spells
#--------------------------------------------------------------------------------
#SPELL reset : "#reset" =
# EFFECT { skill 340, 0;
# skill 341, 0;
# skill 342, 0;
# skill 343, 0;
# skill 344, 0;
# skill 345, 0;
# }
SPELL lesser-heal (target : STRING) : "#imx" =
LET level = 0
school = LIFE
IN (MANA 5, CASTTIME 400,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE if_then_else(failed(pc(target)), 1,
rdistance(location(caster), location(pc(target))) < 2 + (spellpower / 50)),
(COMPONENTS ["MaggotSlime"] OR COMPONENTS ["BugLeg"]))
=> EFFECT CALL adjust_spellpower(school);
IF failed(pc(target))
THEN target = caster; # quest handling goes here
ELSE target = pc(target);
CALL heal(target, 200);
SPELL flare-dart : "#fla" =
LET level = 0
school = WAR
IN (MANA 15, CASTTIME 500,
REQUIRE skill(caster, MAGIC) > level,
(REQUIRE skill(caster, school) > 2 OR COMPONENTS ["SulphurPowder"]))
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 5, 0);
damage = min(40 + skill(caster, school) * 20,
10 + spellpower);
damage_bonus = 5 + level(caster) / 3;
CALL install_attack_spell(3 + spellpower / 30,
1200,
4, 31);
ATTRIGGER CALL attack_check(target);
CALL elt_damage (target, damage, damage_bonus, ELT_WATER, ELT_FIRE, 15);
SPELL magic-blade : "#mbl" =
LET level = 0
school = WAR
IN (MANA 12, CASTTIME 500,
REQUIRE skill(caster, MAGIC) > level)
=> ( COMPONENTS ["SharpKnife"] =>
EFFECT CALL adjust_spellpower(WAR);
sfx(caster, 5, 0);
CALL install_melee_spell(10 + spellpower / 10, 900, 30);
ATTRIGGER CALL melee_damage(target, 60 + spellpower / 20, 5 + str(caster));
| COMPONENTS ["Knife"] =>
EFFECT CALL adjust_spellpower(WAR);
sfx(caster, 5, 0);
CALL install_melee_spell(10 + spellpower / 10, 900, 30);
ATTRIGGER CALL melee_damage(target, 40 + spellpower / 20, 5 + str(caster));
)
SPELL aggravate : "#qaw" =
LET level = 0
school = NATURE
IN (MANA 5, CASTTIME 300,
REQUIRE skill(caster, MAGIC) > level)
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
FOREACH MOB target IN rbox(location(caster), 1 + spellpower / 20) DO
(sfx(target, 2, 0);
aggravate(target, 0, caster);)
LOCAL SPELL summon-maggots : "#kbm" =
LET level = 0
school = ASTRAL
IN (MANA 20, CASTTIME 3000,
REQUIRE skill(caster, MAGIC) > level,
COMPONENTS ["MaggotSlime"])
=> EFFECT CALL adjust_spellpower(school);
CALL summon_spell(1002, 3 + spellpower / 15, 5000 - (spellpower * 10), spellpower * 500, 1);
#--------------------------------------------------------------------------------
# Level 1 spells
#--------------------------------------------------------------------------------
SPELL make-arrows : "#mkarrows" =
LET level = 1
school = TRANSMUTE
IN (MANA 20, CASTTIME 2000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS ["RawLog"])
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
create_item(caster, "Arrow", 1 + spellpower / 15);
SPELL make-shirt : "#mkshirt" =
LET level = 1
school = TRANSMUTE
IN (MANA 20, CASTTIME 2000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS [5 * "CottonCloth"])
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
create_item(caster, "CottonShirt", 1);
SPELL make-tanktop : "#mktanktop" =
LET level = 1
school = TRANSMUTE
IN (MANA 20, CASTTIME 2000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS [4 * "CottonCloth"])
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
create_item(caster, "TanktopWhite", 1);
SPELL make-short-tanktop : "#mkshorttanktop" =
LET level = 1
school = TRANSMUTE
IN (MANA 20, CASTTIME 2000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS [3 * "CottonCloth"])
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
create_item(caster, "ShortTanktopWhite", 1);
SPELL lightning-strike : "#kig" =
LET level = 1
school = WAR
IN (MANA 25, 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 = min(100 + skill(caster, school) * 50,
50 + spellpower * 2);
damage_bonus = level(caster) + spellpower;
CALL install_attack_spell(1 + spellpower / 60,
3000,
8, 31);
ATTRIGGER CALL attack_check(target);
CALL elt_damage (target, damage, damage_bonus, ELT_EARTH, ELT_WIND, 17 + random(3));
SPELL fire-ball : "#pof" =
LET level = 1
school = WAR
IN (MANA 30, CASTTIME 1000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
(REQUIRE skill(caster, school) > 3 OR 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 magic-knuckles (target : PC) : "#iom" =
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(target);
CALL install_melee_spell(10 + spellpower / 12, 1000, 0);
ATTRIGGER CALL melee_damage(target, 40 + (str / 2) + spellpower / 20, 5 + str);
SPELL flying-backpack (target : PC) : "#uuy" =
LET level = 1
school = NATURE
IN (MANA 12, CASTTIME 1000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
REQUIRE rdistance(location(target), location(caster)) < 2 + spellpower / 30)
=> EFFECT CALL adjust_spellpower(school);
sfx(target, 3, 0);
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.");
ATEND message (target, "Your backpack is no longer levitating.");
sfx(target, 2, 0);
SPELL protect (target : PC) : "#ism" =
LET level = 1
school = NATURE
IN (MANA 18, CASTTIME 1000,
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 sfx(caster, 2, 0);
status_change(target, SC_PHYS_SHIELD, 5 + max(15, spellpower / 12), 0, 0, 0, 5000 + (spellpower * 500));
message (target, "You feel more protected.");
ATEND message (target, "You feel less protected.");
sfx(target, 111, 0);
LOCAL SPELL rain : "#flosh" =
LET level = 1
school = NATURE
IN (MANA 20, CASTTIME 3000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
(REQUIRE skill(caster, school) > 3 OR COMPONENTS ["BottleOfWater"]))
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
area = rbox(location(caster), 3 + spellpower / 30);
FOR i = 0 TO spellpower DO (
FOR j = 0 TO spellpower / 100 DO (
location = random_location(area);
sfx(location, 25, 0);
FOREACH TARGET target IN rbox(location, 1) DO
IF element(target) = ELT_FIRE
THEN injure(caster, target, 5 + random(5 + spellpower / 10), 0);
)
WAIT 300 - min(spellpower, 200) + random(100);
);
SPELL happy-curse (target : PC) : "#happy" =
LET level = 1
school = NATURE
IN (MANA 10, CASTTIME 1000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
(REQUIRE skill(caster, school) > 3 OR COMPONENTS ["WhiteFur"]),
REQUIRE rdistance(location(target), location(caster)) < 1 + spellpower / 100)
=> EFFECT CALL adjust_spellpower(school);
FOR i = 0 TO (spellpower / 10) DO (emote(target, 3); WAIT 500;);
SPELL barrier (target : PC) : "#iso" =
LET level = 1
school = NATURE
IN (MANA 18, CASTTIME 1000,
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, 3, 0);
IF (caster <> target)
THEN sfx(caster, 2, 0);
status_change(target, SC_MBARRIER, 30 + max(30, spellpower / 8), 0, 0, 0, 2000 + (spellpower * 200));
message (target, "You are surrounded by a magical barrier.");
ATEND message (target, "Your magical barrier disspiates.");
sfx(target, 2, 0);
LOCAL SPELL summon-scorps : "#kbms" =
LET level = 1
school = ASTRAL
IN (MANA 25, CASTTIME 3000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS ["ScorpionStinger"])
=> EFFECT CALL adjust_spellpower(school);
sfx(target, 2, 0);
CALL summon_spell(1003, 1 + spellpower / 30, 5000 - (spellpower * 9), spellpower * 400, 2);
LOCAL SPELL summon-red-scorps : "#kbmq" =
LET level = 1
school = ASTRAL
IN (MANA 30, CASTTIME 3000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
COMPONENTS ["RedScorpionStinger"])
=> EFFECT CALL adjust_spellpower(school);
sfx(target, 2, 0);
CALL summon_spell(1004, 1 + spellpower / 40, 5000 - (spellpower * 8), spellpower * 350, 3);
SPELL detect-players : "#ewm" =
LET level = 1
school = MAGIC
IN (MANA 10, CASTTIME 300,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level)
=> EFFECT CALL adjust_spellpower(school);
sfx(target, 2, 0);
message = "";
FOREACH PC target IN rbox(location(caster), spellpower / 2)
DO IF target <> caster
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 lay-on-hands (target : STRING) : "#loh" =
LET level = 1
school = LIFE
IN (MANA 8, CASTTIME 400,
REQUIRE hp(caster) > max_hp(caster) / 20,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE if_then_else(failed(pc(target)), 1,
(rdistance(location(caster),
location(pc(target))) < 2 + (spellpower / 50))
&& not (running_status_update(pc(target), SC_HALT_REGENERATE))
))
=> EFFECT CALL adjust_spellpower(school);
IF failed(pc(target))
THEN target = caster; # quest handling goes here
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;
)
instaheal(caster, 0 - payment, 0);
CALL quickheal(target, power);
status_change(caster, SC_HALT_REGENERATE, 0, 0, 0, 0, 10000);
#--------------------------------------------------------------------------------
# Level 2 spells
#--------------------------------------------------------------------------------
SPELL summon-partner : "#aid" =
LET level = 2
school = ASTRAL
IN (MANA 30, CASTTIME 2000,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level,
REQUIRE is_married(caster))
=> EFFECT CALL adjust_spellpower(school);
sfx(caster, 2, 0);
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 (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 : "#aw" =
LET level = 4
school = ASTRAL
IN (MANA 40, CASTTIME 400,
REQUIRE skill(caster, MAGIC) > level,
REQUIRE skill(caster, school) > level)
=> EFFECT sfx(caster, 2, 0);
shroud(caster, 0x04);
#--------------------------------------------------------------------------------
# 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)))=>
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 have 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;
}
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.");