summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/actor.h8
-rw-r--r--crawl-ref/source/dgn-shoals.cc242
-rw-r--r--crawl-ref/source/dgn-shoals.h1
-rw-r--r--crawl-ref/source/effects.cc2
-rw-r--r--crawl-ref/source/misc.cc2
-rw-r--r--crawl-ref/source/player.cc14
-rw-r--r--crawl-ref/source/player.h1
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);