#include "AppHdr.h" #include #include "show.h" #include "areas.h" #include "cloud.h" #include "coord.h" #include "coordit.h" #include "env.h" #include "exclude.h" #include "fprop.h" #include "itemprop.h" #include "mon-stuff.h" #include "mon-util.h" #include "monster.h" #include "options.h" #include "overmap.h" #include "random.h" #include "showsymb.h" #include "state.h" #include "stuff.h" #include "areas.h" #include "terrain.h" #include "viewgeom.h" #include "viewmap.h" show_type::show_type() : cls(SH_NOTHING), feat(DNGN_UNSEEN), item(SHOW_ITEM_NONE), mons(MONS_NO_MONSTER), colour(0) { } show_type::show_type(monster_type montype) : cls(SH_MONSTER), feat(DNGN_UNSEEN), item(SHOW_ITEM_NONE), mons(montype), colour(0) { } show_type::show_type(dungeon_feature_type f) : cls(SH_FEATURE), feat(f), item(SHOW_ITEM_NONE), mons(MONS_NO_MONSTER), colour(0) { } static show_item_type _item_to_show_code(const item_def &item); show_type::show_type(const item_def &it) : cls(SH_ITEM), feat(DNGN_UNSEEN), item(_item_to_show_code(it)), mons(MONS_NO_MONSTER), colour(0) { } show_type::show_type(show_item_type t) : cls(SH_ITEM), feat(DNGN_UNSEEN), item(t), mons(MONS_NO_MONSTER), colour(0) { } bool show_type::operator < (const show_type &other) const { if (cls < other.cls) return (false); else if (cls > other.cls) return (true); switch (cls) { case SH_FEATURE: return (feat < other.feat); case SH_ITEM: return (item < other.item); case SH_MONSTER: return (mons < other.mons); default: return (false); } } void show_def::_set_backup(const coord_def &ep) { backup(ep) = grid(ep); } static bool _show_bloodcovered(const coord_def& where) { if (!is_bloodcovered(where)) return (false); dungeon_feature_type feat = env.grid(where); // Altars, stairs (of any kind) and traps should not be coloured red. return (!is_critical_feature(feat) && !feat_is_trap(feat)); } static unsigned short _tree_colour(const coord_def& where) { uint32_t h = where.x; h+=h<<10; h^=h>>6; h += where.y; h+=h<<10; h^=h>>6; h+=h<<3; h^=h>>11; h+=h<<15; return (h>>30) ? GREEN : LIGHTGREEN; } static unsigned short _feat_colour(const coord_def &where, const dungeon_feature_type feat) { unsigned short colour = BLACK; const feature_def &fdef = get_feature_def(feat); // TODO: consolidate with feat_is_stair etc. bool excluded_stairs = (feat >= DNGN_STONE_STAIRS_DOWN_I && feat <= DNGN_ESCAPE_HATCH_UP && is_exclude_root(where)); if (excluded_stairs) colour = Options.tc_excluded; else if (feat >= DNGN_MINMOVE && you.get_beholder(where)) { // Colour grids that cannot be reached due to beholders // dark grey. colour = DARKGREY; } else if (feat >= DNGN_MINMOVE && is_sanctuary(where)) { if (testbits(env.pgrid(where), FPROP_SANCTUARY_1)) colour = YELLOW; else if (testbits(env.pgrid(where), FPROP_SANCTUARY_2)) { if (!one_chance_in(4)) colour = WHITE; // 3/4 else if (!one_chance_in(3)) colour = LIGHTCYAN; // 1/6 else colour = LIGHTGREY; // 1/12 } } else if (_show_bloodcovered(where)) colour = RED; else if (env.grid_colours(where)) colour = env.grid_colours(where); else { colour = fdef.colour; if (colour == BLACK && feat == DNGN_TREES) colour = _tree_colour(where); if (fdef.em_colour && fdef.em_colour != fdef.colour && emphasise(where)) { colour = fdef.em_colour; } } // TODO: should be a feat_is_whatever(feat) if (feat >= DNGN_FLOOR_MIN && feat <= DNGN_FLOOR_MAX || feat == DNGN_UNDISCOVERED_TRAP) { if (!haloers(where).empty()) { if (silenced(where)) colour = LIGHTCYAN; else colour = YELLOW; } else if (silenced(where)) colour = CYAN; } return (colour); } void show_def::_update_feat_at(const coord_def &gp, const coord_def &ep) { grid(ep).cls = SH_FEATURE; grid(ep).feat = grid_appearance(gp); grid(ep).colour = _feat_colour(gp, grid(ep).feat); if (get_feature_def(grid(ep)).is_notable()) seen_notable_thing(grid(ep).feat, gp); } static show_item_type _item_to_show_code(const item_def &item) { switch (item.base_type) { case OBJ_ORBS: return (SHOW_ITEM_ORB); case OBJ_WEAPONS: return (SHOW_ITEM_WEAPON); case OBJ_MISSILES: return (SHOW_ITEM_MISSILE); case OBJ_ARMOUR: return (SHOW_ITEM_ARMOUR); case OBJ_WANDS: return (SHOW_ITEM_WAND); case OBJ_FOOD: return (SHOW_ITEM_FOOD); case OBJ_SCROLLS: return (SHOW_ITEM_SCROLL); case OBJ_JEWELLERY: return (jewellery_is_amulet(item)? SHOW_ITEM_AMULET : SHOW_ITEM_RING); case OBJ_POTIONS: return (SHOW_ITEM_POTION); case OBJ_BOOKS: return (SHOW_ITEM_BOOK); case OBJ_STAVES: return (SHOW_ITEM_STAVE); case OBJ_MISCELLANY: return (SHOW_ITEM_MISCELLANY); case OBJ_CORPSES: return (SHOW_ITEM_CORPSE); case OBJ_GOLD: return (SHOW_ITEM_GOLD); default: return (SHOW_ITEM_ORB); // bad item character } } void show_def::_update_item_at(const coord_def &gp, const coord_def &ep) { const item_def *eitem; // Check for mimics. const monsters* m = monster_at(gp); if (m && mons_is_unknown_mimic(m)) eitem = &get_mimic_item(m); else if (you.visible_igrd(gp) != NON_ITEM) eitem = &mitm[you.visible_igrd(gp)]; else return; unsigned short &ecol = grid(ep).colour; glyph g = get_item_glyph(eitem); const dungeon_feature_type feat = grd(gp); if (Options.feature_item_brand && is_critical_feature(feat)) ecol |= COLFLAG_FEATURE_ITEM; else if (Options.trap_item_brand && feat_is_trap(feat)) ecol |= COLFLAG_TRAP_ITEM; else { ecol = g.col; if (feat_is_water(feat)) ecol = _feat_colour(gp, feat); // monster(mimic)-owned items have link = NON_ITEM+1+midx if (eitem->link > NON_ITEM && you.visible_igrd(gp) != NON_ITEM) ecol |= COLFLAG_ITEM_HEAP; else if (eitem->link < NON_ITEM && !crawl_state.arena) ecol |= COLFLAG_ITEM_HEAP; grid(ep).cls = SH_ITEM; grid(ep).item = _item_to_show_code(*eitem); } #ifdef USE_TILE int idx = you.visible_igrd(gp); if (idx != NON_ITEM) { if (feat_is_stair(feat)) tile_place_item_marker(ep.x, ep.y, idx); else tile_place_item(ep.x, ep.y, idx); } #endif } void show_def::_update_cloud(int cloudno) { const coord_def e = grid2show(env.cloud[cloudno].pos); int which_colour = get_cloud_colour(cloudno); _set_backup(e); if (env.cloud[cloudno].type != CLOUD_GLOOM) { grid(e).cls = SH_CLOUD; } grid(e).colour = which_colour; #ifdef USE_TILE tile_place_cloud(e.x, e.y, env.cloud[cloudno]); #endif } static void _check_monster_pos(const monsters* monster) { int s = monster->mindex(); ASSERT(mgrd(monster->pos()) == s); // [rob] The following in case asserts aren't enabled. // [enne] - It's possible that mgrd and monster->x/y are out of // sync because they are updated separately. If we can see this // monster, then make sure that the mgrd is set correctly. if (mgrd(monster->pos()) != s) { // If this mprf triggers for you, please note any special // circumstances so we can track down where this is coming // from. mprf(MSGCH_ERROR, "monster %s (%d) at (%d, %d) was " "improperly placed. Updating mgrd.", monster->name(DESC_PLAIN, true).c_str(), s, monster->pos().x, monster->pos().y); mgrd(monster->pos()) = s; } } void show_def::_update_monster(const monsters* mons) { _check_monster_pos(mons); const coord_def pos = mons->pos(); const coord_def e = grid2show(pos); if (mons_is_unknown_mimic(mons)) return; if (!mons->visible_to(&you)) { // ripple effect? if (grd(pos) == DNGN_SHALLOW_WATER && !mons_flies(mons) && env.cgrid(pos) == EMPTY_CLOUD) { _set_backup(e); grid(e).cls = SH_INVIS_EXPOSED; // Translates between colours used for shallow and deep water, // if not using the normal LIGHTCYAN / BLUE. The ripple uses // the deep water colour. unsigned short base_colour = env.grid_colours(pos); static const unsigned short ripple_table[] = {BLUE, // BLACK => BLUE (default) BLUE, // BLUE => BLUE GREEN, // GREEN => GREEN CYAN, // CYAN => CYAN RED, // RED => RED MAGENTA, // MAGENTA => MAGENTA BROWN, // BROWN => BROWN DARKGREY, // LIGHTGREY => DARKGREY DARKGREY, // DARKGREY => DARKGREY BLUE, // LIGHTBLUE => BLUE GREEN, // LIGHTGREEN => GREEN BLUE, // LIGHTCYAN => BLUE RED, // LIGHTRED => RED MAGENTA, // LIGHTMAGENTA => MAGENTA BROWN, // YELLOW => BROWN LIGHTGREY}; // WHITE => LIGHTGREY grid(e).colour = ripple_table[base_colour & 0x0f]; } else if (is_opaque_cloud(env.cgrid(pos)) && !mons->submerged() && !mons_is_insubstantial(mons->type)) { grid(e).cls = SH_INVIS_EXPOSED; grid(e).colour = get_cloud_colour(env.cgrid(pos)); } return; } // Mimics are always left on map. if (!mons_is_mimic(mons->type)) _set_backup(e); grid(e).cls = SH_MONSTER; if (!crawl_state.arena && you.misled()) grid(e).mons = mons->get_mislead_type(); #ifndef USE_TILE else if (mons->type == MONS_SLIME_CREATURE && mons->number > 1) grid(e).mons = MONS_MERGED_SLIME_CREATURE; #endif else grid(e).mons = mons->type; grid(e).colour = get_mons_glyph(mons).col; #ifdef USE_TILE tile_place_monster(mons->pos().x, mons->pos().y, mons->mindex(), true); #endif } void show_def::update_at(const coord_def &gp, const coord_def &ep) { grid(ep).cls = SH_NOTHING; // The sequence is grid, items, clouds, monsters. _update_feat_at(gp, ep); // If there's items on the boundary (shop inventory), // we don't show them. if (!in_bounds(gp)) return; _update_item_at(gp, ep); const int cloud = env.cgrid(gp); if (cloud != EMPTY_CLOUD && env.cloud[cloud].type != CLOUD_NONE && env.cloud[cloud].pos == gp) { _update_cloud(cloud); } const monsters *mons = monster_at(gp); if (mons && mons->alive()) _update_monster(mons); } void show_def::init() { grid.init(show_type()); backup.init(show_type()); for (radius_iterator ri(&you.get_los()); ri; ++ri) update_at(*ri, grid2show(*ri)); } show_type to_knowledge(show_type obj) { if (Options.item_colour && obj.cls == SH_ITEM) return (obj); if (obj.cls == SH_MONSTER) { obj.colour = DARKGREY; return (obj); } const feature_def& fdef = get_feature_def(obj); obj.colour = fdef.seen_colour; return (obj); }