#include "AppHdr.h"
#include "exclude.h"
#include "coord.h"
#include "mon-util.h"
#include "overmap.h"
#include "stuff.h"
#include "tags.h"
#include "terrain.h"
#include "travel.h"
#include "tutorial.h"
#include "view.h"
static bool _mon_needs_auto_exclude(const monsters *mon, bool sleepy = false)
{
if (mons_is_stationary(mon))
{
if (sleepy)
return (false);
return (!mons_is_mimic(mon->type)
|| testbits(mon->flags, MF_KNOWN_MIMIC));
}
return (mon->asleep());
}
bool need_auto_exclude(const monsters *mon, bool sleepy)
{
std::string name = mon->name(DESC_BASENAME);
lowercase(name);
for (unsigned i = 0; i < Options.auto_exclude.size(); ++i)
if (Options.auto_exclude[i].matches(name)
&& _mon_needs_auto_exclude(mon, sleepy)
&& mon->attitude == ATT_HOSTILE)
{
return (true);
}
return (false);
}
void set_auto_exclude(const monsters *mon)
{
if (need_auto_exclude(mon) && !is_exclude_root(mon->pos()))
{
set_exclude(mon->pos(), LOS_RADIUS, true);
#ifdef USE_TILE
viewwindow(true, false);
#endif
learned_something_new(TUT_AUTO_EXCLUSION, mon->pos());
}
}
void remove_auto_exclude(const monsters *mon, bool sleepy)
{
if (need_auto_exclude(mon, sleepy))
{
del_exclude(mon->pos());
#ifdef USE_TILE
viewwindow(true, false);
#endif
}
}
opacity_type _feat_opacity(dungeon_feature_type feat)
{
return (feat_is_opaque(feat) ? OPC_OPAQUE : OPC_CLEAR);
}
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
{
return OPC_CLEAR;
}
}
};
static opacity_excl opc_excl;
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(autoexcl), mon(mons), vault(vaultexcl)
{
set_los();
}
void travel_exclude::set_los()
{
uptodate = true;
if (radius > 0)
{
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);
}
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
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()
{
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();
}
void cycle_exclude_radius(const coord_def &p)
{
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);
}
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);
}
void set_exclude(const coord_def &p, int radius, bool autoexcl, bool vaultexcl)
{
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);
}
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);
}
}
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 ", ", "));
}
void marshallExcludes(writer& outf, const exclvec& excludes)
{
marshallShort(outf, excludes.size());
if (excludes.size())
{
for (int i = 0, count = excludes.size(); i < count; ++i)
{
marshallCoord(outf, excludes[i].pos);
marshallShort(outf, excludes[i].radius);
marshallBoolean(outf, excludes[i].autoex);
marshallShort(outf, excludes[i].mon);
}
}
}
void unmarshallExcludes(reader& inf, char minorVersion, exclvec &excludes)
{
excludes.clear();
int nexcludes = unmarshallShort(inf);
if (nexcludes)
{
for (int i = 0; i < nexcludes; ++i)
{
coord_def c;
unmarshallCoord(inf, c);
const int radius = unmarshallShort(inf);
bool autoexcl = false;
monster_type mon = MONS_NO_MONSTER;
if (minorVersion >= TAG_ANNOTATE_EXCL)
{
autoexcl = unmarshallBoolean(inf);
mon = static_cast<monster_type>(unmarshallShort(inf));
}
excludes.push_back(travel_exclude(c, radius, autoexcl, mon));
}
}
}