summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/docs/changes.stone_soup4
-rw-r--r--crawl-ref/source/chardump.cc10
-rw-r--r--crawl-ref/source/dat/database/godspeak.txt10
-rw-r--r--crawl-ref/source/decks.cc4
-rw-r--r--crawl-ref/source/delay.cc2
-rw-r--r--crawl-ref/source/effects.cc10
-rw-r--r--crawl-ref/source/fight.cc5
-rw-r--r--crawl-ref/source/files.cc24
-rw-r--r--crawl-ref/source/item_use.cc88
-rw-r--r--crawl-ref/source/itemprop.cc27
-rw-r--r--crawl-ref/source/itemprop.h3
-rw-r--r--crawl-ref/source/misc.cc5
-rw-r--r--crawl-ref/source/monstuff.cc11
-rw-r--r--crawl-ref/source/monstuff.h2
-rw-r--r--crawl-ref/source/output.cc12
-rw-r--r--crawl-ref/source/player.cc8
-rw-r--r--crawl-ref/source/stuff.cc2
-rw-r--r--crawl-ref/source/terrain.cc8
-rw-r--r--crawl-ref/source/xom.cc291
-rw-r--r--crawl-ref/source/xom.h4
20 files changed, 436 insertions, 94 deletions
diff --git a/crawl-ref/docs/changes.stone_soup b/crawl-ref/docs/changes.stone_soup
index 746ecc73e9..e30834b0f6 100644
--- a/crawl-ref/docs/changes.stone_soup
+++ b/crawl-ref/docs/changes.stone_soup
@@ -115,7 +115,7 @@ Items
* Missile enchantment decreases chance of destruction on impact.
* Freezing weapons can now slow cold-blooded monsters.
* The staff of channeling now trains Evocations.
-* Potions of magic no longer increase max. mana.
+* Potions of magic no longer increase max. magic points.
* Item descriptions now mention enchantment/charging limits.
* Randarts now get their known properties autoinscribed.
* Randarts are now noted as identified even if the plusses are still unknown.
@@ -147,7 +147,7 @@ Gods
* Beogh accepts more kinds of kills.
* Beogh has a non-cannibalism conduct.
* Disallow shapeshifters in orc form to become Beoghites' followers.
-* Can see card descriptions when using Nemelex' Triple Draw or Stack Five.
+* Offer card descriptions when using Nemelex' Triple Draw or Stack Five.
Tiles
-----
diff --git a/crawl-ref/source/chardump.cc b/crawl-ref/source/chardump.cc
index 4ed7d4b4f3..7d246a7561 100644
--- a/crawl-ref/source/chardump.cc
+++ b/crawl-ref/source/chardump.cc
@@ -56,6 +56,7 @@ REVISION("$Rev$");
#include "transfor.h"
#include "version.h"
#include "view.h"
+#include "xom.h"
struct dump_params;
@@ -650,6 +651,15 @@ static void _sdump_religion(dump_params &par)
text += " " + verb + " demanding penance.\n";
}
}
+ else
+ {
+ if (par.se)
+ text += "You were ";
+ else
+ text += "You are ";
+ text += describe_xom_favour(false);
+ text += "\n";
+ }
}
}
diff --git a/crawl-ref/source/dat/database/godspeak.txt b/crawl-ref/source/dat/database/godspeak.txt
index 86c95b00a7..573990921c 100644
--- a/crawl-ref/source/dat/database/godspeak.txt
+++ b/crawl-ref/source/dat/database/godspeak.txt
@@ -378,6 +378,16 @@ Xom brings you back to life.
Xom smiles on you.
%%%%
+Xom repel stairs
+
+Xom alters the dungeon around you.
+
+Xom changes the scenery.
+
+Xom pokes at a staircase.
+
+"Tag, you're it!"
+%%%%
# Xom laughing
# (Currently only used post-game in response to "You die...")
Xom laughter
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc
index 048c2b3d56..76dec12063 100644
--- a/crawl-ref/source/decks.cc
+++ b/crawl-ref/source/decks.cc
@@ -1685,9 +1685,9 @@ static void _stairs_card(int power, deck_rarity_type rarity)
you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
if (grid_stair_direction(grd(you.pos())) == CMD_NO_CMD)
- you.duration[DUR_REPEL_STAIRS_MOVE] = 1000;
+ you.duration[DUR_REPEL_STAIRS_MOVE] = 1000;
else
- you.duration[DUR_REPEL_STAIRS_CLIMB] = 1000;
+ you.duration[DUR_REPEL_STAIRS_CLIMB] = 500; // more annoying
std::vector<coord_def> stairs_avail;
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc
index 68b662178b..d8a1c7ff85 100644
--- a/crawl-ref/source/delay.cc
+++ b/crawl-ref/source/delay.cc
@@ -1190,7 +1190,7 @@ static void _finish_delay(const delay_queue_item &delay)
if (m)
{
// One square, a few squares, anywhere...
- if (!shift_monster(m) && !monster_blink(m))
+ if (!shift_monster(m) && !monster_blink(m, true))
monster_teleport(m, true, true);
}
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 509fc8c2c8..2bccb6103a 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -3201,15 +3201,15 @@ void handle_time(long time_delta)
mutagenic_randart = true;
}
- // we take off about .5 points per turn
+ // We take off about .5 points per turn.
if (!you.duration[DUR_INVIS] && !you.duration[DUR_HASTE] && coinflip())
added_contamination--;
- // only punish if contamination caused by mutagenic randarts
- // (haste and invisibility already penalized earlier)
+ // Only punish if contamination caused by mutagenic randarts.
+ // (Haste and invisibility already penalized earlier.)
contaminate_player( added_contamination, mutagenic_randart );
- // only check for badness once every other turn
+ // Only check for badness once every other turn.
if (coinflip())
{
// [ds] Be less harsh with glow mutation; Brent and Mark Mackey note
@@ -3255,7 +3255,7 @@ void handle_time(long time_delta)
beam.explode();
}
- // we want to warp the player, not do good stuff!
+ // We want to warp the player, not do good stuff!
if (one_chance_in(5))
mutate(RANDOM_MUTATION);
else
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index f673ba2435..0bbd1a3158 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -66,6 +66,7 @@ REVISION("$Rev$");
#define NOTE_DEBUG_CHAOS_EFFECTS
#endif
+#define NOTE_DEBUG_CHAOS_EFFECTS
#ifdef NOTE_DEBUG_CHAOS_EFFECTS
#include "notes.h"
#endif
@@ -2934,8 +2935,8 @@ int melee_attack::random_chaos_brand()
}
break;
case SPWPN_CONFUSE:
- if (defender->holiness() != MH_NONLIVING
- && defender->holiness() != MH_PLANT)
+ if (defender->holiness() == MH_NONLIVING
+ || defender->holiness() == MH_PLANT)
{
susceptible = false;
}
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index 107a86b710..d6f163c63d 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -1371,24 +1371,30 @@ bool load( dungeon_feature_type stair_taken, load_mode_type load_mode,
dungeon_feature_type feat = grd(you.pos());
if (feat != DNGN_ENTER_SHOP
&& grid_stair_direction(feat) != CMD_NO_CMD
- && grid_stair_direction(stair_taken) != CMD_NO_CMD
- && coinflip()
- && slide_feature_over(you.pos(), coord_def(-1, -1), false))
+ && grid_stair_direction(stair_taken) != CMD_NO_CMD)
{
std::string stair_str =
feature_description(feat, NUM_TRAPS, false,
DESC_CAP_THE, false);
std::string verb = stair_climb_verb(feat);
- mprf("%s slides away from you right after you %s through it!",
- stair_str.c_str(), verb.c_str());
+ if (coinflip()
+ && slide_feature_over(you.pos(), coord_def(-1, -1), false))
+ {
+ mprf("%s slides away from you right after you %s through "
+ "it!", stair_str.c_str(), verb.c_str());
+ }
+
+ if (coinflip())
+ {
+ // Stairs stop fleeing from you now you actually caught one.
+ mprf("%s settles down.", stair_str.c_str(), verb.c_str());
+ you.duration[DUR_REPEL_STAIRS_MOVE] = 0;
+ you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
+ }
}
}
- // Stairs running from you is done now that you actually caught one.
- you.duration[DUR_REPEL_STAIRS_MOVE] = 0;
- you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
-
// If butchering was interrupted by switching levels (banishment)
// then switch back from butchering tool if there's no hostiles
// nearby.
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 0ecc9228cc..9e57ea98e6 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -4381,8 +4381,10 @@ static bool _vorpalise_weapon()
bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
{
+ bool to_hit = (which_stat == ENCHANT_TO_HIT);
+
// Cannot be enchanted nor uncursed.
- if (!is_enchantable_weapon(wpn, true))
+ if (!is_enchantable_weapon(wpn, true, to_hit))
{
if (!quiet)
canned_msg( MSG_NOTHING_HAPPENS );
@@ -4394,14 +4396,17 @@ bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
// Missiles only have one stat.
if (wpn.base_type == OBJ_MISSILES)
+ {
which_stat = ENCHANT_TO_HIT;
+ to_hit = true;
+ }
- int enchant_level = (which_stat == ENCHANT_TO_HIT) ? wpn.plus
- : wpn.plus2;
+ int enchant_level = (to_hit ? wpn.plus
+ : wpn.plus2);
// Even if not affected, it may be uncursed.
- if (!is_enchantable_weapon(wpn, false)
- || enchant_level >= 4 && x_chance_in_y(enchant_level, 9))
+ if (!is_enchantable_weapon(wpn, false, to_hit)
+ || enchant_level >= 4 && x_chance_in_y(enchant_level, MAX_WPN_ENCHANT))
{
if (is_cursed)
{
@@ -4419,6 +4424,10 @@ bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
if (!quiet)
canned_msg(MSG_NOTHING_HAPPENS);
+ // Xom thinks it's funny if enchantment is possible but fails.
+ if (is_enchantable_weapon(wpn, false, to_hit))
+ xom_is_stimulated(32);
+
return (false);
}
}
@@ -4428,7 +4437,7 @@ bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
if (wpn.base_type == OBJ_WEAPONS)
{
- if (which_stat == ENCHANT_TO_HIT)
+ if (to_hit)
{
if (!quiet)
mprf("%s glows green for a moment.", iname.c_str());
@@ -4457,7 +4466,6 @@ bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn)
if (is_cursed)
do_uncurse_item(wpn);
- xom_is_stimulated(16);
return (true);
}
@@ -4540,6 +4548,10 @@ bool enchant_armour(int &ac_change, bool quiet, item_def &arm)
if (!quiet)
canned_msg(MSG_NOTHING_HAPPENS);
+ // Xom thinks it's funny if enchantment is possible but fails.
+ if (is_enchantable_armour(arm, false))
+ xom_is_stimulated(32);
+
return (false);
}
}
@@ -4557,7 +4569,6 @@ bool enchant_armour(int &ac_change, bool quiet, item_def &arm)
if (is_cursed)
do_uncurse_item(arm);
- xom_is_stimulated(16);
return (true);
}
@@ -4978,38 +4989,57 @@ void read_scroll(int slot)
const bool is_cursed = item_cursed(wpn);
+ if (wpn.base_type != OBJ_WEAPONS && wpn.base_type != OBJ_MISSILES
+ || !is_cursed
+ && !is_enchantable_weapon(wpn, true, true)
+ && !is_enchantable_weapon(wpn, true, false))
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ id_the_scroll = false;
+ break;
+ }
+ // It's a weapon or stack of missiles that is not an artefact
+ // and not fully enchanted, or at least needs to be uncursed.
+
// Get item name now before changing enchantment.
std::string iname = wpn.name(DESC_CAP_YOUR);
- if (wpn.base_type == OBJ_WEAPONS)
- {
- mprf("%s glows bright yellow for a while.", iname.c_str());
+ // Uncursing is always possible.
+ bool success = is_cursed;
+ if (_handle_enchant_weapon(ENCHANT_TO_HIT, true))
+ success = true;
- _handle_enchant_weapon(ENCHANT_TO_HIT, true);
+ if (is_enchantable_weapon(wpn, true, true) && coinflip()
+ && _handle_enchant_weapon(ENCHANT_TO_HIT, true))
+ {
+ success = true;
+ }
- if (coinflip())
- _handle_enchant_weapon(ENCHANT_TO_HIT, true);
+ // Only weapons use the second stat.
+ if (wpn.base_type == OBJ_WEAPONS)
+ {
+ if (_handle_enchant_weapon(ENCHANT_TO_DAM, true))
+ success = true;
- _handle_enchant_weapon(ENCHANT_TO_DAM, true);
+ if (is_enchantable_weapon(wpn, true, false) && coinflip()
+ && _handle_enchant_weapon(ENCHANT_TO_DAM, true))
+ {
+ success = true;
+ }
+ }
- if (coinflip())
- _handle_enchant_weapon(ENCHANT_TO_DAM, true);
+ if (is_cursed)
+ do_uncurse_item(wpn);
- if (is_cursed)
- do_uncurse_item(wpn);
- }
- else if (wpn.base_type == OBJ_MISSILES)
+ if (success)
{
mprf("%s glow%s bright yellow for a while.", iname.c_str(),
wpn.quantity > 1 ? "" : "s");
-
- _handle_enchant_weapon(ENCHANT_TO_HIT, true);
-
- if (coinflip())
- _handle_enchant_weapon(ENCHANT_TO_HIT, true);
-
- if (is_cursed)
- do_uncurse_item(wpn);
+ }
+ else
+ {
+ canned_msg(MSG_NOTHING_HAPPENS);
+ id_the_scroll = false;
}
}
else
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index 829b6ca1b9..21fd25c77c 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -484,6 +484,17 @@ void do_curse_item( item_def &item, bool quiet )
if (item.flags & ISFLAG_CURSED)
return;
+ if (!quiet)
+ {
+ mprf("Your %s glows black for a moment.",
+ item.name(DESC_PLAIN).c_str());
+
+ // If we get the message, we know the item is cursed now.
+ item.flags |= ISFLAG_KNOW_CURSE;
+ }
+
+ item.flags |= ISFLAG_CURSED;
+
// Xom is amused by the player's items being cursed, especially
// if they're worn/equipped.
if (in_inventory(item))
@@ -508,17 +519,6 @@ void do_curse_item( item_def &item, bool quiet )
}
xom_is_stimulated(amusement);
}
-
- if (!quiet)
- {
- mprf("Your %s glows black for a moment.",
- item.name(DESC_PLAIN).c_str());
-
- // If we get the message, we know the item is cursed now.
- item.flags |= ISFLAG_KNOW_CURSE;
- }
-
- item.flags |= ISFLAG_CURSED;
}
void do_uncurse_item( item_def &item )
@@ -1389,7 +1389,7 @@ int wand_charge_value(int type)
}
}
-bool is_enchantable_weapon(const item_def &wpn, bool uncurse)
+bool is_enchantable_weapon(const item_def &wpn, bool uncurse, bool first)
{
if (wpn.base_type != OBJ_WEAPONS && wpn.base_type != OBJ_MISSILES)
return (false);
@@ -1399,7 +1399,8 @@ bool is_enchantable_weapon(const item_def &wpn, bool uncurse)
if (wpn.base_type == OBJ_WEAPONS)
{
if (is_artefact(wpn)
- || wpn.plus >= MAX_WPN_ENCHANT && wpn.plus2 >= MAX_WPN_ENCHANT)
+ || first && wpn.plus >= MAX_WPN_ENCHANT
+ || !first && wpn.plus2 >= MAX_WPN_ENCHANT)
{
return (uncurse && item_cursed(wpn));
}
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 63a77f7c41..a4b88ea11b 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -674,7 +674,8 @@ bool check_armour_shape( const item_def &item, bool quiet );
bool item_is_rechargeable(const item_def &it, bool hide_charged = false,
bool weapons = false);
int wand_charge_value(int type);
-bool is_enchantable_weapon(const item_def &wpn, bool uncurse);
+bool is_enchantable_weapon(const item_def &wpn, bool uncurse,
+ bool first = true);
bool is_enchantable_armour(const item_def &arm, bool uncurse,
bool unknown = false);
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 589fdfe492..488babbb43 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -1635,6 +1635,11 @@ static bool _stair_moves_pre(dungeon_feature_type stair)
else
pct = 50;
+ // When the effect is still strong, the chance to actually catch a stair
+ // is smaller. (Assuming the duration starts out at 500.)
+ const int dur = std::max(0, you.duration[DUR_REPEL_STAIRS_CLIMB] - 200);
+ pct += dur/20;
+
if (!x_chance_in_y(pct, 100))
return (false);
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index c5948c9bef..e58056765b 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -2201,13 +2201,16 @@ static coord_def _random_monster_nearby_habitable_space(const monsters& mon,
return (target);
}
-bool monster_blink(monsters *monster)
+bool monster_blink(monsters *monster, bool quiet)
{
coord_def near = _random_monster_nearby_habitable_space(*monster, false,
true);
if (near == monster->pos())
return (false);
+ if (!quiet)
+ simple_monster_message(monster, " blinks!");
+
if (!(monster->flags & MF_WAS_IN_VIEW))
monster->seen_context = "thin air";
@@ -5627,10 +5630,7 @@ static bool _handle_special_ability(monsters *monster, bolt & beem)
case MONS_KILLER_KLOWN:
case MONS_PRINCE_RIBBIT:
if (one_chance_in(7) || mons_is_caught(monster) && one_chance_in(3))
- {
- simple_monster_message(monster, " blinks.");
used = monster_blink(monster);
- }
break;
case MONS_MANTICORE:
@@ -6011,7 +6011,6 @@ static bool _handle_scroll(monsters *monster)
if (mons_near(monster))
{
simple_monster_message(monster, " reads a scroll.");
- simple_monster_message(monster, " blinks!");
monster_blink(monster);
read = true;
ident = ID_KNOWN_TYPE;
@@ -6602,8 +6601,6 @@ static bool _handle_spell(monsters *monster, bolt &beem)
if (monsterNearby)
{
mons_cast_noise(monster, beem, spell_cast);
-
- simple_monster_message(monster, " blinks!");
monster_blink(monster);
monster->lose_energy(EUT_SPELL);
diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h
index 9a9975def7..899e304abd 100644
--- a/crawl-ref/source/monstuff.h
+++ b/crawl-ref/source/monstuff.h
@@ -118,7 +118,7 @@ void monster_drop_ething(monsters *monster, bool mark_item_origins = false,
/* ***********************************************************************
* called from: fight
* *********************************************************************** */
-bool monster_blink(monsters *monster);
+bool monster_blink(monsters *monster, bool quiet = false);
/* ***********************************************************************
diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc
index f4e638d309..7838ce124d 100644
--- a/crawl-ref/source/output.cc
+++ b/crawl-ref/source/output.cc
@@ -1944,7 +1944,17 @@ static std::vector<formatted_string> _get_overview_stats()
char god_colour_tag[20];
god_colour_tag[0] = 0;
std::string godpowers(god_name(you.religion));
- if (you.religion != GOD_NO_GOD)
+ if (you.religion == GOD_XOM)
+ {
+ snprintf(god_colour_tag, sizeof god_colour_tag, "<%s>",
+ colour_to_str(god_colour(you.religion)).c_str());
+
+ if (you.gift_timeout == 0)
+ godpowers += " - BORED";
+ else if (you.gift_timeout == 1)
+ godpowers += " - getting BORED";
+ }
+ else if (you.religion != GOD_NO_GOD)
{
if (player_under_penance())
strcpy(god_colour_tag, "<red>*");
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index ba506a7fa7..3a04aaecda 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -335,6 +335,11 @@ bool move_player_to_grid( const coord_def& p, bool stepped, bool allow_shift,
else
pct = 50;
+ // When the effect is still strong, the chance to actually catch
+ // a stair is smaller. (Assuming the duration starts out at 1000.)
+ const int dur = std::max(0, you.duration[DUR_REPEL_STAIRS_MOVE] - 700);
+ pct += dur/10;
+
if (x_chance_in_y(pct, 100))
{
if (slide_feature_over(you.pos(), coord_def(-1, -1), false))
@@ -346,6 +351,9 @@ bool move_player_to_grid( const coord_def& p, bool stepped, bool allow_shift,
mprf("%s slides away as you move %s it!", stair_str.c_str(),
prep.c_str());
+
+ if (player_in_a_dangerous_place() && one_chance_in(5))
+ xom_is_stimulated(32);
}
}
}
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index d7dbcd38e1..9413dd1460 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -403,9 +403,11 @@ static int follower_tag_radius2()
// If only friendlies are adjacent, we set a max radius of 6, otherwise
// only adjacent friendlies may follow.
for (adjacent_iterator ai; ai; ++ai)
+ {
if (const monsters *mon = monster_at(*ai))
if (!mons_friendly(mon))
return (2);
+ }
return (6 * 6);
}
diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc
index 6c83d1f162..09ee06a4f5 100644
--- a/crawl-ref/source/terrain.cc
+++ b/crawl-ref/source/terrain.cc
@@ -883,12 +883,12 @@ bool slide_feature_over(const coord_def &src, coord_def prefered_dest,
else
{
int squares = 0;
- for (radius_iterator ri(src, 1, true, false, true); ri; ++ri)
+ for (adjacent_iterator ai(src); ai; ++ai)
{
- if (_ok_dest_grid(orig_actor, orig_feat, *ri))
+ if (_ok_dest_grid(orig_actor, orig_feat, *ai)
+ && one_chance_in(++squares))
{
- if (one_chance_in(++squares))
- prefered_dest = *ri;
+ prefered_dest = *ai;
}
}
}
diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc
index 52cb556a7a..99e57e31c8 100644
--- a/crawl-ref/source/xom.cc
+++ b/crawl-ref/source/xom.cc
@@ -164,7 +164,7 @@ static bool _xom_feels_nasty()
return (you.penance[GOD_XOM] || _xom_is_bored());
}
-bool xom_is_nice()
+bool xom_is_nice(int tension)
{
if (you.penance[GOD_XOM])
return (false);
@@ -172,7 +172,14 @@ bool xom_is_nice()
if (you.religion == GOD_XOM)
{
// If you.gift_timeout was 0, then Xom was BORED. He HATES that.
- return (you.gift_timeout > 0 && you.piety >= random2(MAX_PIETY+1));
+ if (you.gift_timeout == 0)
+ return (false);
+
+ // At high tension Xom is more likely to be nice.
+ int tension_bonus = (tension <= 0 ? 0 : random2(tension));
+
+ // Whether Xom is nice depends largely on his mood (== piety).
+ return (x_chance_in_y(you.piety + tension_bonus, MAX_PIETY + 1));
}
else // CARD_XOM
return coinflip();
@@ -1410,10 +1417,11 @@ static bool _xom_give_mutations(bool good)
const char* lookup = (good ? "good mutations" : "random mutations");
god_speaks(GOD_XOM, _get_xom_speech(lookup).c_str());
+ const int num_tries = random2(4) + 1;
#ifdef NOTE_DEBUG_XOM
static char mut_buf[80];
- snprintf(mut_buf, sizeof(mut_buf), "XOM: give %s mutations",
- good || !_xom_feels_nasty() ? "good" : "random");
+ snprintf(mut_buf, sizeof(mut_buf), "XOM: give %s mutation%s",
+ good ? "good" : "random", num_tries > 1 ? "s" : "");
take_note(Note(NOTE_MESSAGE, 0, 0, mut_buf), true);
#endif
@@ -1424,7 +1432,7 @@ static bool _xom_give_mutations(bool good)
bool failMsg = true;
- for (int i = random2(4); i >= 0; --i)
+ for (int i = num_tries; i > 0; --i)
{
if (mutate(good ? RANDOM_GOOD_MUTATION : RANDOM_XOM_MUTATION,
failMsg, false, true, false, false,
@@ -1491,8 +1499,30 @@ static bool _xom_throw_divine_lightning()
if (!player_in_a_dangerous_place())
return (false);
+ // Make sure there's at least one enemy within the lightning radius.
+ bool found_hostile = false;
+ for (radius_iterator ri(you.pos(), 2, true, true, true); ri; ++ri)
+ {
+ if (monsters* mon = monster_at(*ri))
+ {
+ if (!mons_wont_attack(mon))
+ {
+ found_hostile = true;
+ break;
+ }
+ }
+ }
+
+ // No hostiles within radius.
+ if (!found_hostile)
+ return (false);
+
+ bool protection = false;
if (you.hp <= random2(201))
+ {
you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION] = 1;
+ protection = true;
+ }
god_speaks(GOD_XOM, "The area is suffused with divine lightning!");
@@ -1526,7 +1556,10 @@ static bool _xom_throw_divine_lightning()
you.reset_escaped_death();
}
#ifdef NOTE_DEBUG_XOM
- take_note(Note(NOTE_MESSAGE, 0, 0, "XOM: divine lightning"), true);
+ static char lightning_buf[80];
+ snprintf(lightning_buf, sizeof(lightning_buf),
+ "XOM: divine lightning%s", protection ? " (protected)" : "");
+ take_note(Note(NOTE_MESSAGE, 0, 0, lightning_buf), true);
#endif
return (true);
}
@@ -2105,8 +2138,8 @@ static bool _xom_lose_stats()
#ifdef NOTE_DEBUG_XOM
static char stat_buf[80];
snprintf(stat_buf, sizeof(stat_buf), "XOM: stat loss: -%d %s (%d/%d)",
- loss, (stat == STAT_STRENGTH ? " Str" :
- stat == STAT_DEXTERITY ? " Dex" : "Int"),
+ loss, (stat == STAT_STRENGTH ? "Str" :
+ stat == STAT_DEXTERITY ? "Dex" : "Int"),
(stat == STAT_STRENGTH ? you.strength :
stat == STAT_DEXTERITY ? you.dex : you.intel),
(stat == STAT_STRENGTH ? you.max_strength :
@@ -2196,6 +2229,210 @@ static bool _xom_player_confusion_effect(int sever)
return (rc);
}
+static bool _move_stair(coord_def stair_pos, bool away)
+{
+ dungeon_feature_type feat = grd(stair_pos);
+ ASSERT(grid_stair_direction(feat) != CMD_NO_CMD);
+
+ coord_def begin, towards;
+
+ bool stairs_moved = false;
+ if (away)
+ {
+ // If the staircase starts out under the player first shove it onto
+ // a neighbouring grid.
+ if (stair_pos == you.pos())
+ {
+ coord_def new_pos(stair_pos);
+ int adj_count = 0;
+ for (adjacent_iterator ai(stair_pos); ai; ++ai)
+ if (grid_stair_direction(grd(*ai)) == CMD_NO_CMD
+ && one_chance_in(++adj_count))
+ {
+ new_pos = *ai;
+ }
+
+ if (new_pos == stair_pos)
+ return (false);
+
+ if (!slide_feature_over(stair_pos, new_pos))
+ return (false);
+
+ stair_pos = new_pos;
+ stairs_moved = true;
+ }
+
+ begin = you.pos();
+ towards = stair_pos;
+ }
+ else
+ {
+ // Can't move towards player if it's already adjacent.
+ if (adjacent(you.pos(), stair_pos))
+ return (false);
+
+ begin = stair_pos;
+ towards = you.pos();
+ }
+
+ ray_def ray;
+ if (!find_ray(begin, towards, true, ray, 0, true))
+ {
+ mpr("Couldn't find ray between player and stairs.", MSGCH_ERROR);
+ return (stairs_moved);
+ }
+
+ // Don't start off under the player.
+ if (away)
+ ray.advance();
+
+ bool found_stairs = false;
+ int past_stairs = 0;
+ while (in_bounds(ray.pos()) && see_grid(ray.pos())
+ && !grid_is_solid(ray.pos()) && ray.pos() != you.pos())
+ {
+ if (ray.pos() == stair_pos)
+ found_stairs = true;
+ if (found_stairs)
+ past_stairs++;
+ ray.advance();
+ }
+ past_stairs--;
+
+ if (!away && grid_is_solid(ray.pos()))
+ {
+ // Transparent wall between stair and player.
+ return (stairs_moved);
+ }
+
+ if (away && !found_stairs)
+ {
+ if (grid_is_solid(ray.pos()))
+ {
+ // Transparent wall between stair and player.
+ return (stairs_moved);
+ }
+
+ mpr("Ray didn't cross stairs.", MSGCH_ERROR);
+ }
+
+ if (away && past_stairs <= 0)
+ {
+ // Stairs already at edge, can't move further away.
+ return (stairs_moved);
+ }
+
+ if (!in_bounds(ray.pos()) || ray.pos() == you.pos())
+ ray.regress();
+
+ while (!see_grid(ray.pos()) || grd(ray.pos()) != DNGN_FLOOR)
+ {
+ ray.regress();
+ if (!in_bounds(ray.pos()) || ray.pos() == you.pos()
+ || ray.pos() == stair_pos)
+ {
+ // No squares in path are a plain floor.
+ return (stairs_moved);
+ }
+ }
+
+ ASSERT(stair_pos != ray.pos());
+
+ std::string stair_str =
+ feature_description(stair_pos, false, DESC_CAP_THE, false);
+
+ mprf("%s slides %s you!", stair_str.c_str(),
+ away ? "away from" : "towards");
+
+ // Animate stair moving.
+ const feature_def &feat_def = get_feature_def(feat);
+
+ bolt beam;
+
+ beam.range = INFINITE_DISTANCE;
+ beam.flavour = BEAM_VISUAL;
+ beam.type = feat_def.symbol;
+ beam.colour = feat_def.colour;
+ beam.source = stair_pos;
+ beam.target = ray.pos();
+ beam.name = "STAIR BEAM";
+ beam.draw_delay = 50; // Make beam animation slower than normal.
+
+ beam.aimed_at_spot = true;
+ beam.fire();
+
+ // Clear out "missile trails"
+ viewwindow(true, false);
+
+ if (!swap_features(stair_pos, ray.pos(), false, false))
+ {
+ mprf(MSGCH_ERROR, "_move_stair(): failed to move %s",
+ stair_str.c_str());
+ return (stairs_moved);
+ }
+ return (true);
+}
+
+static bool _repel_stairs()
+{
+ // Repeating the effect while it's still active is boring.
+ if (you.duration[DUR_REPEL_STAIRS_MOVE]
+ || you.duration[DUR_REPEL_STAIRS_CLIMB])
+ {
+ return (false);
+ }
+
+ std::vector<coord_def> stairs_avail;
+ for (radius_iterator ri(you.pos(), LOS_RADIUS, false, true); ri; ++ri)
+ {
+ dungeon_feature_type feat = grd(*ri);
+ if (grid_stair_direction(feat) != CMD_NO_CMD
+ && feat != DNGN_ENTER_SHOP)
+ {
+ stairs_avail.push_back(*ri);
+ }
+ }
+
+ // Should only happen if there are stairs in view.
+ if (stairs_avail.empty())
+ {
+ mpr("No stairs found!");
+ return (false);
+ }
+
+ god_speaks(GOD_XOM,
+ _get_xom_speech("repel stairs").c_str());
+
+ you.duration[DUR_REPEL_STAIRS_MOVE] = 1000;
+
+ if (one_chance_in(5)
+ || grid_stair_direction(grd(you.pos())) != CMD_NO_CMD
+ && grd(you.pos()) != DNGN_ENTER_SHOP)
+ {
+ you.duration[DUR_REPEL_STAIRS_CLIMB] = 500;
+ }
+
+ std::random_shuffle(stairs_avail.begin(), stairs_avail.end());
+ int count_moved = 0;
+ for (unsigned int i = 0; i < stairs_avail.size(); i++)
+ if (_move_stair(stairs_avail[i], true))
+ count_moved++;
+
+ if (!count_moved)
+ {
+ if (one_chance_in(8))
+ mpr("Nothing appears to happen... Ominous!");
+ else
+ canned_msg(MSG_NOTHING_HAPPENS);
+ }
+
+#ifdef NOTE_DEBUG_XOM
+ take_note(Note(NOTE_MESSAGE, 0, 0, "XOM: repel stairs"), true);
+#endif
+
+ return (true);
+}
+
static bool _xom_draining_torment_effect(int sever)
{
const std::string speech = _get_xom_speech("draining or torment");
@@ -2230,8 +2467,8 @@ static bool _xom_draining_torment_effect(int sever)
torment_player(0, TORMENT_XOM);
#ifdef NOTE_DEBUG_XOM
static char torment_buf[80];
- snprintf(torment_buf, sizeof(torment_buf), "XOM: torment (%d/%d)",
- you.hp, you.hp_max);
+ snprintf(torment_buf, sizeof(torment_buf),
+ "XOM: torment (%d/%d hp)", you.hp, you.hp_max);
take_note(Note(NOTE_MESSAGE, 0, 0, torment_buf), true);
#endif
rc = true;
@@ -2386,23 +2623,28 @@ static bool _xom_is_bad(int sever, int tension)
done = _xom_polymorph_nearby_monster(false);
badness = 3;
}
+ else if ((!nasty || tension > 0) && x_chance_in_y(11, sever))
+ {
+ done = _repel_stairs();
+ badness = (you.duration[DUR_REPEL_STAIRS_CLIMB] ? 3 : 2);
+ }
// It's pointless to confuse player if there's no danger nearby.
- else if (tension > 0 && x_chance_in_y(11, sever))
+ else if (tension > 0 && x_chance_in_y(12, sever))
{
done = _xom_player_confusion_effect(sever);
badness = (random2(tension) > 5 ? 2 : 1);
}
- else if (x_chance_in_y(12, sever))
+ else if (x_chance_in_y(13, sever))
{
done = _xom_draining_torment_effect(sever);
badness = (random2(tension) > 5 ? 3 : 2);
}
- else if (x_chance_in_y(13, sever))
+ else if (x_chance_in_y(14, sever))
{
done = _xom_summon_hostiles(sever);
badness = 3 + coinflip();
}
- else if (x_chance_in_y(14, sever))
+ else if (x_chance_in_y(15, sever))
{
_xom_miscast(3, nasty);
badness = 4 + coinflip();
@@ -2629,7 +2871,14 @@ void xom_acts(bool niceness, int sever, int tension)
const FixedVector<unsigned char, NUM_MUTATIONS> orig_mutation
= you.mutation;
- if (niceness && !one_chance_in(20))
+#ifdef DEBUG_XOM
+ static char xom_buf[100];
+ snprintf(xom_buf, sizeof(xom_buf), "xom_acts(%s, %d, %d), mood: %d",
+ (niceness ? "true" : "false"), sever, tension, you.piety);
+ take_note(Note(NOTE_MESSAGE, 0, 0, xom_buf), true);
+#endif
+
+ if (niceness && !one_chance_in(15))
{
// Good stuff.
while (!_xom_is_good(sever, tension))
@@ -2637,6 +2886,18 @@ void xom_acts(bool niceness, int sever, int tension)
}
else
{
+#ifdef NOTE_DEBUG_XOM
+ if (_xom_is_bored())
+ take_note(Note(NOTE_MESSAGE, 0, 0, "XOM is BORED!"), true);
+#ifdef DEBUG_XOM
+ else if (niceness)
+ {
+ take_note(Note(NOTE_MESSAGE, 0, 0, "good act randomly turned bad"),
+ true);
+ }
+#endif
+#endif
+
// Bad mojo.
while (!_xom_is_bad(sever, tension))
;
diff --git a/crawl-ref/source/xom.h b/crawl-ref/source/xom.h
index cd1c0a67bb..e17f242d68 100644
--- a/crawl-ref/source/xom.h
+++ b/crawl-ref/source/xom.h
@@ -26,13 +26,13 @@ void xom_is_stimulated(int maxinterestingness,
bool force_message = false);
void xom_is_stimulated(int maxinterestingness, const std::string& message,
bool force_message = false);
-bool xom_is_nice();
+bool xom_is_nice(int tension = -1);
void xom_acts(bool niceness, int sever, int tension = -1);
const char *describe_xom_favour(bool upper = false);
inline void xom_acts(int sever, int tension = -1)
{
- xom_acts(xom_is_nice(), sever, tension);
+ xom_acts(xom_is_nice(tension), sever, tension);
}
void xom_check_lost_item(const item_def& item);