EXYVMVO2EMMZW4LRYSHHHNRFLQ7P6YHNM7SLUO2X2KUMV3WQHQZQC EC2RWFR6OZNMDBBOERNPI5XH6FWAUCJUWU22HK7ROQL2U7JVAN3AC 6AOBHPEVZFUDSTZ6FR6PRQ6H2V766R2PJRXS4UMYI6SAELGAV5EAC NT5SNG44MU2JRBQA55TR27YDMA6SMKAP2ANDCROS3T2IL7ATLFUAC NRZI542DZ2F4R5O34V5DPGMJWKRJBOBSZPQRXTVCSDAPCM3KO3KQC EHV4CAJV2NVOHTFHFZP6XFW56X7G4PQKBWEEPXQPYQ57SOCYJYRAC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC TAHSTXR7ROOMDFUSBUU4ZAIEWQLAS5CIRCTARLD4Q2BGNLSL7E5QC AUJG42P2TOWAVVU6HBT3D7USOSZCRPQS7FEUGV57HVNULEFDPTSQC T5QIFBTUF5UQT66HK6U7FU7DTSFLK5MSTDFMJOSR2XPX7B32DGEQC FRJZHFXLXDE7MANBAGFHRWIH77HC2I4JRGQ5TAVYZS676DU72QSAC XDACRDVLDEUFUBN4L7ES5WBD3YSLBHMRZ4Q5PXIUMOK44D3TLWSAC SSQP7MS6LZYY73QEF66EYNNQJJSB6TVLLWXLWL7JJAYBLXCEY2XAC 5FA5IEAXTMXYS2VUBVDKBKHPKAIOY4GN5SXYJORBYWQIGHVW3FFQC 45OFFQNRRS46LXSPQ3SPNP2OXMBECE22A6NWQPITDEOXLRKYSQKAC ROWW5YUHXITC4QBBSYRVPE6HJ2XMFX3K3M2LHPX4WRDKMEFJVTOQC PU6VE6DFO4AJPL4RLKMMAIEGYDRNMTH4N4KSERK6B5QU5ZP53ZUAC OZ2NHOTPZXWCZ7YUMY7AHFAQOCZYL7UI2CAMIYY4LG7LB6F2NUIAC ZNMT5CZHP2FC4HTLNA7KYEDGFBXSCUE5QHJOALVPE6RDPHSEDXRQC 7K2MOEL4VXTJG2XV575MS3YHL7VWWYBROAV5RCWUBFSF25XXV4XAC QEEJFAETO6B2J4IWDIDCJ5UNIFNNHHG22IWF2CUJRTJJBNE47CWQC 547JREUJXTZNYVGHNNAET5F5O5JYYGNTDQB6ABZNT7YX5EY64OHAC EJRKMYKMOYRQXTWGFTMADEWIGWLMWHMUDA73AUT7HO5OBK2GUQEAC AL7EYY4HB7JNEFGDB6NVVHCVVUYYKUJKC4UFH4T7XUT3P5NT4NAAC 453NICOL4ZKH7YQAWQFKSZZRDJ53XECMTQC7VEMXWV2N4IEAL5VAC 6Y2GZNY5I5YY34QKEPSSDCGAVVLFMOFI4QI34KBNQDRC76JM5AFAC G7DNYFW745Q567EF3TPR2FCQ4ATPN236ON7X5TLYC7TEPZW3BAFAC LPV66WRRLBAXH3Z6BYUK36N33H6VFGXMLRGYAE2WGHGN6DTZMVBAC R4V76NHTUCQ25LBZKMS34INQEXVTJ5TT3QZSPGG6S2WR2FYKK5DQC HVEU33HIUHCYFINJMVSSOIRUPZGGVC7NMWUN2EADG2W373JP3WOAC CBPDARXV7QY5JAOCPIYTGKZJRTGYJF4GF4JAJP5V2GW4AI5M3SSAC TQXNC2YQVJZXBPSLDEANMM2EDGHTH4DSN74OJIRMUF7D46SESHZAC 354AVBXVMW7MMEPBTMB3Q7JHJTEWGSKVPRR7PEYZGEBWQV33TFSAC HIPFIMUOA7DFOFV3DQ55YZJVGNU2GNDYFUCB4MRPUR5DTYDO5YMAC XP3TQISCLEST3ZNTF6OZ6FYMHIIPMVTNVBIIG47LA5PS7J234SQAC HC35ORPWMUNGV4G2TVNPID4ZDRNTWXE4U37LYT4QGSEQR2EXQJNAC 7PVZC6EFCEFBDQLSBR7OREADW43UF2I3HKJZSQBART5SOIJTYECQC IPXXB4VRVZWOU5DKQ5ZTD37LS3QNK2R6APNZUO672YEEJT6OFAYQC GWGKGHFGBLVPDSSDWYFORHZHMWOR3SFC5PJNF732V7FEKWWJKPOAC OYYZVCE3QHBVJM6IEKK5HTJRG5YOVQNCBRMSJENTHOI2WPJLNCFAC RS2Q66UPKG2NLNBE6UMX273UEVNGTNLVEITB2PCRVHZWTS2WXTAQC Q3B3UVMYEVC4YJUPYVSNTR4DJH4E6J4JJDHZNT5LNOCHCPPMEMXAC 7IG33VVCNNO7RQ5LNZSNDYADT5AZYME2BNZFY33D3CA2Z7A5AZSAC PDOFPXD2X6VI23AHKCGQ5RVDBG74CNP2E3YOHKXLOARHHBXEK3HQC PL6I2CMSTHY5ZHWVMIQE5YTM5S5VPKBNZM6QJVHZSSKOJGIJ5W4AC LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC TVRUXTWYN7ZEPAC7WCNNNUUOSNTAT7IHSGQJI45LP4OJPIT6DT2QC RS24ZF3Y47QA2534EHQWZ35O2CI4JUOIVHUPRANCCNLVINSCYFXQC 25BU4E7ND5KWNWCNIEOQPI5ITHMRNVQUQNJURBFSEP5FVASYDVQQC 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 centreint radius; // exclusion radiuslos_def los; // los from exclusion centrebool 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;};
}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 centrestatic 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);}}}#endifstatic 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 checksif (!player_in_mappable_area())return;#ifdef USE_TILEfor (int i = curr_excludes.size()-1; i >= 0; i--)_tile_exclude_gmap_update(curr_excludes[i].pos);#endifcurr_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 twiceif (travel_exclude *exc = _find_exclude_root(p)){if (exc->radius == LOS_RADIUS)set_exclude(p, 0);else{ASSERT(exc->radius == 0);del_exclude(p);}}elseset_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));elsecount_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 ", ", "));
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 centreint radius; // exclusion radiuslos_def los; // los from exclusion centrebool 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.ccbool is_excluded(const coord_def &p, const exclvec &exc = curr_excludes);
}}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 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 centrestatic 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);}}}#endifstatic 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 checksif (!player_in_mappable_area())return;#ifdef USE_TILEfor (int i = curr_excludes.size()-1; i >= 0; i--)_tile_exclude_gmap_update(curr_excludes[i].pos);#endifcurr_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 twiceif (travel_exclude *exc = _find_exclude_root(p)){if (exc->radius == LOS_RADIUS)set_exclude(p, 0);else{ASSERT(exc->radius == 0);del_exclude(p);}}elseset_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));elsecount_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 ", ", "));}