diff options
-rw-r--r-- | crawl-ref/source/actor.h | 8 | ||||
-rw-r--r-- | crawl-ref/source/dgn-shoals.cc | 242 | ||||
-rw-r--r-- | crawl-ref/source/dgn-shoals.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/effects.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 14 | ||||
-rw-r--r-- | crawl-ref/source/player.h | 1 |
7 files changed, 264 insertions, 6 deletions
diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index f8e920eb14..b29e826307 100644 --- a/crawl-ref/source/actor.h +++ b/crawl-ref/source/actor.h @@ -28,7 +28,15 @@ public: virtual bool is_summoned(int* duration = NULL, int* summon_type = NULL) const = 0; + // [ds] Low-level moveto() - moves the actor without updating relevant + // grids, such as mgrd. virtual void moveto(const coord_def &c) = 0; + + // High-level actor movement. If in doubt, use this. Returns true if the + // actor cannot be moved to the target, possibly because it is already + // occupied. + virtual bool move_to_pos(const coord_def &c) = 0; + virtual void set_position(const coord_def &c); virtual const coord_def& pos() const { return position; } diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc index 1f7485dd5b..b21315f1a8 100644 --- a/crawl-ref/source/dgn-shoals.cc +++ b/crawl-ref/source/dgn-shoals.cc @@ -6,13 +6,20 @@ #include "dungeon.h" #include "dgn-shoals.h" #include "env.h" +#include "items.h" #include "maps.h" +#include "mon-place.h" +#include "mon-util.h" #include "random.h" +#include "terrain.h" #include <algorithm> #include <vector> #include <cmath> +const char *ENVP_SHOALS_TIDE_KEY = "shoals-tide-height"; +const char *ENVP_SHOALS_TIDE_VEL = "shoals-tide-velocity"; + inline short &shoals_heights(const coord_def &c) { return (*env.heightmap)(c); @@ -45,9 +52,8 @@ static double _to_radians(int degrees) return degrees * M_PI / 180; } -static dungeon_feature_type _shoals_feature_at(const coord_def &c) +static dungeon_feature_type _shoals_feature_by_height(int height) { - const int height = shoals_heights(c); return height >= SHT_STONE ? DNGN_STONE_WALL : height >= SHT_ROCK? DNGN_ROCK_WALL : height >= SHT_FLOOR? DNGN_FLOOR : @@ -55,6 +61,12 @@ static dungeon_feature_type _shoals_feature_at(const coord_def &c) : DNGN_DEEP_WATER; } +static dungeon_feature_type _shoals_feature_at(const coord_def &c) +{ + const int height = shoals_heights(c); + return _shoals_feature_by_height(height); +} + static void _shoals_init_heights() { env.heightmap.reset(new grid_heightmap); @@ -240,7 +252,7 @@ static std::vector<coord_def> _shoals_water_depth_change_points() static inline void _shoals_deepen_water_at(coord_def p, int distance) { - shoals_heights(p) -= distance * 5; + shoals_heights(p) -= distance * 7; } static void _shoals_deepen_water() @@ -365,7 +377,28 @@ static void _shoals_furniture(int margin) = static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_UP_I + i); } } +} +static void _shoals_deepen_edges() +{ + const int edge = 2; + // Water of the edge of the screen is too deep to be exposed by tides. + for (int y = 1; y < GYM - 2; ++y) + { + for (int x = 1; x <= edge; ++x) + { + shoals_heights(coord_def(x, y)) -= 800; + shoals_heights(coord_def(GXM - 1 - x, y)) -= 800; + } + } + for (int x = 1; x < GXM - 2; ++x) + { + for (int y = 1; y <= edge; ++y) + { + shoals_heights(coord_def(x, y)) -= 800; + shoals_heights(coord_def(x, GYM - 1 - y)) -= 800; + } + } } void prepare_shoals(int level_number) @@ -382,5 +415,208 @@ void prepare_shoals(int level_number) _shoals_smooth(); _shoals_apply_level(); _shoals_deepen_water(); + _shoals_deepen_edges(); _shoals_furniture(_shoals_margin); } + +// The raw tide height / TIDE_MULTIPLIER is the actual tide height. The higher +// the tide multiplier, the slower the tide advances and recedes. A multiplier +// of X implies that the tide will advance visibly about once in X turns. +const int TIDE_MULTIPLIER = 30; + +const int LOW_TIDE = -25 * TIDE_MULTIPLIER; +const int HIGH_TIDE = 25 * TIDE_MULTIPLIER; +const int TIDE_DECEL_MARGIN = 8; +const int START_TIDE_RISE = 2; + +static void _shoals_run_tide(int &tide, int &acc) +{ + tide += acc; + tide = std::max(std::min(tide, HIGH_TIDE), LOW_TIDE); + if ((tide == HIGH_TIDE && acc > 0) + || (tide == LOW_TIDE && acc < 0)) + acc = -acc; + bool in_decel_margin = + (abs(tide - HIGH_TIDE) < TIDE_DECEL_MARGIN) + || (abs(tide - LOW_TIDE) < TIDE_DECEL_MARGIN); + if ((abs(acc) == 2) == in_decel_margin) + acc = in_decel_margin? acc / 2 : acc * 2; +} + +static coord_def _shoals_escape_place_from(coord_def bad_place, + bool monster_free) +{ + int best_height = -1000; + coord_def chosen; + for (adjacent_iterator ai(bad_place); ai; ++ai) + { + coord_def p(*ai); + const dungeon_feature_type feat(grd(p)); + if (!feat_is_solid(feat) && !feat_destroys_items(feat) + && (!monster_free || !actor_at(p))) + { + if (best_height == -1000 || shoals_heights(p) > best_height) + { + best_height = shoals_heights(p); + chosen = p; + } + } + } + return chosen; +} + +static bool _shoals_tide_sweep_items_clear(coord_def c) +{ + int link = igrd(c); + if (link == NON_ITEM) + return true; + + const coord_def target(_shoals_escape_place_from(c, false)); + if (target.origin()) + return false; + + move_item_stack_to_grid(c, target); + return true; +} + +static bool _shoals_tide_sweep_actors_clear(coord_def c) +{ + actor *victim = actor_at(c); + if (!victim) + return true; + + if (victim->atype() == ACT_MONSTER) + { + monsters *mvictim = dynamic_cast<monsters *>(victim); + // Plants and statues cannot be moved away; the tide cannot + // drown them. + if (mons_class_is_stationary(mvictim->type)) + return false; + + // If the monster doesn't need help, move along. + if (monster_habitable_grid(mvictim, DNGN_DEEP_WATER)) + return true; + } + coord_def evacuation_point(_shoals_escape_place_from(c, true)); + // The tide moves on even if we cannot evacuate the tile! + if (!evacuation_point.origin()) + victim->move_to_pos(evacuation_point); + return true; +} + +// The tide will attempt to push items and non-water-capable monsters to +// adjacent squares. +static bool _shoals_tide_sweep_clear(coord_def c) +{ + return _shoals_tide_sweep_items_clear(c) + && _shoals_tide_sweep_actors_clear(c); +} + +static void _shoals_apply_tide_feature_at(coord_def c, + dungeon_feature_type feat) +{ + if (feat == DNGN_DEEP_WATER && !_shoals_tide_sweep_clear(c)) + feat = DNGN_SHALLOW_WATER; + + const dungeon_feature_type current_feat = grd(c); + if (feat == current_feat) + return; + + dungeon_terrain_changed(c, feat, true, false, true); +} + +static void _shoals_apply_tide_at(coord_def c, int tide) +{ + const int effective_height = shoals_heights(c) - tide; + dungeon_feature_type newfeat = + _shoals_feature_by_height(effective_height); + // Make sure we're not sprouting new walls. + if (feat_is_wall(newfeat)) + newfeat = DNGN_FLOOR; + + _shoals_apply_tide_feature_at(c, newfeat); +} + +static void _shoals_apply_tide(int tide) +{ + std::vector<coord_def> pages[2]; + int current_page = 0; + + // Start from corners of the map. + pages[current_page].push_back(coord_def(1,1)); + pages[current_page].push_back(coord_def(GXM - 2, 1)); + pages[current_page].push_back(coord_def(1, GYM - 2)); + pages[current_page].push_back(coord_def(GXM - 2, GYM - 2)); + + FixedArray<bool, GXM, GYM> seen_points(false); + + while (!pages[current_page].empty()) + { + int next_page = !current_page; + std::vector<coord_def> &cpage(pages[current_page]); + std::vector<coord_def> &npage(pages[next_page]); + + for (int i = 0, size = cpage.size(); i < size; ++i) + { + coord_def c(cpage[i]); + const bool was_wet(feat_is_water(grd(c))); + seen_points(c) = true; + _shoals_apply_tide_at(c, tide); + + // Only wet squares can propagate the tide onwards. + if (was_wet) + { + for (adjacent_iterator ai(c); ai; ++ai) + { + coord_def adj(*ai); + if (!in_bounds(adj)) + continue; + if (!seen_points(adj)) + { + const dungeon_feature_type feat = grd(adj); + if (feat_is_water(feat) || feat == DNGN_FLOOR) + { + npage.push_back(adj); + seen_points(adj) = true; + } + } + } + } + } + + cpage.clear(); + current_page = next_page; + } +} + +static void _shoals_init_tide() +{ + if (!env.properties.exists(ENVP_SHOALS_TIDE_KEY)) + { + env.properties[ENVP_SHOALS_TIDE_KEY] = short(0); + env.properties[ENVP_SHOALS_TIDE_VEL] = short(2); + } +} + +void shoals_apply_tides(int turns_elapsed) +{ + if (!player_in_branch(BRANCH_SHOALS) || !turns_elapsed + || !env.heightmap.get()) + { + return; + } + + const int TIDE_UNIT = HIGH_TIDE - LOW_TIDE; + // If we've been gone a long time, eliminate some unnecessary math. + if (turns_elapsed > TIDE_UNIT * 2) + turns_elapsed = turns_elapsed % TIDE_UNIT + TIDE_UNIT; + + _shoals_init_tide(); + int tide = env.properties[ENVP_SHOALS_TIDE_KEY].get_short(); + int acc = env.properties[ENVP_SHOALS_TIDE_VEL].get_short(); + while (turns_elapsed-- > 0) + _shoals_run_tide(tide, acc); + env.properties[ENVP_SHOALS_TIDE_KEY] = short(tide); + env.properties[ENVP_SHOALS_TIDE_VEL] = short(acc); + _shoals_apply_tide(tide / TIDE_MULTIPLIER); +} diff --git a/crawl-ref/source/dgn-shoals.h b/crawl-ref/source/dgn-shoals.h index 023f321fe6..aae09ff653 100644 --- a/crawl-ref/source/dgn-shoals.h +++ b/crawl-ref/source/dgn-shoals.h @@ -2,5 +2,6 @@ #define DGN_SHOALS_H void prepare_shoals(int level_number); +void shoals_apply_tides(int turns_elapsed); #endif diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 30687dd58d..bede6d46c2 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -27,6 +27,7 @@ #include "coordit.h" #include "decks.h" #include "delay.h" +#include "dgn-shoals.h" #include "dungeon.h" #include "directn.h" #include "dgnevent.h" @@ -4263,6 +4264,7 @@ void update_level(double elapsedTime) #endif update_corpses(elapsedTime); + shoals_apply_tides(turns); if (env.sanctuary_time) { diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 5697133c4a..3312bfcf94 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -36,6 +36,7 @@ #include "coordit.h" #include "database.h" #include "delay.h" +#include "dgn-shoals.h" #include "directn.h" #include "dgnevent.h" #include "directn.h" @@ -3092,6 +3093,7 @@ void run_environment_effects() } run_corruption_effects(you.time_taken); + shoals_apply_tides(div_rand_round(you.time_taken, 10)); } coord_def pick_adjacent_free_square(const coord_def& p) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 4e5cf2dc77..e7e54c63d6 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -7031,6 +7031,17 @@ void player::moveto(const coord_def &c) set_position(c); } +bool player::move_to_pos(const coord_def &c) +{ + actor *target = actor_at(c); + if (!target || target->submerged()) + { + moveto(c); + return true; + } + return false; +} + void player::shiftto(const coord_def &c) { crawl_view.shift_player_to(c); @@ -7423,6 +7434,3 @@ void player::set_duration(duration_type dur, int turns, you.duration[dur] = 0; increase_duration(dur, turns, cap, msg); } - - - diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h index d7f5306c08..a02e5f5c4c 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -314,6 +314,7 @@ public: void set_position(const coord_def &c); // Low-level move the player. Use this instead of changing pos directly. void moveto(const coord_def &c); + bool move_to_pos(const coord_def &c); // Move the player during an abyss shift. void shiftto(const coord_def &c); bool blink_to(const coord_def& c, bool quiet = false); |