diff options
Diffstat (limited to 'crawl-ref')
-rw-r--r-- | crawl-ref/source/exclude.cc | 291 | ||||
-rw-r--r-- | crawl-ref/source/exclude.h | 39 | ||||
-rw-r--r-- | crawl-ref/source/travel.cc | 304 | ||||
-rw-r--r-- | crawl-ref/source/travel.h | 33 |
4 files changed, 334 insertions, 333 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 ", ", ")); +} diff --git a/crawl-ref/source/exclude.h b/crawl-ref/source/exclude.h index 77b1244a9e..1a39d82f53 100644 --- a/crawl-ref/source/exclude.h +++ b/crawl-ref/source/exclude.h @@ -1,9 +1,48 @@ #ifndef EXCLUDE_H #define EXCLUDE_H +#include "los.h" + bool need_auto_exclude(const monsters *mon, bool sleepy = false); void set_auto_exclude(const monsters *mon); void remove_auto_exclude(const monsters *mon, bool sleepy = false); +void init_exclusion_los(); +void update_exclusion_los(std::vector<coord_def> changed); + +bool is_exclude_root(const coord_def &p); +void cycle_exclude_radius(const coord_def &p); +void del_exclude(const coord_def &p); +void set_exclude(const coord_def &p, int radius = LOS_RADIUS, + bool autoexcl = false, bool vaultexcl = false); +void maybe_remove_autoexclusion(const coord_def &p); +std::string get_exclusion_desc(); +void clear_excludes(); + +struct travel_exclude +{ + coord_def pos; // exclusion centre + int radius; // exclusion radius + los_def los; // los from exclusion centre + bool uptodate; // Is los up to date? + bool autoex; // Was set automatically. + monster_type mon; // Monster around which exclusion is centered. + bool vault; // Is this exclusion set by a vault? + + travel_exclude(const coord_def &p, int r = LOS_RADIUS, + bool autoex = false, monster_type mons = MONS_NO_MONSTER, + bool vault = false); + + int radius_sq() const; + void set_los(); + bool in_bounds(const coord_def& p) const; + bool affects(const coord_def& p) const; +}; + +typedef std::vector<travel_exclude> exclvec; +extern exclvec curr_excludes; // in travel.cc + +bool is_excluded(const coord_def &p, const exclvec &exc = curr_excludes); + #endif diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index 57ecbfb305..14eb85f72d 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -20,6 +20,7 @@ #include "describe.h" #include "dgnevent.h" #include "directn.h" +#include "exclude.h" #include "itemname.h" #include "itemprop.h" #include "items.h" @@ -85,7 +86,7 @@ static std::vector<stair_info> curr_stairs; // Squares that are not safe to travel to on the current level. typedef std::vector<travel_exclude> exclvec; -static exclvec curr_excludes; +exclvec curr_excludes; // This is where we last tried to take a stair during interlevel travel. // Note that last_stair.depth should be set to -1 before initiating interlevel @@ -270,178 +271,6 @@ bool feat_is_traversable(dungeon_feature_type grid) return (traversable_terrain[grid] == TRAVERSABLE); } -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 init_exclusion_los() -{ - for (unsigned int i = 0; i < curr_excludes.size(); i++) - curr_excludes[i].set_los(); -} - -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(); - } -} - -/* - * 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(); -} - -static 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); -} - -bool is_excluded(const coord_def &p) -{ - return _is_excluded(p, curr_excludes); -} - -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(); -} - const char *run_mode_name(int runmode) { return (runmode == RMODE_TRAVEL ? "travel" : @@ -468,129 +297,6 @@ inline bool is_stash(const LevelStashes *ls, int x, int y) return s && s->enabled; } -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 ", ", ")); -} - static bool _is_monster_blocked(const coord_def& c) { const monsters *mons = monster_at(c); @@ -2688,7 +2394,7 @@ static int _find_transtravel_stair( const level_id &cur, if (cur == target.id) { // Are we in an exclude? If so, bail out. - if (_is_excluded( stair, li.get_excludes() )) + if (is_excluded( stair, li.get_excludes() )) return (-1); // If there's no target position on the target level, or we're on the @@ -2872,7 +2578,7 @@ static bool _loadlev_populate_stair_distances(const level_pos &target) if (travel_load_map(target.id.branch, absdungeon_depth(target.id.branch, target.id.depth))) { - std::vector<travel_exclude> old_excludes = curr_excludes; + exclvec old_excludes = curr_excludes; curr_excludes.clear(); LevelInfo &li = travel_cache.get_level_info(target.id); @@ -3278,7 +2984,7 @@ std::string stair_info::describe() const void LevelInfo::set_level_excludes() { curr_excludes = excludes; - _update_exclusion_los(true); + init_exclusion_los(); } bool LevelInfo::empty() const diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h index cfc8f95c9a..3dcf059577 100644 --- a/crawl-ref/source/travel.h +++ b/crawl-ref/source/travel.h @@ -8,6 +8,7 @@ #include "externs.h" +#include "exclude.h" #include "los.h" #include <stdio.h> @@ -60,23 +61,14 @@ void initialise_travel(); void init_travel_terrain_check(bool check_race_equip = true); void stop_running(void); void travel_init_new_level(); -void cycle_exclude_radius(const coord_def &p); -void del_exclude(const coord_def &p); -void set_exclude(const coord_def &p, int radius = LOS_RADIUS, - bool autoexcl = false, bool vaultexcl = false); -void maybe_remove_autoexclusion(const coord_def &p); -std::string get_exclusion_desc(); -void clear_excludes(); unsigned char is_waypoint(const coord_def &p); -bool is_exclude_root(const coord_def &p); command_type direction_to_command(char x, char y); bool is_resting(void); #ifdef CLUA_BINDINGS const char *trap_name(const coord_def &p); #endif void explore_pickup_event(int did_pickup, int tried_pickup); -bool is_excluded(const coord_def &p); bool feat_is_traversable(dungeon_feature_type feat); bool is_unknown_stair(const coord_def &p, dungeon_feature_type remembered_feat = NUM_REAL_FEATURES); @@ -350,29 +342,6 @@ public: bool can_travel() const { return (type == PHYSICAL); } }; -void init_exclusion_los(); -void update_exclusion_los(std::vector<coord_def> changed); - -struct travel_exclude -{ - coord_def pos; // exclusion centre - int radius; // exclusion radius - los_def los; // los from exclusion centre - bool uptodate; // Is los up to date? - bool autoex; // Was set automatically. - monster_type mon; // Monster around which exclusion is centered. - bool vault; // Is this exclusion set by a vault? - - travel_exclude(const coord_def &p, int r = LOS_RADIUS, - bool autoex = false, monster_type mons = MONS_NO_MONSTER, - bool vault = false); - - int radius_sq() const; - void set_los(); - bool in_bounds(const coord_def& p) const; - bool affects(const coord_def& p) const; -}; - // Information on a level that interlevel travel needs. struct LevelInfo { |