From acaa14cd0e82de32f1a8f76e54405023eb0b1973 Mon Sep 17 00:00:00 2001 From: Robert Vollmert Date: Tue, 27 Oct 2009 03:27:26 +0100 Subject: Move most exclusion code from travel.cc to exclude.cc. --- crawl-ref/source/exclude.cc | 291 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 289 insertions(+), 2 deletions(-) (limited to 'crawl-ref/source/exclude.cc') 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 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 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 ", ", ")); +} -- cgit v1.2.3-54-g00ecf