/**
* @file
* @brief Functions non-standard unrandarts uses.
**/
/*
* util/art-data.pl scans through this file to grab the functions
* non-standard unrandarts use and put them into the unranddata structs
* in art-data.h, so the function names must have the form of
* _UNRAND_ENUM_func_name() in order to be recognised.
*/
#ifdef ART_FUNC_H
#error "art-func.h included twice!"
#endif
#ifdef ART_DATA_H
#error "art-func.h must be included before art-data.h"
#endif
#define ART_FUNC_H
#include "beam.h" // For Lajatang of Order's silver damage
#include "cloud.h" // For storm bow's and robe of clouds' rain
#include "effects.h" // For Sceptre of Torment tormenting
#include "env.h" // For storm bow env.cgrid
#include "fight.h"
#include "food.h" // For evokes
#include "ghost.h" // For is_dragonkind ghost_demon datas
#include "godconduct.h" // did_god_conduct
#include "misc.h"
#include "mgen_data.h" // For Sceptre of Asmodeus evoke
#include "mon-place.h" // For Sceptre of Asmodeus evoke
#include "player.h"
#include "spl-cast.h" // For evokes
#include "spl-damage.h" // For the Singing Sword.
#include "spl-miscast.h" // For Staff of Wucad Mu and Scythe of Curses miscasts
#include "spl-summoning.h" // For Zonguldrok animating dead
#include "terrain.h" // For storm bow
/*******************
* Helper functions.
*******************/
static void _equip_mpr(bool* show_msgs, const char* msg,
msg_channel_type chan = MSGCH_PLAIN)
{
bool def_show = true;
if (show_msgs == NULL)
show_msgs = &def_show;
if (*show_msgs)
mprf(chan, "%s", msg);
// Caller shouldn't give any more messages.
*show_msgs = false;
}
/*******************
* Unrand functions.
*******************/
static void _ASMODEUS_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (attacker->is_player())
did_god_conduct(DID_UNHOLY, 3);
}
static bool _evoke_sceptre_of_asmodeus()
{
if (!x_chance_in_y(you.skill(SK_EVOCATIONS, 100), 3000))
return false;
const monster_type mon = random_choose_weighted(
3, MONS_EFREET,
3, MONS_SUN_DEMON,
3, MONS_BALRUG,
2, MONS_HELLION,
1, MONS_BRIMSTONE_FIEND,
0);
mgen_data mg(mon, BEH_CHARMED, &you,
0, 0, you.pos(), MHITYOU,
MG_FORCE_BEH, you.religion);
mg.extra_flags |= (MF_NO_REWARD | MF_HARD_RESET);
monster *m = create_monster(mg);
if (m)
{
mpr("The Sceptre summons one of its servants.");
did_god_conduct(DID_UNHOLY, 3);
m->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 6));
if (!player_angers_monster(m))
mpr("You don't feel so good about this...");
}
else
mpr("The air shimmers briefly.");
return true;
}
static bool _ASMODEUS_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (_evoke_sceptre_of_asmodeus())
{
make_hungry(200, false, true);
*did_work = true;
*pract = 1;
}
return false;
}
////////////////////////////////////////////////////
static void _CEREBOV_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (attacker->is_player())
did_god_conduct(DID_UNHOLY, 3);
if (dam)
{
if (defender->is_player()
&& defender->res_fire() <= 3
&& !you.duration[DUR_FIRE_VULN])
{
mpr("The Sword of Cerebov burns away your fire resistance.");
you.increase_duration(DUR_FIRE_VULN, 3 + random2(dam), 50);
}
if (defender->is_monster()
&& !mondied
&& !defender->as_monster()->res_hellfire()
&& !defender->as_monster()->has_ench(ENCH_FIRE_VULN))
{
mprf("The Sword of Cerebov burns away %s fire resistance.",
defender->name(DESC_ITS).c_str());
defender->as_monster()->add_ench(
mon_enchant(ENCH_FIRE_VULN, 1, attacker,
(3 + random2(dam)) * BASELINE_DELAY));
}
}
}
////////////////////////////////////////////////////
static void _curses_miscast(actor* victim, int power, int fail)
{
MiscastEffect(victim, WIELD_MISCAST, SPTYP_NECROMANCY, power, fail,
"the Scythe of Curses", NH_NEVER);
}
static void _CURSES_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "A shiver runs down your spine.");
if (!unmeld)
_curses_miscast(&you, random2(9), random2(70));
}
static void _CURSES_world_reacts(item_def *item)
{
if (one_chance_in(30))
curse_an_item();
}
static void _CURSES_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (attacker->is_player())
did_god_conduct(DID_NECROMANCY, 3);
if (!mondied && defender->has_lifeforce())
_curses_miscast(defender, random2(9), random2(70));
}
/////////////////////////////////////////////////////
static void _DISPATER_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (attacker->is_player())
did_god_conduct(DID_UNHOLY, 3);
}
static bool _DISPATER_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (!enough_hp(11, true))
{
mpr("You're too close to death to use this item.");
*unevokable = true;
return true;
}
if (!enough_mp(5, false))
{
*unevokable = true;
return true;
}
*did_work = true;
int power = you.skill(SK_EVOCATIONS, 8);
if (your_spells(SPELL_HELLFIRE, power, false) == SPRET_ABORT)
{
*unevokable = true;
return false;
}
mpr("You feel the staff feeding on your energy!");
dec_hp(5 + random2avg(19, 2), false);
dec_mp(2 + random2avg(5, 2));
make_hungry(100, false, true);
*pract = (coinflip() ? 2 : 1);
return false;
}
////////////////////////////////////////////////////
// XXX: Staff giving a boost to poison spells is hardcoded in
// player_spec_poison()
static void _olgreb_pluses(item_def *item)
{
// Giving Olgreb's staff a little lift since staves of poison have
// been made better. -- bwr
item->plus = you.skill(SK_POISON_MAGIC) / 3;
}
static void _OLGREB_equip(item_def *item, bool *show_msgs, bool unmeld)
{
if (you.can_smell())
_equip_mpr(show_msgs, "You smell chlorine.");
else
_equip_mpr(show_msgs, "The staff glows a sickly green.");
_olgreb_pluses(item);
}
static void _OLGREB_unequip(item_def *item, bool *show_msgs)
{
if (you.can_smell())
_equip_mpr(show_msgs, "The smell of chlorine vanishes.");
else
_equip_mpr(show_msgs, "The staff's sickly green glow vanishes.");
}
static void _OLGREB_world_reacts(item_def *item)
{
_olgreb_pluses(item);
}
static bool _OLGREB_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
if (!enough_mp(4, false))
{
*unevokable = true;
return true;
}
if (!x_chance_in_y(you.skill(SK_EVOCATIONS, 100) + 100, 600))
return false;
*did_work = true;
int power = div_rand_round(20 + you.skill(SK_EVOCATIONS, 20), 4);
// Allow aborting (for example if friendlies are nearby).
if (your_spells(SPELL_OLGREBS_TOXIC_RADIANCE, power, false) == SPRET_ABORT)
{
*unevokable = true;
return false;
}
if (x_chance_in_y(you.skill(SK_EVOCATIONS, 100) + 100, 2000))
your_spells(SPELL_VENOM_BOLT, power, false);
dec_mp(4);
make_hungry(50, false, true);
*pract = 1;
return false;
}
static void _OLGREB_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
int skill = attacker->skill(SK_POISON_MAGIC, 100);
if (defender->alive()
&& (coinflip() || x_chance_in_y(skill, 800)))
{
defender->poison(attacker, 2, defender->has_lifeforce()
&& x_chance_in_y(skill, 800));
if (attacker->is_player())
did_god_conduct(DID_POISON, 3);
}
}
////////////////////////////////////////////////////
static void _power_pluses(item_def *item)
{
item->plus = min(you.hp / 10, 27);
}
static void _POWER_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You sense an aura of extreme power.");
_power_pluses(item);
}
static void _POWER_world_reacts(item_def *item)
{
_power_pluses(item);
}
////////////////////////////////////////////////////
static void _SINGING_SWORD_equip(item_def *item, bool *show_msgs, bool unmeld)
{
bool def_show = true;
if (show_msgs == NULL)
show_msgs = &def_show;
if (!*show_msgs)
return;
if (!item->props.exists("ss_welcome"))
{
mprf(MSGCH_TALK, "The sword says, \"Hi! I'm the Singing Sword!\"");
item->props["ss_welcome"].get_bool() = true;
}
else
mprf(MSGCH_TALK, "The Singing Sword hums in delight!");
*show_msgs = false;
}
static void _SINGING_SWORD_unequip(item_def *item, bool *show_msgs)
{
set_artefact_name(*item, "Singing Sword");
_equip_mpr(show_msgs, "The Singing Sword sighs.", MSGCH_TALK);
}
static void _SINGING_SWORD_world_reacts(item_def *item)
{
int tension = get_tension(GOD_NO_GOD);
int tier = (tension <= 0) ? 1 : (tension < 40) ? 2 : 3;
bool silent = silenced(you.pos());
string old_name = get_artefact_name(*item);
string new_name;
if (silent)
new_name = "Sulking Sword";
else if (tier < 2)
new_name = "Singing Sword";
else
new_name = "Screaming Sword";
if (old_name != new_name)
{
set_artefact_name(*item, new_name);
you.wield_change = true;
}
// not as spammy at low tension
if (!x_chance_in_y(7, (tier == 1) ? 1000 : (tier == 2) ? 100 : 10))
return;
// it will still struggle more with higher tension
if (silent)
tier = 0;
if (tier == 3 && one_chance_in(10))
tier++; // SCREAM -- double damage
const char *tenname[] = {"silenced", "no_tension", "low_tension",
"high_tension", "SCREAM"};
const string key = tenname[tier];
string msg = getSpeakString("singing sword " + key);
msg = maybe_pick_random_substring(msg);
msg = maybe_capitalise_substring(msg);
const int loudness[] = {0, 0, 15, 25, 35};
item_noise(*item, msg, loudness[tier]);
if (tier < 3)
return; // no damage on low tiers
sonic_damage(tier == 4);
}
////////////////////////////////////////////////////
static void _PRUNE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You feel pruney.");
}
static void _PRUNE_world_reacts(item_def *item)
{
if (one_chance_in(10))
did_god_conduct(DID_CHAOS, 1);
}
////////////////////////////////////////////////////
static void _TORMENT_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "A terribly searing pain shoots up your arm!");
}
static void _TORMENT_world_reacts(item_def *item)
{
if (one_chance_in(200))
{
torment(&you, TORMENT_SPWLD, you.pos());
did_god_conduct(DID_UNHOLY, 1);
}
}
static void _TORMENT_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (coinflip())
return;
torment(attacker, TORMENT_SPWLD, attacker->pos());
if (attacker->is_player())
did_god_conduct(DID_UNHOLY, 5);
}
/////////////////////////////////////////////////////
static void _TROG_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You feel bloodthirsty!");
}
static void _TROG_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "You feel less violent.");
}
////////////////////////////////////////////////////
static void _wucad_miscast(actor* victim, int power,int fail)
{
MiscastEffect(victim, WIELD_MISCAST, SPTYP_DIVINATION, power, fail,
"the Staff of Wucad Mu", NH_NEVER);
}
static bool _WUCAD_MU_evoke(item_def *item, int* pract, bool* did_work,
bool* unevokable)
{
#if TAG_MAJOR_VERSION == 34
if (you.species == SP_DJINNI)
{
mpr("The staff is unable to affect your essence.");
*unevokable = true;
return true;
}
#endif
if (you.magic_points == you.max_magic_points)
{
mpr("Your reserves of magic are full.");
*unevokable = true;
return true;
}
if (!x_chance_in_y(you.skill(SK_EVOCATIONS, 100) + 100, 2500))
return false;
if (one_chance_in(4))
{
_wucad_miscast(&you, random2(9), random2(70));
return false;
}
mpr("Magical energy flows into your mind!");
inc_mp(3 + random2(5) + you.skill_rdiv(SK_EVOCATIONS, 1, 3));
make_hungry(50, false, true);
*pract = 1;
*did_work = true;
return false;
}
///////////////////////////////////////////////////
// XXX: Always getting maximal vampiric drain is hardcoded in
// melee_attack::apply_damage_brand()
static void _VAMPIRES_TOOTH_equip(item_def *item, bool *show_msgs, bool unmeld)
{
if (you.is_undead == US_ALIVE)
{
_equip_mpr(show_msgs,
"You feel a strange hunger, and smell blood in the air...");
if (!unmeld)
make_hungry(4500, false, false);
}
else if (you.species == SP_VAMPIRE)
_equip_mpr(show_msgs, "You feel a bloodthirsty glee!");
else
_equip_mpr(show_msgs, "You feel strangely empty.");
}
///////////////////////////////////////////////////
// XXX: Pluses at creation time are hardcoded in make_item_unrandart()
static void _VARIABILITY_world_reacts(item_def *item)
{
do_uncurse_item(*item);
if (x_chance_in_y(2, 5))
item->plus += (coinflip() ? +1 : -1);
if (item->plus < -4)
item->plus = -4;
else if (item->plus > 16)
item->plus = 16;
}
///////////////////////////////////////////////////
static void _ZONGULDROK_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You sense an extremely unholy aura.");
}
static void _ZONGULDROK_world_reacts(item_def *item)
{
animate_dead(&you, 1 + random2(3), BEH_HOSTILE, MHITYOU, 0,
"the Sword of Zonguldrok");
did_god_conduct(DID_NECROMANCY, 1);
did_god_conduct(DID_CORPSE_VIOLATION, 1);
}
static void _ZONGULDROK_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (attacker->is_player())
{
did_god_conduct(DID_NECROMANCY, 3);
did_god_conduct(DID_CORPSE_VIOLATION, 3);
}
}
///////////////////////////////////////////////////
static void _STORM_BOW_world_reacts(item_def *item)
{
if (!one_chance_in(300))
return;
for (radius_iterator ri(you.pos(), 2, C_ROUND, LOS_SOLID); ri; ++ri)
if (!cell_is_solid(*ri) && env.cgrid(*ri) == EMPTY_CLOUD && one_chance_in(5))
place_cloud(CLOUD_RAIN, *ri, random2(20), &you, 3);
}
///////////////////////////////////////////////////
static void _GONG_melee_effects(item_def* item, actor* wearer,
actor* attacker, bool dummy, int dam)
{
if (silenced(wearer->pos()))
return;
string msg = getSpeakString("shield of the gong");
if (msg.empty())
msg = "You hear a strange loud sound.";
mprf(MSGCH_SOUND, "%s", msg.c_str());
noisy(40, wearer->pos());
}
///////////////////////////////////////////////////
static void _RCLOUDS_world_reacts(item_def *item)
{
cloud_type cloud;
if (one_chance_in(4))
cloud = CLOUD_RAIN;
else
cloud = CLOUD_MIST;
for (radius_iterator ri(you.pos(), 2, C_ROUND, LOS_SOLID); ri; ++ri)
if (!cell_is_solid(*ri) && env.cgrid(*ri) == EMPTY_CLOUD
&& one_chance_in(20))
{
place_cloud(cloud, *ri, random2(10), &you, 1);
}
}
static void _RCLOUDS_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "A thin mist springs up around you!");
}
///////////////////////////////////////////////////
static void _DEMON_AXE_melee_effects(item_def* item, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (one_chance_in(10))
cast_summon_demon(50+random2(100));
if (attacker->is_player())
did_god_conduct(DID_UNHOLY, 3);
}
static void _DEMON_AXE_world_reacts(item_def *item)
{
monster* closest = NULL;
for (distance_iterator di(you.pos(), true, true, LOS_RADIUS); di; ++di)
{
monster *mon = monster_at(*di);
if (mon && you.can_see(mon)
&& you.possible_beholder(mon)
&& !mons_class_flag(mon->type, M_NO_EXP_GAIN))
{
closest = mon;
goto found;
}
}
return;
found:
if (!you.beheld_by(closest))
{
mprf("Visions of slaying %s flood into your mind.",
closest->name(DESC_THE).c_str());
// The monsters (if any) currently mesmerising the player do not include
// this monster. To avoid trapping the player, all other beholders
// are removed.
you.clear_beholders();
}
if (you.confused())
{
mpr("Your confusion fades away as the thirst for blood takes over your mind.");
you.duration[DUR_CONF] = 0;
}
you.add_beholder(closest, true);
}
static void _DEMON_AXE_unequip(item_def *item, bool *show_msgs)
{
if (you.beheld())
{
// This shouldn't clear mermaids and sirens, but we lack the information
// why they behold us -- usually, it's due to the axe. Since unwielding
// it costs scrolls of rem curse, we might say getting the demon away is
// enough of a shock to get you back to senses.
you.clear_beholders();
mpr("Your thirst for blood fades away.");
}
}
///////////////////////////////////////////////////
static void _WYRMBANE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, player_genus(GENPC_DRACONIAN) || you.form == TRAN_DRAGON
? "You feel an overwhelming desire to commit suicide."
: "You feel an overwhelming desire to slay dragons!");
}
static bool is_dragonkind(const actor *act)
{
if (mons_genus(act->mons_species()) == MONS_DRAGON
|| mons_genus(act->mons_species()) == MONS_DRAKE
|| mons_genus(act->mons_species()) == MONS_DRACONIAN)
{
return true;
}
if (act->is_player())
return you.form == TRAN_DRAGON;
// Else the actor is a monster.
const monster* mon = act->as_monster();
if (mons_is_zombified(mon)
&& (mons_genus(mon->base_monster) == MONS_DRAGON
|| mons_genus(mon->base_monster) == MONS_DRAKE
|| mons_genus(mon->base_monster) == MONS_DRACONIAN))
{
return true;
}
if (mons_is_ghost_demon(mon->type)
&& species_genus(mon->ghost->species) == GENPC_DRACONIAN)
{
return true;
}
return false;
}
static void _WYRMBANE_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (!is_dragonkind(defender))
return;
// Since the target will become a DEAD MONSTER if it dies due to the extra
// damage to dragons, we need to grab this information now.
int hd = min(defender->as_monster()->get_experience_level(), 18);
string name = defender->name(DESC_THE);
if (!mondied)
{
mprf("%s %s!",
defender->name(DESC_THE).c_str(),
defender->conj_verb("convulse").c_str());
defender->hurt(attacker, 1 + random2(3*dam/2));
mondied = !defender->alive();
}
if (!mondied || !defender || defender->is_summoned()
|| (defender->is_monster()
&& testbits(defender->as_monster()->flags, MF_NO_REWARD)))
{
return;
}
// The cap can be reached by:
// * iron dragon, golden dragon, pearl dragon (18)
// * Xtahua (19)
// * bone dragon, Serpent of Hell (20)
// * Tiamat (22)
// * pghosts (up to 27)
dprf("Killed a drac with hd %d.", hd);
if (weapon->plus < hd)
{
weapon->plus++;
mprf("The lance glows as it skewers %s.",
name.c_str());
you.wield_change = true;
}
}
///////////////////////////////////////////////////
static void _UNDEADHUNTER_melee_effects(item_def* item, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (defender->holiness() == MH_UNDEAD && !one_chance_in(3) && !mondied)
{
mprf("%s %s blasted by disruptive energy!",
defender->name(DESC_THE).c_str(),
defender->is_player() ? "are" : "is");
defender->hurt(attacker, random2avg((1 + (dam * 3)), 3));
}
}
///////////////////////////////////////////////////
static void _BRILLIANCE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
invalidate_agrid(true);
}
static void _BRILLIANCE_unequip(item_def *item, bool *show_msgs)
{
invalidate_agrid(true);
}
///////////////////////////////////////////////////
static void _SHADOWS_equip(item_def *item, bool *show_msgs, bool unmeld)
{
invalidate_agrid(true);
}
static void _SHADOWS_unequip(item_def *item, bool *show_msgs)
{
invalidate_agrid(true);
}
///////////////////////////////////////////////////
static void _DEVASTATOR_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "Time to lay down the shillelagh law.");
}
static void _DEVASTATOR_melee_effects(item_def* item, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (dam)
shillelagh(attacker, defender->pos(), dam);
}
///////////////////////////////////////////////////
static void _DRAGONSKIN_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You feel oddly protected from the elements.");
}
static void _DRAGONSKIN_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "You no longer feel protected from the elements.");
}
///////////////////////////////////////////////////
static void _BLACK_KNIGHT_HORSE_world_reacts(item_def *item)
{
if (one_chance_in(10))
did_god_conduct(DID_UNHOLY, 1);
}
///////////////////////////////////////////////////
static void _NIGHT_equip(item_def *item, bool *show_msgs, bool unmeld)
{
update_vision_range();
_equip_mpr(show_msgs, "The light fades from your surroundings.");
}
static void _NIGHT_unequip(item_def *item, bool *show_msgs)
{
update_vision_range();
_equip_mpr(show_msgs, "The light returns to your surroundings.");
}
///////////////////////////////////////////////////
static void _plutonium_sword_miscast(actor* victim, int power, int fail)
{
MiscastEffect(victim, MELEE_MISCAST, SPTYP_TRANSMUTATION, power, fail,
"the plutonium sword", NH_NEVER);
}
static void _PLUTONIUM_SWORD_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied,
int dam)
{
if (!mondied && one_chance_in(5))
{
mpr("Mutagenic energy flows through the plutonium sword!");
_plutonium_sword_miscast(defender, random2(9), random2(70));
if (attacker->is_player())
did_god_conduct(DID_CHAOS, 3);
}
}
///////////////////////////////////////////////////
static void _SNAKEBITE_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (!mondied && x_chance_in_y(2, 5))
{
curare_actor(attacker, defender, 2, "curare",
attacker->name(DESC_PLAIN));
}
}
///////////////////////////////////////////////////
static void _WOE_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
const char *verb = "bugger", *adv = "";
switch (random2(8))
{
case 0: verb = "cleave", adv = " in twain"; break;
case 1: verb = "pulverise", adv = " into a thin bloody mist"; break;
case 2: verb = "hew", adv = " savagely"; break;
case 3: verb = "fatally mangle", adv = ""; break;
case 4: verb = "dissect", adv = " like a pig carcass"; break;
case 5: verb = "chop", adv = " into pieces"; break;
case 6: verb = "butcher", adv = " messily"; break;
case 7: verb = "slaughter", adv = " joyfully"; break;
}
if (you.see_cell(attacker->pos()) || you.see_cell(defender->pos()))
{
mprf("%s %s%s %s%s.", attacker->name(DESC_THE).c_str(), verb,
attacker->is_player() ? "" : "s", defender->name(DESC_THE).c_str(),
adv);
}
if (!mondied)
defender->hurt(attacker, defender->stat_hp());
}
///////////////////////////////////////////////////
static void _HELLFIRE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "Your hands smoulder for a moment.");
}
static setup_missile_type _HELLFIRE_launch(item_def* item, bolt* beam,
string* ammo_name, bool* returning)
{
ASSERT(beam->item
&& beam->item->base_type == OBJ_MISSILES
&& !is_artefact(*(beam->item)));
beam->item->special = SPMSL_EXPLODING; // so that it mulches
beam->item->props[HELLFIRE_BOLT_KEY].get_bool() = true;
beam->name = "hellfire bolt";
*ammo_name = "a hellfire bolt";
beam->colour = LIGHTRED;
beam->glyph = DCHAR_FIRED_ZAP;
bolt *expl = new bolt(*beam);
expl->flavour = BEAM_HELLFIRE;
expl->is_explosion = true;
expl->damage = dice_def(2, 5);
expl->name = "hellfire";
beam->special_explosion = expl;
return SM_FINISHED;
}
///////////////////////////////////////////////////
/**
* Calculate the bonus damage that the Elemental Staff does with an attack of
* the given flavour.
*
* @param flavour The elemental flavour of attack; may be BEAM_NONE for earth
* (physical) attacks.
* @param defender The victim of the attack. (Not const because checking res
* may end up IDing items on monsters... )
* @return The amount of damage that the defender will recieve.
*/
static int _calc_elemental_staff_damage(beam_type flavour,
actor* defender)
{
const int base_bonus_dam = 10 + random2(15);
if (flavour == BEAM_NONE) // earth
return defender->apply_ac(base_bonus_dam);
// XXX: refactor this into some more general function (why isn't there one
// already???)
int resist = 0;
switch (flavour)
{
case BEAM_FIRE:
resist = defender->res_fire();
break;
case BEAM_COLD:
resist = defender->res_cold();
break;
case BEAM_ELECTRICITY:
resist = defender->res_elec();
break;
default:
break;
}
return resist_adjust_damage(defender, flavour, resist, base_bonus_dam);
}
static void _ELEMENTAL_STAFF_melee_effects(item_def*, actor* attacker,
actor* defender, bool mondied,
int)
{
const int evoc = attacker->skill(SK_EVOCATIONS, 27);
if (mondied || !(x_chance_in_y(evoc, 27*27) || x_chance_in_y(evoc, 27*27)))
return;
const char *verb = NULL;
beam_type flavour = BEAM_NONE;
switch (random2(4))
{
case 0:
verb = "burn";
flavour = BEAM_FIRE;
break;
case 1:
verb = "freeze";
flavour = BEAM_COLD;
break;
case 2:
verb = "electrocute";
flavour = BEAM_ELECTRICITY;
break;
default:
dprf("Bad damage type for elemental staff; defaulting to earth");
// fallthrough to earth
case 3:
verb = "crush";
break;
}
const int bonus_dam = _calc_elemental_staff_damage(flavour, defender);
if (bonus_dam <= 0)
return;
mprf("%s %s %s.",
attacker->name(DESC_THE).c_str(),
attacker->is_player() ? verb : pluralise(verb).c_str(),
defender->name(DESC_THE).c_str());
defender->hurt(attacker, bonus_dam, flavour);
if (defender->alive() && flavour != BEAM_NONE)
defender->expose_to_element(flavour, 2);
}
///////////////////////////////////////////////////
static void _ARC_BLADE_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "The arc blade crackles to life.");
}
static void _ARC_BLADE_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "The arc blade stops crackling.");
}
static void _ARC_BLADE_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied,
int dam)
{
if (!mondied && one_chance_in(3))
{
if (discharge_monsters(defender->pos(),
75 + random2avg(75, 2),
0,
attacker) == 0)
{
if (you.can_see(attacker))
mpr("The arc blade crackles.");
else
mpr("You hear the crackle of electricity.");
}
}
}
///////////////////////////////////////////////////
static void _SPELLBINDER_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied,
int dam)
{
// Only cause miscasts if the target has magic to disrupt.
if ((defender->is_player()
|| mons_antimagic_affected(defender->as_monster()))
&& !mondied)
{
int school = SPTYP_NONE;
if (defender->is_player())
{
for (int i = 0; i < you.spell_no; i++)
school |= get_spell_disciplines(you.spells[i]);
}
else
{
const monster* mons = defender->as_monster();
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
school |= get_spell_disciplines(mons->spells[i]);
}
if (school != SPTYP_NONE)
{
vector schools;
for (int i = 0; i <= SPTYP_LAST_EXPONENT; i++)
if (testbits(school, 1 << i))
schools.push_back(static_cast(1 << i));
ASSERT(schools.size() > 0);
MiscastEffect(defender, attacker->mindex(),
schools[random2(schools.size())],
random2(9),
random2(70), "the demon whip \"Spellbinder\"",
NH_NEVER);
}
}
}
///////////////////////////////////////////////////
static void _ORDER_melee_effects(item_def* item, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (!mondied)
{
string msg = "";
int silver_dam = silver_damages_victim(defender, dam, msg);
if (silver_dam)
{
if (you.can_see(defender))
mpr(msg.c_str());
defender->hurt(attacker, silver_dam);
}
}
}
///////////////////////////////////////////////////
static void _FIRESTARTER_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "You are filled with an inner flame.");
}
static void _FIRESTARTER_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "Your inner flame fades away.");
}
static void _FIRESTARTER_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (dam)
{
if (defender->is_monster()
&& !mondied
&& !defender->as_monster()->has_ench(ENCH_INNER_FLAME))
{
mprf("%s is filled with an inner flame.",
defender->name(DESC_THE).c_str());
defender->as_monster()->add_ench(
mon_enchant(ENCH_INNER_FLAME, 0, attacker,
(3 + random2(dam)) * BASELINE_DELAY));
}
}
}
///////////////////////////////////////////////////
static void _CHILLY_DEATH_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "The dagger glows with an icy blue light!");
}
static void _CHILLY_DEATH_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "The dagger stops glowing.");
}
static void _CHILLY_DEATH_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (dam)
{
if (defender->is_monster()
&& !mondied
&& !defender->as_monster()->has_ench(ENCH_FROZEN))
{
mprf("%s is flash-frozen.",
defender->name(DESC_THE).c_str());
defender->as_monster()->add_ench(
mon_enchant(ENCH_FROZEN, 0, attacker,
(5 + random2(dam)) * BASELINE_DELAY));
}
else if (defender->is_player()
&& !you.duration[DUR_FROZEN])
{
mprf(MSGCH_WARN, "You are encased in ice.");
you.increase_duration(DUR_FROZEN, 5 + random2(dam));
}
}
}
///////////////////////////////////////////////////
static void _FLAMING_DEATH_equip(item_def *item, bool *show_msgs, bool unmeld)
{
_equip_mpr(show_msgs, "The scimitar bursts into red hot flame!");
}
static void _FLAMING_DEATH_unequip(item_def *item, bool *show_msgs)
{
_equip_mpr(show_msgs, "The scimitar stops flaming.");
}
static void _FLAMING_DEATH_melee_effects(item_def* weapon, actor* attacker,
actor* defender, bool mondied, int dam)
{
if (!mondied && (dam > 2 && one_chance_in(3)))
{
if (defender->is_player())
napalm_player(random2avg(7, 3) + 1, attacker->name(DESC_A, true));
else
{
napalm_monster(
defender->as_monster(),
attacker,
min(4, 1 + random2(attacker->get_hit_dice())/2));
}
}
}
///////////////////////////////////////////////////
static void _MAJIN_equip(item_def *item, bool *show_msgs, bool unmeld)
{
if (you.max_magic_points)
_equip_mpr(show_msgs, "You feel a darkness envelop your magic.");
}
static void _MAJIN_unequip(item_def *item, bool *show_msgs)
{
if (you.max_magic_points)
{
_equip_mpr(show_msgs,
"The darkness slowly releases its grasp on your magic.");
}
}
///////////////////////////////////////////////////
static int _octorings_worn()
{
int worn = 0;
for (int i = EQ_LEFT_RING; i < NUM_EQUIP; ++i)
{
if (you.melded[i] || you.equip[i] == -1)
continue;
item_def& ring = you.inv[you.equip[i]];
if (is_unrandom_artefact(ring, UNRAND_OCTOPUS_KING_RING))
worn++;
}
return worn;
}
static void _OCTOPUS_KING_equip(item_def *item, bool *show_msgs, bool unmeld)
{
int rings = _octorings_worn();
if (rings == 8)
_equip_mpr(show_msgs, "You feel like a king!");
else if (rings)
_equip_mpr(show_msgs, "You feel regal.");
item->plus = 8 + rings;
}
static void _OCTOPUS_KING_world_reacts(item_def *item)
{
item->plus = 8 + _octorings_worn();
}