diff options
-rw-r--r-- | crawl-ref/source/beam.cc | 68 | ||||
-rw-r--r-- | crawl-ref/source/beam.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/cmd-keys.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/decks.cc | 157 | ||||
-rw-r--r-- | crawl-ref/source/decks.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 13 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 111 | ||||
-rw-r--r-- | crawl-ref/source/files.cc | 25 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 52 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 26 | ||||
-rw-r--r-- | crawl-ref/source/terrain.cc | 316 | ||||
-rw-r--r-- | crawl-ref/source/terrain.h | 12 |
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); |