/*
* File: godwrath.cc
* Summary: Divine retribution.
*/
#include "AppHdr.h"
#include "godwrath.h"
#include "externs.h"
#include "artefact.h"
#include "attitude-change.h"
#include "database.h"
#include "decks.h"
#include "effects.h"
#include "env.h"
#include "enum.h"
#include "food.h"
#include "ghost.h"
#include "godabil.h"
#include "it_use2.h"
#include "message.h"
#include "misc.h"
#include "mon-util.h"
#include "mon-place.h"
#include "terrain.h"
#include "mgen_data.h"
#include "coord.h"
#include "makeitem.h"
#include "mon-stuff.h"
#include "mutation.h"
#include "ouch.h"
#include "religion.h"
#include "spells1.h"
#include "spells2.h"
#include "spells3.h"
#include "spells4.h"
#include "spl-mis.h"
#include "stash.h"
#include "state.h"
#include "transform.h"
#include "shout.h"
#include "xom.h"
#include <sstream>
static void _god_smites_you(god_type god, const char *message = NULL,
kill_method_type death_type = NUM_KILLBY);
static bool _beogh_idol_revenge();
static void _tso_blasts_cleansing_flame(const char *message = NULL);
static bool _tso_holy_revenge();
static bool _yred_random_zombified_hostile()
{
const bool skel = one_chance_in(4);
monster_type z_base;
monster_type z_type;
do
z_base = pick_random_zombie();
while (skel && !mons_skeleton(z_base));
if (mons_zombie_size(z_base) == Z_BIG)
z_type = skel ? MONS_SKELETON_LARGE : MONS_ZOMBIE_LARGE;
else
z_type = skel ? MONS_SKELETON_SMALL : MONS_ZOMBIE_SMALL;
return (create_monster(mgen_data::hostile_at(z_type,
"the anger of Yredelemnul", true,
0, 0, you.pos(), 0, GOD_YREDELEMNUL, z_base)) != -1);
}
static bool _okawaru_random_servant()
{
monster_type mon_type;
const int temp_rand = random2(100);
// warriors
mon_type = ((temp_rand < 15) ? MONS_ORC_WARRIOR : // 15%
(temp_rand < 30) ? MONS_ORC_KNIGHT : // 15%
(temp_rand < 40) ? MONS_NAGA_WARRIOR : // 10%
(temp_rand < 50) ? MONS_CENTAUR_WARRIOR : // 10%
(temp_rand < 60) ? MONS_STONE_GIANT : // 10%
(temp_rand < 70) ? MONS_FIRE_GIANT : // 10%
(temp_rand < 80) ? MONS_FROST_GIANT : // 10%
(temp_rand < 90) ? MONS_CYCLOPS : // 10%
(temp_rand < 95) ? MONS_HILL_GIANT // 5%
: MONS_TITAN); // 5%
return (create_monster(
mgen_data::hostile_at(mon_type, "the fury of Okawaru",
true, 6, MON_SUMM_WRATH, you.pos(), 0,
GOD_OKAWARU)) != -1);
}
static bool _tso_retribution()
{
// holy warriors/cleansing theme
const god_type god = GOD_SHINING_ONE;
int punishment = random2(7);
switch (punishment)
{
case 0:
case 1:
case 2: // summon holy warriors (3/7)
{
bool success = false;
int how_many = 1 + random2(you.experience_level / 5) + random2(3);
for (; how_many > 0; --how_many)
{
if (summon_holy_warrior(100, god, 0, true, true, true))
success = true;
}
simple_god_message(success ? " sends the divine host to punish "
"you for your evil ways!"
: "'s divine host fails to appear.", god);
break;
}
case 3:
case 4: // cleansing flame (2/7)
_tso_blasts_cleansing_flame();
break;
case 5:
case 6: // either noisiness or silence (2/7)
if (coinflip())
{
simple_god_message(" booms out: \"Take the path of righteousness! REPENT!\"", god);
noisy(25, you.pos()); // same as scroll of noise
}
else
{
god_speaks(god, "You feel the Shining One's silent rage upon you!");
cast_silence(25);
}
break;
}
return (false);
}
static void _zin_remove_good_mutations()
{
if (!how_mutated())
return;
bool success = false;
simple_god_message(" draws some chaos from your body!", GOD_ZIN);
bool failMsg = true;
for (int i = 7; i >= 0; --i)
{
// Ensure that only good mutations are removed.
if (i <= random2(10)
&& delete_mutation(RANDOM_GOOD_MUTATION, failMsg, false, true,
true))
{
success = true;
}
else
failMsg = false;
}
if (success && !how_mutated())
{
simple_god_message(" rids your body of chaos!", GOD_ZIN);
dec_penance(GOD_ZIN, 1);
}
}
static bool _zin_retribution()
{
// preaching/creeping doom theme
const god_type god = GOD_ZIN;
int punishment = random2(8);
// If not mutated, do something else instead.
if (punishment > 7 && !how_mutated())
punishment = random2(6);
switch (punishment)
{
case 0:
case 1:
case 2: // recital
simple_god_message(" recites the Axioms of Law to you!", god);
switch (random2(3))
{
case 0:
confuse_player(3 + random2(10), false);
break;
case 1:
you.hibernate();
break;
case 2:
you.paralyse(NULL, 3 + random2(10));
break;
}
break;
case 3:
case 4: // famine
simple_god_message(" sends a famine down upon you!", god);
make_hungry(you.hunger / 2, false);
break;
case 5: // noisiness
simple_god_message(" booms out: \"Turn to the light! REPENT!\"", god);
noisy(25, you.pos()); // same as scroll of noise
break;
case 6:
case 7: // remove good mutations
_zin_remove_good_mutations();
break;
}
return (false);
}
static void _ely_dull_inventory_weapons()
{
int chance = 50;
int num_dulled = 0;
int quiver_link;
you.m_quiver->get_desired_item(NULL, &quiver_link);
for (int i = 0; i < ENDOFPACK; ++i)
{
if (!you.inv[i].is_valid())
continue;
if (you.inv[i].base_type == OBJ_WEAPONS
|| you.inv[i].base_type == OBJ_MISSILES)
{
// Don't dull artefacts at all, or weapons below -1/-1.
if (you.inv[i].base_type == OBJ_WEAPONS)
{
if (is_artefact(you.inv[i]) || you.inv[i].plus <= -1
&& you.inv[i].plus2 <= -1)
{
continue;
}
}
// Don't dull missiles below -1.
else if (you.inv[i].plus <= -1)
continue;
// 2/3 of the time, don't do anything.
if (!one_chance_in(3))
continue;
bool wielded = false;
bool quivered = false;
if (you.inv[i].link == you.equip[EQ_WEAPON])
wielded = true;
if (you.inv[i].link == quiver_link)
quivered = true;
// Dull the weapon or missile(s).
if (you.inv[i].plus > -1)
you.inv[i].plus--;
if (you.inv[i].base_type == OBJ_WEAPONS
&& you.inv[i].plus2 > -1)
{
you.inv[i].plus2--;
}
// Update the weapon/ammo display, if necessary.
if (wielded)
you.wield_change = true;
if (quivered)
you.redraw_quiver = true;
chance += item_value(you.inv[i], true) / 50;
num_dulled++;
}
}
if (num_dulled > 0)
{
if (x_chance_in_y(chance + 1, 100))
dec_penance(GOD_ELYVILON, 1);
simple_god_message(
make_stringf(" dulls %syour weapons.",
num_dulled == 1 ? "one of " : "").c_str(),
GOD_ELYVILON);
}
}
static bool _elyvilon_retribution()
{
// healing/interference with fighting theme
const god_type god = GOD_ELYVILON;
simple_god_message("'s displeasure finds you.", god);
switch (random2(5))
{
case 0:
case 1:
confuse_player(3 + random2(10), false);
break;
case 2: // mostly flavour messages
MiscastEffect(&you, -god, SPTYP_POISON, one_chance_in(3) ? 1 : 0,
"the displeasure of Elyvilon");
break;
case 3:
case 4: // Dull weapons in your inventory.
_ely_dull_inventory_weapons();
break;
}
return (true);
}
static bool _cheibriados_retribution()
{
// time god/slowness theme
const god_type god = GOD_CHEIBRIADOS;
simple_god_message(" bends time around you.", god);
switch (random2(5))
{
case 0:
case 1:
case 2:
case 3:
mpr("You lose track of time.");
you.put_to_sleep(NULL, 50);
break;
case 4:
if (you.duration[DUR_SLOW] < 180 * BASELINE_DELAY)
{
dec_penance(god, 1);
mpr("You feel the world leave you behind!", MSGCH_WARN);
you.set_duration(DUR_EXHAUSTED, 200);
slow_player(100);
}
break;
}
return (true);
}
static bool _makhleb_retribution()
{
// demonic servant theme
const god_type god = GOD_MAKHLEB;
if (random2(you.experience_level) > 7 && !one_chance_in(5))
{
bool success = (create_monster(
mgen_data::hostile_at(
static_cast<monster_type>(
MONS_EXECUTIONER + random2(5)),
"the fury of Makhleb",
true, 0, 0, you.pos(), 0, god)) != -1);
simple_god_message(success ? " sends a greater servant after you!"
: "'s greater servant is unavoidably "
"detained.", god);
}
else
{
int how_many = 1 + (you.experience_level / 7);
int count = 0;
for (; how_many > 0; --how_many)
{
if (create_monster(
mgen_data::hostile_at(
static_cast<monster_type>(
MONS_NEQOXEC + random2(5)),
"the fury of Makhleb",
true, 0, 0, you.pos(), 0, god)) != -1)
{
count++;
}
}
simple_god_message(count > 1 ? " sends minions to punish you." :
count > 0 ? " sends a minion to punish you."
: "'s minions fail to arrive.", god);
}
return (true);
}
static bool _kikubaaqudgha_retribution()
{
// death/necromancy theme
const god_type god = GOD_KIKUBAAQUDGHA;
god_speaks(god, coinflip() ? "You hear Kikubaaqudgha cackling."
: "Kikubaaqudgha's malice focuses upon you.");
if (random2(you.experience_level) > 4)
{
// Either zombies, or corpse rot + skeletons.
receive_corpses(you.experience_level * 4, you.pos());
if (coinflip())
corpse_rot();
}
if (coinflip())
{
// necromancy miscast, 20% chance of additional miscast
do
{
MiscastEffect(&you, -god, SPTYP_NECROMANCY,
5 + you.experience_level,
random2avg(88, 3), "the malice of Kikubaaqudgha");
}
while (one_chance_in(5));
}
else if (one_chance_in(10))
{
// torment, or 3 necromancy miscasts
if (!player_res_torment(false))
torment(TORMENT_KIKUBAAQUDGHA, you.pos());
else
{
for (int i = 0; i < 3; ++i)
{
MiscastEffect(&you, -god, SPTYP_NECROMANCY,
5 + you.experience_level,
random2avg(88, 3), "the malice of Kikubaaqudgha");
}
}
}
// Every act of retribution causes corpses in view to rise against
// you.
animate_dead(&you, 1 + random2(3), BEH_HOSTILE, MHITYOU, 0,
"the malice of Kikubaaqudgha");
return (true);
}
static bool _yredelemnul_retribution()
{
// undead theme
const god_type god = GOD_YREDELEMNUL;
if (random2(you.experience_level) > 4)
{
if (you.religion == god && coinflip() && yred_slaves_abandon_you())
{
;
}
else
{
const bool zombified = one_chance_in(4);
int how_many = 1 + random2(1 + (you.experience_level / 5));
int count = 0;
for (; how_many > 0; --how_many)
{
if (zombified)
{
if (_yred_random_zombified_hostile())
count++;
}
else
count += yred_random_servants(100, true);
}
simple_god_message(count > 1 ? " sends servants to punish you." :
count > 0 ? " sends a servant to punish you."
: "'s servants fail to arrive.", god);
}
}
else
{
simple_god_message("'s anger turns toward you for a moment.", god);
MiscastEffect(&you, -god, SPTYP_NECROMANCY, 5 + you.experience_level,
random2avg(88, 3), "the anger of Yredelemnul");
}
return (true);
}
static bool _trog_retribution()
{
// physical/berserk theme
const god_type god = GOD_TROG;
if (coinflip())
{
int count = 0;
int points = 3 + you.experience_level * 3;
{
no_messages msg;
while (points > 0)
{
int cost = std::min(random2(8) + 3, points);
// quick reduction for large values
if (points > 20 && coinflip())
{
points -= 10;
cost = 10;
}
points -= cost;
if (summon_berserker(cost * 20, god, 0, true))
count++;
}
}
simple_god_message(count > 1 ? " sends monsters to punish you." :
count > 0 ? " sends a monster to punish you."
: " has no time to punish you... now.",
god);
}
else if (!one_chance_in(3))
{
simple_god_message("'s voice booms out, \"Feel my wrath!\"", god);
// A collection of physical effects that might be better
// suited to Trog than wild fire magic... messages could
// be better here... something more along the lines of apathy
// or loss of rage to go with the anti-berserk effect-- bwr
switch (random2(6))
{
case 0:
potion_effect(POT_DECAY, 100);
break;
case 1:
case 2:
lose_stat(STAT_STRENGTH, 1 + random2(you.strength / 5), true,
"divine retribution from Trog");
break;
case 3:
if (!you.duration[DUR_PARALYSIS])
{
dec_penance(god, 3);
mpr( "You suddenly pass out!", MSGCH_WARN );
you.duration[DUR_PARALYSIS] = 2 + random2(6);
}
break;
case 4:
case 5:
if (you.duration[DUR_SLOW] < 180 * BASELINE_DELAY)
{
dec_penance(god, 1);
mpr( "You suddenly feel exhausted!", MSGCH_WARN );
you.set_duration(DUR_EXHAUSTED, 200);
slow_player(100);
}
break;
}
}
else
{
//jmf: returned Trog's old Fire damage
// -- actually, this function partially exists to remove that,
// we'll leave this effect in, but we'll remove the wild
// fire magic. -- bwr
dec_penance(god, 2);
mpr("You feel Trog's fiery rage upon you!", MSGCH_WARN);
MiscastEffect(&you, -god, SPTYP_FIRE, 8 + you.experience_level,
random2avg(98, 3), "the fiery rage of Trog");
}
return (true);
}
static bool _beogh_retribution()
{
// orcish theme
const god_type god = GOD_BEOGH;
switch (random2(8))
{
case 0: // smiting (25%)
case 1:
_god_smites_you(GOD_BEOGH);
break;
case 2: // send out one or two dancing weapons (12.5%)
{
int num_created = 0;
int num_to_create = (coinflip()) ? 1 : 2;
// Need a species check, in case this retribution is a result of
// drawing the Wrath card.
const bool am_orc = (you.species == SP_HILL_ORC);
for (int i = 0; i < num_to_create; ++i)
{
const int temp_rand = random2(13);
const int wpn_type = ((temp_rand == 0) ? WPN_CLUB :
(temp_rand == 1) ? WPN_MACE :
(temp_rand == 2) ? WPN_FLAIL :
(temp_rand == 3) ? WPN_MORNINGSTAR :
(temp_rand == 4) ? WPN_DAGGER :
(temp_rand == 5) ? WPN_SHORT_SWORD :
(temp_rand == 6) ? WPN_LONG_SWORD :
(temp_rand == 7) ? WPN_SCIMITAR :
(temp_rand == 8) ? WPN_GREAT_SWORD :
(temp_rand == 9) ? WPN_HAND_AXE :
(temp_rand == 10) ? WPN_BATTLEAXE :
(temp_rand == 11) ? WPN_SPEAR
: WPN_HALBERD);
// Now create monster.
const int mon =
create_monster(
mgen_data::hostile_at(MONS_DANCING_WEAPON,
"the wrath of Beogh",
true, 0, 0, you.pos(), 0, god));
if (mon != -1)
{
ASSERT(menv[mon].weapon() != NULL);
item_def& wpn(*menv[mon].weapon());
// FIXME: Mega-hack (breaks encapsulation too).
wpn.flags &= ~ISFLAG_RACIAL_MASK;
if (am_orc)
set_item_ego_type(wpn, OBJ_WEAPONS, SPWPN_ORC_SLAYING);
else
{
wpn.flags |= ISFLAG_ORCISH;
set_item_ego_type(wpn, OBJ_WEAPONS, SPWPN_ELECTROCUTION);
}
if (coinflip())
wpn.flags |= ISFLAG_CURSED;
wpn.plus = random2(3);
wpn.plus2 = random2(3);
wpn.sub_type = wpn_type;
set_ident_flags(wpn, ISFLAG_KNOW_TYPE);
item_colour(wpn);
if (coinflip())
menv[mon].flags |= MF_HARD_RESET;
ghost_demon newstats;
newstats.init_dancing_weapon(wpn,
you.experience_level * 50 / 9);
menv[mon].set_ghost(newstats);
menv[mon].dancing_weapon_init();
num_created++;
}
}
if (num_created > 0)
{
std::ostringstream msg;
msg << " throws "
<< (num_created == 1 ? "an implement" : "implements")
<< " of " << (am_orc ? "orc slaying" : "electrocution")
<< " at you.";
simple_god_message(msg.str().c_str(), god);
break;
} // else fall through
}
case 3: // 25%, relatively harmless
case 4: // in effect, only for penance
if (you.religion == god && beogh_followers_abandon_you())
break;
// else fall through
default: // send orcs after you (3/8 to 5/8)
{
const int points = you.experience_level + 3
+ random2(you.experience_level * 3);
monster_type punisher;
// "natural" bands
if (points >= 30) // min: lvl 7, always: lvl 27
punisher = MONS_ORC_WARLORD;
else if (points >= 24) // min: lvl 6, always: lvl 21
punisher = MONS_ORC_HIGH_PRIEST;
else if (points >= 18) // min: lvl 4, always: lvl 15
punisher = MONS_ORC_KNIGHT;
else if (points > 10) // min: lvl 3, always: lvl 8
punisher = MONS_ORC_WARRIOR;
else
punisher = MONS_ORC;
int mons = create_monster(
mgen_data::hostile_at(punisher,
"the wrath of Beogh",
true, 0, 0, you.pos(), MG_PERMIT_BANDS, god));
// sometimes name band leader
if (mons != -1 && one_chance_in(3))
give_monster_proper_name(&menv[mons]);
simple_god_message(
mons != -1 ? " sends forth an army of orcs."
: " is still gathering forces against you.", god);
}
}
return (true);
}
static bool _okawaru_retribution()
{
// warrior theme
const god_type god = GOD_OKAWARU;
int how_many = 1 + (you.experience_level / 5);
int count = 0;
for (; how_many > 0; --how_many)
count += _okawaru_random_servant();
simple_god_message(count > 0 ? " sends forces against you!"
: "'s forces are busy with other wars.", god);
return (true);
}
static bool _sif_muna_retribution()
{
// magic/intelligence theme
const god_type god = GOD_SIF_MUNA;
simple_god_message("'s wrath finds you.", god);
dec_penance(god, 1);
switch (random2(10))
{
case 0:
case 1:
lose_stat(STAT_INTELLIGENCE, 1 + random2(you.intel / 5), true,
"divine retribution from Sif Muna");
break;
case 2:
case 3:
case 4:
confuse_player(3 + random2(10), false);
break;
case 5:
case 6:
MiscastEffect(&you, -god, SPTYP_DIVINATION, 9, 90,
"the will of Sif Muna");
break;
case 7:
case 8:
if (you.magic_points > 0)
{
dec_mp(100); // This should zero it.
mpr("You suddenly feel drained of magical energy!", MSGCH_WARN);
}
break;
case 9:
// This will set all the extendable duration spells to
// a duration of one round, thus potentially exposing
// the player to real danger.
antimagic();
mpr("You sense a dampening of magic.", MSGCH_WARN);
break;
}
return (true);
}
static bool _lugonu_retribution()
{
// abyssal servant theme
const god_type god = GOD_LUGONU;
if (coinflip())
{
simple_god_message("'s wrath finds you!", god);
MiscastEffect(&you, -god, SPTYP_TRANSLOCATION, 9, 90, "Lugonu's touch");
// No return - Lugonu's touch is independent of other effects.
}
else if (coinflip())
{
// Give extra opportunities for embarrassing teleports.
simple_god_message("'s wrath finds you!", god);
mpr("Space warps around you!");
if (!one_chance_in(3))
you_teleport_now(false);
else
random_blink(false);
// No return.
}
if (random2(you.experience_level) > 7 && !one_chance_in(5))
{
bool success = (create_monster(
mgen_data::hostile_at(
static_cast<monster_type>(
MONS_GREEN_DEATH + random2(3)),
"the touch of Lugonu",
true, 0, 0, you.pos(), 0, god)) != -1);
simple_god_message(success ? " sends a demon after you!"
: "'s demon is unavoidably detained.", god);
}
else
{
bool success = false;
int how_many = 1 + (you.experience_level / 7);
for (; how_many > 0; --how_many)
{
if (create_monster(
mgen_data::hostile_at(
static_cast<monster_type>(
MONS_NEQOXEC + random2(5)),
"the touch of Lugonu",
true, 0, 0, you.pos(), 0, god)) != -1)
{
success = true;
}
}
simple_god_message(success ? " sends minions to punish you."
: "'s minions fail to arrive.", god);
}
return (false);
}
static bool _vehumet_retribution()
{
// conjuration/summoning theme
const god_type god = GOD_VEHUMET;
simple_god_message("'s vengeance finds you.", god);
MiscastEffect(&you, -god, coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING,
8 + you.experience_level, random2avg(98, 3),
"the wrath of Vehumet");
return (true);
}
static bool _nemelex_retribution()
{
// card theme
const god_type god = GOD_NEMELEX_XOBEH;
// like Xom, this might actually help the player -- bwr
simple_god_message(" makes you draw from the Deck of Punishment.", god);
draw_from_deck_of_punishment();
return (true);
}
static bool _jiyva_retribution()
{
const god_type god = GOD_JIYVA;
if (you.can_safely_mutate() && one_chance_in(7))
{
const int mutat = 1 + random2(3);
god_speaks(god, "You feel Jiyva alter your body.");
for (int i = 0; i < mutat; ++i)
mutate(RANDOM_BAD_MUTATION, true, false, true);
}
else if (there_are_monsters_nearby() && coinflip())
{
int tries = 0;
bool found_one = false;
monsters *mon;
while (tries < 10)
{
mon = choose_random_nearby_monster(0);
if (!mon || !mon_can_be_slimified(mon)
|| mon->attitude != ATT_HOSTILE)
{
tries++;
continue;
}
else
{
found_one = true;
break;
}
}
if (found_one)
{
mprf(MSGCH_GOD, "Jiyva's putrescence saturates the %s!",
mon->name(DESC_NOCAP_THE).c_str());
slimify_monster(mon, true);
}
}
else if (!one_chance_in(3))
{
god_speaks(god, "Mutagenic energy floods into your body!");
contaminate_player(random2(you.penance[GOD_JIYVA]) / 2);
if (coinflip())
{
transformation_type form = TRAN_NONE;
switch (random2(3))
{
case 0:
form = TRAN_BAT;
break;
case 1:
form = TRAN_STATUE;
break;
case 2:
form = TRAN_SPIDER;
break;
}
if (transform(random2(you.penance[GOD_JIYVA]) * 2, form, true))
you.transform_uncancellable = false;
}
}
else
{
const monster_type slimes[] = {
MONS_GIANT_EYEBALL, MONS_EYE_OF_DRAINING,
MONS_EYE_OF_DEVASTATION, MONS_GREAT_ORB_OF_EYES,
MONS_GIANT_SPORE, MONS_SHINING_EYE, MONS_GIANT_ORANGE_BRAIN,
MONS_JELLY, MONS_BROWN_OOZE, MONS_ACID_BLOB, MONS_AZURE_JELLY,
MONS_DEATH_OOZE, MONS_SLIME_CREATURE
};
int how_many = 1 + (you.experience_level / 10) + random2(3);
bool success = false;
for (; how_many > 0; --how_many)
{
const monster_type slime = RANDOM_ELEMENT(slimes);
if (create_monster(
mgen_data::hostile_at(static_cast<monster_type>(slime),
"the vengence of Jiyva",
true, 0, 0, you.pos(), 0, god)) != -1)
{
success = true;
}
}
god_speaks(god, success ? "Some slimes ooze up out of the ground!"
: "The ground quivers slightly.");
}
return (true);
}
static bool _fedhas_retribution()
{
const god_type god = GOD_FEDHAS;
// We have 3 forms of retribution, but players under penance will be
// spared the 'you are now surrounded by oklob plants, please die' one.
const int retribution_options = you.religion == GOD_FEDHAS ? 2 : 3;
switch (random2(retribution_options))
{
case 0:
// Try and spawn some hostile giant spores, if none are created
// fall through to the elemental miscast effects.
if (corpse_spores(BEH_HOSTILE))
{
simple_god_message(" produces spores.", GOD_FEDHAS);
break;
}
case 1:
{
// Elemental miscast effects.
simple_god_message(" invokes the elements against you.", GOD_FEDHAS);
spschool_flag_type stype = SPTYP_NONE;
switch (random2(4))
{
case 0:
stype= SPTYP_ICE;
break;
case 1:
stype = SPTYP_EARTH;
break;
case 2:
stype = SPTYP_FIRE;
break;
case 3:
stype = SPTYP_AIR;
break;
};
MiscastEffect(&you, -god, stype, 5 + you.experience_level,
random2avg(88, 3), "the enmity of Fedhas Madash");
break;
}
case 2:
// We are going to spawn some oklobs but first we need to find
// out a little about the situation.
std::vector<std::vector<coord_def> > radius_points;
collect_radius_points(radius_points, you.pos(),
you.get_los_no_trans());
unsigned free_thresh = 30;
mgen_data temp(MONS_OKLOB_PLANT,
BEH_HOSTILE, 0, 0, 0,
coord_def(),
MHITNOT,
MG_FORCE_PLACE,
GOD_FEDHAS);
temp.non_actor_summoner = "the enmity of Fedhas Madash";
// If we have a lot of space to work with (the circle with
// radius 6 is substantially unoccupied), we can do something
// flashy.
if (radius_points[5].size() > free_thresh)
{
int seen_count;
temp.cls = MONS_PLANT;
place_ring(radius_points[0],
you.pos(),
temp,
1, radius_points[0].size(),
seen_count);
temp.cls = MONS_OKLOB_PLANT;
place_ring(radius_points[5],
you.pos(),
temp,
random_range(3, 8), 1,
seen_count);
}
// Otherwise we do something with the nearest neighbors
// (assuming the player isn't already surrounded).
else if (!radius_points[0].empty())
{
unsigned target_count = random_range(2, 8);
if (target_count < radius_points[0].size())
prioritise_adjacent(you.pos(), radius_points[0]);
else
target_count = radius_points[0].size();
unsigned i = radius_points[0].size() - target_count;
for (; i < radius_points[0].size(); ++i)
{
temp.pos = radius_points[0].at(i);
temp.cls = coinflip() ?
MONS_WANDERING_MUSHROOM : MONS_OKLOB_PLANT;
create_monster(temp,false);
}
god_speaks(god, "Plants grow around you in an ominous manner.");
return (false);
}
}
return (true);
}
bool divine_retribution(god_type god, bool no_bonus)
{
ASSERT(god != GOD_NO_GOD);
if (god == GOD_JIYVA && jiyva_is_dead())
return (false);
// Good gods don't use divine retribution on their followers, and
// gods don't use divine retribution on followers of gods they don't
// hate.
if ((god == you.religion && is_good_god(god))
|| (god != you.religion && !god_hates_your_god(god)))
{
return (false);
}
god_acting gdact(god, true);
bool do_more = true;
bool did_retrib = true;
switch (god)
{
// One in ten chance that Xom might do something good...
case GOD_XOM: xom_acts(one_chance_in(10), abs(you.piety - 100)); break;
case GOD_SHINING_ONE: do_more = _tso_retribution(); break;
case GOD_ZIN: do_more = _zin_retribution(); break;
case GOD_MAKHLEB: do_more = _makhleb_retribution(); break;
case GOD_KIKUBAAQUDGHA: do_more = _kikubaaqudgha_retribution(); break;
case GOD_YREDELEMNUL: do_more = _yredelemnul_retribution(); break;
case GOD_TROG: do_more = _trog_retribution(); break;
case GOD_BEOGH: do_more = _beogh_retribution(); break;
case GOD_OKAWARU: do_more = _okawaru_retribution(); break;
case GOD_LUGONU: do_more = _lugonu_retribution(); break;
case GOD_VEHUMET: do_more = _vehumet_retribution(); break;
case GOD_NEMELEX_XOBEH: do_more = _nemelex_retribution(); break;
case GOD_SIF_MUNA: do_more = _sif_muna_retribution(); break;
case GOD_ELYVILON: do_more = _elyvilon_retribution(); break;
case GOD_JIYVA: do_more = _jiyva_retribution(); break;
case GOD_FEDHAS: do_more = _fedhas_retribution(); break;
case GOD_CHEIBRIADOS: do_more = _cheibriados_retribution(); break;
default:
#if DEBUG_DIAGNOSTICS || DEBUG_RELIGION
mprf(MSGCH_DIAGNOSTICS, "No retribution defined for %s.",
god_name(god).c_str());
#endif
do_more = false;
did_retrib = false;
break;
}
if (no_bonus)
return (did_retrib);
// Sometimes divine experiences are overwhelming...
if (do_more && one_chance_in(5) && you.experience_level < random2(37))
{
if (coinflip())
{
mpr( "The divine experience confuses you!", MSGCH_WARN);
confuse_player(3 + random2(10));
}
else
{
if (you.duration[DUR_SLOW] < 180 * BASELINE_DELAY)
{
mpr( "The divine experience leaves you feeling exhausted!",
MSGCH_WARN );
slow_player(random2(20));
}
}
}
// Just the thought of retribution mollifies the god by at least a
// point...the punishment might have reduced penance further.
dec_penance(god, 1 + random2(3));
return (did_retrib);
}
bool do_god_revenge(conduct_type thing_done)
{
bool retval = false;
switch (thing_done)
{
case DID_DESTROY_ORCISH_IDOL:
retval = _beogh_idol_revenge();
break;
case DID_KILL_HOLY:
case DID_HOLY_KILLED_BY_UNDEAD_SLAVE:
case DID_HOLY_KILLED_BY_SERVANT:
retval = _tso_holy_revenge();
break;
default:
break;
}
return (retval);
}
// Currently only used when orcish idols have been destroyed.
static std::string _get_beogh_speech(const std::string key)
{
std::string result = getSpeakString("Beogh " + key);
if (result.empty())
return ("Beogh is angry!");
return (result);
}
// Destroying orcish idols (a.k.a. idols of Beogh) may anger Beogh.
static bool _beogh_idol_revenge()
{
god_acting gdact(GOD_BEOGH, true);
// Beogh watches his charges closely, but for others doesn't always
// notice.
if (you.religion == GOD_BEOGH
|| (you.species == SP_HILL_ORC && coinflip())
|| one_chance_in(3))
{
const char *revenge;
if (you.religion == GOD_BEOGH)
revenge = _get_beogh_speech("idol follower").c_str();
else if (you.species == SP_HILL_ORC)
revenge = _get_beogh_speech("idol hill orc").c_str();
else
revenge = _get_beogh_speech("idol other").c_str();
_god_smites_you(GOD_BEOGH, revenge);
return (true);
}
return (false);
}
static void _tso_blasts_cleansing_flame(const char *message)
{
// TSO won't protect you from his own cleansing flame, and Xom is too
// capricious to protect you from it.
if (you.religion != GOD_SHINING_ONE && you.religion != GOD_XOM
&& !player_under_penance() && x_chance_in_y(you.piety, MAX_PIETY * 2))
{
god_speaks(you.religion,
make_stringf("\"Mortal, I have averted the wrath of %s... "
"this time.\"",
god_name(GOD_SHINING_ONE).c_str()).c_str());
}
else
{
// If there's a message, display it before firing.
if (message)
god_speaks(GOD_SHINING_ONE, message);
simple_god_message(" blasts you with cleansing flame!",
GOD_SHINING_ONE);
// damage is 2d(pow), *3/2 for undead and demonspawn
cleansing_flame(5 + (you.experience_level * 7) / 12,
CLEANSING_FLAME_TSO, you.pos());
}
}
// Currently only used when holy beings have been killed.
static std::string _get_tso_speech(const std::string key)
{
std::string result = getSpeakString("TSO " + key);
if (result.empty())
return ("The Shining One is angry!");
return (result);
}
// Killing holy beings may anger TSO.
static bool _tso_holy_revenge()
{
god_acting gdact(GOD_SHINING_ONE, true);
// TSO watches evil god worshippers more closely.
if (!is_good_god(you.religion)
&& ((is_evil_god(you.religion) && one_chance_in(6))
|| one_chance_in(8)))
{
const char *revenge;
if (is_evil_god(you.religion))
revenge = _get_tso_speech("holy evil").c_str();
else
revenge = _get_tso_speech("holy other").c_str();
_tso_blasts_cleansing_flame(revenge);
return (true);
}
return (false);
}
static void _god_smites_you(god_type god, const char *message,
kill_method_type death_type)
{
ASSERT(god != GOD_NO_GOD);
// Your god won't protect you from his own smiting, and Xom is too
// capricious to protect you from any god's smiting.
if (you.religion != god && you.religion != GOD_XOM
&& !player_under_penance() && x_chance_in_y(you.piety, MAX_PIETY * 2))
{
god_speaks(you.religion,
make_stringf("\"Mortal, I have averted the wrath of %s... "
"this time.\"", god_name(god).c_str()).c_str());
}
else
{
if (death_type == NUM_KILLBY)
{
switch (god)
{
case GOD_BEOGH: death_type = KILLED_BY_BEOGH_SMITING; break;
case GOD_SHINING_ONE: death_type = KILLED_BY_TSO_SMITING; break;
default: death_type = KILLED_BY_DIVINE_WRATH; break;
}
}
std::string aux;
if (death_type != KILLED_BY_BEOGH_SMITING
&& death_type != KILLED_BY_TSO_SMITING)
{
aux = "smote by " + god_name(god);
}
// If there's a message, display it before smiting.
if (message)
god_speaks(god, message);
int divine_hurt = 10 + random2(10);
for (int i = 0; i < 5; ++i)
divine_hurt += random2(you.experience_level);
simple_god_message(" smites you!", god);
ouch(divine_hurt, NON_MONSTER, death_type, aux.c_str());
dec_penance(god, 1);
}
}