summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-19 05:12:15 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-19 05:12:15 +0000
commit30824b7084af3c2dce35d2eae4d4600f67ab4e06 (patch)
tree852963a375c0c492fd9202bfa2d1d64842b4f285 /crawl-ref
parent10391ff4e676a29fbc2836d1494ed2aae4b112a7 (diff)
downloadcrawl-ref-30824b7084af3c2dce35d2eae4d4600f67ab4e06.tar.gz
crawl-ref-30824b7084af3c2dce35d2eae4d4600f67ab4e06.zip
Added "The Stairs" card (not yet in any deck), eventually to be the card which
re-arranges all stairs on the level, but now is just used to test the code used to move stairs in LOS away or towards the player. Added beam flavour BEAM_VISUAL for animating someting moving in a line which doesn't have any effects on its own, and removed BEAM_LINE_OF_SIGHT, which was no longer being used. Added bolt structure field "delay", which can be used to change the delay between animating each square in a beam from the default of 15 msec. For eventual use as a Xom effect added the stairs-are-avoiding-you durations DUR_REPEL_STAIRS_MOVE and DUR_REPEL_STAIRS_CLIMB. If DUR_REPEL_STAIRS_MOVE alone is set then stepping onto a stairs/portal/etc has a 50% chance of making it move away in a random direction. If DUR_REPEL_STAIRS_CLIMB alone is set then attempting to climb a stairs/etc has a 50% chance of making it move away before you can get through it (and ending your turn in the process). If both are set then moving onto it and attempting to climb each have a 29% chance of making it move, for a combined chance of 49.59% of a move-and-climb making the stairs move away. Once a stair is successfully taken there's a 50% chance of the stair on the other end moving away from the player, and then both durations are reset to 0. These can be tested by drawing The Stairs card ("&c stair"), which will set DUR_REPEL_STAIRS_CLIMB if you're on top of a stair or DUR_REPEL_STAIRS_MOVE if your'e not. Added a few wizard targetting command placeholders. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7865 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-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);