summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/beam.cc68
-rw-r--r--crawl-ref/source/beam.h2
-rw-r--r--crawl-ref/source/cmd-keys.h2
-rw-r--r--crawl-ref/source/decks.cc157
-rw-r--r--crawl-ref/source/decks.h1
-rw-r--r--crawl-ref/source/enum.h13
-rw-r--r--crawl-ref/source/fight.cc111
-rw-r--r--crawl-ref/source/files.cc25
-rw-r--r--crawl-ref/source/misc.cc52
-rw-r--r--crawl-ref/source/mon-util.cc3
-rw-r--r--crawl-ref/source/player.cc26
-rw-r--r--crawl-ref/source/terrain.cc316
-rw-r--r--crawl-ref/source/terrain.h12
13 files changed, 600 insertions, 188 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index da31322807..5adae5ff09 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -1531,7 +1531,7 @@ static void _munge_bounced_bolt(bolt &old_bolt, bolt &new_bolt,
void fire_beam(bolt &pbolt)
{
- ASSERT(pbolt.flavour > BEAM_NONE && pbolt.flavour < NUM_BEAMS);
+ ASSERT(pbolt.flavour > BEAM_NONE && pbolt.flavour < BEAM_FIRST_PSEUDO);
ASSERT(!pbolt.drop_item || pbolt.item);
ASSERT(!pbolt.dropped_item);
@@ -1540,6 +1540,9 @@ void fire_beam(bolt &pbolt)
const bool beam_invisible =
pbolt.type == 0 || (!pbolt.name.empty() && pbolt.name[0] == '0');
+ ASSERT(pbolt.flavour != BEAM_VISUAL
+ || (!beam_invisible && pbolt.colour != BLACK));
+
const int reflections = pbolt.reflections;
if (reflections == 0)
{
@@ -1554,8 +1557,8 @@ void fire_beam(bolt &pbolt)
pbolt.seen = true;
int midx = mgrd(pbolt.source);
- if (!pbolt.is_tracer && !YOU_KILL(pbolt.thrower)
- && !crawl_state.is_god_acting()
+ if (pbolt.flavour != BEAM_VISUAL && !pbolt.is_tracer
+ && !YOU_KILL(pbolt.thrower) && !crawl_state.is_god_acting()
&& (midx == NON_MONSTER || !you.can_see(&menv[midx])))
{
mprf("%s appears from out of thin air!",
@@ -1611,21 +1614,18 @@ void fire_beam(bolt &pbolt)
#endif
#if DEBUG_DIAGNOSTICS
- if (pbolt.flavour != BEAM_LINE_OF_SIGHT)
- {
- mprf( MSGCH_DIAGNOSTICS, "%s%s%s [%s] (%d,%d) to (%d,%d): "
- "ty=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
- (pbolt.is_beam) ? "beam" : "missile",
- (pbolt.is_explosion) ? "*" :
- (pbolt.is_big_cloud) ? "+" : "",
- (pbolt.is_tracer) ? " tracer" : "",
- pbolt.name.c_str(),
- pbolt.source.x, pbolt.source.y,
- pbolt.target.x, pbolt.target.y,
- pbolt.type, pbolt.colour, pbolt.flavour,
- pbolt.hit, pbolt.damage.num, pbolt.damage.size,
- pbolt.range);
- }
+ mprf( MSGCH_DIAGNOSTICS, "%s%s%s [%s] (%d,%d) to (%d,%d): "
+ "ty=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
+ (pbolt.is_beam) ? "beam" : "missile",
+ (pbolt.is_explosion) ? "*" :
+ (pbolt.is_big_cloud) ? "+" : "",
+ (pbolt.is_tracer) ? " tracer" : "",
+ pbolt.name.c_str(),
+ pbolt.source.x, pbolt.source.y,
+ pbolt.target.x, pbolt.target.y,
+ pbolt.type, pbolt.colour, pbolt.flavour,
+ pbolt.hit, pbolt.damage.num, pbolt.damage.size,
+ pbolt.range);
#endif
// init
@@ -1753,7 +1753,8 @@ void fire_beam(bolt &pbolt)
pbolt.seen = true;
}
- if (!was_seen && pbolt.seen && !pbolt.is_tracer)
+ if (pbolt.flavour != BEAM_VISUAL && !was_seen && pbolt.seen
+ && !pbolt.is_tracer)
{
mprf("%s appears from out of your range of vision.",
article_a(pbolt.name, false).c_str());
@@ -1824,7 +1825,7 @@ void fire_beam(bolt &pbolt)
if (tile_beam != -1 && in_los_bounds(drawpos))
{
tiles.add_overlay(testpos, tile_beam);
- delay(15);
+ delay(pbolt.delay);
}
else
#endif
@@ -1841,7 +1842,7 @@ void fire_beam(bolt &pbolt)
// Get curses to update the screen so we can see the beam.
update_screen();
- delay(15);
+ delay(pbolt.delay);
#ifdef MISSILE_TRAILS_OFF
// mv: It's not optimal but is usually enough.
@@ -2939,13 +2940,6 @@ static void _beam_explodes(bolt &beam, const coord_def& p)
static bool _beam_term_on_target(bolt &beam, const coord_def& p)
{
- if (beam.flavour == BEAM_LINE_OF_SIGHT)
- {
- if (beam.thrower != KILL_YOU_MISSILE)
- beam.foe_count++;
- return (true);
- }
-
// Generic - all explosion-type beams can be targeted at empty space,
// and will explode there. This semantic also means that a creature
// in the target cell will have no chance to dodge or block, so we
@@ -3085,10 +3079,6 @@ int affect(bolt &beam, const coord_def& _p, item_def *item, bool affect_items)
// Extra range used by hitting something.
int rangeUsed = 0;
- // Line of sight never affects anything.
- if (beam.flavour == BEAM_LINE_OF_SIGHT)
- return (0);
-
coord_def p = _p;
if (!in_bounds(_p))
p = beam.pos;
@@ -3096,6 +3086,14 @@ int affect(bolt &beam, const coord_def& _p, item_def *item, bool affect_items)
if (!item)
item = beam.item;
+ if (beam.flavour == BEAM_VISUAL)
+ {
+ if (mgrd(p) != NON_MONSTER)
+ behaviour_event( &menv[mgrd(p)], ME_DISTURB );
+
+ return (0);
+ }
+
if (grid_is_solid(grd(p)))
{
if (beam.is_tracer) // Tracers always stop on walls.
@@ -3654,6 +3652,7 @@ static bool _beam_is_harmless(bolt &beam, monsters *mon)
// The others are handled here.
switch (beam.flavour)
{
+ case BEAM_VISUAL:
case BEAM_DIGGING:
return (true);
@@ -3704,6 +3703,7 @@ static bool _beam_is_harmless_player(bolt &beam)
// The others are handled here.
switch (beam.flavour)
{
+ case BEAM_VISUAL:
case BEAM_DIGGING:
return (true);
@@ -5756,7 +5756,7 @@ bolt::bolt() : range(0), type('*'),
beam_source(MHITNOT), name(), short_name(), is_beam(false),
is_explosion(false), is_big_cloud(false), aimed_at_spot(false),
aux_source(), affects_nothing(false), effect_known(true),
- obvious_effect(false),
+ delay(15), obvious_effect(false),
fr_count(0), foe_count(0), fr_power(0), foe_power(0),
fr_hurt(0), foe_hurt(0), fr_helped(0),foe_helped(0),
dropped_item(false), item_pos(), item_index(NON_ITEM),
@@ -5934,9 +5934,9 @@ std::string beam_type_name(beam_type type)
case BEAM_POTION_BLUE_SMOKE: return("blue smoke");
case BEAM_POTION_PURP_SMOKE: return("purple smoke");
case BEAM_POTION_RANDOM: return("random potion");
+ case BEAM_VISUAL: return ("visual effects");
case BEAM_TORMENT_DAMAGE: return("torment damage");
case BEAM_STEAL_FOOD: return("steal food");
- case BEAM_LINE_OF_SIGHT: return("line of sight");
case NUM_BEAMS:
DEBUGSTR("invalid beam type");
return("INVALID");
diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h
index f8968e3967..4b3b1f9822 100644
--- a/crawl-ref/source/beam.h
+++ b/crawl-ref/source/beam.h
@@ -65,6 +65,8 @@ struct bolt
bool effect_known; // did we _know_ this would happen?
+ int delay; // delay used when drawing beam.
+
// OUTPUT parameters (tracing, ID)
bool obvious_effect; // did an 'obvious' effect happen?
diff --git a/crawl-ref/source/cmd-keys.h b/crawl-ref/source/cmd-keys.h
index 2cffddfe32..af367cdf98 100644
--- a/crawl-ref/source/cmd-keys.h
+++ b/crawl-ref/source/cmd-keys.h
@@ -140,6 +140,8 @@
{'w', CMD_TARGET_WIZARD_PATHFIND},
{'G', CMD_TARGET_WIZARD_GAIN_LEVEL},
{'M', CMD_TARGET_WIZARD_MISCAST},
+{'S', CMD_TARGET_WIZARD_MAKE_SUMMONED},
+{'~', CMD_TARGET_WIZARD_POLYMORPH},
#endif
{'v', CMD_TARGET_DESCRIBE},
{'?', CMD_TARGET_HELP},
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc
index 2bba3b68af..025f7a283d 100644
--- a/crawl-ref/source/decks.cc
+++ b/crawl-ref/source/decks.cc
@@ -276,6 +276,7 @@ const char* card_name(card_type card)
case CARD_DOWSING: return "Dowsing";
case CARD_TROWEL: return "the Trowel";
case CARD_MINEFIELD: return "the Minefield";
+ case CARD_STAIRS: return "the Stairs";
case CARD_GENIE: return "the Genie";
case CARD_TOMB: return "the Tomb";
case CARD_WATER: return "Water";
@@ -1550,6 +1551,161 @@ static void _minefield_card(int power, deck_rarity_type rarity)
}
}
+static int stair_draw_count = 0;
+
+static void _move_stair(coord_def stair_pos, bool away)
+{
+ ASSERT(stair_pos != you.pos());
+
+ dungeon_feature_type feat = grd(stair_pos);
+ ASSERT(grid_stair_direction(feat) != CMD_NO_CMD);
+
+ coord_def begin, towards;
+
+ if (away)
+ {
+ begin = you.pos();
+ towards = stair_pos;
+ }
+ else
+ {
+ // Can't move towards player if it's already adjacent.
+ if (adjacent(you.pos(), stair_pos))
+ return;
+
+ 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;
+ }
+
+ // 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;
+
+ if (away && !found_stairs)
+ {
+ if (grid_is_solid(ray.pos()))
+ // Transparent wall between stair and player.
+ return;
+
+ mpr("Ray didn't cross stairs.", MSGCH_ERROR);
+ }
+
+ if (away && past_stairs <= 0)
+ // Stairs already at edge, can't move further away.
+ return;
+
+ 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;
+ }
+ }
+
+ 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.delay = 50; // Make beam animation slower than normal.
+ beam.name = "STAIR BEAM";
+
+ beam.aimed_at_spot = true;
+
+ fire_beam(beam);
+
+ // 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());
+}
+
+static void _stairs_card(int power, deck_rarity_type rarity)
+{
+ UNUSED(power);
+ UNUSED(rarity);
+
+ you.duration[DUR_REPEL_STAIRS_MOVE] = 0;
+ you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
+
+ if (grid_stair_direction(grd(you.pos())) == CMD_NO_CMD)
+ you.duration[DUR_REPEL_STAIRS_MOVE] = 1000;
+ else
+ you.duration[DUR_REPEL_STAIRS_CLIMB] = 1000;
+
+ std::vector<coord_def> stairs_avail;
+
+ radius_iterator ri(you.pos(), LOS_RADIUS, false, true, true);
+ for (; 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);
+ }
+ }
+
+ if (stairs_avail.size() == 0)
+ {
+ mpr("No stairs available to move.");
+ return;
+ }
+
+ std::random_shuffle(stairs_avail.begin(), stairs_avail.end());
+
+ for (unsigned int i = 0; i < stairs_avail.size(); i++)
+ _move_stair(stairs_avail[i], stair_draw_count % 2);
+
+ stair_draw_count++;
+}
+
static int _drain_monsters(coord_def where, int pow, int garbage)
{
UNUSED( garbage );
@@ -2794,6 +2950,7 @@ bool card_effect(card_type which_card, deck_rarity_type rarity,
case CARD_GLASS: _glass_card(power, rarity); break;
case CARD_DOWSING: _dowsing_card(power, rarity); break;
case CARD_MINEFIELD: _minefield_card(power, rarity); break;
+ case CARD_STAIRS: _stairs_card(power, rarity); break;
case CARD_GENIE: _genie_card(power, rarity); break;
case CARD_CURSE: _curse_card(power, rarity); break;
case CARD_WARPWRIGHT: _warpwright_card(power, rarity); break;
diff --git a/crawl-ref/source/decks.h b/crawl-ref/source/decks.h
index a9537a4be6..6fccd687c2 100644
--- a/crawl-ref/source/decks.h
+++ b/crawl-ref/source/decks.h
@@ -105,6 +105,7 @@ enum card_type
CARD_SPADE, // dig
CARD_TROWEL, // create feature/vault
CARD_MINEFIELD, // plant traps
+ CARD_STAIRS, // moves stairs around
CARD_GENIE, // acquirement OR rotting/deterioration
CARD_BARGAIN, // shopping discount
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index b679185b2c..b6547ae8bc 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -250,10 +250,15 @@ enum beam_type // beam[].flavour
BEAM_POTION_PURP_SMOKE,
BEAM_POTION_RANDOM,
+ BEAM_LAST_REAL = BEAM_POTION_RANDOM,
+
+ // For getting the visual effect of a beam.
+ BEAM_VISUAL, // 58
+
BEAM_TORMENT_DAMAGE, // Pseudo-beam for damage flavour.
- BEAM_STEAL_FOOD, // Pseudo-beam for harpyes stealing food.
+ BEAM_FIRST_PSEUDO = BEAM_TORMENT_DAMAGE,
+ BEAM_STEAL_FOOD, // {60} Pseudo-beam for harpies stealing food.
- BEAM_LINE_OF_SIGHT, // 60 - only used for checking monster LOS
NUM_BEAMS
};
@@ -637,6 +642,8 @@ enum command_type
CMD_TARGET_WIZARD_PATHFIND,
CMD_TARGET_WIZARD_GAIN_LEVEL,
CMD_TARGET_WIZARD_MISCAST,
+ CMD_TARGET_WIZARD_MAKE_SUMMONED,
+ CMD_TARGET_WIZARD_POLYMORPH,
CMD_TARGET_MOUSE_MOVE,
CMD_TARGET_MOUSE_SELECT,
CMD_TARGET_HELP,
@@ -1170,6 +1177,8 @@ enum duration_type
DUR_TELEPATHY,
DUR_PETRIFIED,
DUR_LOWERED_MR,
+ DUR_REPEL_STAIRS_MOVE,
+ DUR_REPEL_STAIRS_CLIMB,
NUM_DURATIONS
};
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index a66dd8c034..7187662e87 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -10,7 +10,6 @@
#include "fight.h"
#include <string.h>
-#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
@@ -2419,20 +2418,6 @@ void melee_attack::chaos_affects_defender()
obvious_effect = false;
}
-static bool _ok_dest_grid(const coord_def &dest)
-{
- dungeon_feature_type feat = grd(dest);
-
- if (grid_destroys_items(feat) || grid_is_solid(feat)
- || grid_is_water(feat) || grid_is_trap(feat, true)
- || is_notable_terrain(feat))
- {
- return (false);
- }
-
- return (true);
-}
-
static bool _move_stairs(const actor* attacker, const actor* defender)
{
const coord_def orig_pos = attacker->pos();
@@ -2455,102 +2440,10 @@ static bool _move_stairs(const actor* attacker, const actor* defender)
coord_def dest(-1, -1);
// Prefer to send it under the defender.
- if (defender->alive() && defender->pos() != attacker->pos()
- && _ok_dest_grid(defender->pos()))
- {
+ if (defender->alive() && defender->pos() != attacker->pos())
dest = defender->pos();
- }
-
- if (!in_bounds(dest))
- {
- radius_iterator ri(attacker->pos(), 1, true, false, true);
-
- int squares = 0;
- for (; ri; ++ri)
- {
- if (_ok_dest_grid(*ri))
- {
- if (one_chance_in(++squares))
- dest = *ri;
- }
- }
- }
-
- if (!in_bounds(dest))
- return (false);
-
- ASSERT(dest != orig_pos);
-
- if (!swap_features(orig_pos, dest))
- return (false);
- // Is player aware of it happening?
- if (!see_grid(orig_pos) && !see_grid(dest))
- return (true);
-
- std::string orig_actor, dest_actor;
- if (orig_pos == you.pos())
- orig_actor = "you";
- else if (mgrd(orig_pos) != NON_MONSTER)
- {
- monsters &mon(menv[mgrd(orig_pos)]);
-
- if (you.can_see(&mon))
- orig_actor = mon.name(DESC_NOCAP_THE);
- }
-
- if (dest == you.pos())
- dest_actor = "you";
- else if (mgrd(dest) != NON_MONSTER)
- {
- monsters &mon(menv[mgrd(dest)]);
-
- if (you.can_see(&mon))
- dest_actor = mon.name(DESC_NOCAP_THE);
- }
-
- std::string stair_name =
- feature_description(dest, false,
- see_grid(orig_pos) ? DESC_CAP_THE : DESC_CAP_A,
- false);
- std::string prep;
-
- if (grid_stair_direction(stair_feat) == CMD_GO_DOWNSTAIRS
- && (stair_name.find("stair") || grid_is_escape_hatch(stair_feat)))
- {
- prep = "beneath";
- }
- else if (grid_is_escape_hatch(stair_feat))
- prep = "above";
- else
- prep = "beside";
-
- std::ostringstream str;
- str << stair_name << " ";
- if (see_grid(orig_pos) && !see_grid(dest))
- {
- str << "suddenly disappears";
- if (!orig_actor.empty())
- str << " from " << prep << " " << orig_actor;
- }
- else if (!see_grid(orig_pos) && see_grid(dest))
- {
- str << "suddenly appears";
- if (!dest_actor.empty())
- str << " " << prep << " " << dest_actor;
- }
- else
- {
- str << "moves";
- if (!orig_actor.empty())
- str << " from " << prep << " " << orig_actor;
- if (!dest_actor.empty())
- str << " to " << prep << " " << dest_actor;
- }
- str << "!";
- mpr(str.str().c_str());
-
- return (true);
+ return slide_feature_over(attacker->pos(), dest);
}
#define DID_AFFECT() \
diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc
index e79d1537bc..1a76aac57b 100644
--- a/crawl-ref/source/files.cc
+++ b/crawl-ref/source/files.cc
@@ -1349,6 +1349,31 @@ bool load( dungeon_feature_type stair_taken, load_mode_type load_mode,
if (load_mode == LOAD_ENTER_LEVEL)
{
+ // 50% chance of repelling the stair you just came through.
+ if (you.duration[DUR_REPEL_STAIRS_MOVE]
+ || you.duration[DUR_REPEL_STAIRS_CLIMB])
+ {
+ 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))
+ {
+ 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());
+ }
+ }
+
+ // 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/misc.cc b/crawl-ref/source/misc.cc
index bcfa786c16..89155a97de 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -1658,6 +1658,46 @@ static bool _marker_vetoes_level_change()
return (marker_vetoes_operation("veto_level_change"));
}
+static bool _stair_moves_pre(dungeon_feature_type stair)
+{
+ if (crawl_state.prev_cmd == CMD_WIZARD)
+ return (false);
+
+ if (stair != grd(you.pos()))
+ return (false);
+
+ if (grid_stair_direction(stair) == CMD_NO_CMD)
+ return (false);
+
+ if (!you.duration[DUR_REPEL_STAIRS_CLIMB])
+ return (false);
+
+ int pct;
+ if (you.duration[DUR_REPEL_STAIRS_MOVE])
+ pct = 29;
+ else
+ pct = 50;
+
+ if (!x_chance_in_y(pct, 100))
+ return (false);
+
+ // Get feature name before sliding stair over.
+ std::string stair_str =
+ feature_description(you.pos(), false, DESC_CAP_THE, false);
+
+ if (!slide_feature_over(you.pos(), coord_def(-1, -1), false))
+ return (false);
+
+ std::string verb = stair_climb_verb(stair);
+
+ mprf("%s moves away as you attempt to %s it!", stair_str.c_str(),
+ verb.c_str());
+
+ you.turn_is_over = true;
+
+ return (true);
+}
+
void up_stairs(dungeon_feature_type force_stair,
entry_cause_type entry_cause)
{
@@ -1684,6 +1724,9 @@ void up_stairs(dungeon_feature_type force_stair,
return;
}
+ if (_stair_moves_pre(stair_find))
+ return;
+
// Since the overloaded message set turn_is_over, I'm assuming that
// the overloaded character makes an attempt... so we're doing this
// check before that one. -- bwr
@@ -2039,6 +2082,8 @@ void down_stairs( int old_level, dungeon_feature_type force_stair,
return;
}
+ if (_stair_moves_pre(stair_find))
+ return;
if (shaft)
{
@@ -2194,13 +2239,8 @@ void down_stairs( int old_level, dungeon_feature_type force_stair,
{
std::string fall_where = "down the stairs";
- if (stair_find == DNGN_ENTER_ABYSS
- || stair_find == DNGN_ENTER_PANDEMONIUM
- || stair_find == DNGN_TRANSIT_PANDEMONIUM
- || stair_find == DNGN_ENTER_PORTAL_VAULT)
- {
+ if (!grid_is_staircase(stair_find))
fall_where = "through the gate";
- }
mprf("In your confused state, you trip and fall %s.",
fall_where.c_str());
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 01197f79ea..c9926d62c5 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -7154,7 +7154,8 @@ void monsters::check_redraw(const coord_def &old) const
void monsters::apply_location_effects(const coord_def &oldpos)
{
- dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos());
+ if (oldpos != pos())
+ dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos());
// monsters stepping on traps:
trap_def* ptrap = find_trap(pos());
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index e1fc214bcb..082f504a07 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -308,6 +308,32 @@ bool move_player_to_grid( const coord_def& p, bool stepped, bool allow_shift,
if (trap_def* ptrap = find_trap(you.pos()))
ptrap->trigger(you, !stepped); // blinking makes it hard to evade
+ command_type stair_dir = grid_stair_direction(new_grid);
+
+ if (stepped && stair_dir != CMD_NO_CMD
+ && you.duration[DUR_REPEL_STAIRS_MOVE])
+ {
+ int pct;
+ if (you.duration[DUR_REPEL_STAIRS_CLIMB])
+ pct = 29;
+ else
+ pct = 50;
+
+ if (x_chance_in_y(pct, 100))
+ {
+ if (slide_feature_over(you.pos(), coord_def(-1, -1), false))
+ {
+ std::string stair_str =
+ feature_description(new_grid, NUM_TRAPS, false,
+ DESC_CAP_THE, false);
+ std::string prep = grid_preposition(new_grid, true, &you);
+
+ mprf("%s slides away as you move %s it!", stair_str.c_str(),
+ prep.c_str());
+ }
+ }
+ }
+
return (true);
}
diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc
index 3ef428aa15..ef81b868c0 100644
--- a/crawl-ref/source/terrain.cc
+++ b/crawl-ref/source/terrain.cc
@@ -11,6 +11,7 @@
#include "terrain.h"
#include <algorithm>
+#include <sstream>
#include "cloud.h"
#include "dgnevent.h"
@@ -53,6 +54,20 @@ bool grid_is_stone_stair(dungeon_feature_type grid)
}
}
+bool grid_is_staircase(dungeon_feature_type grid)
+{
+ if (grid_is_stone_stair(grid))
+ return (true);
+
+ // All branch entries/exits are staircases, except for Zot.
+ if (grid == DNGN_ENTER_ZOT || grid == DNGN_RETURN_FROM_ZOT)
+ return (false);
+
+ return (grid >= DNGN_ENTER_FIRST_BRANCH && grid <= DNGN_ENTER_LAST_BRANCH
+ || grid >= DNGN_RETURN_FROM_FIRST_BRANCH
+ && grid <= DNGN_RETURN_FROM_LAST_BRANCH);
+}
+
bool grid_is_escape_hatch(dungeon_feature_type grid)
{
return (grid == DNGN_ESCAPE_HATCH_UP || grid == DNGN_ESCAPE_HATCH_DOWN);
@@ -488,21 +503,7 @@ static void _dgn_check_terrain_monsters(const coord_def &pos)
{
const int mindex = mgrd(pos);
if (mindex != NON_MONSTER)
- {
- monsters *mons = &menv[mindex];
-
- if (mons->has_ench(ENCH_SUBMERGED)
- && !monster_can_submerge(mons, grd(pos)))
- {
- mons->del_ench(ENCH_SUBMERGED);
- }
-
- if (grid_is_solid(grd(pos)))
- monster_teleport(mons, true, false);
- else
- mons_check_pool(mons, mons->pos(), KILL_MISC, -1);
- }
-
+ menv[mindex].apply_location_effects(pos);
}
// Clear blood off of terrain that shouldn't have it. Also clear
@@ -538,20 +539,15 @@ void _dgn_check_terrain_player(const coord_def pos)
if (pos != you.pos())
return;
- if (you.can_pass_through(pos) || !grid_is_solid(grd(pos)))
+ if (you.can_pass_through(pos))
{
- if (!you.airborne())
- {
- // If the monster can't stay submerged in the new terrain
- // and there aren't any adjacent squares where it can
- // stay submerged then move it.
- if (mgrd(you.pos()) != NON_MONSTER
- && !mons_is_submerged( &menv[ mgrd(you.pos()) ] ))
- {
- monster_teleport( &menv[ mgrd(you.pos()) ], true, false);
- }
- move_player_to_grid(pos, false, true, false);
- }
+ // If the monster can't stay submerged in the new terrain and
+ // there aren't any adjacent squares where it can stay
+ // submerged then move it.
+ const int midx = mgrd(you.pos());
+ if ( midx != NON_MONSTER && !mons_is_submerged( &menv[midx] ) )
+ monster_teleport( &menv[midx], true, false);
+ move_player_to_grid(pos, false, true, true);
}
else
you_teleport_now(true, false);
@@ -591,18 +587,106 @@ void dungeon_terrain_changed(const coord_def &pos,
set_terrain_changed(pos.x, pos.y);
}
-bool swap_features(const coord_def &pos1, const coord_def &pos2,
- bool swap_everything)
+static void _announce_swap_real(coord_def orig_pos, coord_def dest_pos)
{
- ASSERT(in_bounds(pos1) && in_bounds(pos2));
- ASSERT(pos1 != pos2);
+ dungeon_feature_type orig_feat = grd(dest_pos);
+
+ std::string orig_name =
+ feature_description(dest_pos, false,
+ see_grid(orig_pos) ? DESC_CAP_THE : DESC_CAP_A,
+ false);
+
+ std::string prep = grid_preposition(orig_feat, true);
+
+ std::string orig_actor, dest_actor;
+ if (orig_pos == you.pos())
+ orig_actor = "you";
+ else if (mgrd(orig_pos) != NON_MONSTER)
+ {
+ monsters &mon(menv[mgrd(orig_pos)]);
+
+ if (you.can_see(&mon))
+ orig_actor = mon.name(DESC_NOCAP_THE);
+ }
+
+ if (dest_pos == you.pos())
+ dest_actor = "you";
+ else if (mgrd(dest_pos) != NON_MONSTER)
+ {
+ monsters &mon(menv[mgrd(dest_pos)]);
+
+ if (you.can_see(&mon))
+ dest_actor = mon.name(DESC_NOCAP_THE);
+ }
+
+ std::ostringstream str;
+ str << orig_name << " ";
+ if (see_grid(orig_pos) && !see_grid(dest_pos))
+ {
+ str << "suddenly disappears";
+ if (!orig_actor.empty())
+ str << " from " << prep << " " << orig_actor;
+ }
+ else if (!see_grid(orig_pos) && see_grid(dest_pos))
+ {
+ str << "suddenly appears";
+ if (!dest_actor.empty())
+ str << " " << prep << " " << dest_actor;
+ }
+ else
+ {
+ str << "moves";
+ if (!orig_actor.empty())
+ str << " from " << prep << " " << orig_actor;
+ if (!dest_actor.empty())
+ str << " to " << prep << " " << dest_actor;
+ }
+ str << "!";
+ mpr(str.str().c_str());
+}
+
+static void _announce_swap(coord_def pos1, coord_def pos2)
+{
+ if (!see_grid(pos1) && !see_grid(pos2))
+ return;
const dungeon_feature_type feat1 = grd(pos1);
const dungeon_feature_type feat2 = grd(pos2);
+ if (feat1 == feat2)
+ return;
+
+ const bool notable_seen1 = is_notable_terrain(feat1) && see_grid(pos1);
+ const bool notable_seen2 = is_notable_terrain(feat2) && see_grid(pos2);
+ coord_def orig_pos, dest_pos;
+
+ if (notable_seen1 && notable_seen2)
+ {
+ _announce_swap_real(pos1, pos2);
+ _announce_swap_real(pos2, pos1);
+ }
+ else if (notable_seen1)
+ _announce_swap_real(pos2, pos1);
+ else if (notable_seen2)
+ _announce_swap_real(pos1, pos2);
+ else if (see_grid(pos2))
+ _announce_swap_real(pos1, pos2);
+ else
+ _announce_swap_real(pos2, pos1);
+}
+
+bool swap_features(const coord_def &pos1, const coord_def &pos2,
+ bool swap_everything, bool announce)
+{
+ ASSERT(in_bounds(pos1) && in_bounds(pos2));
+ ASSERT(pos1 != pos2);
+
if (is_sanctuary(pos1) || is_sanctuary(pos2))
return (false);
+ const dungeon_feature_type feat1 = grd(pos1);
+ const dungeon_feature_type feat2 = grd(pos2);
+
if (is_notable_terrain(feat1) && !see_grid(pos1)
&& is_terrain_known(pos1.x, pos1.y))
{
@@ -624,6 +708,9 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
trap_def* trap1 = find_trap(pos1);
trap_def* trap2 = find_trap(pos2);
+ shop_struct* shop1 = get_shop(pos1);
+ shop_struct* shop2 = get_shop(pos2);
+
// Find a temporary holding place for pos1 stuff to be moved to
// before pos2 is moved to pos1.
coord_def temp(-1, -1);
@@ -662,8 +749,6 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
(void) move_notable_thing(pos2, pos1);
env.markers.move(pos2, pos1);
dungeon_events.move_listeners(pos2, pos1);
- grd(pos2) = feat1;
- grd(pos1) = feat2;
env.map(pos1).property = prop2;
env.map(pos2).property = prop1;
@@ -671,6 +756,9 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
env.markers.move(temp, pos2);
dungeon_events.move_listeners(temp, pos2);
+ grd(pos2) = feat1;
+ grd(pos1) = feat2;
+
env.grid_colours(pos1) = col2;
env.grid_colours(pos2) = col1;
@@ -679,6 +767,11 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
if (trap2)
trap2->pos = pos1;
+ if (shop1)
+ shop1->pos = pos2;
+ if (shop2)
+ shop2->pos = pos1;
+
if (!swap_everything)
{
_dgn_check_terrain_items(pos1, false);
@@ -690,6 +783,9 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
_dgn_check_terrain_monsters(pos2);
_dgn_check_terrain_player(pos2);
set_terrain_changed(pos2.x, pos2.y);
+
+ if (announce)
+ _announce_swap(pos1, pos2);
return (true);
}
@@ -699,12 +795,28 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
igrd(pos1) = i2;
igrd(pos2) = i1;
+ if (igrd(pos1) != NON_ITEM)
+ {
+ for (stack_iterator si(igrd(pos1)); si; ++si)
+ si->pos = pos1;
+ }
+ if (igrd(pos2) != NON_ITEM)
+ {
+ for (stack_iterator si(igrd(pos2)); si; ++si)
+ si->pos = pos2;
+ }
+
const int m1 = mgrd(pos1);
const int m2 = mgrd(pos1);
mgrd(pos1) = m2;
mgrd(pos2) = m1;
+ if (mgrd(pos1) != NON_MONSTER)
+ menv[mgrd(pos1)].position = pos1;
+ if (mgrd(pos2) != NON_MONSTER)
+ menv[mgrd(pos2)].position = pos2;
+
move_cloud(env.cgrid(pos1), temp);
move_cloud(env.cgrid(pos2), pos1);
move_cloud(env.cgrid(temp), pos2);
@@ -723,7 +835,77 @@ bool swap_features(const coord_def &pos1, const coord_def &pos2,
set_terrain_changed(pos1.x, pos1.y);
set_terrain_changed(pos2.x, pos2.y);
- return (false);
+ if (announce)
+ _announce_swap(pos1, pos2);
+
+ return (true);
+}
+
+static bool _ok_dest_grid(const actor* orig_actor,
+ const dungeon_feature_type orig_feat,
+ const coord_def dest_pos)
+{
+ const dungeon_feature_type dest_feat = grd(dest_pos);
+
+ if (orig_feat == dest_feat)
+ return (false);
+
+ if (is_notable_terrain(dest_feat))
+ return (false);
+
+ actor* dest_actor = NULL;
+
+ if (dest_pos == you.pos())
+ dest_actor = dynamic_cast<actor*>(&you);
+ else if (mgrd(dest_pos) != NON_MONSTER)
+ dest_actor = dynamic_cast<actor*>(&menv[mgrd(dest_pos)]);
+
+ if (orig_actor && !orig_actor->is_habitable_feat(dest_feat))
+ return (false);
+ if (dest_actor && !dest_actor->is_habitable_feat(orig_feat))
+ return (false);
+
+ return (true);
+}
+
+bool slide_feature_over(const coord_def &src, coord_def prefered_dest,
+ bool announce)
+{
+ ASSERT(in_bounds(src));
+
+ const dungeon_feature_type orig_feat = grd(src);
+ actor* orig_actor = NULL;
+
+ if (src == you.pos())
+ orig_actor = dynamic_cast<actor*>(&you);
+ else if (mgrd(src) != NON_MONSTER)
+ orig_actor = dynamic_cast<actor*>(&menv[mgrd(src)]);
+
+ if (in_bounds(prefered_dest)
+ && _ok_dest_grid(orig_actor, orig_feat, prefered_dest))
+ {
+ ASSERT(prefered_dest != src);
+ }
+ else
+ {
+ radius_iterator ri(src, 1, true, false, true);
+
+ int squares = 0;
+ for (; ri; ++ri)
+ {
+ if (_ok_dest_grid(orig_actor, orig_feat, *ri))
+ {
+ if (one_chance_in(++squares))
+ prefered_dest = *ri;
+ }
+ }
+ }
+
+ if (!in_bounds(prefered_dest))
+ return (false);
+
+ ASSERT(prefered_dest != src);
+ return swap_features(src, prefered_dest, false, announce);
}
// Returns true if we manage to scramble free.
@@ -856,3 +1038,67 @@ dungeon_feature_type feat_by_desc(std::string desc)
return (DNGN_UNSEEN);
}
+
+std::string grid_preposition(dungeon_feature_type grid, bool active,
+ const actor* who)
+{
+ const bool airborne = !who || who->airborne();
+ const command_type dir = grid_stair_direction(grid);
+
+ if (dir == CMD_NO_CMD)
+ {
+ if (grid == DNGN_STONE_ARCH)
+ return "beside";
+ else if (grid_is_solid(grid))
+ {
+ if (active)
+ return "around";
+ else
+ return "inside";
+ }
+ else if (!airborne && grid >= DNGN_LAVA && grid <= DNGN_WATER_STUCK)
+ {
+ if (active)
+ return "around";
+ else
+ return "in";
+ }
+ else
+ {
+ if (active)
+ return "over";
+ else
+ return "above";
+ }
+ }
+
+ if (dir == CMD_GO_DOWNSTAIRS
+ && (grid_is_staircase(grid) || grid_is_escape_hatch(grid)))
+ {
+ if (active)
+ return "over";
+ else
+ return "above";
+ }
+ else if (grid_is_escape_hatch(grid))
+ {
+ if (active)
+ return "under";
+ else
+ return "beneath";
+ }
+ else
+ return "beside";
+}
+
+std::string stair_climb_verb(dungeon_feature_type grid)
+{
+ ASSERT(grid_stair_direction(grid) != CMD_NO_CMD);
+
+ if (grid_is_staircase(grid))
+ return "climb";
+ else if (grid_is_escape_hatch(grid))
+ return "use";
+ else
+ return "pass through";
+}
diff --git a/crawl-ref/source/terrain.h b/crawl-ref/source/terrain.h
index c3f855a346..d4c7bfc812 100644
--- a/crawl-ref/source/terrain.h
+++ b/crawl-ref/source/terrain.h
@@ -11,6 +11,7 @@
#include "enum.h"
+class actor;
struct coord_def;
// last updated 12may2000 {dlb}
@@ -29,12 +30,17 @@ bool grid_is_solid(const coord_def &c);
bool grid_is_rock(dungeon_feature_type grid);
bool grid_is_permarock(dungeon_feature_type grid);
bool grid_is_stone_stair(dungeon_feature_type grid);
+bool grid_is_staircase(dungeon_feature_type grid);
bool grid_is_escape_hatch(dungeon_feature_type grid);
bool grid_is_trap(dungeon_feature_type grid, bool undiscovered_too = false);
command_type grid_stair_direction(dungeon_feature_type grid);
bool grid_sealable_portal(dungeon_feature_type grid);
bool grid_is_portal(dungeon_feature_type grid);
+std::string grid_preposition(dungeon_feature_type grid, bool active = false,
+ const actor* who = NULL);
+std::string stair_climb_verb(dungeon_feature_type grid);
+
bool grid_is_water(dungeon_feature_type grid);
bool grid_is_watery(dungeon_feature_type grid);
god_type grid_altar_god( dungeon_feature_type grid );
@@ -62,7 +68,11 @@ void dungeon_terrain_changed(const coord_def &pos,
bool preserve_items = false);
bool swap_features(const coord_def &pos1, const coord_def &pos2,
- bool swap_everything = false);
+ bool swap_everything = false, bool announce = true);
+
+bool slide_feature_over(const coord_def &src,
+ coord_def prefered_dest = coord_def(-1, -1),
+ bool announce = true);
bool is_critical_feature(dungeon_feature_type feat);