summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/exclude.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-10-27 03:27:26 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-10-27 03:28:33 +0100
commitacaa14cd0e82de32f1a8f76e54405023eb0b1973 (patch)
tree44be8e7c2fd611b36cdca082da22925f91515e59 /crawl-ref/source/exclude.cc
parentf848a81e607b8b674e10040b899c68901662a310 (diff)
downloadcrawl-ref-acaa14cd0e82de32f1a8f76e54405023eb0b1973.tar.gz
crawl-ref-acaa14cd0e82de32f1a8f76e54405023eb0b1973.zip
Move most exclusion code from travel.cc to exclude.cc.
Diffstat (limited to 'crawl-ref/source/exclude.cc')
-rw-r--r--crawl-ref/source/exclude.cc291
1 files changed, 289 insertions, 2 deletions
diff --git a/crawl-ref/source/exclude.cc b/crawl-ref/source/exclude.cc
index d41bf3324f..077487b1b1 100644
--- a/crawl-ref/source/exclude.cc
+++ b/crawl-ref/source/exclude.cc
@@ -8,13 +8,13 @@
#include "exclude.h"
#include "mon-util.h"
+#include "overmap.h"
#include "stuff.h"
+#include "terrain.h"
#include "travel.h"
#include "tutorial.h"
#include "view.h"
-// TODO: move other exclusion code here.
-
static bool _mon_needs_auto_exclude(const monsters *mon, bool sleepy = false)
{
if (mons_is_stationary(mon))
@@ -75,4 +75,291 @@ void remove_auto_exclude(const monsters *mon, bool sleepy)
}
}
+opacity_type _feat_opacity(dungeon_feature_type feat)
+{
+ return (feat_is_opaque(feat) ? OPC_OPAQUE : OPC_CLEAR);
+}
+
+// A cell is considered clear unless the player knows it's
+// opaque.
+struct opacity_excl : opacity_func
+{
+ CLONE(opacity_excl)
+
+ opacity_type operator()(const coord_def& p) const
+ {
+ if (!is_terrain_seen(p))
+ return OPC_CLEAR;
+ else if (!is_terrain_changed(p))
+ return _feat_opacity(env.grid(p));
+ else if (env.map(p).object < NUM_REAL_FEATURES)
+ return _feat_opacity((dungeon_feature_type) env.map(p).object);
+ else
+ {
+ // If you have seen monsters, items or clouds there,
+ // it must have been passable.
+ return OPC_CLEAR;
+ }
+ }
+};
+static opacity_excl opc_excl;
+
+// Note: bounds_radius gives a circle with square radius r*r+1;
+// this doesn't work well for radius 0, but then we want to
+// skip LOS calculation in that case anyway since it doesn't
+// currently short-cut for small bounds. So radius 0 is special-cased.
+
+travel_exclude::travel_exclude(const coord_def &p, int r,
+ bool autoexcl, monster_type mons, bool vaultexcl)
+: pos(p), radius(r),
+los(los_def(p, opc_excl, bounds_radius(r))),
+uptodate(false), autoex(autoex), mon(mons), vault(vaultexcl)
+{
+ set_los();
+}
+
+void travel_exclude::set_los()
+{
+ uptodate = true;
+ if (radius > 0)
+ {
+ // Radius might have been changed, and this is cheap.
+ los.set_bounds(bounds_radius(radius));
+ los.update();
+ }
+}
+
+bool travel_exclude::affects(const coord_def& p) const
+{
+ if (!uptodate)
+ mprf(MSGCH_ERROR, "exclusion not up-to-date: e (%d,%d) p (%d,%d)",
+ pos.x, pos.y, p.x, p.y);
+ if (radius == 0)
+ return (p == pos);
+ return (los.see_cell(p));
+}
+
+bool travel_exclude::in_bounds(const coord_def &p) const
+{
+ return (radius == 0 && p == pos
+ || los.in_bounds(p));
+}
+
+void _mark_excludes_non_updated(const coord_def &p)
+{
+ for (exclvec::iterator it = curr_excludes.begin();
+ it != curr_excludes.end(); ++it)
+ {
+ it->uptodate = it->uptodate && it->in_bounds(p);
+ }
+}
+
+void _update_exclusion_los(bool all=false)
+{
+ for (exclvec::iterator it = curr_excludes.begin();
+ it != curr_excludes.end(); ++it)
+ {
+ if (all || !it->uptodate)
+ it->set_los();
+ }
+}
+
+void init_exclusion_los()
+{
+ _update_exclusion_los(true);
+}
+
+/*
+ * Update exclusions' LOS to reflect changes within their range.
+ * "changed" is a list of coordinates that have been changed.
+ * Only exclusions that might have one of the changed points
+ * in view are updated.
+ */
+void update_exclusion_los(std::vector<coord_def> changed)
+{
+ if (changed.empty())
+ return;
+
+ for (unsigned int i = 0; i < changed.size(); ++i)
+ _mark_excludes_non_updated(changed[i]);
+ _update_exclusion_los();
+}
+
+bool is_excluded(const coord_def &p, const exclvec &exc)
+{
+ for (unsigned int i = 0; i < exc.size(); ++i)
+ if (exc[i].affects(p))
+ return (true);
+ return (false);
+}
+
+static travel_exclude *_find_exclude_root(const coord_def &p)
+{
+ for (unsigned int i = 0; i < curr_excludes.size(); ++i)
+ if (curr_excludes[i].pos == p)
+ return (&curr_excludes[i]);
+ return (NULL);
+}
+
+bool is_exclude_root(const coord_def &p)
+{
+ return (_find_exclude_root(p));
+}
+
+#ifdef USE_TILE
+// update Gmap for squares surrounding exclude centre
+static void _tile_exclude_gmap_update(const coord_def &p)
+{
+ for (int x = -8; x <= 8; x++)
+ for (int y = -8; y <= 8; y++)
+ {
+ int px = p.x+x, py = p.y+y;
+ if (in_bounds(coord_def(px,py)))
+ {
+ tiles.update_minimap(px, py);
+ }
+ }
+}
+#endif
+
+static void _exclude_update()
+{
+ if (can_travel_interlevel())
+ {
+ LevelInfo &li = travel_cache.get_level_info(level_id::current());
+ li.update();
+ }
+ set_level_exclusion_annotation(get_exclusion_desc());
+}
+
+static void _exclude_update(const coord_def &p)
+{
+#ifdef USE_TILE
+ _tile_exclude_gmap_update(p);
+#endif
+ _exclude_update();
+}
+
+void clear_excludes()
+{
+ // Sanity checks
+ if (!player_in_mappable_area())
+ return;
+
+#ifdef USE_TILE
+ for (int i = curr_excludes.size()-1; i >= 0; i--)
+ _tile_exclude_gmap_update(curr_excludes[i].pos);
+#endif
+
+ curr_excludes.clear();
+ clear_level_exclusion_annotation();
+
+ _exclude_update();
+}
+
+// Cycles the radius of an exclusion, including "off" state.
+void cycle_exclude_radius(const coord_def &p)
+{
+ // XXX: scanning through curr_excludes twice
+ if (travel_exclude *exc = _find_exclude_root(p))
+ {
+ if (exc->radius == LOS_RADIUS)
+ set_exclude(p, 0);
+ else
+ {
+ ASSERT(exc->radius == 0);
+ del_exclude(p);
+ }
+ }
+ else
+ set_exclude(p, LOS_RADIUS);
+}
+
+// Remove a possible exclude.
+void del_exclude(const coord_def &p)
+{
+ for (unsigned int i = 0; i < curr_excludes.size(); ++i)
+ if (curr_excludes[i].pos == p)
+ {
+ curr_excludes.erase(curr_excludes.begin() + i);
+ break;
+ }
+ _exclude_update(p);
+}
+
+// Set or update an exclude.
+void set_exclude(const coord_def &p, int radius, bool autoexcl, bool vaultexcl)
+{
+ // Sanity checks; excludes can be set in Pan and regular dungeon
+ // levels only.
+ if (!player_in_mappable_area())
+ return;
+
+ if (!in_bounds(p))
+ return;
+
+ if (travel_exclude *exc = _find_exclude_root(p))
+ {
+ exc->radius = radius;
+ exc->set_los();
+ }
+ else
+ {
+ monster_type montype = MONS_NO_MONSTER;
+ const monsters *m = monster_at(p);
+ if (m && you.can_see(m))
+ montype = m->type;
+
+ curr_excludes.push_back(travel_exclude(p, radius, autoexcl,
+ montype, vaultexcl));
+ }
+
+ _exclude_update(p);
+}
+
+// If a grid that was placed automatically no longer contains the original
+// monster (or it is invisible), remove the exclusion.
+void maybe_remove_autoexclusion(const coord_def &p)
+{
+ if (travel_exclude *exc = _find_exclude_root(p))
+ {
+ const monsters *m = monster_at(p);
+ if (exc->autoex && (!m || !you.can_see(m) || m->type != exc->mon))
+ del_exclude(p);
+ }
+}
+
+// Lists all exclusions on the current level.
+std::string get_exclusion_desc()
+{
+ std::vector<std::string> monsters;
+ int count_other = 0;
+ for (unsigned int i = 0; i < curr_excludes.size(); ++i)
+ {
+ if (!invalid_monster_type(curr_excludes[i].mon))
+ monsters.push_back(mons_type_name(curr_excludes[i].mon, DESC_PLAIN));
+ else
+ count_other++;
+ }
+
+ if (count_other > 0)
+ {
+ snprintf(info, INFO_SIZE, "%d %sexclusion%s",
+ count_other, monsters.empty() ? "" : "more ",
+ count_other > 1 ? "s" : "");
+ monsters.push_back(info);
+ }
+ else if (monsters.empty())
+ return "";
+
+ std::string desc = "";
+ if (monsters.size() > 1 || count_other == 0)
+ {
+ snprintf(info, INFO_SIZE, "exclusion%s: ",
+ monsters.size() > 1 ? "s" : "");
+ desc += info;
+ }
+ return (desc + comma_separated_line(monsters.begin(), monsters.end(),
+ ", and ", ", "));
+}