summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/debug.cc7
-rw-r--r--crawl-ref/source/fight.cc2
-rw-r--r--crawl-ref/source/mutation.cc38
-rw-r--r--crawl-ref/source/mutation.h3
-rw-r--r--crawl-ref/source/spl-cast.cc269
-rw-r--r--crawl-ref/source/spl-mis.h18
-rw-r--r--crawl-ref/source/xom.cc23
7 files changed, 275 insertions, 85 deletions
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 806be6e4ab..7ac71feebd 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -5090,6 +5090,13 @@ void debug_miscast( int target_index )
while (target->alive() && repeats-- > 0)
{
+ if (kbhit())
+ {
+ mpr("Key pressed, interrupting miscast testing.");
+ getchm();
+ break;
+ }
+
miscast->do_miscast();
if (level != 0)
_miscast_screen_update();
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 7187662e87..213110157d 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -2701,7 +2701,7 @@ void melee_attack::do_miscast()
}
MiscastEffect(miscast_target, source, (spschool_flag_type) miscast_type,
- miscast_level, cause, NH_NEVER, hand_str, false);
+ miscast_level, cause, NH_NEVER, 0, hand_str, false);
// Don't do miscast twice for one attack.
miscast_level = -1;
diff --git a/crawl-ref/source/mutation.cc b/crawl-ref/source/mutation.cc
index ee902d24c2..f531d437f1 100644
--- a/crawl-ref/source/mutation.cc
+++ b/crawl-ref/source/mutation.cc
@@ -2984,27 +2984,29 @@ int how_mutated(bool all, bool levels)
return (j);
}
-bool give_bad_mutation(bool failMsg, bool force_mutation)
+bool give_bad_mutation(bool failMsg, bool force_mutation, bool non_fatal)
{
mutation_type mutat = NUM_MUTATIONS;
- switch (random2(13))
- {
- case 0: mutat = MUT_CARNIVOROUS; break;
- case 1: mutat = MUT_HERBIVOROUS; break;
- case 2: mutat = MUT_FAST_METABOLISM; break;
- case 3: mutat = MUT_WEAK; break;
- case 4: mutat = MUT_DOPEY; break;
- case 5: mutat = MUT_CLUMSY; break;
- case 6: mutat = MUT_TELEPORT; break;
- case 7: mutat = MUT_DEFORMED; break;
- case 8: mutat = MUT_SCREAM; break;
- case 9: mutat = MUT_DETERIORATION; break;
- case 10: mutat = MUT_BLURRY_VISION; break;
- case 11: mutat = MUT_FRAIL; break;
- case 12: mutat = MUT_LOW_MAGIC; break;
- }
-
+ do {
+ switch (random2(13))
+ {
+ case 0: mutat = MUT_CARNIVOROUS; break;
+ case 1: mutat = MUT_HERBIVOROUS; break;
+ case 2: mutat = MUT_FAST_METABOLISM; break;
+ case 3: mutat = MUT_WEAK; break;
+ case 4: mutat = MUT_DOPEY; break;
+ case 5: mutat = MUT_CLUMSY; break;
+ case 6: mutat = MUT_TELEPORT; break;
+ case 7: mutat = MUT_DEFORMED; break;
+ case 8: mutat = MUT_SCREAM; break;
+ case 9: mutat = MUT_DETERIORATION; break;
+ case 10: mutat = MUT_BLURRY_VISION; break;
+ case 11: mutat = MUT_FRAIL; break;
+ case 12: mutat = MUT_LOW_MAGIC; break;
+ }
+ } while (non_fatal && !accept_mutation(mutat, true, true));
+
const bool result = mutate(mutat, failMsg, force_mutation);
if (result)
learned_something_new(TUT_YOU_MUTATED);
diff --git a/crawl-ref/source/mutation.h b/crawl-ref/source/mutation.h
index fb28c16378..cd6fb9d624 100644
--- a/crawl-ref/source/mutation.h
+++ b/crawl-ref/source/mutation.h
@@ -66,7 +66,8 @@ const char *mutation_name(mutation_type which_mutat, int level = -1);
/* ***********************************************************************
* called from: items - spells
* *********************************************************************** */
-bool give_bad_mutation(bool failMsg = true, bool force_mutation = false);
+bool give_bad_mutation(bool failMsg = true, bool force_mutation = false,
+ bool non_fatal = false);
// last updated 12may2000 {dlb}
/* ***********************************************************************
diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc
index 82d5096de8..83d521b401 100644
--- a/crawl-ref/source/spl-cast.cc
+++ b/crawl-ref/source/spl-cast.cc
@@ -2192,15 +2192,19 @@ void exercise_spell( spell_type spell, bool spc, bool success )
did_god_conduct( DID_SPELL_PRACTISE, exer_norm );
}
+#define MAX_RECURSE 100
+
MiscastEffect::MiscastEffect(actor* _target, int _source, spell_type _spell,
int _pow, int _fail, std::string _cause,
nothing_happens_when_type _nothing_happens,
- std::string _hand_str, bool _can_plural) :
+ int _lethality_margin, std::string _hand_str,
+ bool _can_plural) :
target(_target), source(_source), cause(_cause), spell(_spell),
school(SPTYP_NONE), pow(_pow), fail(_fail), level(-1), kc(KC_NCATEGORIES),
kt(KILL_NONE), mon_target(NULL), mon_source(NULL),
- nothing_happens_when(_nothing_happens), hand_str(_hand_str),
- can_plural_hand(_can_plural)
+ nothing_happens_when(_nothing_happens),
+ lethality_margin(_lethality_margin),
+ hand_str(_hand_str), can_plural_hand(_can_plural)
{
ASSERT(is_valid_spell(_spell));
unsigned int schools = get_spell_disciplines(_spell);
@@ -2215,12 +2219,14 @@ MiscastEffect::MiscastEffect(actor* _target, int _source,
spschool_flag_type _school, int _level,
std::string _cause,
nothing_happens_when_type _nothing_happens,
- std::string _hand_str, bool _can_plural) :
+ int _lethality_margin, std::string _hand_str,
+ bool _can_plural) :
target(_target), source(_source), cause(_cause), spell(SPELL_NO_SPELL),
school(_school), pow(-1), fail(-1), level(_level), kc(KC_NCATEGORIES),
kt(KILL_NONE), mon_target(NULL), mon_source(NULL),
- nothing_happens_when(_nothing_happens), hand_str(_hand_str),
- can_plural_hand(_can_plural)
+ nothing_happens_when(_nothing_happens),
+ lethality_margin(_lethality_margin),
+ hand_str(_hand_str), can_plural_hand(_can_plural)
{
ASSERT(!_cause.empty());
ASSERT(count_bits(_school) == 1);
@@ -2235,12 +2241,14 @@ MiscastEffect::MiscastEffect(actor* _target, int _source,
spschool_flag_type _school, int _pow, int _fail,
std::string _cause,
nothing_happens_when_type _nothing_happens,
- std::string _hand_str, bool _can_plural) :
+ int _lethality_margin, std::string _hand_str,
+ bool _can_plural) :
target(_target), source(_source), cause(_cause), spell(SPELL_NO_SPELL),
school(_school), pow(_pow), fail(_fail), level(-1), kc(KC_NCATEGORIES),
kt(KILL_NONE), mon_target(NULL), mon_source(NULL),
- nothing_happens_when(_nothing_happens), hand_str(_hand_str),
- can_plural_hand(_can_plural)
+ nothing_happens_when(_nothing_happens),
+ lethality_margin(_lethality_margin),
+ hand_str(_hand_str), can_plural_hand(_can_plural)
{
ASSERT(!_cause.empty());
ASSERT(count_bits(_school) == 1);
@@ -2250,6 +2258,11 @@ MiscastEffect::MiscastEffect(actor* _target, int _source,
do_miscast();
}
+MiscastEffect::~MiscastEffect()
+{
+ ASSERT(recursion_depth == 0);
+}
+
void MiscastEffect::init()
{
ASSERT(spell != SPELL_NO_SPELL && school == SPTYP_NONE
@@ -2260,6 +2273,10 @@ void MiscastEffect::init()
ASSERT(target != NULL);
ASSERT(target->alive());
+ ASSERT(lethality_margin == 0 || target->atype() == ACT_PLAYER);
+
+ recursion_depth = 0;
+
source_known = target_known = false;
mon_target = mon_source = NULL;
@@ -2417,10 +2434,16 @@ bool MiscastEffect::neither_end_silenced()
void MiscastEffect::do_miscast()
{
+ ASSERT(recursion_depth >= 0 && recursion_depth < MAX_RECURSE);
+
+ if (recursion_depth == 0)
+ did_msg = false;
+
+ unwind_var<int> unwind_depth(recursion_depth);
+ recursion_depth++;
+
// Repeated calls to do_miscast() on a single object instance have
- // killed a target which was alive when the object was created,
- // or the target is a dead but not-yet-exploded giant spore or
- // ball lightning.
+ // killed a target which was alive when the object was created.
if (!target->alive())
{
mprf(MSGCH_DIAGNOSTICS, "Miscast target '%s' already dead",
@@ -2537,9 +2560,13 @@ void MiscastEffect::do_miscast()
void MiscastEffect::do_msg(bool suppress_nothing_happnes)
{
+ ASSERT(!did_msg);
+
if (mon_target != NULL && !mons_near(mon_target))
return;
+ did_msg = true;
+
std::string msg;
if (!all_msg.empty())
@@ -2597,12 +2624,14 @@ void MiscastEffect::do_msg(bool suppress_nothing_happnes)
mpr(msg.c_str(), msg_ch);
}
-void MiscastEffect::_ouch(int dam, beam_type flavour)
+bool MiscastEffect::_ouch(int dam, beam_type flavour)
{
- do_msg(true);
+ // Delay do_msg() until after avoid_lethal()
if (target->atype() == ACT_MONSTER)
{
+ do_msg(true);
+
bolt beem;
beem.flavour = flavour;
@@ -2613,6 +2642,13 @@ void MiscastEffect::_ouch(int dam, beam_type flavour)
}
else
{
+ dam = check_your_resists(dam, flavour);
+
+ if (avoid_lethal(dam))
+ return (false);
+
+ do_msg(true);
+
kill_method_type method;
if (source == NON_MONSTER && spell != SPELL_NO_SPELL)
@@ -2639,21 +2675,76 @@ void MiscastEffect::_ouch(int dam, beam_type flavour)
else
method = KILLED_BY_SOMETHING;
- dam = check_your_resists(dam, flavour);
-
bool see_source = mon_source ? you.can_see(mon_source) : false;
ouch(dam, kill_source, method, cause.c_str(), see_source);
}
+ return (true);
}
-void MiscastEffect::_explosion()
+bool MiscastEffect::_explosion()
{
ASSERT(!beam.name.empty());
ASSERT(beam.damage.num != 0 && beam.damage.size != 0);
ASSERT(beam.flavour != BEAM_NONE);
+ int max_dam = beam.damage.num * beam.damage.size;
+ max_dam = check_your_resists(max_dam, beam.flavour);
+ if (avoid_lethal(max_dam))
+ return (false);
+
do_msg(true);
explosion(beam, false, true);
+
+ return (true);
+}
+
+bool MiscastEffect::_lose_stat(unsigned char which_stat,
+ unsigned char stat_loss)
+{
+ if (lethality_margin <= 0)
+ return lose_stat(which_stat, stat_loss, false, cause);
+
+ if (which_stat == STAT_RANDOM)
+ {
+ const int might = you.duration[DUR_MIGHT] ? 5 : 0;
+
+ std::vector<unsigned char> stat_types;
+ if ((you.strength - might - stat_loss) > 0)
+ stat_types.push_back(STAT_STRENGTH);
+ if ((you.intel - stat_loss) > 0)
+ stat_types.push_back(STAT_INTELLIGENCE);
+ if ((you.dex - stat_loss) > 0)
+ stat_types.push_back(STAT_DEXTERITY);
+
+ if (stat_types.size() == 0)
+ {
+ if (avoid_lethal(you.hp))
+ return (false);
+ else
+ return lose_stat(which_stat, stat_loss, false, cause);
+ }
+
+ which_stat = stat_types[random2(stat_types.size())];
+ }
+
+ int val;
+
+ switch(which_stat)
+ {
+ case STAT_STRENGTH: val = you.strength; break;
+ case STAT_INTELLIGENCE: val = you.intel; break;
+ case STAT_DEXTERITY: val = you.dex; break;
+
+ default: DEBUGSTR("Invalid stat type."); return (false);
+ }
+
+ if ((val - stat_loss) <= 0)
+ {
+ if (avoid_lethal(you.hp))
+ return (false);
+ }
+
+ return lose_stat(which_stat, stat_loss, false, cause);
}
void MiscastEffect::_potion_effect(int pot_eff, int pot_pow)
@@ -2708,6 +2799,38 @@ void MiscastEffect::send_abyss()
target->banish(cause);
}
+bool MiscastEffect::avoid_lethal(int dam)
+{
+ if (lethality_margin <= 0 || (you.hp - dam) > lethality_margin)
+ return (false);
+
+ if (recursion_depth == MAX_RECURSE)
+ {
+#if DEBUG_DIAGNOSTICS || DEBUG_MISCAST
+ mpr("Couldn't avoid lethal miscast: too much recursion.",
+ MSGCH_ERROR);
+#endif
+ return (false);
+ }
+
+ if (did_msg)
+ {
+#if DEBUG_DIAGNOSTICS || DEBUG_MISCAST
+ mpr("Couldn't avoid lethal miscast: already printed message for this "
+ "miscast.", MSGCH_ERROR);
+#endif
+ return (false);
+ }
+
+#if DEBUG_DIAGNOSTICS || DEBUG_MISCAST
+ mpr("Avoided lethal miscast.", MSGCH_DIAGNOSTICS);
+#endif
+
+ do_miscast();
+
+ return (true);
+}
+
bool MiscastEffect::_create_monster(monster_type what, int abj_deg,
bool alert)
{
@@ -3097,8 +3220,8 @@ void MiscastEffect::_translocation(int severity)
you_msg = "Space bends around you!";
mon_msg_seen = "Space bends around @the_monster@!";
mon_msg_unseen = "A piece of empty space twists and distorts.";
- _ouch(4 + random2avg(7, 2));
- target->blink(false);
+ if (_ouch(4 + random2avg(7, 2)))
+ target->blink(false);
break;
case 5:
if (_create_monster(MONS_SPATIAL_VORTEX, 3))
@@ -3127,7 +3250,8 @@ void MiscastEffect::_translocation(int severity)
mon_msg_seen = "Space warps around @the_monster!";
mon_msg_unseen = "A piece of empty space twists and writhes.";
- _ouch(5 + random2avg(9, 2));
+ if (!_ouch(5 + random2avg(9, 2)))
+ return;
if (one_chance_in(3))
target->teleport(true);
@@ -3173,7 +3297,9 @@ void MiscastEffect::_translocation(int severity)
mon_msg_seen = "Space warps crazily around @the_monster@!";
mon_msg_unseen = "A rift temporarily opens in the fabric of space!";
- _ouch(9 + random2avg(17, 2));
+ if (!_ouch(9 + random2avg(17, 2)))
+ return;
+
you_teleport_now( true );
potion_effect(POT_CONFUSION, 60);
break;
@@ -3435,12 +3561,11 @@ void MiscastEffect::_divination_you(int severity)
case 0:
if (you.is_undead)
mpr("You suddenly recall your previous life!");
- else if (lose_stat(STAT_INTELLIGENCE, 1 + random2(3),
- false, cause))
+ else if (_lose_stat(STAT_INTELLIGENCE, 1 + random2(3)))
{
mpr("You have damaged your brain!");
}
- else
+ else if (!did_msg)
mpr("You have a terrible headache.");
break;
case 1:
@@ -3466,12 +3591,11 @@ void MiscastEffect::_divination_you(int severity)
case 2:
if (you.is_undead)
mpr("You suddenly recall your previous life.");
- else if (lose_stat(STAT_INTELLIGENCE, 3 + random2(3),
- false, cause))
+ else if (_lose_stat(STAT_INTELLIGENCE, 3 + random2(3)))
{
mpr("You have damaged your brain!");
}
- else
+ else if (!did_msg)
mpr("You have a terrible headache.");
break;
}
@@ -3593,7 +3717,8 @@ void MiscastEffect::_necromancy(int severity)
}
break;
}
- do_msg();
+ if (!did_msg)
+ do_msg();
break;
case 2: // much nastier
@@ -3623,7 +3748,11 @@ void MiscastEffect::_necromancy(int severity)
if (target->atype() == ACT_PLAYER && !player_prot_life()
&& one_chance_in(3))
{
- drain_exp();
+ if (lethality_margin == 0 || you.experience > 0
+ || !avoid_lethal(you.hp))
+ {
+ drain_exp();
+ }
break;
} // otherwise it just flows through...
@@ -3632,6 +3761,7 @@ void MiscastEffect::_necromancy(int severity)
if (target->res_torment())
{
you_msg = "You feel weird for a moment.";
+ do_msg();
}
else
{
@@ -3639,7 +3769,6 @@ void MiscastEffect::_necromancy(int severity)
"your body!";
_ouch(15 + random2avg(23, 2));
}
- do_msg();
break;
}
break;
@@ -3684,6 +3813,12 @@ void MiscastEffect::_necromancy(int severity)
break;
case 4:
+ if (lethality_margin > 0 && you.experience == 0
+ && avoid_lethal(you.hp))
+ {
+ return;
+ }
+
mpr("You are engulfed in negative energy!");
if (!player_prot_life())
@@ -3693,7 +3828,7 @@ void MiscastEffect::_necromancy(int severity)
} // otherwise it just flows through...
case 5:
- lose_stat(STAT_RANDOM, 1 + random2avg(7, 2), false, cause);
+ _lose_stat(STAT_RANDOM, 1 + random2avg(7, 2));
break;
}
@@ -3799,31 +3934,48 @@ void MiscastEffect::_transmigration(int severity)
switch (random2(3))
{
case 0:
- if (target->atype() == ACT_PLAYER)
- {
- mpr("Your body is flooded with distortional energies!");
- you.magic_contamination += random2avg(35, 3);
- }
+ you_msg = "Your body is flooded with distortional energies!";
- _ouch(3 + random2avg(18, 2));
+ if (_ouch(3 + random2avg(18, 2)) && target->atype() == ACT_PLAYER)
+ you.magic_contamination += random2avg(35, 3);
break;
case 1:
+ // HACK: Avoid lethality before deleting mutation, since
+ // afterwards a message would already have been given.
+ if (lethality_margin > 0
+ && (you.hp - lethality_margin) <= 27
+ && avoid_lethal(you.hp))
+ {
+ return;
+ }
+
if (target->atype() == ACT_PLAYER)
{
- mpr("You feel very strange.");
- delete_mutation(RANDOM_MUTATION);
+ you_msg = "You feel very strange.";
+ delete_mutation(RANDOM_MUTATION, true, false,
+ lethality_margin > 0);
}
_ouch(5 + random2avg(23, 2));
break;
case 2:
+ // HACK: Avoid lethality before giving mutation, since
+ // afterwards a message would already have been given.
+ if (lethality_margin > 0
+ && (you.hp - lethality_margin) <= 27
+ && avoid_lethal(you.hp))
+ {
+ return;
+ }
+
if (target->atype() == ACT_PLAYER)
{
- mpr("Your body is distorted in a weirdly horrible way!");
- const bool failMsg = !give_bad_mutation();
+ you_msg = "Your body is distorted in a weirdly horrible way!";
+ const bool failMsg = !give_bad_mutation(true, false,
+ lethality_margin > 0);
if (coinflip())
- give_bad_mutation(failMsg);
+ give_bad_mutation(failMsg, false, lethality_margin > 0);
}
_ouch(5 + random2avg(23, 2));
break;
@@ -3908,7 +4060,10 @@ void MiscastEffect::_fire(int severity)
mon_msg_seen = "Flames sear @the_monster@.";
if (target->res_fire() < 0)
- _ouch(2 + random2avg(13, 2));
+ {
+ if (!_ouch(2 + random2avg(13, 2)))
+ return;
+ }
else
do_msg();
target->expose_to_element(BEAM_FIRE, 3);
@@ -3925,8 +4080,8 @@ void MiscastEffect::_fire(int severity)
mon_msg_seen = "@The_monster@ is blasted with fire.";
mon_msg_unseen = "A flame briefly burns in thin air.";
- _ouch(5 + random2avg(29, 2), BEAM_FIRE);
- target->expose_to_element(BEAM_FIRE, 5);
+ if (_ouch(5 + random2avg(29, 2), BEAM_FIRE))
+ target->expose_to_element(BEAM_FIRE, 5);
break;
case 1:
@@ -3953,9 +4108,8 @@ void MiscastEffect::_fire(int severity)
mon_msg_unseen = "A large flame burns hotly for a moment in the "
"thin air.";
- _ouch(9 + random2avg(33, 2), BEAM_FIRE);
-
- target->expose_to_element(BEAM_FIRE, 10);
+ if (_ouch(9 + random2avg(33, 2), BEAM_FIRE))
+ target->expose_to_element(BEAM_FIRE, 10);
break;
case 1:
all_msg = "There is a sudden and violent explosion of flames!";
@@ -4059,7 +4213,10 @@ void MiscastEffect::_ice(int severity)
mon_msg_seen = "@The_monster@ is covered in a thin layer of ice.";
if (target->res_cold() < 0)
- _ouch(4 + random2avg(5, 2));
+ {
+ if (!_ouch(4 + random2avg(5, 2)))
+ return;
+ }
else
do_msg();
target->expose_to_element(BEAM_COLD, 2);
@@ -4074,9 +4231,8 @@ void MiscastEffect::_ice(int severity)
you_msg = "Heat is drained from your body.";
// Monster messages needed.
- _ouch(5 + random2(6) + random2(7), BEAM_COLD);
-
- target->expose_to_element(BEAM_COLD, 4);
+ if (_ouch(5 + random2(6) + random2(7), BEAM_COLD))
+ target->expose_to_element(BEAM_COLD, 4);
break;
case 1:
@@ -4102,9 +4258,8 @@ void MiscastEffect::_ice(int severity)
you_msg = "You are blasted with ice!";
mon_msg_seen = "@The_monster@ is blasted with ice!";
- _ouch(9 + random2avg(23, 2), BEAM_ICE);
-
- target->expose_to_element(BEAM_COLD, 9);
+ if (_ouch(9 + random2avg(23, 2), BEAM_ICE))
+ target->expose_to_element(BEAM_COLD, 9);
break;
case 1:
you_msg = "Freezing gasses pour from your @hands@!";
@@ -4504,7 +4659,7 @@ void MiscastEffect::_poison(int severity)
if (player_res_poison())
canned_msg(MSG_NOTHING_HAPPENS);
else
- lose_stat(STAT_RANDOM, 1, false, cause);
+ _lose_stat(STAT_RANDOM, 1);
break;
}
break;
@@ -4534,7 +4689,7 @@ void MiscastEffect::_poison(int severity)
if (player_res_poison())
canned_msg(MSG_NOTHING_HAPPENS);
else
- lose_stat(STAT_RANDOM, 1 + random2avg(5, 2), false, cause);
+ _lose_stat(STAT_RANDOM, 1 + random2avg(5, 2));
break;
}
break;
diff --git a/crawl-ref/source/spl-mis.h b/crawl-ref/source/spl-mis.h
index 781e999795..c1f3dee006 100644
--- a/crawl-ref/source/spl-mis.h
+++ b/crawl-ref/source/spl-mis.h
@@ -44,16 +44,22 @@ public:
MiscastEffect(actor* _target, int _source, spell_type _spell, int _pow,
int _fail, std::string _cause = "",
nothing_happens_when_type _nothing_happens = NH_DEFAULT,
+ int _lethality_margin = 0,
std::string _hand_str = "", bool _can_plural_hand = true);
MiscastEffect(actor* _target, int _source, spschool_flag_type _school,
int _level, std::string _cause,
nothing_happens_when_type _nothing_happens = NH_DEFAULT,
+ int _lethality_margin = 0,
std::string _hand_str = "", bool _can_plural_hand = true);
MiscastEffect(actor* _target, int _source, spschool_flag_type _school,
int _pow, int _fail, std::string _cause,
nothing_happens_when_type _nothing_happens = NH_DEFAULT,
+ int _lethality_margin = 0,
std::string _hand_str = "", bool _can_plural_hand = true);
+
+ ~MiscastEffect();
+
void do_miscast();
private:
@@ -78,6 +84,8 @@ private:
nothing_happens_when_type nothing_happens_when;
+ int lethality_margin;
+
std::string hand_str;
bool can_plural_hand;
@@ -97,6 +105,9 @@ private:
msg_channel_type msg_ch;
+ int recursion_depth;
+ bool did_msg;
+
private:
void init();
std::string get_default_cause();
@@ -104,12 +115,15 @@ private:
bool neither_end_silenced();
void do_msg(bool suppress_nothing_happens = false);
- void _ouch(int dam, beam_type flavour = BEAM_NONE);
- void _explosion();
+ bool _ouch(int dam, beam_type flavour = BEAM_NONE);
+ bool _explosion();
+ bool _lose_stat(unsigned char which_stat, unsigned char stat_loss);
void _potion_effect(int pot_eff, int pow);
bool _create_monster(monster_type what, int abj_deg, bool alert = false);
void send_abyss();
+ bool avoid_lethal(int dam);
+
void _conjuration(int severity);
void _enchantment(int severity);
void _translocation(int severity);
diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc
index 7f93085920..4902fb2217 100644
--- a/crawl-ref/source/xom.cc
+++ b/crawl-ref/source/xom.cc
@@ -1103,6 +1103,14 @@ static bool _xom_is_bad(int sever, int tension)
const bool nasty = you.penance[GOD_XOM]
|| (you.religion == GOD_XOM && you.gift_timeout == 0);
+ // If not being nasty then prevent spell miscasts from killing the
+ // player.
+ int lethality_margin;
+ if (nasty)
+ lethality_margin = 0;
+ else
+ lethality_margin = random_range(1, 4);
+
god_acting gdact(GOD_XOM);
while (!done)
@@ -1120,12 +1128,13 @@ static bool _xom_is_bad(int sever, int tension)
done = true;
}
- else if (x_chance_in_y(4, sever) && (nasty || you.hp > 12))
+ else if (x_chance_in_y(4, sever))
{
god_speaks(GOD_XOM, _get_xom_speech("minor miscast effect").c_str());
MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(2),
- "the capriciousness of Xom");
+ "the capriciousness of Xom", NH_DEFAULT,
+ lethality_margin);
done = true;
}
@@ -1165,12 +1174,13 @@ static bool _xom_is_bad(int sever, int tension)
done = true;
}
- else if (x_chance_in_y(6, sever) && (nasty || you.hp > 25))
+ else if (x_chance_in_y(6, sever))
{
god_speaks(GOD_XOM, _get_xom_speech("medium miscast effect").c_str());
MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(3),
- "the capriciousness of Xom");
+ "the capriciousness of Xom", NH_DEFAULT,
+ lethality_margin);
done = true;
}
@@ -1368,12 +1378,13 @@ static bool _xom_is_bad(int sever, int tension)
}
}
}
- else if (x_chance_in_y(14, sever) && (nasty || you.hp > 30))
+ else if (x_chance_in_y(14, sever))
{
god_speaks(GOD_XOM, _get_xom_speech("major miscast effect").c_str());
MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(4),
- "the severe capriciousness of Xom");
+ "the severe capriciousness of Xom", NH_DEFAULT,
+ lethality_margin);
done = true;
}