summaryrefslogtreecommitdiff
path: root/src/spells3.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/spells3.cc')
-rw-r--r--src/spells3.cc4606
1 files changed, 4606 insertions, 0 deletions
diff --git a/src/spells3.cc b/src/spells3.cc
new file mode 100644
index 00000000..35604f7d
--- /dev/null
+++ b/src/spells3.cc
@@ -0,0 +1,4606 @@
+#include "spells3.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd5.hpp"
+#include "feature_type.hpp"
+#include "lua_bind.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_type.hpp"
+#include "school_book.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells4.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+s32b NOXIOUSCLOUD = -1; /* Identifier */
+s32b AIRWINGS = -1; /* Identifier */
+s32b INVISIBILITY;
+s32b POISONBLOOD;
+s32b THUNDERSTORM;
+s32b STERILIZE;
+
+s32b BLINK;
+s32b DISARM;
+s32b TELEPORT;
+s32b TELEAWAY;
+s32b RECALL;
+s32b PROBABILITY_TRAVEL;
+
+s32b DEMON_BLADE;
+s32b DEMON_MADNESS;
+s32b DEMON_FIELD;
+s32b DOOM_SHIELD;
+s32b UNHOLY_WORD;
+s32b DEMON_CLOAK;
+s32b DEMON_SUMMON;
+s32b DISCHARGE_MINION;
+s32b CONTROL_DEMON;
+
+s32b STARIDENTIFY;
+s32b IDENTIFY;
+s32b VISION;
+s32b SENSEHIDDEN;
+s32b REVEALWAYS;
+s32b SENSEMONSTERS;
+
+s32b STONESKIN;
+s32b DIG;
+s32b STONEPRISON;
+s32b STRIKE;
+s32b SHAKE;
+
+s32b ERU_SEE;
+s32b ERU_LISTEN;
+s32b ERU_UNDERSTAND;
+s32b ERU_PROT;
+
+s32b GLOBELIGHT;
+s32b FIREFLASH;
+s32b FIERYAURA;
+s32b FIREWALL;
+s32b FIREGOLEM;
+
+s32b CALL_THE_ELEMENTS;
+s32b CHANNEL_ELEMENTS;
+s32b ELEMENTAL_WAVE;
+s32b VAPORIZE;
+s32b GEOLYSIS;
+s32b DRIPPING_TREAD;
+s32b GROW_BARRIER;
+s32b ELEMENTAL_MINION;
+
+s32b MANATHRUST;
+s32b DELCURSES;
+s32b RESISTS;
+s32b MANASHIELD;
+
+s32b MANWE_SHIELD;
+s32b MANWE_AVATAR;
+s32b MANWE_BLESS;
+s32b MANWE_CALL;
+
+s32b MELKOR_CURSE;
+s32b MELKOR_CORPSE_EXPLOSION;
+s32b MELKOR_MIND_STEAL;
+
+s32b RECHARGE;
+s32b SPELLBINDER;
+s32b DISPERSEMAGIC;
+s32b TRACKER;
+s32b INERTIA_CONTROL;
+timer_type *TIMER_INERTIA_CONTROL = 0;
+
+s32b CHARM;
+s32b CONFUSE;
+s32b ARMOROFFEAR;
+s32b STUN;
+
+s32b MAGELOCK;
+s32b SLOWMONSTER;
+s32b ESSENCESPEED;
+s32b BANISHMENT;
+
+s32b TULKAS_AIM;
+s32b TULKAS_WAVE;
+s32b TULKAS_SPIN;
+
+s32b DRAIN;
+s32b GENOCIDE;
+s32b WRAITHFORM;
+s32b FLAMEOFUDUN;
+
+s32b TIDALWAVE;
+s32b ICESTORM;
+s32b ENTPOTION;
+s32b VAPOR;
+s32b GEYSER;
+
+s32b YAVANNA_CHARM_ANIMAL;
+s32b YAVANNA_GROW_GRASS;
+s32b YAVANNA_TREE_ROOTS;
+s32b YAVANNA_WATER_BITE;
+s32b YAVANNA_UPROOT;
+
+s32b GROWTREE;
+s32b HEALING;
+s32b RECOVERY;
+s32b REGENERATION;
+s32b SUMMONANNIMAL;
+s32b GROW_ATHELAS = -1;
+
+s32b DEVICE_HEAL_MONSTER;
+s32b DEVICE_SPEED_MONSTER;
+s32b DEVICE_WISH;
+s32b DEVICE_SUMMON;
+s32b DEVICE_MANA;
+s32b DEVICE_NOTHING;
+s32b DEVICE_HOLY_FIRE;
+s32b DEVICE_THUNDERLORDS;
+
+s32b MUSIC_STOP;
+s32b MUSIC_HOLD;
+s32b MUSIC_CONF;
+s32b MUSIC_STUN;
+s32b MUSIC_LITE;
+s32b MUSIC_HEAL;
+s32b MUSIC_HERO;
+s32b MUSIC_TIME;
+s32b MUSIC_MIND;
+s32b MUSIC_BLOW;
+s32b MUSIC_WIND;
+s32b MUSIC_YLMIR;
+s32b MUSIC_AMBARKANTA;
+
+s32b AULE_FIREBRAND;
+s32b AULE_ENCHANT_WEAPON;
+s32b AULE_ENCHANT_ARMOUR;
+s32b AULE_CHILD;
+
+s32b MANDOS_TEARS_LUTHIEN = -1;
+s32b MANDOS_SPIRIT_FEANTURI = -1;
+s32b MANDOS_TALE_DOOM = -1;
+s32b MANDOS_CALL_HALLS = -1;
+
+s32b ULMO_BELEGAER;
+s32b ULMO_DRAUGHT_ULMONAN;
+s32b ULMO_CALL_ULUMURI;
+s32b ULMO_WRATH;
+
+s32b VARDA_LIGHT_VALINOR;
+s32b VARDA_CALL_ALMAREN;
+s32b VARDA_EVENSTAR;
+s32b VARDA_STARKINDLER;
+
+static s32b get_level_s(int sp, int max)
+{
+ return get_level(sp, max, 1);
+}
+
+static void find_position(int y, int x, int *yy, int *xx)
+{
+ int attempts = 500;
+
+ do
+ {
+ scatter(yy, xx, y, x, 6);
+ }
+ while (!(in_bounds(*yy, *xx) && cave_floor_bold(*yy, *xx)) && --attempts);
+}
+
+static casting_result cast(bool_ effect)
+{
+ return effect ? CAST_OBVIOUS : CAST_HIDDEN;
+}
+
+static casting_result cplus(casting_result old, bool_ effect)
+{
+ if (old == NO_CAST)
+ {
+ return cast(effect);
+ }
+ else
+ {
+ if ((old == CAST_OBVIOUS) || (effect == TRUE)) {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return CAST_HIDDEN;
+ }
+ }
+}
+
+GENERATE_MONSTER_LOOKUP_FN(get_fire_golem, "Fire golem")
+
+// -------------------------------------------------------------
+
+casting_result air_noxious_cloud()
+{
+ int dir, type;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (get_level_s(NOXIOUSCLOUD, 50) >= 30)
+ {
+ type = GF_UNBREATH;
+ }
+ else
+ {
+ type = GF_POIS;
+ }
+
+ fire_cloud(type, dir, 7 + get_level_s(NOXIOUSCLOUD, 150), 3, 5 + get_level_s(NOXIOUSCLOUD, 40));
+ return CAST_OBVIOUS;
+}
+
+const char *air_noxious_cloud_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad 3 dur " FMTs32b,
+ (7 + get_level_s(NOXIOUSCLOUD, 150)),
+ (5 + get_level_s(NOXIOUSCLOUD, 40)));
+ return buf;
+}
+
+casting_result air_wings_of_winds()
+{
+ if (get_level_s(AIRWINGS, 50) >= 16)
+ {
+ if (p_ptr->tim_fly == 0)
+ {
+ return cast(set_tim_fly(randint(10) + 5 + get_level_s(AIRWINGS, 25)));
+ }
+ }
+ else
+ {
+ if (p_ptr->tim_ffall == 0)
+ {
+ return cast(set_tim_ffall(randint(10) + 5 + get_level_s(AIRWINGS, 25)));
+ }
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_wings_of_winds_info()
+{
+ static char buf[128];
+ sprintf(buf, "dur " FMTs32b "+d10", (5 + get_level_s(AIRWINGS, 25)));
+ return buf;
+}
+
+casting_result air_invisibility()
+{
+ if (p_ptr->tim_invisible == 0)
+ {
+ return cast(set_invis(randint(20) + 15 + get_level_s(INVISIBILITY, 50), 20 + get_level_s(INVISIBILITY, 50)));
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_invisibility_info()
+{
+ static char buf[128];
+ sprintf(buf, "dur " FMTs32b "+d20 power " FMTs32b,
+ (15 + get_level_s(INVISIBILITY, 50)),
+ (20 + get_level_s(INVISIBILITY, 50)));
+ return buf;
+}
+
+casting_result air_poison_blood()
+{
+ casting_result result = NO_CAST;
+
+ if (p_ptr->oppose_pois == 0)
+ {
+ result = cplus(result, set_oppose_pois(randint(30) + 25 + get_level_s(POISONBLOOD, 25)));
+ }
+
+ if ((p_ptr->tim_poison == 0) &&
+ (get_level_s(POISONBLOOD, 50) >= 15))
+ {
+ result = cplus(result, set_poison(randint(30) + 25 + get_level_s(POISONBLOOD, 25)));
+ }
+
+ return result;
+}
+
+const char *air_poison_blood_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d30",
+ (25 + get_level_s(POISONBLOOD, 25)));
+ return buf;
+}
+
+casting_result air_thunderstorm()
+{
+ if (p_ptr->tim_thunder == 0)
+ {
+ return cast(set_tim_thunder(randint(10) + 10 + get_level_s(THUNDERSTORM, 25), 5 + get_level_s(THUNDERSTORM, 10), 10 + get_level_s(THUNDERSTORM, 25)));
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_thunderstorm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d10",
+ (5 + get_level_s(THUNDERSTORM, 10)),
+ (10 + get_level_s(THUNDERSTORM, 25)),
+ (10 + get_level_s(THUNDERSTORM, 25)));
+ return buf;
+}
+
+casting_result air_sterilize()
+{
+ set_no_breeders((30) + 20 + get_level_s(STERILIZE, 70));
+ return CAST_OBVIOUS;
+}
+
+const char *air_sterilize_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d30",
+ (20 + get_level_s(STERILIZE, 70)));
+ return buf;
+}
+
+casting_result convey_blink()
+{
+ if (get_level_s(BLINK, 50) >= 30)
+ {
+ int oy = p_ptr->py;
+ int ox = p_ptr->px;
+
+ teleport_player(10 + get_level_s(BLINK, 8));
+ create_between_gate(0, oy, ox);
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ teleport_player(10 + get_level_s(BLINK, 8));
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *convey_blink_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "distance " FMTs32b,
+ (10 + get_level_s(BLINK, 8)));
+ return buf;
+}
+
+casting_result convey_disarm()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, destroy_doors_touch());
+ if (get_level_s(DISARM, 50) >= 10)
+ {
+ result = cplus(result, destroy_traps_touch());
+ }
+
+ return result;
+}
+
+casting_result convey_teleport()
+{
+ p_ptr->energy -= (25 - get_level_s(TELEPORT, 50));
+ teleport_player(100 + get_level_s(TELEPORT, 100));
+ return CAST_OBVIOUS;
+}
+
+const char *convey_teleport_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "distance " FMTs32b,
+ (100 + get_level_s(TELEPORT, 100)));
+ return buf;
+}
+
+casting_result convey_teleport_away()
+{
+ if (get_level_s(TELEAWAY, 50) >= 20)
+ {
+ return cast(project_hack(GF_AWAY_ALL, 100));
+ }
+ else if (get_level_s(TELEAWAY, 50) >= 10)
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_AWAY_ALL, dir, 100, 3 + get_level_s(TELEAWAY, 4)));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ return cast(teleport_monster(dir));
+ }
+}
+
+static int recall_get_d()
+{
+ int d = 21 - get_level_s(RECALL, 15);
+ if (d < 0)
+ {
+ d = 0;
+ }
+ return d;
+}
+
+static int recall_get_f()
+{
+ int f = 15 - get_level_s(RECALL, 10);
+ if (f < 1)
+ {
+ f = 1;
+ }
+ return f;
+}
+
+casting_result convey_recall()
+{
+ int x,y;
+ cave_type *c_ptr;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+
+ if ((y == p_ptr->py) &&
+ (x == p_ptr->px))
+ {
+ int d = recall_get_d();
+ int f = recall_get_f();
+ recall_player(d, f);
+ return CAST_OBVIOUS;
+ }
+ else if (c_ptr->m_idx > 0)
+ {
+ swap_position(y, x);
+ return CAST_OBVIOUS;
+ }
+ else if (!c_ptr->o_idxs.empty())
+ {
+ // Set the target
+ target_who = -1;
+ target_col = x;
+ target_row = y;
+ // Fetch item
+ if (get_level_s(RECALL, 50) >= 15)
+ {
+ fetch(5, 10 + get_level_s(RECALL, 150), FALSE);
+ }
+ else
+ {
+ fetch(5, 10 + get_level_s(RECALL, 150), TRUE);
+ }
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *convey_recall_info()
+{
+ static char buf[128];
+ int d = recall_get_d();
+ int f = recall_get_f();
+
+ sprintf(buf,
+ "dur %d+d%d weight " FMTs32b "lb",
+ f, d, (1 + get_level_s(RECALL, 15)));
+ return buf;
+}
+
+casting_result convey_probability_travel()
+{
+ return cast(set_prob_travel(randint(20) + get_level_s(PROBABILITY_TRAVEL, 60)));
+}
+
+const char *convey_probability_travel_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ get_level_s(PROBABILITY_TRAVEL, 60));
+ return buf;
+}
+
+casting_result demonology_demon_blade()
+{
+ int rad, type;
+
+ type = GF_FIRE;
+ if (get_level_s(DEMON_BLADE, 50) >= 30)
+ {
+ type = GF_HELL_FIRE;
+ }
+
+ rad = 0;
+ if (get_level_s(DEMON_BLADE, 50) >= 45)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(randint(20) + get_level_s(DEMON_BLADE, 80),
+ type,
+ 4 + get_level_s(DEMON_BLADE, 40),
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *demonology_demon_blade_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20 dam " FMTs32b "/blow",
+ (get_level_s(DEMON_BLADE, 80)),
+ (4 + get_level_s(DEMON_BLADE, 40)));
+ return buf;
+}
+
+casting_result demonology_demon_madness()
+{
+ casting_result result = NO_CAST;
+ int dir, type, y1, x1, y2, x2;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ type = GF_CHAOS;
+ if (magik(33))
+ {
+ type = GF_CONFUSION;
+ }
+ if (magik(33))
+ {
+ type = GF_CHARM;
+ }
+
+ // Calculate the coordinates of arrival
+ {
+ // Use the given direction
+ int tx = p_ptr->px + (ddx[dir] * 100);
+ int ty = p_ptr->py + (ddy[dir] * 100);
+
+ // Hack -- Use an actual "target"
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+ y1 = ty;
+ x1 = tx;
+ }
+
+ // Calculate the appropriate place
+ y2 = p_ptr->py - (y1 - p_ptr->py);
+ x2 = p_ptr->px - (x1 - p_ptr->px);
+
+ result = cplus(result,
+ project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y1, x1,
+ 20 + get_level_s(DEMON_MADNESS, 200),
+ type,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+ result = cplus(result,
+ project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y2, x2,
+ 20 + get_level_s(DEMON_MADNESS, 200),
+ type,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+
+ return result;
+}
+
+const char *demonology_demon_madness_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad " FMTs32b,
+ (20 + get_level_s(DEMON_MADNESS, 200)),
+ (1 + get_level(DEMON_MADNESS, 4, 0)));
+ return buf;
+}
+
+casting_result demonology_demon_field()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_cloud(GF_NEXUS,
+ dir,
+ 20 + get_level_s(DEMON_FIELD, 70),
+ 7,
+ 30 + get_level_s(DEMON_FIELD, 100)));
+}
+
+const char *demonology_demon_field_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " dur " FMTs32b,
+ (20 + get_level_s(DEMON_FIELD, 70)),
+ (30 + get_level_s(DEMON_FIELD, 100)));
+ return buf;
+}
+
+casting_result demonology_doom_shield()
+{
+ return cast(set_shield(randint(10) + 20 + get_level_s(DOOM_SHIELD, 100),
+ -300 + get_level_s(DOOM_SHIELD, 100),
+ SHIELD_COUNTER,
+ 1 + get_level_s(DOOM_SHIELD, 14),
+ 10 + get_level_s(DOOM_SHIELD, 15)));
+}
+
+const char *demonology_doom_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10 dam " FMTs32b "d" FMTs32b,
+ (20 + get_level_s(DOOM_SHIELD, 100)),
+ (1 + get_level_s(DOOM_SHIELD, 14)),
+ (10 + get_level_s(DOOM_SHIELD, 15)));
+ return buf;
+}
+
+casting_result demonology_unholy_word()
+{
+ int x, y;
+ cave_type *c_ptr = NULL;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx > 0)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You can only target a pet.");
+ return NO_CAST;
+ }
+
+ /* Oops he is angry now */
+ if (magik(30 - get_level(UNHOLY_WORD, 25, 0)))
+ {
+ char buf[128];
+ monster_desc(buf, m_ptr, 0);
+ if (buf[0] != '\0')
+ {
+ buf[0] = toupper(buf[0]);
+ }
+
+ msg_format("%s turns against you.", buf);
+ }
+ else
+ {
+ char buf[128];
+ s32b heal;
+
+ monster_desc(buf, m_ptr, 0);
+ msg_format("You consume %s.", buf);
+
+ heal = (m_ptr->hp * 100) / m_ptr->maxhp;
+ heal = ((30 + get_level(UNHOLY_WORD, 50, 0)) * heal) / 100;
+
+ hp_player(heal);
+
+ delete_monster_idx(c_ptr->m_idx);
+ }
+
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *demonology_unholy_word_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal mhp%% of " FMTs32b "%%",
+ (30 + get_level(UNHOLY_WORD, 50, 0)));
+ return buf;
+}
+
+casting_result demonology_demon_cloak()
+{
+ return cast(set_tim_reflect(randint(5) + 5 + get_level(DEMON_CLOAK, 15, 0)));
+}
+
+const char *demonology_demon_cloak_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d5",
+ (5 + get_level(DEMON_CLOAK, 15, 0)));
+ return buf;
+}
+
+casting_result demonology_summon_demon()
+{
+ int type, level, minlevel;
+
+ level = dun_level;
+
+ minlevel = 4;
+ if (level < minlevel)
+ {
+ level = minlevel;
+ }
+
+ summon_specific_level = 5 + get_level_s(DEMON_SUMMON, 100);
+
+ type = SUMMON_DEMON;
+ if (get_level_s(DEMON_SUMMON, 50) >= 35)
+ {
+ type = SUMMON_HI_DEMON;
+ }
+
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, level, type, TRUE))
+ {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ msg_print("Something blocks your summoning!");
+ return CAST_HIDDEN;
+ }
+}
+
+const char *demonology_summon_demon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ (5 + get_level_s(DEMON_SUMMON, 100)));
+ return buf;
+}
+
+casting_result demonology_discharge_minion()
+{
+ cave_type *c_ptr;
+ int x, y;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx > 0)
+ {
+ s32b dam;
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You can only target a pet.");
+ return NO_CAST;
+ }
+
+ delete_monster_idx(c_ptr->m_idx);
+
+ dam = m_ptr->hp;
+ dam = (dam * (20 + get_level(DISCHARGE_MINION, 60, 0))) / 100;
+ if (dam > 100 + get_level(DISCHARGE_MINION, 500, 0))
+ {
+ dam = 100 + get_level(DISCHARGE_MINION, 500, 0);
+ }
+
+ /* We use project instead of fire_ball because we must tell it exactly where to land */
+ return cast(project(0, 2, y, x, dam,
+ GF_GRAVITY,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *demonology_discharge_minion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "%% max " FMTs32b,
+ (20 + get_level(DISCHARGE_MINION, 60, 0)),
+ (100 + get_level(DISCHARGE_MINION, 500, 0)));
+ return buf;
+}
+
+casting_result demonology_control_demon()
+{
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_CONTROL_DEMON, dir, 50 + get_level_s(CONTROL_DEMON, 250), 0));
+}
+
+const char *demonology_control_demon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ (50 + get_level_s(CONTROL_DEMON, 250)));
+ return buf;
+}
+
+casting_result divination_greater_identify()
+{
+ if (get_check("Cast on yourself?"))
+ {
+ self_knowledge(NULL);
+ }
+ else
+ {
+ identify_fully();
+ }
+ return CAST_OBVIOUS;
+}
+
+casting_result divination_identify()
+{
+ if (get_level_s(IDENTIFY, 50) >= 27)
+ {
+ casting_result result = NO_CAST;
+ result = cplus(result, identify_pack());
+ result = cplus(result, fire_ball(GF_IDENTIFY, 0, 1, get_level_s(IDENTIFY, 3)));
+ return result;
+ }
+ else if (get_level_s(IDENTIFY, 50) >= 17)
+ {
+ casting_result result = NO_CAST;
+ result = cplus(result, identify_pack());
+ result = cplus(result, fire_ball(GF_IDENTIFY, 0, 1, 0));
+ return result;
+ }
+ else if (ident_spell())
+ {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *divination_identify_info()
+{
+ static char buf[128];
+
+ if (get_level_s(IDENTIFY, 50) >= 27)
+ {
+ sprintf(buf, "rad " FMTs32b, get_level_s(IDENTIFY, 3));
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result divination_vision()
+{
+ if (get_level_s(VISION, 50) >= 25)
+ {
+ wiz_lite_extra();
+ }
+ else
+ {
+ map_area();
+ }
+ return CAST_OBVIOUS;
+
+}
+
+casting_result divination_sense_hidden()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, detect_traps(15 + get_level(SENSEHIDDEN, 40, 0)));
+ if (get_level_s(SENSEHIDDEN, 50) >= 15)
+ {
+ result = cplus(result, set_tim_invis(10 + randint(20) + get_level_s(SENSEHIDDEN, 40)));
+ }
+
+ return result;
+}
+
+const char *divination_sense_hidden_info()
+{
+ static char buf[128];
+
+ if (get_level_s(SENSEHIDDEN, 50) >= 15)
+ {
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b "+d20",
+ (15 + get_level_s(SENSEHIDDEN, 40)),
+ (10 + get_level_s(SENSEHIDDEN, 40)));
+ }
+ else
+ {
+ sprintf(buf,
+ "rad " FMTs32b,
+ (15 + get_level_s(SENSEHIDDEN, 40)));
+ }
+
+ return buf;
+}
+
+casting_result divination_reveal_ways()
+{
+ casting_result result = NO_CAST;
+ result = cplus(result, detect_doors(10 + get_level(REVEALWAYS, 40, 0)));
+ result = cplus(result, detect_stairs(10 + get_level(REVEALWAYS, 40, 0)));
+ return result;
+}
+
+const char *divination_reveal_ways_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (10 + get_level_s(REVEALWAYS, 40)));
+ return buf;
+}
+
+casting_result divination_sense_monsters()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, detect_monsters_normal(10 + get_level(SENSEMONSTERS, 40, 0)));
+ if (get_level_s(SENSEMONSTERS, 50) >= 30)
+ {
+ result = cplus(result, set_tim_esp(10 + randint(10) + get_level_s(SENSEMONSTERS, 20)));
+ }
+ return result;
+}
+
+const char *divination_sense_monsters_info()
+{
+ static char buf[128];
+
+ if (get_level_s(SENSEMONSTERS, 50) >= 30)
+ {
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b "+d10",
+ (10 + get_level_s(SENSEMONSTERS, 40)),
+ (10 + get_level_s(SENSEMONSTERS, 20)));
+ }
+ else
+ {
+ sprintf(buf,
+ "rad " FMTs32b,
+ (10 + get_level_s(SENSEMONSTERS, 40)));
+ }
+
+ return buf;
+}
+
+casting_result earth_stone_skin()
+{
+ int type;
+
+ type = 0;
+ if (get_level_s(STONESKIN, 50) >= 25)
+ {
+ type = SHIELD_COUNTER;
+ }
+
+ return cast(set_shield(randint(10) + 10 + get_level_s(STONESKIN, 100),
+ 10 + get_level_s(STONESKIN, 50),
+ type,
+ 2 + get_level_s(STONESKIN, 5),
+ 3 + get_level_s(STONESKIN, 5)));
+}
+
+const char *earth_stone_skin_info()
+{
+ static char buf[128];
+
+ if (get_level_s(STONESKIN, 50) >= 25)
+ {
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d10 AC " FMTs32b,
+ (2 + get_level_s(STONESKIN, 5)),
+ (3 + get_level_s(STONESKIN, 5)),
+ (10 + get_level_s(STONESKIN, 100)),
+ (10 + get_level_s(STONESKIN, 50)));
+ }
+ else
+ {
+ sprintf(buf,
+ "dur " FMTs32b "+d10 AC " FMTs32b,
+ (10 + get_level_s(STONESKIN, 100)),
+ (10 + get_level_s(STONESKIN, 50)));
+ }
+
+ return buf;
+}
+
+casting_result earth_dig()
+{
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(wall_to_mud(dir));
+}
+
+casting_result earth_stone_prison()
+{
+ int x,y;
+
+ if (get_level_s(STONEPRISON, 50) >= 10)
+ {
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+ }
+ else
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+
+ wall_stone(y, x);
+ return CAST_OBVIOUS;
+}
+
+casting_result earth_strike()
+{
+ int dir, dmg;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ dmg = 50 + get_level_s(STRIKE, 50);
+ if (get_level_s(STRIKE, 50) >= 12)
+ {
+ return cast(fire_ball(GF_FORCE, dir, dmg, 1));
+ }
+ else
+ {
+ return cast(fire_ball(GF_FORCE, dir, dmg, 0));
+ }
+}
+
+const char *earth_strike_info()
+{
+ static char buf[128];
+ int dmg = 50 + get_level_s(STRIKE, 50);
+
+ if (get_level_s(STRIKE, 50) >= 12)
+ {
+ sprintf(buf, "dam %d rad 1", dmg);
+ }
+ else
+ {
+ sprintf(buf, "dam %d", dmg);
+ }
+
+ return buf;
+}
+
+casting_result earth_shake()
+{
+ int x,y;
+
+ if (get_level_s(SHAKE, 50) >= 10)
+ {
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+ }
+ else
+ {
+ x = p_ptr->px;
+ y = p_ptr->py;
+ }
+ earthquake(y, x, 4 + get_level_s(SHAKE, 10));
+ return CAST_OBVIOUS;
+}
+
+const char *earth_shake_info()
+{
+ static char buf[128];
+ sprintf(buf, "rad " FMTs32b, (4 + get_level_s(SHAKE, 10)));
+ return buf;
+}
+
+casting_result eru_see_the_music()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, set_tim_invis(randint(20) + 10 + get_level_s(ERU_SEE, 100)));
+
+ if (get_level_s(ERU_SEE, 50) >= 30)
+ {
+ wiz_lite_extra();
+ result = CAST_OBVIOUS;
+ }
+ else if (get_level_s(ERU_SEE, 50) >= 10)
+ {
+ map_area();
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(ERU_SEE, 50) >= 20)
+ {
+ result = cplus(result, set_blind(0));
+ }
+
+ return result;
+}
+
+const char *eru_see_the_music_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ (10 + get_level_s(ERU_SEE, 100)));
+ return buf;
+}
+
+casting_result eru_listen_to_the_music()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(ERU_LISTEN, 50) >= 30)
+ {
+ result = cplus(result, ident_all());
+ result = cplus(result, identify_pack());
+ }
+ else if (get_level_s(ERU_LISTEN, 50) >= 14)
+ {
+ result = cplus(result, identify_pack());
+ }
+ else
+ {
+ result = cplus(result, ident_spell());
+ }
+
+ return result;
+}
+
+casting_result eru_know_the_music()
+{
+ if (get_level_s(ERU_UNDERSTAND, 50) >= 10)
+ {
+ identify_pack_fully();
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return cast(identify_fully());
+ }
+}
+
+casting_result eru_lay_of_protection()
+{
+ return cast(fire_ball(GF_MAKE_GLYPH, 0, 1, 1 + get_level(ERU_PROT, 2, 0)));
+}
+
+const char *eru_lay_of_protection_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (1 + get_level(ERU_PROT, 2, 0)));
+ return buf;
+}
+
+casting_result fire_globe_of_light()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(GLOBELIGHT, 50) >= 3)
+ {
+ result = cplus(result, lite_area(10, 4));
+ }
+ else
+ {
+ lite_room(p_ptr->py, p_ptr->px);
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(GLOBELIGHT, 50) >= 15)
+ {
+ result = cplus(result,
+ fire_ball(GF_LITE,
+ 0,
+ 10 + get_level_s(GLOBELIGHT, 100),
+ 5 + get_level_s(GLOBELIGHT, 6)));
+ p_ptr->update |= PU_VIEW;
+ }
+
+ return result;
+}
+
+const char *fire_globe_of_light_info()
+{
+ static char buf[128];
+
+ if (get_level_s(GLOBELIGHT, 50) >= 15)
+ {
+ sprintf(buf, "dam " FMTs32b " rad " FMTs32b,
+ (10 + get_level_s(GLOBELIGHT, 100)),
+ (5 + get_level_s(GLOBELIGHT, 6)));
+ }
+ else
+ {
+ buf[0] = '\0';
+ }
+
+ return buf;
+}
+
+casting_result fire_fireflash()
+{
+ int dir;
+ int type = GF_FIRE;
+
+ if (get_level_s(FIREFLASH, 50) >= 20)
+ {
+ type = GF_HOLY_FIRE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(type, dir,
+ 20 + get_level_s(FIREFLASH, 500),
+ 2 + get_level_s(FIREFLASH, 5)));
+}
+
+const char *fire_fireflash_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad " FMTs32b,
+ (20 + get_level_s(FIREFLASH, 500)),
+ (2 + get_level_s(FIREFLASH, 5)));
+ return buf;
+}
+
+casting_result fire_fiery_shield()
+{
+ int type = SHIELD_FIRE;
+ if (get_level_s(FIERYAURA, 50) >= 8)
+ {
+ type = SHIELD_GREAT_FIRE;
+ }
+
+ return cast(set_shield(randint(20) + 10 + get_level_s(FIERYAURA, 70),
+ 10,
+ type,
+ 5 + get_level_s(FIERYAURA, 10),
+ 5 + get_level_s(FIERYAURA, 7)));
+}
+
+const char *fire_fiery_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d20",
+ (5 + get_level_s(FIERYAURA, 15)),
+ (5 + get_level_s(FIERYAURA, 7)),
+ (10 + get_level_s(FIERYAURA, 70)));
+ return buf;
+}
+
+casting_result fire_firewall()
+{
+ int dir;
+ int type = GF_FIRE;
+ if (get_level_s(FIREWALL, 50) >= 6)
+ {
+ type = GF_HELL_FIRE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ fire_wall(type, dir,
+ 40 + get_level_s(FIREWALL, 150),
+ 10 + get_level_s(FIREWALL, 14));
+ return CAST_OBVIOUS;
+}
+
+const char *fire_firewall_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " dur " FMTs32b,
+ (40 + get_level_s(FIREWALL, 150)),
+ (10 + get_level_s(FIREWALL, 14)));
+ return buf;
+}
+
+object_filter_t const &item_tester_hook_fire_golem()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ TVal(TV_LITE),
+ Or(
+ SVal(SV_LITE_TORCH),
+ SVal(SV_LITE_LANTERN)));
+ return instance;
+}
+
+casting_result fire_golem()
+{
+ /* Can we reconnect ? */
+ if (do_control_reconnect())
+ {
+ msg_print("Control re-established.");
+ return NO_CAST;
+ }
+
+ int item;
+ if (!get_item(&item,
+ "Which light source do you want to use to create the golem?",
+ "You have no light source for the golem",
+ USE_INVEN | USE_EQUIP,
+ item_tester_hook_fire_golem()))
+ {
+ return NO_CAST;
+ }
+
+ /* Destroy the source object */
+ inc_stack_size(item, -1);
+
+ /* Find a place for it */
+ int x, y;
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ /* Summon it */
+ int r_idx = get_fire_golem();
+ m_allow_special[r_idx] = TRUE;
+ int m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ m_allow_special[r_idx] = FALSE;
+
+ /* level it */
+ if (m_idx != 0)
+ {
+ monster_set_level(m_idx, 7 + get_level_s(FIREGOLEM, 70));
+ p_ptr->control = m_idx;
+ m_list[m_idx].mflag |= MFLAG_CONTROL;
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *fire_golem_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "golem level " FMTs32b,
+ (7 + get_level_s(FIREGOLEM, 70)));
+ return buf;
+}
+
+casting_result geomancy_call_the_elements()
+{
+ int dir = 0;
+
+ if (get_level_s(CALL_THE_ELEMENTS, 50) >= 17)
+ {
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ }
+
+ fire_ball(GF_ELEMENTAL_GROWTH,
+ dir,
+ 1,
+ 1 + get_level(CALL_THE_ELEMENTS, 5, 0));
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_call_the_elements_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (1 + get_level(CALL_THE_ELEMENTS, 5, 0)));
+ return buf;
+}
+
+casting_result geomancy_channel_elements()
+{
+ channel_the_elements(p_ptr->py, p_ptr->px, get_level_s(CHANNEL_ELEMENTS, 50));
+ return CAST_OBVIOUS;
+}
+
+typedef struct eff_type eff_type;
+struct eff_type {
+ s16b feat;
+ s16b low_effect;
+ s16b high_effect;
+ int damage;
+};
+
+static eff_type *geomancy_find_effect(eff_type effs[], int feat)
+{
+ int i;
+ for (i = 0; effs[i].feat >= 0; i++)
+ {
+ eff_type *p = &effs[i];
+ if (p->feat == feat)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static u32b dir_to_eff_flags(int dir)
+{
+ assert(dir >= 1);
+ assert(dir <= 9);
+
+ switch (dir)
+ {
+ case 1: return EFF_DIR1;
+ case 2: return EFF_DIR2;
+ case 3: return EFF_DIR3;
+ case 4: return EFF_DIR4;
+ case 5: return 0;
+ case 6: return EFF_DIR6;
+ case 7: return EFF_DIR7;
+ case 8: return EFF_DIR8;
+ case 9: return EFF_DIR9;
+ default:
+ assert(FALSE);
+ }
+ /* Default */
+ return 0;
+}
+
+casting_result geomancy_elemental_wave()
+{
+ int dir = 0, y = 0, x = 0;
+ eff_type *eff_ptr = NULL;
+ eff_type t[] =
+ {
+ /* Earth */
+ { FEAT_GRASS, GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 200) },
+ { FEAT_FLOWER, GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 300) },
+
+ /* Water */
+ { FEAT_SHAL_WATER, GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 200) },
+ { FEAT_DEEP_WATER, GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 300) },
+ { FEAT_ICE, GF_ICE, GF_ICE, 10 + get_skill_scale(SKILL_WATER, 200) },
+
+ /* Fire */
+ { FEAT_SAND, GF_LITE, GF_LITE, 10 + get_skill_scale(SKILL_FIRE, 400) },
+ { FEAT_SHAL_LAVA, GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 200) },
+ { FEAT_DEEP_LAVA, GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 300) },
+ { -1, -1, -1, -1 },
+ };
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir] + p_ptr->py;
+ x = ddx[dir] + p_ptr->px;
+
+ eff_ptr = geomancy_find_effect(t, cave[y][x].feat);
+
+ if (!eff_ptr)
+ {
+ msg_print("You cannot channel this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ s16b typ = eff_ptr->low_effect;
+ u32b dir_flag = dir_to_eff_flags(dir);
+
+ if (get_level_s(ELEMENTAL_WAVE, 50) >= 20)
+ {
+ typ = eff_ptr->high_effect;
+ }
+
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ fire_wave(typ,
+ 0,
+ eff_ptr->damage,
+ 0,
+ 6 + get_level_s(ELEMENTAL_WAVE, 20),
+ EFF_WAVE + EFF_LAST + dir_flag);
+
+ return CAST_OBVIOUS;
+ }
+}
+
+casting_result geomancy_vaporize()
+{
+ eff_type *eff_ptr = NULL;
+ eff_type t[] = {
+ /* Earth stuff */
+ { FEAT_GRASS, GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 100) },
+ { FEAT_FLOWER, GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 150) },
+ { FEAT_DARK_PIT, GF_DARK, GF_DARK, 5 + get_skill_scale(SKILL_EARTH, 200) },
+ /* Water stuff */
+ { FEAT_SHAL_WATER, GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 100) },
+ { FEAT_DEEP_WATER, GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 150) },
+ { FEAT_ICE, GF_ICE, GF_ICE, 5 + get_skill_scale(SKILL_WATER, 100) },
+ /* Fire stuff */
+ { FEAT_SAND, GF_LITE, GF_LITE, 5 + get_skill_scale(SKILL_FIRE, 200) },
+ { FEAT_SHAL_LAVA, GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 100) },
+ { FEAT_DEEP_LAVA, GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 150) },
+ { -1, -1, -1, -1 },
+ };
+
+ eff_ptr = geomancy_find_effect(t, cave[p_ptr->py][p_ptr->px].feat);
+
+ if (!eff_ptr)
+ {
+ msg_print("You cannot channel this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ s16b typ = eff_ptr->low_effect;
+ if (get_level_s(VAPORIZE, 50) >= 20)
+ {
+ typ = eff_ptr->high_effect;
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_FLOOR);
+
+ fire_cloud(typ,
+ 0,
+ eff_ptr->damage,
+ 1 + get_level_s(VAPORIZE, 4),
+ 10 + get_level_s(VAPORIZE, 20));
+
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *geomancy_vaporize_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b,
+ (1 + get_level_s(VAPORIZE, 4)),
+ (10 + get_level_s(VAPORIZE, 20)));
+ return buf;
+}
+
+bool_ geomancy_vaporize_depends()
+{
+ return get_skill(SKILL_AIR) >= 4;
+}
+
+casting_result geomancy_geolysis()
+{
+ int dir = 0;
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ msg_print("Elements recombine before you, laying down an open path.");
+ geomancy_dig(p_ptr->py, p_ptr->px, dir, 5 + get_level_s(GEOLYSIS, 12));
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_geolysis_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "length " FMTs32b,
+ (5 + get_level_s(GEOLYSIS, 12)));
+ return buf;
+}
+
+bool_ geomancy_geolysis_depends()
+{
+ return get_skill(SKILL_EARTH) >= 7;
+}
+
+casting_result geomancy_dripping_tread()
+{
+ if (p_ptr->dripping_tread == 0)
+ {
+ p_ptr->dripping_tread = randint(15) + 10 + get_level_s(DRIPPING_TREAD, 50);
+ msg_print("You start dripping raw elemental energies.");
+ }
+ else
+ {
+ p_ptr->dripping_tread = 0;
+ msg_print("You stop dripping raw elemental energies.");
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_dripping_tread_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d15 movs",
+ (10 + get_level_s(DRIPPING_TREAD, 50)));
+ return buf;
+}
+
+bool_ geomancy_dripping_tread_depends()
+{
+ return get_skill(SKILL_WATER) >= 10;
+}
+
+casting_result geomancy_grow_barrier()
+{
+ int dir = 0;
+
+ if (get_level_s(GROW_BARRIER, 50) >= 20)
+ {
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ }
+
+ fire_ball(GF_ELEMENTAL_WALL, dir, 1, 1);
+ return CAST_OBVIOUS;
+}
+
+bool_ geomancy_grow_barrier_depends()
+{
+ return get_skill(SKILL_EARTH) >= 12;
+}
+
+typedef struct geo_summon geo_summon;
+struct geo_summon {
+ s16b feat;
+ s16b skill_idx;
+ cptr *summon_names;
+};
+
+geo_summon *geomancy_find_summon(geo_summon summons[], int feat)
+{
+ int i;
+ for (i = 0; summons[i].feat >= 0; i++)
+ {
+ geo_summon *summon = &summons[i];
+ if (summon->feat == feat)
+ {
+ return summon;
+ }
+ }
+ return NULL;
+}
+
+int geomancy_count_elements(cptr *elements)
+{
+ int i;
+ for (i = 0; elements[i] != NULL; i++)
+ {
+ }
+ return i;
+}
+
+casting_result geomancy_elemental_minion()
+{
+ int dir = 0;
+ int x = 0, y = 0;
+ geo_summon *summon_ptr = NULL;
+ cptr earth_summons[] = {
+ "Earth elemental",
+ "Xorn",
+ "Xaren",
+ NULL
+ };
+ cptr air_summons[] = {
+ "Air elemental",
+ "Ancient blue dragon",
+ "Great Storm Wyrm",
+ "Sky Drake",
+ NULL
+ };
+ cptr fire_summons[] = {
+ "Fire elemental",
+ "Ancient red dragon",
+ NULL
+ };
+ cptr water_summons[] = {
+ "Water elemental",
+ "Water troll",
+ "Water demon",
+ NULL
+ };
+ geo_summon summons[] = {
+ { FEAT_WALL_EXTRA, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_OUTER, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_INNER, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_SOLID, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA_H, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ_H, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA_K, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ_K, SKILL_EARTH, earth_summons },
+
+ { FEAT_DARK_PIT, SKILL_AIR, air_summons },
+
+ { FEAT_SANDWALL, SKILL_FIRE, fire_summons },
+ { FEAT_SANDWALL_H, SKILL_FIRE, fire_summons },
+ { FEAT_SANDWALL_K, SKILL_FIRE, fire_summons },
+ { FEAT_SHAL_LAVA, SKILL_FIRE, fire_summons },
+ { FEAT_DEEP_LAVA, SKILL_FIRE, fire_summons },
+
+ { FEAT_ICE_WALL, SKILL_WATER, water_summons },
+ { FEAT_SHAL_WATER, SKILL_WATER, water_summons },
+ { FEAT_DEEP_WATER, SKILL_WATER, water_summons },
+
+ { -1, -1, NULL },
+ };
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir] + p_ptr->py;
+ x = ddx[dir] + p_ptr->px;
+
+ summon_ptr = geomancy_find_summon(summons, cave[y][x].feat);
+
+ if (!summon_ptr)
+ {
+ msg_print("You cannot summon from this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ cptr *names = summon_ptr->summon_names;
+ int max = get_skill_scale(summon_ptr->skill_idx,
+ geomancy_count_elements(names));
+ int r_idx = test_monster_name(names[rand_int(max)]);
+ int mx, my, m_idx;
+
+ /* Summon it */
+ find_position(y, x, &my, &mx);
+ m_idx = place_monster_one(my, mx, r_idx, 0, FALSE, MSTATUS_FRIEND);
+
+ /* Level it */
+ if (m_idx)
+ {
+ monster_set_level(m_idx, 10 + get_level_s(ELEMENTAL_MINION, 120));
+ }
+
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *geomancy_elemental_minion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "min level " FMTs32b,
+ (10 + get_level_s(ELEMENTAL_MINION, 120)));
+ return buf;
+}
+
+static void get_manathrust_dam(s16b *num, s16b *sides)
+{
+ *num = 3 + get_level_s(MANATHRUST, 50);
+ *sides = 1 + get_level_s(MANATHRUST, 20);
+}
+
+casting_result mana_manathrust()
+{
+ int dir;
+ s16b num = 0;
+ s16b sides = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_manathrust_dam(&num, &sides);
+ return cast(fire_bolt(GF_MANA, dir, damroll(num, sides)));
+}
+
+const char *mana_manathrust_info()
+{
+ s16b num = 0;
+ s16b sides = 0;
+ static char buf[128];
+
+ get_manathrust_dam(&num, &sides);
+ sprintf(buf,
+ "dam " FMTs16b "d" FMTs16b,
+ num,
+ sides);
+ return buf;
+}
+
+casting_result mana_remove_curses()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(DELCURSES, 50) >= 20)
+ {
+ result = cplus(result, remove_all_curse());
+ }
+ else
+ {
+ result = cplus(result, remove_curse());
+ }
+
+ if (result == CAST_OBVIOUS)
+ {
+ msg_print("The curse is broken!");
+ }
+
+ return result;
+}
+
+casting_result mana_elemental_shield()
+{
+ casting_result res = NO_CAST;
+
+ if (p_ptr->oppose_fire == 0)
+ {
+ res = cplus(res, set_oppose_fire(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_cold == 0)
+ {
+ res = cplus(res, set_oppose_cold(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_elec == 0)
+ {
+ res = cplus(res, set_oppose_elec(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_acid == 0)
+ {
+ res = cplus(res, set_oppose_acid(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ return res;
+}
+
+const char *mana_elemental_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ (15 + get_level_s(RESISTS, 50)));
+ return buf;
+}
+
+casting_result mana_disruption_shield()
+{
+ if (get_level_s(MANASHIELD, 50) >= 5)
+ {
+ if (p_ptr->invuln == 0)
+ {
+ return cast(set_invuln(randint(5) + 3 + get_level_s(MANASHIELD, 10)));
+ }
+ }
+ else if (p_ptr->disrupt_shield == 0)
+ {
+ return cast(set_disrupt_shield(randint(5) + 3 + get_level_s(MANASHIELD, 10)));
+ }
+
+ return NO_CAST;
+}
+
+const char *mana_disruption_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d5",
+ (3 + get_level_s(MANASHIELD, 10)));
+ return buf;
+}
+
+casting_result manwe_wind_shield()
+{
+ casting_result res = NO_CAST;
+ s32b dur = get_level_s(MANWE_SHIELD, 50) + 10 + randint(20);
+
+ res = cplus(res, set_protevil(dur));
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 10)
+ {
+ int type = 0;
+ if (get_level_s(MANWE_SHIELD, 50) >= 20)
+ {
+ type = SHIELD_COUNTER;
+ }
+
+ res = cplus(res,
+ set_shield(dur,
+ get_level_s(MANWE_SHIELD, 30),
+ type,
+ 1 + get_level_s(MANWE_SHIELD, 2),
+ 1 + get_level_s(MANWE_SHIELD, 6)));
+ }
+
+ return res;
+}
+
+const char *manwe_wind_shield_info()
+{
+ static char buf[128];
+
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ (get_level_s(MANWE_SHIELD, 50) + 10));
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 10)
+ {
+ char tmp[128];
+ sprintf(tmp, " AC " FMTs32b, get_level_s(MANWE_SHIELD, 30));
+ strcat(buf, tmp);
+ }
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 20)
+ {
+ char tmp[128];
+ sprintf(tmp, " dam " FMTs32b "d" FMTs32b,
+ (1 + get_level_s(MANWE_SHIELD, 2)),
+ (1 + get_level_s(MANWE_SHIELD, 6)));
+ strcat(buf, tmp);
+ }
+
+ return buf;
+}
+
+casting_result manwe_avatar()
+{
+ s16b mimic_idx = resolve_mimic_name("Maia");
+ assert(mimic_idx >= 0);
+
+ return cast(set_mimic(get_level_s(MANWE_AVATAR, 20) + randint(10),
+ mimic_idx,
+ p_ptr->lev));
+}
+
+const char *manwe_avatar_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ get_level_s(MANWE_AVATAR, 20));
+ return buf;
+}
+
+casting_result manwe_blessing()
+{
+ casting_result res = NO_CAST;
+ s32b dur = get_level_s(MANWE_BLESS, 70) + 30 + randint(40);
+
+ res = cplus(res, set_blessed(dur));
+ res = cplus(res, set_afraid(0));
+ res = cplus(res, set_lite(0));
+
+ if (get_level_s(MANWE_BLESS, 50) >= 10)
+ {
+ res = cplus(res, set_hero(dur));
+ }
+ if (get_level_s(MANWE_BLESS, 50) >= 20)
+ {
+ res = cplus(res, set_shero(dur));
+ }
+ if (get_level_s(MANWE_BLESS, 50) >= 30)
+ {
+ res = cplus(res, set_holy(dur));
+ }
+
+ return res;
+}
+
+const char *manwe_blessing_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d40",
+ get_level_s(MANWE_BLESS, 70) + 30);
+ return buf;
+}
+
+casting_result manwe_call()
+{
+ int y = 0, x = 0, m_idx = -1, r_idx = -1;
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ r_idx = test_monster_name("Great eagle");
+ assert(r_idx >= 1);
+
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+
+ if (m_idx > 0)
+ {
+ monster_set_level(m_idx, 20 + get_level(MANWE_CALL, 70, 0));
+ return CAST_OBVIOUS;
+ }
+
+ return NO_CAST;
+}
+
+const char *manwe_call_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ get_level_s(MANWE_CALL, 70) + 20);
+ return buf;
+}
+
+void do_melkor_curse(int m_idx)
+{
+ assert(m_idx >= 0);
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 35)
+ {
+ auto const r_ptr = m_ptr->race();
+
+ m_ptr->maxhp = m_ptr->maxhp - r_ptr->hside;
+ if (m_ptr->maxhp < 1)
+ {
+ m_ptr->maxhp = 1;
+ }
+ if (m_ptr->hp > m_ptr->maxhp)
+ {
+ m_ptr->hp = m_ptr->maxhp;
+ }
+
+ p_ptr->redraw |= PR_FRAME;
+ }
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 25)
+ {
+ m_ptr->speed = m_ptr->speed - get_level_s(MELKOR_CURSE, 7);
+ m_ptr->mspeed = m_ptr->mspeed - get_level_s(MELKOR_CURSE, 7);
+
+ if (m_ptr->speed < 70)
+ {
+ m_ptr->speed = 70;
+ }
+
+ if (m_ptr->mspeed < 70)
+ {
+ m_ptr->mspeed = 70;
+ }
+ }
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 15)
+ {
+ m_ptr->ac = m_ptr->ac - get_level_s(MELKOR_CURSE, 50);
+
+ if (m_ptr->ac < -70)
+ {
+ m_ptr->ac = -70;
+ }
+ }
+
+ /* Reduce melee too */
+ {
+ int i;
+ int pow = get_level_s(MELKOR_CURSE, 2);
+
+ for (i = 0; i < 4; i++)
+ {
+ if (m_ptr->blow[i].d_dice <= 0)
+ {
+ break;
+ }
+
+ if (m_ptr->blow[i].d_dice < pow)
+ {
+ pow = m_ptr->blow[i].d_dice;
+ }
+ if (m_ptr->blow[i].d_side < pow)
+ {
+ pow = m_ptr->blow[i].d_side;
+ }
+
+ m_ptr->blow[i].d_dice = m_ptr->blow[i].d_dice - pow;
+ }
+ }
+
+ /* Describe what happened */
+ {
+ char buf[128];
+
+ monster_desc(buf, m_ptr, 0);
+ buf[0] = toupper(buf[0]);
+
+ strcat(buf, " looks weaker.");
+ msg_print(buf);
+ }
+
+ /* wake it */
+ m_ptr->csleep = 0;
+}
+
+casting_result melkor_curse()
+{
+ int dir = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (target_who < 0)
+ {
+ msg_print("You must target a monster.");
+ return NO_CAST;
+ }
+ else
+ {
+ do_melkor_curse(target_who);
+ return CAST_OBVIOUS;
+ }
+}
+
+casting_result melkor_corpse_explosion()
+{
+ return cast(fire_ball(GF_CORPSE_EXPL,
+ 0,
+ 20 + get_level_s(MELKOR_CORPSE_EXPLOSION, 70),
+ 2 + get_level_s(MELKOR_CORPSE_EXPLOSION, 5)));
+}
+
+const char *melkor_corpse_explosion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "%%",
+ 20 + get_level_s(MELKOR_CORPSE_EXPLOSION, 70));
+ return buf;
+}
+
+casting_result melkor_mind_steal()
+{
+ int dir = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (target_who < 0)
+ {
+ msg_print("You must target a monster.");
+ return NO_CAST;
+ }
+ else
+ {
+ monster_type *m_ptr = &m_list[target_who];
+ int chance = get_level_s(MELKOR_MIND_STEAL, 50);
+
+ char buf[128];
+ monster_desc(buf, m_ptr, 0);
+ buf[0] = toupper(buf[0]);
+
+ auto const r_ptr = m_ptr->race();
+ if ((randint(m_ptr->level) < chance) &&
+ ((r_ptr->flags1 & RF1_UNIQUE) == 0))
+ {
+ p_ptr->control = target_who;
+ m_ptr->mflag |= MFLAG_CONTROL;
+ strcat(buf, " falls under your control.");
+ }
+ else
+ {
+ strcat(buf, " resists.");
+ }
+
+ msg_print(buf);
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *melkor_mind_steal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "chance 1d(mlvl)<" FMTs32b,
+ get_level_s(MELKOR_MIND_STEAL, 50));
+ return buf;
+}
+
+casting_result meta_recharge()
+{
+ return cast(recharge(60 + get_level_s(RECHARGE, 140)));
+}
+
+const char *meta_recharge_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ 60 + get_level_s(RECHARGE, 140));
+ return buf;
+}
+
+static int get_spellbinder_max()
+{
+ int i = get_level_s(SPELLBINDER, 4);
+ if (i > 4)
+ {
+ i = 4;
+ }
+ return i;
+}
+
+casting_result meta_spellbinder()
+{
+ if (p_ptr->spellbinder_num != 0)
+ {
+ struct trigger {
+ int idx;
+ cptr desc;
+ };
+ struct trigger triggers[] = {
+ { SPELLBINDER_HP75, "75% HP", },
+ { SPELLBINDER_HP50, "50% HP", },
+ { SPELLBINDER_HP25, "25% HP", },
+ { -1, NULL, },
+ };
+ int trigger_idx = -1;
+ int i;
+
+ assert(p_ptr->spellbinder_trigger >= 0);
+
+ for (trigger_idx = 0; triggers[trigger_idx].idx >= 0; trigger_idx++)
+ {
+ if (triggers[trigger_idx].idx == p_ptr->spellbinder_trigger)
+ {
+ break;
+ }
+ }
+
+ msg_print("The spellbinder is already active.");
+ msg_format("It will trigger at %s.", triggers[trigger_idx].desc);
+ msg_print("With the spells: ");
+ for (i = 0; i < p_ptr->spellbinder_num; i++)
+ {
+ msg_print(spell_type_name(spell_at(p_ptr->spellbinder[i])));
+ }
+
+ /* Doesn't cost anything */
+ return NO_CAST;
+ }
+ else
+ {
+ char c;
+ int i;
+
+ if (!get_com("Trigger at [a]75% hp [b]50% hp [c]25% hp?", &c))
+ {
+ return NO_CAST;
+ }
+
+ switch (c)
+ {
+ case 'a':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP75;
+ break;
+ case 'b':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP50;
+ break;
+ case 'c':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP25;
+ break;
+ default:
+ return NO_CAST;
+
+ }
+
+ p_ptr->spellbinder_num = get_spellbinder_max();
+ i = p_ptr->spellbinder_num;
+ while (i > 0)
+ {
+ s32b s = get_school_spell("bind", 0);
+ if (s == -1)
+ {
+ p_ptr->spellbinder_trigger = 0;
+ p_ptr->spellbinder_num = 0;
+ return CAST_OBVIOUS;
+ } else {
+ if (spell_type_skill_level(spell_at(s)) > 7 + get_level_s(SPELLBINDER, 35))
+ {
+ msg_format("You are only allowed spells with a base level of " FMTs32b ".", (7 + get_level_s(SPELLBINDER, 35)));
+ return CAST_OBVIOUS;
+ }
+ }
+
+ p_ptr->spellbinder[i] = s;
+ i = i - 1;
+ }
+
+ p_ptr->energy = p_ptr->energy - 3100;
+ msg_print("Spellbinder ready.");
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *meta_spellbinder_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "number %d max level " FMTs32b,
+ get_spellbinder_max(),
+ (7 + get_level_s(SPELLBINDER, 35)));
+ return buf;
+}
+
+casting_result meta_disperse_magic()
+{
+ casting_result res = NO_CAST;
+
+ res = cplus(res, set_blind(0));
+ res = cplus(res, set_lite(0));
+ if (get_level_s(DISPERSEMAGIC, 50) >= 5)
+ {
+ res = cplus(res, set_confused(0));
+ res = cplus(res, set_image(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 10)
+ {
+ res = cplus(res, set_slow(0));
+ res = cplus(res, set_fast(0, 0));
+ res = cplus(res, set_light_speed(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 15)
+ {
+ res = cplus(res, set_stun(0));
+ res = cplus(res, set_cut(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 20)
+ {
+ res = cplus(res, set_hero(0));
+ res = cplus(res, set_shero(0));
+ res = cplus(res, set_blessed(0));
+ res = cplus(res, set_shield(0, 0, 0, 0, 0));
+ res = cplus(res, set_afraid(0));
+ res = cplus(res, set_parasite(0, 0));
+ res = cplus(res, set_mimic(0, 0, 0));
+ }
+ return res;
+}
+
+casting_result meta_tracker()
+{
+ if ((last_teleportation_y < 0) ||
+ (last_teleportation_x < 0))
+ {
+ msg_print("There has not been any teleporatation here.");
+ }
+ else
+ {
+ teleport_player_to(last_teleportation_y, last_teleportation_x);
+ }
+ return CAST_OBVIOUS;
+}
+
+static void stop_inertia_controlled_spell()
+{
+ assert(TIMER_INERTIA_CONTROL != NULL);
+
+ p_ptr->inertia_controlled_spell = -1;
+ TIMER_INERTIA_CONTROL->enabled = FALSE;
+ p_ptr->update = p_ptr->update | PU_MANA;
+}
+
+void meta_inertia_control_hook_birth_objects()
+{
+ stop_inertia_controlled_spell();
+}
+
+casting_result meta_inertia_control()
+{
+ s32b s, difficulty, delay;
+ spell_type *spell;
+
+ if (p_ptr->inertia_controlled_spell != -1)
+ {
+ msg_print("You cancel your inertia flow control.");
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ s = get_school_spell("control", 0);
+ if (s == -1)
+ {
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ spell = spell_at(s);
+
+ if (!spell_type_inertia(spell, &difficulty, &delay))
+ {
+ msg_print("This spell inertia flow can not be controlled.");
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ if (difficulty > get_level_s(INERTIA_CONTROL, 10))
+ {
+ msg_format("This spell inertia flow(" FMTs32b ") is too strong to be controlled by your current spell.", difficulty);
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ p_ptr->inertia_controlled_spell = s;
+ TIMER_INERTIA_CONTROL->enabled = TRUE;
+ TIMER_INERTIA_CONTROL->delay = delay;
+ TIMER_INERTIA_CONTROL->countdown = delay;
+ p_ptr->update |= PU_MANA;
+ msg_format("Inertia flow controlling spell %s.", spell_type_name(spell_at(s)));
+ return CAST_OBVIOUS;
+}
+
+const char *meta_inertia_control_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ get_level_s(INERTIA_CONTROL, 10));
+ return buf;
+}
+
+void meta_inertia_control_timer_callback()
+{
+ /* Don't cast a controlled spell in wilderness mode */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ }
+ else if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ }
+ else if ((p_ptr->inertia_controlled_spell != -1) &&
+ (!p_ptr->wild_mode))
+ {
+ lua_cast_school_spell(p_ptr->inertia_controlled_spell, TRUE);
+ }
+}
+
+void meta_inertia_control_calc_mana(int *msp)
+{
+ if (p_ptr->inertia_controlled_spell != -1)
+ {
+ *msp = *msp - (get_mana(p_ptr->inertia_controlled_spell) * 4);
+ if (*msp < 0)
+ {
+ *msp = 0;
+ }
+ }
+}
+
+static int mind_charm_power()
+{
+ return 10 + get_level_s(CHARM, 150);
+}
+
+casting_result mind_charm()
+{
+ int pwr = mind_charm_power();
+ int level = get_level_s(CHARM, 50);
+
+ if (level >= 35)
+ {
+ return cast(project_hack(GF_CHARM, pwr));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (level >= 15)
+ {
+ return cast(fire_ball(GF_CHARM, dir, pwr, 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_CHARM, dir, pwr));
+ }
+ }
+}
+
+const char *mind_charm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_charm_power());
+ return buf;
+}
+
+static int mind_confuse_power()
+{
+ return 10 + get_level_s(CONFUSE, 150);
+}
+
+casting_result mind_confuse()
+{
+ int pwr = mind_confuse_power();
+ int level = get_level_s(CONFUSE, 50);
+
+ if (level >= 35)
+ {
+ return cast(project_hack(GF_OLD_CONF, pwr));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (level >= 15)
+ {
+ return cast(fire_ball(GF_OLD_CONF, dir, pwr, 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_OLD_CONF, dir, pwr));
+ }
+ }
+}
+
+const char *mind_confuse_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_confuse_power());
+ return buf;
+}
+
+static int mind_armor_of_fear_base_duration()
+{
+ return 10 + get_level_s(ARMOROFFEAR, 100);
+}
+
+static int mind_armor_of_fear_power_sides()
+{
+ return 1 + get_level_s(ARMOROFFEAR, 7);
+}
+
+static int mind_armor_of_fear_power_dice()
+{
+ return 5 + get_level_s(ARMOROFFEAR, 20);
+}
+
+casting_result mind_armor_of_fear()
+{
+ return cast(set_shield(randint(10) + mind_armor_of_fear_base_duration(),
+ 10,
+ SHIELD_FEAR,
+ mind_armor_of_fear_power_sides(),
+ mind_armor_of_fear_power_dice()));
+}
+
+const char *mind_armor_of_fear_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d10 power %dd%d",
+ mind_armor_of_fear_base_duration(),
+ mind_armor_of_fear_power_sides(),
+ mind_armor_of_fear_power_dice());
+ return buf;
+}
+
+static int mind_stun_power()
+{
+ return 10 + get_level_s(STUN, 150);
+}
+
+casting_result mind_stun()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (get_level_s(STUN, 50) >= 20)
+ {
+ return cast(fire_ball(GF_STUN, dir, mind_stun_power(), 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_STUN, dir, mind_stun_power()));
+ }
+}
+
+const char *mind_stun_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_stun_power());
+ return buf;
+}
+
+casting_result tempo_magelock()
+{
+ if (get_level_s(MAGELOCK, 50) >= 30)
+ {
+ int x,y;
+
+ if (get_level_s(MAGELOCK, 50) >= 40)
+ {
+ cave_type *c_ptr = NULL;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+
+ if ((!(f_info[c_ptr->feat].flags1 | FF1_FLOOR)) ||
+ (f_info[c_ptr->feat].flags1 | FF1_PERMANENT) ||
+ (!los(p_ptr->py, p_ptr->px, y, x)))
+ {
+ msg_print("You cannot place it there.");
+ return NO_CAST;
+ }
+ } else {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+ cave_set_feat(y, x, 3);
+ return CAST_OBVIOUS;
+ } else {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ return cast(wizard_lock(dir));
+ }
+}
+
+static s32b tempo_slow_monster_power()
+{
+ return 40 + get_level_s(SLOWMONSTER, 160);
+}
+
+casting_result tempo_slow_monster()
+{
+ int dir;
+ s32b pwr;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ pwr = tempo_slow_monster_power();
+ if (get_level_s(SLOWMONSTER, 50) >= 20)
+ {
+ return cast(fire_ball(GF_OLD_SLOW, dir, pwr, 1));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_OLD_SLOW, dir, pwr));
+ }
+}
+
+const char *tempo_slow_monster_info()
+{
+ static char buf[128];
+ s32b pwr = tempo_slow_monster_power();
+
+ if (get_level_s(SLOWMONSTER, 50) >= 20)
+ {
+ sprintf(buf, "power " FMTs32b " rad 1", pwr);
+ }
+ else
+ {
+ sprintf(buf, "power " FMTs32b, pwr);
+ }
+ return buf;
+}
+
+static s32b tempo_essence_of_speed_base_duration()
+{
+ return 10 + get_level_s(ESSENCESPEED, 50);
+}
+
+static s32b tempo_essence_of_speed_bonus()
+{
+ return 5 + get_level_s(ESSENCESPEED, 20);
+}
+
+casting_result tempo_essence_of_speed()
+{
+ if (p_ptr->fast == 0)
+ {
+ return cast(set_fast(randint(10) + tempo_essence_of_speed_base_duration(),
+ tempo_essence_of_speed_bonus()));
+ }
+ return NO_CAST;
+}
+
+const char *tempo_essence_of_speed_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10 speed " FMTs32b,
+ tempo_essence_of_speed_base_duration(),
+ tempo_essence_of_speed_bonus());
+ return buf;
+}
+
+static s32b tempo_banishment_power()
+{
+ return 40 + get_level_s(BANISHMENT, 160);
+}
+
+casting_result tempo_banishment()
+{
+ casting_result result = NO_CAST;
+ s32b pwr = tempo_banishment_power();
+
+ result = cplus(result, project_hack(GF_AWAY_ALL, pwr));
+
+ if (get_level_s(BANISHMENT, 50) >= 15)
+ {
+ result = cplus(result,
+ project_hack(GF_STASIS, 20 + get_level_s(BANISHMENT, 120)));
+ }
+
+ return result;
+}
+
+const char *tempo_banishment_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ tempo_banishment_power());
+ return buf;
+}
+
+casting_result tulkas_divine_aim()
+{
+ casting_result result = NO_CAST;
+ s32b dur = get_level_s(TULKAS_AIM, 50) + randint(10);
+
+ result = cplus(result, set_strike(dur));
+ if (get_level_s(TULKAS_AIM, 50) >= 20)
+ {
+ result = cplus(result, set_tim_deadly(dur));
+ }
+
+ return result;
+}
+
+const char *tulkas_divine_aim_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ get_level_s(TULKAS_AIM, 50));
+ return buf;
+}
+
+casting_result tulkas_wave_of_power()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_bolt(GF_ATTACK, dir, get_level_s(TULKAS_WAVE, p_ptr->num_blow)));
+}
+
+const char *tulkas_wave_of_power_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "blows " FMTs32b,
+ get_level_s(TULKAS_WAVE, p_ptr->num_blow));
+ return buf;
+}
+
+casting_result tulkas_whirlwind()
+{
+ return cast(fire_ball(GF_ATTACK, 0, 1, 1));
+}
+
+/* Return the number of Udun/Melkor spells in a given book */
+int udun_in_book(s32b sval, s32b pval)
+{
+ int count = 0;
+
+ random_book_setup(sval, pval);
+
+ /* Get the school book */
+ school_book *school_book = school_books_at(sval);
+
+ /* Go through spells */
+ for (auto spell_idx : school_book->spell_idxs) {
+ spell_type *spell = spell_at(spell_idx);
+ for (auto school_idx : spell_type_get_schools(spell))
+ {
+ if ((school_idx == SCHOOL_UDUN) ||
+ (school_idx == SCHOOL_MELKOR))
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+int levels_in_book(s32b sval, s32b pval)
+{
+ int levels = 0;
+
+ random_book_setup(sval, pval);
+
+ /* Get the school book */
+ school_book *school_book = school_books_at(sval);
+
+ /* Parse all spells */
+ for (auto spell_idx : school_book->spell_idxs)
+ {
+ spell_type *spell = spell_at(spell_idx);
+ levels += spell_type_skill_level(spell);
+ }
+
+ return levels;
+}
+
+static object_filter_t const &udun_object_is_drainable()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ TVal(TV_WAND),
+ TVal(TV_ROD_MAIN),
+ TVal(TV_STAFF));
+ return instance;
+}
+
+casting_result udun_drain()
+{
+ /* Ask for an item */
+ int item;
+ if (!get_item(&item,
+ "What item to drain?",
+ "You have nothing you can drain",
+ USE_INVEN,
+ udun_object_is_drainable()))
+ {
+ return NO_CAST;
+ }
+
+ /* Drain */
+ object_type *o_ptr = get_object(item);
+
+ switch (o_ptr->tval)
+ {
+ case TV_STAFF:
+ case TV_WAND:
+ {
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Generate mana */
+ increase_mana(o_ptr->pval * k_ptr->level * o_ptr->number);
+
+ /* Destroy it */
+ inc_stack_size(item, -99);
+
+ break;
+ }
+
+ case TV_ROD_MAIN:
+ {
+ /* Generate mana */
+ increase_mana(o_ptr->timeout);
+
+ /* Drain it */
+ o_ptr->timeout = 0;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= PN_COMBINE | PN_REORDER;
+ p_ptr->window |= PW_INVEN | PW_EQUIP | PW_PLAYER;
+ break;
+ }
+
+ default:
+ assert(FALSE);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+casting_result udun_genocide()
+{
+ if (get_level_s(GENOCIDE, 50) < 10)
+ {
+ genocide(TRUE);
+ }
+ else
+ {
+ if (get_check("Genocide all monsters near you? "))
+ {
+ mass_genocide(TRUE);
+ }
+ else
+ {
+ genocide(TRUE);
+ }
+ }
+
+ return CAST_OBVIOUS;
+}
+
+static int udun_wraithform_base_duration()
+{
+ return 20 + get_level_s(WRAITHFORM, 40);
+}
+
+casting_result udun_wraithform()
+{
+ return cast(set_shadow(randint(30) + udun_wraithform_base_duration()));
+}
+
+const char *udun_wraithform_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d30",
+ udun_wraithform_base_duration());
+ return buf;
+}
+
+static int udun_flame_of_udun_base_duration()
+{
+ return 5 + get_level_s(FLAMEOFUDUN, 30);
+}
+
+casting_result udun_flame_of_udun()
+{
+ return cast(set_mimic(randint(15) + udun_flame_of_udun_base_duration(),
+ resolve_mimic_name("Balrog"),
+ get_level_s(FLAMEOFUDUN, 50)));
+}
+
+const char *udun_flame_of_udun_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d15",
+ udun_flame_of_udun_base_duration());
+ return buf;
+}
+
+static int tidal_wave_damage()
+{
+ return 40 + get_level_s(TIDALWAVE, 200);
+}
+
+static int tidal_wave_duration()
+{
+ return 6 + get_level_s(TIDALWAVE, 10);
+}
+
+casting_result water_tidal_wave()
+{
+ fire_wave(GF_WAVE,
+ 0,
+ tidal_wave_damage(),
+ 0,
+ tidal_wave_duration(),
+ EFF_WAVE);
+ return CAST_OBVIOUS;
+}
+
+const char *water_tidal_wave_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d dur %d",
+ tidal_wave_damage(),
+ tidal_wave_duration());
+ return buf;
+}
+
+static int water_ice_storm_damage()
+{
+ return 80 + get_level_s(ICESTORM, 200);
+}
+
+static int water_ice_storm_radius()
+{
+ return 1 + get_level(ICESTORM, 3, 0);
+}
+
+static int water_ice_storm_duration()
+{
+ return 20 + get_level_s(ICESTORM, 70);
+}
+
+casting_result water_ice_storm()
+{
+ int type = GF_COLD;
+
+ if (get_level_s(ICESTORM, 50) >= 10)
+ {
+ type = GF_ICE;
+ }
+
+ fire_wave(type,
+ 0,
+ water_ice_storm_damage(),
+ water_ice_storm_radius(),
+ water_ice_storm_duration(),
+ EFF_STORM);
+
+ return CAST_OBVIOUS;
+}
+
+const char *water_ice_storm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d rad %d dur %d",
+ water_ice_storm_damage(),
+ water_ice_storm_radius(),
+ water_ice_storm_duration());
+ return buf;
+}
+
+static int water_ent_potion_base_duration()
+{
+ return 25 + get_level_s(ENTPOTION, 40);;
+}
+
+casting_result water_ent_potion()
+{
+ set_food(PY_FOOD_MAX - 1);
+ msg_print("The Ent's Potion fills your stomach.");
+
+ if (get_level_s(ENTPOTION, 50) >= 5)
+ {
+ set_afraid(0);
+ }
+ if (get_level_s(ENTPOTION, 50) >= 12)
+ {
+ set_hero(p_ptr->hero + randint(25) + water_ent_potion_base_duration());
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *water_ent_potion_info()
+{
+ if (get_level_s(ENTPOTION, 50) >= 12)
+ {
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d25",
+ water_ent_potion_base_duration());
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+static int water_vapor_damage()
+{
+ return 3 + get_level_s(VAPOR, 20);
+}
+
+static int water_vapor_radius()
+{
+ return 3 + get_level(VAPOR, 9, 0);
+}
+
+static int water_vapor_duration()
+{
+ return 5;
+}
+
+casting_result water_vapor()
+{
+ fire_cloud(GF_WATER,
+ 0,
+ water_vapor_damage(),
+ water_vapor_radius(),
+ water_vapor_duration());
+ return CAST_OBVIOUS;
+}
+
+const char *water_vapor_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d rad %d dur %d",
+ water_vapor_damage(),
+ water_vapor_radius(),
+ water_vapor_duration());
+ return buf;
+}
+
+static void get_geyser_damage(int *dice, int *sides)
+{
+ assert(dice != NULL);
+ assert(sides != NULL);
+
+ *dice = get_level_s(GEYSER, 10);
+ *sides = 3 + get_level_s(GEYSER, 35);
+}
+
+casting_result water_geyser()
+{
+ int dir, dice, sides;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_geyser_damage(&dice, &sides);
+ return cast(fire_bolt_or_beam(2 * get_level_s(GEYSER, 85),
+ GF_WATER,
+ dir,
+ damroll(dice, sides)));
+}
+
+const char *water_geyser_info()
+{
+ static char buf[128];
+ int dice, sides;
+
+ get_geyser_damage(&dice, &sides);
+
+ sprintf(buf,
+ "dam %dd%d",
+ dice,
+ sides);
+ return buf;
+}
+
+static int charm_animal_power()
+{
+ return 10 + get_level_s(YAVANNA_CHARM_ANIMAL, 170);
+}
+
+static int charm_animal_radius()
+{
+ return get_level_s(YAVANNA_CHARM_ANIMAL, 2);
+}
+
+casting_result yavanna_charm_animal()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_CONTROL_ANIMAL,
+ dir,
+ charm_animal_power(),
+ charm_animal_radius()));
+}
+
+const char *yavanna_charm_animal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d rad %d",
+ charm_animal_power(),
+ charm_animal_radius());
+ return buf;
+}
+
+static int yavanna_grow_grass_radius()
+{
+ return get_level_s(YAVANNA_GROW_GRASS, 4);
+}
+
+casting_result yavanna_grow_grass()
+{
+ grow_grass(yavanna_grow_grass_radius());
+ return CAST_OBVIOUS;
+}
+
+const char *yavanna_grow_grass_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad %d",
+ yavanna_grow_grass_radius());
+ return buf;
+}
+
+static int tree_roots_duration()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 30);
+}
+
+static int tree_roots_ac()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 60);
+}
+
+static int tree_roots_damage()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 20);
+}
+
+casting_result yavanna_tree_roots()
+{
+ return cast(set_roots(tree_roots_duration(),
+ tree_roots_ac(),
+ tree_roots_damage()));
+}
+
+const char *yavanna_tree_roots_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d AC %d dam %d",
+ tree_roots_duration(),
+ tree_roots_ac(),
+ tree_roots_damage());
+ return buf;
+}
+
+static int water_bite_base_duration()
+{
+ return 30 + get_level_s(YAVANNA_WATER_BITE, 150);
+}
+
+static int water_bite_damage()
+{
+ return 10 + get_level_s(YAVANNA_WATER_BITE, 50);
+}
+
+casting_result yavanna_water_bite()
+{
+ int rad = 0;
+
+ if (get_level_s(YAVANNA_WATER_BITE, 50) >= 25)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(randint(30) + water_bite_base_duration(),
+ GF_WATER,
+ water_bite_damage(),
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *yavanna_water_bite_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d30 dam %d/blow",
+ water_bite_base_duration(),
+ water_bite_damage());
+ return buf;
+}
+
+static int uproot_mlevel()
+{
+ return 30 + get_level_s(YAVANNA_UPROOT, 70);
+}
+
+casting_result yavanna_uproot()
+{
+ int dir, x, y;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir];
+ x = ddx[dir];
+
+ y += p_ptr->py;
+ x += p_ptr->px;
+
+ c_ptr = &cave[y][x];
+
+ if (c_ptr->feat == FEAT_TREES)
+ {
+ s16b m_idx;
+
+ cave_set_feat(y, x, FEAT_GRASS);
+
+ /* Summon it */
+ find_position(y, x, &y, &x);
+ m_idx = place_monster_one(y, x, test_monster_name("Ent"), 0, FALSE, MSTATUS_FRIEND);
+
+ /* level it */
+ if (m_idx != 0)
+ {
+ monster_set_level(m_idx, uproot_mlevel());
+ }
+
+ msg_print("The tree awakes!");
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ msg_print("There is no tree there.");
+ return NO_CAST;
+ }
+}
+
+const char *yavanna_uproot_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "lev %d",
+ uproot_mlevel());
+ return buf;
+}
+
+static int nature_grow_trees_radius()
+{
+ return 2 + get_level_s(GROWTREE, 7);
+}
+
+casting_result nature_grow_trees()
+{
+ grow_trees(nature_grow_trees_radius());
+ return CAST_OBVIOUS;
+}
+
+const char *nature_grow_trees_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad %d",
+ nature_grow_trees_radius());
+ return buf;
+}
+
+static int nature_healing_percentage()
+{
+ return 15 + get_level_s(HEALING, 35);
+}
+
+static int nature_healing_hp()
+{
+ return p_ptr->mhp * nature_healing_percentage() / 100;
+}
+
+casting_result nature_healing()
+{
+ return cast(hp_player(nature_healing_hp()));
+}
+
+const char *nature_healing_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d%% = %dhp",
+ nature_healing_percentage(),
+ nature_healing_hp());
+ return buf;
+}
+
+casting_result nature_recovery()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, set_poisoned(p_ptr->poisoned / 2));
+ if (get_level_s(RECOVERY, 50) >= 5)
+ {
+ result = cplus(result, set_poisoned(0));
+ result = cplus(result, set_cut(0));
+ }
+ if (get_level_s(RECOVERY, 50) >= 10)
+ {
+ result = cplus(result, do_res_stat(A_STR, TRUE));
+ result = cplus(result, do_res_stat(A_CON, TRUE));
+ result = cplus(result, do_res_stat(A_DEX, TRUE));
+ result = cplus(result, do_res_stat(A_WIS, TRUE));
+ result = cplus(result, do_res_stat(A_INT, TRUE));
+ result = cplus(result, do_res_stat(A_CHR, TRUE));
+ }
+ if (get_level_s(RECOVERY, 50) >= 15)
+ {
+ result = cplus(result, restore_level());
+ }
+
+ return result;
+}
+
+static int regeneration_base_duration()
+{
+ return 5 + get_level_s(REGENERATION, 50);
+}
+
+static int regeneration_power()
+{
+ return 300 + get_level_s(REGENERATION, 700);
+}
+
+casting_result nature_regeneration()
+{
+ if (p_ptr->tim_regen == 0)
+ {
+ return cast(set_tim_regen(randint(10) + regeneration_base_duration(),
+ regeneration_power()));
+ }
+ return NO_CAST;
+}
+
+const char *nature_regeneration_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d10 power %d",
+ regeneration_base_duration(),
+ regeneration_power());
+ return buf;
+}
+
+static int summon_animal_level()
+{
+ return 25 + get_level_s(SUMMONANNIMAL, 50);
+}
+
+casting_result nature_summon_animal()
+{
+ summon_specific_level = summon_animal_level();
+ return cast(summon_specific_friendly(p_ptr->py,
+ p_ptr->px,
+ dun_level,
+ SUMMON_ANIMAL,
+ TRUE));
+}
+
+const char *nature_summon_animal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ summon_animal_level());
+ return buf;
+}
+
+casting_result nature_grow_athelas()
+{
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ return CAST_OBVIOUS;
+ }
+
+ return CAST_HIDDEN;
+}
+
+static int device_heal_monster_hp()
+{
+ return 20 + get_level_s(DEVICE_HEAL_MONSTER, 380);
+}
+
+casting_result device_heal_monster()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_OLD_HEAL, dir, device_heal_monster_hp(), 0));
+}
+
+const char *device_heal_monster_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d",
+ device_heal_monster_hp());
+ return buf;
+}
+
+casting_result device_haste_monster()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_OLD_SPEED, dir, 1, 0));
+}
+
+const char *device_haste_monster_info()
+{
+ return "speed +10";
+}
+
+casting_result device_wish()
+{
+ make_wish();
+ return CAST_OBVIOUS;
+}
+
+casting_result device_summon_monster()
+{
+ casting_result result = NO_CAST;
+ int i;
+
+ for (i = 0; i < 4 + get_level_s(DEVICE_SUMMON, 30); i++)
+ {
+ result = cplus(result, summon_specific(p_ptr->py, p_ptr->px, dun_level, 0));
+ }
+
+ return result;
+}
+
+static int device_mana_pct()
+{
+ return 20 + get_level_s(DEVICE_MANA, 50);
+}
+
+casting_result device_mana()
+{
+ increase_mana((p_ptr->msp * device_mana_pct()) / 100);
+ return CAST_OBVIOUS;
+}
+
+const char *device_mana_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "restore %d%%",
+ device_mana_pct());
+ return buf;
+}
+
+casting_result device_nothing()
+{
+ return CAST_HIDDEN;
+}
+
+static int holy_fire_damage()
+{
+ return 50 + get_level_s(DEVICE_HOLY_FIRE, 300);
+}
+
+casting_result device_holy_fire()
+{
+ return cast(project_hack(GF_HOLY_FIRE, holy_fire_damage()));
+}
+
+const char *device_holy_fire_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d",
+ holy_fire_damage());
+ return buf;
+}
+
+casting_result device_thunderlords()
+{
+ switch (game_module_idx)
+ {
+ case MODULE_TOME:
+ {
+ if (dun_level > 0)
+ {
+ msg_print("As you blow the horn a thunderlord pops out of nowhere and grabs you.");
+ recall_player(0, 1);
+ }
+ else
+ {
+ msg_print("You cannot use it there.");
+ }
+ return CAST_OBVIOUS;
+ }
+
+ case MODULE_THEME:
+ {
+ if (dun_level > 0)
+ {
+ msg_print("As you blow the horn, an Eagle of Manwe appears overhead.");
+ recall_player(0, 1);
+ }
+ else
+ {
+ msg_print("You cannot use it there.");
+ }
+ return CAST_OBVIOUS;
+ }
+
+ default:
+ assert(FALSE);
+ return NO_CAST;
+ }
+}
+
+void static start_lasting_spell(int spl)
+{
+ p_ptr->music_extra = -spl;
+}
+
+casting_result music_stop_singing_spell()
+{
+ start_lasting_spell(0);
+ return CAST_OBVIOUS;
+}
+
+static int holding_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_HOLD, 100);
+}
+
+int music_holding_pattern_lasting()
+{
+ project_hack(GF_OLD_SLOW, holding_pattern_power());
+ return get_mana(MUSIC_HOLD);
+}
+
+casting_result music_holding_pattern_spell()
+{
+ start_lasting_spell(MUSIC_HOLD);
+ return CAST_OBVIOUS;
+}
+
+const char *music_holding_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ holding_pattern_power());
+ return buf;
+}
+
+static int illusion_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_CONF, 100);
+}
+
+int music_illusion_pattern_lasting()
+{
+ project_hack(GF_OLD_CONF, illusion_pattern_power());
+ return get_mana(MUSIC_CONF);
+}
+
+casting_result music_illusion_pattern_spell()
+{
+ start_lasting_spell(MUSIC_CONF);
+ return CAST_OBVIOUS;
+}
+
+const char *music_illusion_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ illusion_pattern_power());
+ return buf;
+}
+
+static int stun_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_STUN, 90);
+}
+
+int music_stun_pattern_lasting()
+{
+ project_hack(GF_STUN, stun_pattern_power());
+ return get_mana(MUSIC_STUN);
+}
+
+casting_result music_stun_pattern_spell()
+{
+ start_lasting_spell(MUSIC_STUN);
+ return CAST_OBVIOUS;
+}
+
+const char *music_stun_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ stun_pattern_power());
+ return buf;
+}
+
+int music_song_of_the_sun_lasting()
+{
+ set_lite(5);
+ return 1;
+}
+
+casting_result music_song_of_the_sun_spell()
+{
+ start_lasting_spell(MUSIC_LITE);
+ return CAST_OBVIOUS;
+}
+
+int flow_of_life_hp()
+{
+ return 7 + get_level_s(MUSIC_HEAL, 100);
+}
+
+int music_flow_of_life_lasting()
+{
+ hp_player(flow_of_life_hp());
+ return get_mana(MUSIC_HEAL);
+}
+
+casting_result music_flow_of_life_spell()
+{
+ start_lasting_spell(MUSIC_HEAL);
+ return CAST_OBVIOUS;
+}
+
+const char *music_flow_of_life_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d/turn",
+ flow_of_life_hp());
+ return buf;
+}
+
+int music_heroic_ballad_lasting()
+{
+ set_hero(5);
+ if (get_level_s(MUSIC_HERO, 50) >= 10)
+ {
+ set_shero(5);
+ }
+ if (get_level_s(MUSIC_HERO, 50) >= 20)
+ {
+ set_strike(5);
+ }
+ if (get_level_s(MUSIC_HERO, 50) >= 25)
+ {
+ set_oppose_cc(5);
+ }
+ return get_mana(MUSIC_HERO);
+}
+
+casting_result music_heroic_ballad_spell()
+{
+ start_lasting_spell(MUSIC_HERO);
+ return CAST_OBVIOUS;
+}
+
+int music_hobbit_melodies_lasting()
+{
+ set_shield(5, 10 + get_level_s(MUSIC_TIME, 50), 0, 0, 0);
+ if (get_level_s(MUSIC_TIME, 50) >= 15)
+ {
+ set_fast(5, 7 + get_level_s(MUSIC_TIME, 10));
+ }
+ return get_mana(MUSIC_TIME);
+}
+
+casting_result music_hobbit_melodies_spell()
+{
+ start_lasting_spell(MUSIC_TIME);
+ return CAST_OBVIOUS;
+}
+
+const char *music_hobbit_melodies_info()
+{
+ static char buf[128];
+ if (get_level_s(MUSIC_TIME, 50) >= 15)
+ {
+ sprintf(buf, "AC " FMTs32b " speed " FMTs32b,
+ 10 + get_level_s(MUSIC_TIME, 50),
+ 7 + get_level_s(MUSIC_TIME, 10));
+ }
+ else
+ {
+ sprintf(buf, "AC " FMTs32b,
+ 10 + get_level_s(MUSIC_TIME, 50));
+ }
+ return buf;
+}
+
+int music_clairaudience_lasting()
+{
+ set_tim_esp(5);
+ if (get_level_s(MUSIC_MIND, 50) >= 10)
+ {
+ fire_ball(GF_IDENTIFY, 0, 1, 1 + get_level(MUSIC_MIND, 3, 0));
+ }
+ return get_mana(MUSIC_MIND);
+}
+
+casting_result music_clairaudience_spell()
+{
+ start_lasting_spell(MUSIC_MIND);
+ return CAST_OBVIOUS;
+}
+
+const char *music_clairaudience_info()
+{
+ static char buf[128];
+
+ if (get_level_s(MUSIC_MIND, 50) >= 10)
+ {
+ sprintf(buf, "rad " FMTs32b,
+ 1 + get_level(MUSIC_MIND, 3, 0));
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result music_blow_spell()
+{
+ fire_ball(GF_SOUND,
+ 0,
+ damroll(2 + get_level(MUSIC_BLOW, 10, 0), 4 + get_level(MUSIC_BLOW, 40, 0)),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return CAST_OBVIOUS;
+}
+
+const char *music_blow_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " rad " FMTs32b,
+ 2 + get_level(MUSIC_BLOW, 10, 0),
+ 4 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return buf;
+}
+
+casting_result music_gush_of_wind_spell()
+{
+ fire_ball(GF_AWAY_ALL,
+ 0,
+ 10 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return CAST_OBVIOUS;
+}
+
+const char *music_gush_of_wind_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dist " FMTs32b " rad " FMTs32b,
+ 10 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return buf;
+}
+
+casting_result music_horns_of_ylmir_spell()
+{
+ earthquake(p_ptr->py, p_ptr->px, 2 + get_level_s(MUSIC_YLMIR, 10));
+ return CAST_OBVIOUS;
+}
+
+const char *music_horns_of_ylmir_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ 2 + get_level_s(MUSIC_YLMIR, 10));
+ return buf;
+}
+
+casting_result music_ambarkanta_spell()
+{
+ alter_reality();
+ return CAST_OBVIOUS;
+}
+
+casting_result aule_firebrand_spell()
+{
+ int rad = 0;
+ int type = GF_FIRE;
+ s32b level = get_level_s(AULE_FIREBRAND, 50);
+
+ if (level > 30)
+ {
+ type = GF_HOLY_FIRE;
+ }
+
+ if (level >= 15)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(level + randint(20),
+ type,
+ 4 + level,
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *aule_firebrand_info()
+{
+ s32b level = get_level_s(AULE_FIREBRAND, 50);
+ static char buf[128];
+
+ sprintf(buf,
+ "dur " FMTs32b "+d20 dam " FMTs32b "/blow",
+ level,
+ 4 + level);
+ return buf;
+}
+
+static object_filter_t const &aule_enchant_weapon_item_tester()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // Cannot enchant artifacts, spell is probably already too overpowered.
+ Not(IsArtifact()),
+ // Only weapons which Aule likes
+ Or(
+ TVal(TV_MSTAFF),
+ TVal(TV_BOW),
+ TVal(TV_HAFTED),
+ TVal(TV_POLEARM),
+ TVal(TV_SWORD),
+ TVal(TV_AXE)));
+ return instance;
+}
+
+casting_result aule_enchant_weapon_spell()
+{
+ s32b level = get_level_s(AULE_ENCHANT_WEAPON, 50);
+ s16b num_h, num_d, num_p;
+
+ num_h = 1 + randint(level/12);
+ num_d = 0;
+ num_p = 0;
+
+ if (level >= 5)
+ {
+ num_d = 1 + randint(level/12);
+ }
+ if (level >= 45)
+ {
+ num_p = 1;
+ }
+
+ int item;
+ if (!get_item(&item,
+ "Which object do you want to enchant?",
+ "You have no objects to enchant.",
+ USE_INVEN,
+ aule_enchant_weapon_item_tester()))
+ {
+ return NO_CAST;
+ }
+
+ object_type *o_ptr = get_object(item);
+
+ o_ptr->to_h = o_ptr->to_h + num_h;
+ o_ptr->to_d = o_ptr->to_d + num_d;
+ o_ptr->pval = o_ptr->pval + num_p;
+
+ return CAST_OBVIOUS;
+}
+
+const char *aule_enchant_weapon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "tries " FMTs32b,
+ 1 + get_level_s(AULE_ENCHANT_WEAPON, 50)/12);
+ return buf;
+}
+
+static object_filter_t const &aule_enchant_armor_item_tester()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // No enchanting artifacts; the spell is already horribly
+ // overpowered.
+ Not(IsArtifact()),
+ // Only armor-like things can be enchanted
+ Or(
+ TVal(TV_BOOTS),
+ TVal(TV_GLOVES),
+ TVal(TV_HELM),
+ TVal(TV_CROWN),
+ TVal(TV_SHIELD),
+ TVal(TV_CLOAK),
+ TVal(TV_SOFT_ARMOR),
+ TVal(TV_HARD_ARMOR),
+ TVal(TV_DRAG_ARMOR)));
+ return instance;
+}
+
+casting_result aule_enchant_armour_spell()
+{
+ s32b level = get_level_s(AULE_ENCHANT_ARMOUR, 50);
+ s16b num_h, num_d, num_a, num_p;
+ int item;
+
+ num_a = 1 + randint(level/10);
+ num_h = 0;
+ num_d = 0;
+ num_p = 0;
+ if (level >= 20)
+ {
+ num_h = 1;
+ num_d = 1;
+ }
+ if (level >= 40)
+ {
+ num_p = 1;
+ }
+
+ if (!get_item(&item,
+ "Which object do you want to enchant?",
+ "You have no objects to enchant.",
+ USE_INVEN,
+ aule_enchant_armor_item_tester()))
+ {
+ return NO_CAST;
+ }
+
+ object_type *o_ptr = get_object(item);
+
+ o_ptr->to_h = o_ptr->to_h + num_h;
+ o_ptr->to_d = o_ptr->to_d + num_d;
+ o_ptr->pval = o_ptr->pval + num_p;
+ o_ptr->to_a = o_ptr->to_a + num_a;
+
+ return CAST_OBVIOUS;
+}
+
+const char *aule_enchant_armour_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "tries " FMTs32b,
+ 1 + get_level_s(AULE_ENCHANT_ARMOUR, 50)/10);
+ return buf;
+}
+
+casting_result aule_child_spell()
+{
+ int y, x;
+ s16b m_idx;
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+ m_idx = place_monster_one(y, x, test_monster_name("Dwarven warrior"),
+ 0, FALSE, MSTATUS_FRIEND);
+
+ if (m_idx)
+ {
+ monster_set_level(m_idx, 20 + get_level(AULE_CHILD, 70, 0));
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *aule_child_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ 20 + get_level_s(AULE_CHILD, 70));
+ return buf;
+}
+
+static int tears_of_luthien_hp()
+{
+ return 10 * get_level_s(MANDOS_TEARS_LUTHIEN, 30);
+}
+
+casting_result mandos_tears_of_luthien_spell()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, hp_player(tears_of_luthien_hp()));
+ result = cplus(result, set_stun(0));
+ result = cplus(result, set_cut(0));
+ result = cplus(result, set_afraid(0));
+
+ return result;
+}
+
+const char *mandos_tears_of_luthien_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heals %d",
+ tears_of_luthien_hp());
+ return buf;
+}
+
+casting_result mandos_spirit_of_the_feanturi_spell()
+{
+ casting_result result = NO_CAST;
+ s32b level = get_level_s(MANDOS_SPIRIT_FEANTURI, 50);
+
+ result = cplus(result, set_afraid(0));
+ result = cplus(result, set_confused(0));
+
+ if (level >= 20)
+ {
+ result = cplus(result, do_res_stat(A_WIS, TRUE));
+ result = cplus(result, do_res_stat(A_INT, TRUE));
+ }
+
+ if (level >= 30)
+ {
+ result = cplus(result, set_image(0));
+ result = cplus(result, heal_insanity(p_ptr->msane * level / 100));
+ }
+
+ return result;
+}
+
+const char *mandos_spirit_of_the_feanturi_info()
+{
+ static char buf[128];
+ s32b level = get_level_s(MANDOS_SPIRIT_FEANTURI, 50) ;
+ if (level >= 20)
+ {
+ sprintf(buf, "heals " FMTs32b "%%", level);
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+static int tale_of_doom_duration()
+{
+ return 5 + get_level_s(MANDOS_TALE_DOOM,10);
+}
+
+casting_result mandos_tale_of_doom_spell()
+{
+ return cast(set_tim_precognition(tale_of_doom_duration()));
+}
+
+const char *mandos_tale_of_doom_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d",
+ tale_of_doom_duration());
+ return buf;
+}
+
+int call_to_the_halls_mlev()
+{
+ return 20 + get_level(MANDOS_CALL_HALLS, 70, 0);
+}
+
+casting_result mandos_call_to_the_halls_spell()
+{
+ int y, x;
+ s16b m_idx;
+ std::vector<int> summons {
+ test_monster_name("Experienced spirit"),
+ test_monster_name("Wise spirit")
+ };
+
+ int r_idx = summons[rand_int(summons.size())];
+ assert(r_idx >= 0);
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ if (m_idx)
+ {
+ monster_set_level(m_idx, call_to_the_halls_mlev());
+ return CAST_OBVIOUS;
+ }
+ return NO_CAST;
+}
+
+const char *mandos_call_to_the_halls_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ call_to_the_halls_mlev());
+ return buf;
+}
+
+static void get_belegaer_damage(int *dice, int *sides)
+{
+ *dice = get_level_s(ULMO_BELEGAER, 10);
+ *sides = 3 + get_level_s(ULMO_BELEGAER, 35);
+}
+
+casting_result ulmo_song_of_belegaer_spell()
+{
+ int dir, dice, sides;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_belegaer_damage(&dice, &sides);
+ return cast(fire_bolt_or_beam(2 * get_level_s(ULMO_BELEGAER, 85),
+ GF_WATER,
+ dir,
+ damroll(dice, sides)));
+}
+
+const char *ulmo_song_of_belegaer_info()
+{
+ static char buf[128];
+ int dice, sides;
+
+ get_belegaer_damage(&dice, &sides);
+ sprintf(buf,
+ "dam %dd%d",
+ dice,
+ sides);
+ return buf;
+}
+
+int draught_of_ulmonan_hp()
+{
+ return 5 * get_level_s(ULMO_DRAUGHT_ULMONAN, 50);
+}
+
+casting_result ulmo_draught_of_ulmonan_spell()
+{
+ casting_result result = NO_CAST;
+ s32b level = get_level_s(ULMO_DRAUGHT_ULMONAN, 50);
+
+ result = cplus(result, hp_player(draught_of_ulmonan_hp()));
+
+ result = cplus(result, set_poisoned(0));
+ result = cplus(result, set_cut(0));
+ result = cplus(result, set_stun(0));
+ result = cplus(result, set_blind(0));
+
+ if (level >= 10)
+ {
+ result = cplus(result, do_res_stat(A_STR, TRUE));
+ result = cplus(result, do_res_stat(A_CON, TRUE));
+ result = cplus(result, do_res_stat(A_DEX, TRUE));
+ }
+
+ if (level >= 20)
+ {
+ result = cplus(result, set_parasite(0, 0));
+ result = cplus(result, set_mimic(0, 0, 0));
+ }
+
+ return result;
+}
+
+const char *ulmo_draught_of_ulmonan_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "cure %d",
+ draught_of_ulmonan_hp());
+ return buf;
+}
+
+static int call_of_the_ulumuri_mlev()
+{
+ return 30 + get_level(ULMO_CALL_ULUMURI, 70, 0);
+}
+
+casting_result ulmo_call_of_the_ulumuri_spell()
+{
+ int x,y;
+ s16b m_idx;
+ std::vector<int> summons {
+ test_monster_name("Water spirit"),
+ test_monster_name("Water elemental")
+ };
+
+ int r_idx = summons[rand_int(summons.size())];
+ assert(r_idx >= 0);
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ if (m_idx)
+ {
+ monster_set_level(m_idx, call_of_the_ulumuri_mlev());
+ return CAST_OBVIOUS;
+ }
+
+ return NO_CAST;
+}
+
+const char *ulmo_call_of_the_ulumuri_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ call_of_the_ulumuri_mlev());
+ return buf;
+}
+
+static int wrath_of_ulmo_damage()
+{
+ return 40 + get_level_s(ULMO_WRATH, 150);
+}
+
+static int wrath_of_ulmo_duration()
+{
+ return 10 + get_level_s(ULMO_WRATH, 14);
+}
+
+casting_result ulmo_wrath_of_ulmo_spell()
+{
+ int dir, type = GF_WATER;
+
+ if (get_level_s(ULMO_WRATH, 50) >= 30)
+ {
+ type = GF_WAVE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ fire_wall(type,
+ dir,
+ wrath_of_ulmo_damage(),
+ wrath_of_ulmo_duration());
+ return CAST_OBVIOUS;
+}
+
+const char *ulmo_wrath_of_ulmo_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d dur %d",
+ wrath_of_ulmo_damage(),
+ wrath_of_ulmo_duration());
+ return buf;
+}
+
+static int light_of_valinor_damage()
+{
+ return 10 + get_level_s(VARDA_LIGHT_VALINOR, 100);
+}
+
+static int light_of_valinor_radius()
+{
+ return 5 + get_level_s(VARDA_LIGHT_VALINOR, 6);
+}
+
+casting_result varda_light_of_valinor_spell()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 3)
+ {
+ result = cplus(result, lite_area(10, 4));
+ }
+ else
+ {
+ lite_room(p_ptr->py, p_ptr->px);
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 15)
+ {
+ result = cplus(result,
+ fire_ball(GF_LITE,
+ 0,
+ light_of_valinor_damage(),
+ light_of_valinor_radius()));
+ }
+
+ return result;
+}
+
+const char *varda_light_of_valinor_info()
+{
+ static char buf[128];
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 15)
+ {
+ sprintf(buf,
+ "dam %d rad %d",
+ light_of_valinor_damage(),
+ light_of_valinor_radius());
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result varda_call_of_almaren_spell()
+{
+ int power = 5 * p_ptr->lev;
+ if (get_level_s(VARDA_CALL_ALMAREN, 50) >= 20)
+ {
+ dispel_evil(power);
+ }
+ else
+ {
+ banish_evil(power);
+ }
+ return CAST_OBVIOUS;
+}
+
+casting_result varda_evenstar_spell()
+{
+ wiz_lite_extra();
+ if (get_level_s(VARDA_EVENSTAR, 50) >= 40)
+ {
+ identify_pack();
+ self_knowledge(NULL);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+static int star_kindler_bursts()
+{
+ return p_ptr->lev / 5;
+}
+
+static int star_kindler_damage()
+{
+ return 20 + get_level_s(VARDA_STARKINDLER, 100);
+}
+
+casting_result varda_star_kindler_spell()
+{
+ int dir, i, n = star_kindler_bursts();
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ fire_ball(GF_LITE,
+ dir,
+ star_kindler_damage(),
+ 10);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *varda_star_kindler_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d bursts %d rad 10",
+ star_kindler_damage(),
+ star_kindler_bursts());
+ return buf;
+}
+