/* * File: viewmap.cc * Summary: Showing the level map (X and background). */ #include "AppHdr.h" #include "viewmap.h" #include "branch.h" #include "cio.h" #include "colour.h" #include "command.h" #include "coord.h" #include "coordit.h" #include "env.h" #include "map_knowledge.h" #include "fprop.h" #include "exclude.h" #include "feature.h" #include "files.h" #include "format.h" #include "macro.h" #include "options.h" #include "place.h" #include "player.h" #include "showsymb.h" #include "stash.h" #include "stuff.h" #include "terrain.h" #include "travel.h" #include "viewchar.h" #include "viewgeom.h" unsigned get_sightmap_char(dungeon_feature_type feat) { return (get_feature_def(feat).symbol); } unsigned get_magicmap_char(dungeon_feature_type feat) { return (get_feature_def(feat).magic_symbol); } // Determines if the given feature is present at (x, y) in _feat_ coordinates. // If you have map coords, add (1, 1) to get grid coords. // Use one of // 1. '<' and '>' to look for stairs // 2. '\t' or '\\' for shops, portals. // 3. '^' for traps // 4. '_' for altars // 5. Anything else will look for the exact same character in the level map. bool is_feature(int feature, const coord_def& where) { if (!env.map_knowledge(where).object && !you.see_cell(where)) return (false); dungeon_feature_type grid = grd(where); switch (feature) { case 'E': return (travel_point_distance[where.x][where.y] == PD_EXCLUDED); case 'F': case 'W': return is_waypoint(where); case 'I': return is_stash(where.x, where.y); case '_': switch (grid) { case DNGN_ALTAR_ZIN: case DNGN_ALTAR_SHINING_ONE: case DNGN_ALTAR_KIKUBAAQUDGHA: case DNGN_ALTAR_YREDELEMNUL: case DNGN_ALTAR_XOM: case DNGN_ALTAR_VEHUMET: case DNGN_ALTAR_OKAWARU: case DNGN_ALTAR_MAKHLEB: case DNGN_ALTAR_SIF_MUNA: case DNGN_ALTAR_TROG: case DNGN_ALTAR_NEMELEX_XOBEH: case DNGN_ALTAR_ELYVILON: case DNGN_ALTAR_LUGONU: case DNGN_ALTAR_BEOGH: case DNGN_ALTAR_JIYVA: case DNGN_ALTAR_FEDHAS: case DNGN_ALTAR_CHEIBRIADOS: return (true); default: return (false); } case '\t': case '\\': switch (grid) { case DNGN_ENTER_HELL: case DNGN_EXIT_HELL: case DNGN_ENTER_LABYRINTH: case DNGN_ENTER_PORTAL_VAULT: case DNGN_EXIT_PORTAL_VAULT: case DNGN_ENTER_SHOP: case DNGN_ENTER_DIS: case DNGN_ENTER_GEHENNA: case DNGN_ENTER_COCYTUS: case DNGN_ENTER_TARTARUS: case DNGN_ENTER_ABYSS: case DNGN_EXIT_ABYSS: case DNGN_ENTER_PANDEMONIUM: case DNGN_EXIT_PANDEMONIUM: case DNGN_TRANSIT_PANDEMONIUM: case DNGN_ENTER_ZOT: case DNGN_RETURN_FROM_ZOT: return (true); default: return (false); } case '<': switch (grid) { case DNGN_ESCAPE_HATCH_UP: case DNGN_STONE_STAIRS_UP_I: case DNGN_STONE_STAIRS_UP_II: case DNGN_STONE_STAIRS_UP_III: case DNGN_RETURN_FROM_ORCISH_MINES: case DNGN_RETURN_FROM_HIVE: case DNGN_RETURN_FROM_LAIR: case DNGN_RETURN_FROM_SLIME_PITS: case DNGN_RETURN_FROM_VAULTS: case DNGN_RETURN_FROM_CRYPT: case DNGN_RETURN_FROM_HALL_OF_BLADES: case DNGN_RETURN_FROM_TEMPLE: case DNGN_RETURN_FROM_SNAKE_PIT: case DNGN_RETURN_FROM_ELVEN_HALLS: case DNGN_RETURN_FROM_TOMB: case DNGN_RETURN_FROM_SWAMP: case DNGN_RETURN_FROM_SHOALS: case DNGN_EXIT_PORTAL_VAULT: return (true); default: return (false); } case '>': switch (grid) { case DNGN_ESCAPE_HATCH_DOWN: case DNGN_STONE_STAIRS_DOWN_I: case DNGN_STONE_STAIRS_DOWN_II: case DNGN_STONE_STAIRS_DOWN_III: case DNGN_ENTER_ORCISH_MINES: case DNGN_ENTER_HIVE: case DNGN_ENTER_LAIR: case DNGN_ENTER_SLIME_PITS: case DNGN_ENTER_VAULTS: case DNGN_ENTER_CRYPT: case DNGN_ENTER_HALL_OF_BLADES: case DNGN_ENTER_TEMPLE: case DNGN_ENTER_SNAKE_PIT: case DNGN_ENTER_ELVEN_HALLS: case DNGN_ENTER_TOMB: case DNGN_ENTER_SWAMP: case DNGN_ENTER_SHOALS: return (true); default: return (false); } case '^': switch (grid) { case DNGN_TRAP_MECHANICAL: case DNGN_TRAP_MAGICAL: case DNGN_TRAP_NATURAL: return (true); default: return (false); } default: return get_map_knowledge_char(where.x, where.y) == (unsigned) feature; } } static bool _is_feature_fudged(int feature, const coord_def& where) { if (!env.map_knowledge(where).object) return (false); if (is_feature(feature, where)) return (true); // 'grid' can fit in an unsigned char, but making this a short shuts up // warnings about out-of-range case values. short grid = grd(where); if (feature == '<') { switch (grid) { case DNGN_EXIT_HELL: case DNGN_EXIT_PORTAL_VAULT: case DNGN_EXIT_ABYSS: case DNGN_EXIT_PANDEMONIUM: case DNGN_RETURN_FROM_ZOT: return (true); default: return (false); } } else if (feature == '>') { switch (grid) { case DNGN_ENTER_DIS: case DNGN_ENTER_GEHENNA: case DNGN_ENTER_COCYTUS: case DNGN_ENTER_TARTARUS: case DNGN_TRANSIT_PANDEMONIUM: case DNGN_ENTER_ZOT: return (true); default: return (false); } } return (false); } static int _find_feature(int feature, int curs_x, int curs_y, int start_x, int start_y, int anchor_x, int anchor_y, int ignore_count, int *move_x, int *move_y) { int cx = anchor_x, cy = anchor_y; int firstx = -1, firsty = -1; int matchcount = 0; // Find the first occurrence of feature 'feature', spiralling around (x,y) int maxradius = GXM > GYM ? GXM : GYM; for (int radius = 1; radius < maxradius; ++radius) for (int axis = -2; axis < 2; ++axis) { int rad = radius - (axis < 0); for (int var = -rad; var <= rad; ++var) { int dx = radius, dy = var; if (axis % 2) dx = -dx; if (axis < 0) { int temp = dx; dx = dy; dy = temp; } int x = cx + dx, y = cy + dy; if (!in_bounds(x, y)) continue; if (_is_feature_fudged(feature, coord_def(x, y))) { ++matchcount; if (!ignore_count--) { // We want to cursor to (x,y) *move_x = x - (start_x + curs_x - 1); *move_y = y - (start_y + curs_y - 1); return matchcount; } else if (firstx == -1) { firstx = x; firsty = y; } } } } // We found something, but ignored it because of an ignorecount if (firstx != -1) { *move_x = firstx - (start_x + curs_x - 1); *move_y = firsty - (start_y + curs_y - 1); return 1; } return 0; } void find_features(const std::vector& features, unsigned char feature, std::vector *found) { for (unsigned feat = 0; feat < features.size(); ++feat) { const coord_def& coord = features[feat]; if (is_feature(feature, coord)) found->push_back(coord); } } static int _find_feature( const std::vector& features, int feature, int curs_x, int curs_y, int start_x, int start_y, int ignore_count, int *move_x, int *move_y, bool forward) { int firstx = -1, firsty = -1, firstmatch = -1; int matchcount = 0; for (unsigned feat = 0; feat < features.size(); ++feat) { const coord_def& coord = features[feat]; if (_is_feature_fudged(feature, coord)) { ++matchcount; if (forward? !ignore_count-- : --ignore_count == 1) { // We want to cursor to (x,y) *move_x = coord.x - (start_x + curs_x - 1); *move_y = coord.y - (start_y + curs_y - 1); return matchcount; } else if (!forward || firstx == -1) { firstx = coord.x; firsty = coord.y; firstmatch = matchcount; } } } // We found something, but ignored it because of an ignorecount if (firstx != -1) { *move_x = firstx - (start_x + curs_x - 1); *move_y = firsty - (start_y + curs_y - 1); return firstmatch; } return 0; } static int _get_number_of_lines_levelmap() { return get_number_of_lines() - (Options.level_map_title ? 1 : 0); } #ifndef USE_TILE static void _draw_level_map(int start_x, int start_y, bool travel_mode, bool on_level) { int bufcount2 = 0; screen_buffer_t buffer2[GYM * GXM * 2]; const int num_lines = std::min(_get_number_of_lines_levelmap(), GYM); const int num_cols = std::min(get_number_of_cols(), GXM); cursor_control cs(false); int top = 1 + Options.level_map_title; cgotoxy(1, top); for (int screen_y = 0; screen_y < num_lines; screen_y++) for (int screen_x = 0; screen_x < num_cols; screen_x++) { coord_def c(start_x + screen_x, start_y + screen_y); if (!map_bounds(c)) { buffer2[bufcount2 + 1] = DARKGREY; buffer2[bufcount2] = 0; } else { buffer2[bufcount2] = env.map_knowledge(c).glyph(); buffer2[bufcount2 + 1] = real_colour(get_map_col(c, travel_mode)); if (c == you.pos() && !crawl_state.arena_suspended && on_level) { // [dshaligram] Draw the @ symbol on the level-map. It's no // longer saved into the env.map_knowledge, so we need to draw it // directly. buffer2[bufcount2 + 1] = WHITE; buffer2[bufcount2] = you.symbol; } // If we've a waypoint on the current square, *and* the // square is a normal floor square with nothing on it, // show the waypoint number. if (Options.show_waypoints) { // XXX: This is a horrible hack. screen_buffer_t &bc = buffer2[bufcount2]; unsigned char ch = is_waypoint(c); if (ch && (bc == get_sightmap_char(DNGN_FLOOR) || bc == get_magicmap_char(DNGN_FLOOR))) { bc = ch; } } } bufcount2 += 2; } puttext(1, top, num_cols, top + num_lines - 1, buffer2); } #endif // USE_TILE static void _reset_travel_colours(std::vector &features, bool on_level) { // We now need to redo travel colours. features.clear(); if (on_level) find_travel_pos(you.pos(), NULL, NULL, &features); else { travel_pathfind tp; tp.set_feature_vector(&features); tp.get_features(); } // Sort features into the order the player is likely to prefer. arrange_features(features); } class feature_list { enum group { G_UP, G_DOWN, G_PORTAL, G_OTHER, G_NONE, NUM_GROUPS = G_NONE }; std::vector data[NUM_GROUPS]; glyph get_glyph(const coord_def& gc) { // XXX: it's unclear whether we want to display all features // or just those not obscured by remembered/detected stuff. dungeon_feature_type feat = env.map_knowledge(gc).feat(); const bool terrain_seen = is_terrain_seen(gc); const feature_def &fdef = get_feature_def(feat); glyph g; g.ch = terrain_seen ? fdef.symbol : fdef.magic_symbol; g.col = get_map_col(gc); return (g); } static group feat_dir(dungeon_feature_type feat) { switch (feat_stair_direction(feat)) { case CMD_GO_UPSTAIRS: return G_UP; case CMD_GO_DOWNSTAIRS: return G_DOWN; default: return G_NONE; } } group get_group(const coord_def& gc) { show_type obj = env.map_knowledge(gc).object; if (obj != SH_FEATURE) return G_NONE; dungeon_feature_type feat = obj.feat; if (feat_is_staircase(feat) || feat_is_escape_hatch(feat)) return feat_dir(feat); if (feat == DNGN_TRAP_NATURAL) return G_DOWN; if (feat_is_altar(feat) || feat == DNGN_ENTER_SHOP) return G_OTHER; if (get_feature_dchar(feat) == DCHAR_ARCH) return G_PORTAL; return G_NONE; } void maybe_add(const coord_def& gc) { #ifndef USE_TILE if (!is_terrain_known(gc)) return; group grp = get_group(gc); if (grp != G_NONE) data[grp].push_back(get_glyph(gc)); #endif } public: void init() { for (unsigned int i = 0; i < NUM_GROUPS; ++i) data[i].clear(); for (rectangle_iterator ri(0); ri; ++ri) maybe_add(*ri); } formatted_string format() const { formatted_string s; for (unsigned int i = 0; i < NUM_GROUPS; ++i) for (unsigned int j = 0; j < data[i].size(); ++j) s.add_glyph(data[i][j]); return s; } }; #ifndef USE_TILE static void _draw_title(const coord_def& cpos, const feature_list& feats) { if (!Options.level_map_title) return; const formatted_string help = formatted_string::parse_string("(Press ? for help)"); const int helplen = std::string(help).length(); std::string pstr = ""; #ifdef WIZARD if (you.wizard) { char buf[10]; snprintf(buf, sizeof(buf), " (%d, %d)", cpos.x, cpos.y); pstr = std::string(buf); } #endif // WIZARD cgotoxy(1, 1); textcolor(WHITE); cprintf("%-*s", get_number_of_cols() - helplen, (upcase_first(place_name( get_packed_place(), true, true)) + pstr).c_str()); formatted_string s = feats.format(); cgotoxy((get_number_of_cols() - s.length()) / 2, 1); s.display(); textcolor(LIGHTGREY); cgotoxy(get_number_of_cols() - helplen + 1, 1); help.display(); } #endif class levelview_excursion : public level_excursion { public: void go_to(const level_id& next) { #ifdef USE_TILE tiles.clear_minimap(); level_excursion::go_to(next); TileNewLevel(false); #else level_excursion::go_to(next); #endif } }; // show_map() now centers the known map along x or y. This prevents // the player from getting "artificial" location clues by using the // map to see how close to the end they are. They'll need to explore // to get that. This function is still a mess, though. -- bwr void show_map( level_pos &spec_place, bool travel_mode, bool allow_esc ) { levelview_excursion le; level_id original(level_id::current()); cursor_control ccon(!Options.use_fake_cursor); int i, j; int move_x = 0, move_y = 0, scroll_y = 0; bool new_level = true; // Vector to track all features we can travel to, in order of distance. std::vector features; // List of all interesting features for display in the (console) title. feature_list feats; int min_x = INT_MAX, max_x = INT_MIN, min_y = INT_MAX, max_y = INT_MIN; const int num_lines = _get_number_of_lines_levelmap(); const int half_screen = (num_lines - 1) / 2; const int top = 1 + Options.level_map_title; int map_lines = 0; int start_x = -1; // no x scrolling const int block_step = Options.level_map_cursor_step; int start_y; // y does scroll int screen_y = -1; int curs_x = -1, curs_y = -1; int search_found = 0, anchor_x = -1, anchor_y = -1; bool map_alive = true; bool redraw_map = true; #ifndef USE_TILE clrscr(); #endif textcolor(DARKGREY); bool on_level = false; while (map_alive) { if (new_level) { on_level = (level_id::current() == original); move_x = 0, move_y = 0, scroll_y = 0; // Vector to track all features we can travel to, in order of distance. if (travel_mode) { travel_init_new_level(); travel_cache.update(); _reset_travel_colours(features, on_level); } feats.init(); min_x = GXM, max_x = 0, min_y = 0, max_y = 0; bool found_y = false; for (j = 0; j < GYM; j++) for (i = 0; i < GXM; i++) { if (env.map_knowledge[i][j].known()) { if (!found_y) { found_y = true; min_y = j; } max_y = j; if (i < min_x) min_x = i; if (i > max_x) max_x = i; } } map_lines = max_y - min_y + 1; start_x = min_x + (max_x - min_x + 1) / 2 - 40; // no x scrolling start_y = 0; // y does scroll coord_def reg; if (on_level) { reg = you.pos(); } else { reg.y = min_y + (max_y - min_y + 1) / 2; reg.x = min_x + (max_x - min_x + 1) / 2; } screen_y = reg.y; // If close to top of known map, put min_y on top // else if close to bottom of known map, put max_y on bottom. // // The num_lines comparisons are done to keep things neat, by // keeping things at the top of the screen. By shifting an // additional one in the num_lines > map_lines case, we can // keep the top line clear... which makes things look a whole // lot better for small maps. if (num_lines > map_lines) screen_y = min_y + half_screen - 1; else if (num_lines == map_lines || screen_y - half_screen < min_y) screen_y = min_y + half_screen; else if (screen_y + half_screen > max_y) screen_y = max_y - half_screen; curs_x = reg.x - start_x + 1; curs_y = reg.y - screen_y + half_screen + 1; search_found = 0, anchor_x = -1, anchor_y = -1; redraw_map = true; new_level = false; } #if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES) // If we've received a HUP signal then the user can't choose a // location, so indicate this by returning an invalid position. if (crawl_state.seen_hups) { spec_place = level_pos(); break; } #endif start_y = screen_y - half_screen; move_x = move_y = 0; if (redraw_map) { coord_def cen(start_x + curs_x - 1, start_y + curs_y - 1); #ifdef USE_TILE // Note: Tile versions just center on the current cursor // location. It silently ignores everything else going // on in this function. --Enne tiles.load_dungeon(cen); #else _draw_title(cen, feats); _draw_level_map(start_x, start_y, travel_mode, on_level); #endif // USE_TILE } #ifndef USE_TILE cursorxy(curs_x, curs_y + top - 1); #endif redraw_map = true; c_input_reset(true); int key = unmangle_direction_keys(getchm(KMC_LEVELMAP), KMC_LEVELMAP, false, false); command_type cmd = key_to_command(key, KMC_LEVELMAP); if (cmd < CMD_MIN_OVERMAP || cmd > CMD_MAX_OVERMAP) cmd = CMD_NO_CMD; if (key == CK_MOUSE_CLICK) { const c_mouse_event cme = get_mouse_event(); const coord_def curp(start_x + curs_x - 1, start_y + curs_y - 1); const coord_def grdp = cme.pos + coord_def(start_x - 1, start_y - top); if (cme.left_clicked() && in_bounds(grdp)) { spec_place = level_pos(level_id::current(), grdp); map_alive = false; } else if (cme.scroll_up()) scroll_y = -block_step; else if (cme.scroll_down()) scroll_y = block_step; else if (cme.right_clicked()) { const coord_def delta = grdp - curp; move_y = delta.y; move_x = delta.x; } } c_input_reset(false); switch (cmd) { case CMD_MAP_HELP: show_levelmap_help(); break; case CMD_MAP_CLEAR_MAP: clear_map(); break; case CMD_MAP_FORGET: forget_map(100, true); break; case CMD_MAP_ADD_WAYPOINT: travel_cache.add_waypoint(start_x + curs_x - 1, start_y + curs_y - 1); // We need to do this all over again so that the user can jump // to the waypoint he just created. _reset_travel_colours(features, on_level); feats.init(); break; // Cycle the radius of an exclude. case CMD_MAP_EXCLUDE_AREA: { const coord_def p(start_x + curs_x - 1, start_y + curs_y - 1); cycle_exclude_radius(p); _reset_travel_colours(features, on_level); feats.init(); break; } case CMD_MAP_CLEAR_EXCLUDES: clear_excludes(); _reset_travel_colours(features, on_level); feats.init(); break; #ifdef WIZARD case CMD_MAP_EXCLUDE_RADIUS: { const coord_def p(start_x + curs_x - 1, start_y + curs_y - 1); set_exclude(p, getchm() - '0'); _reset_travel_colours(features, on_level); feats.init(); break; } #endif case CMD_MAP_MOVE_DOWN_LEFT: move_x = -1; move_y = 1; break; case CMD_MAP_MOVE_DOWN: move_y = 1; move_x = 0; break; case CMD_MAP_MOVE_UP_RIGHT: move_x = 1; move_y = -1; break; case CMD_MAP_MOVE_UP: move_y = -1; move_x = 0; break; case CMD_MAP_MOVE_UP_LEFT: move_y = -1; move_x = -1; break; case CMD_MAP_MOVE_LEFT: move_x = -1; move_y = 0; break; case CMD_MAP_MOVE_DOWN_RIGHT: move_y = 1; move_x = 1; break; case CMD_MAP_MOVE_RIGHT: move_x = 1; move_y = 0; break; case CMD_MAP_PREV_LEVEL: case CMD_MAP_NEXT_LEVEL: { level_id next; next = (cmd == CMD_MAP_PREV_LEVEL) ? find_up_level(level_id::current()) : find_down_level(level_id::current()); if (next.is_valid() && next != level_id::current() && is_existing_level(next)) { le.go_to(next); new_level = true; } break; } case CMD_MAP_GOTO_LEVEL: { std::string name; const level_pos pos = prompt_translevel_target(TPF_DEFAULT_OPTIONS, name).p; if (pos.id.depth < 1 || pos.id.depth > branches[pos.id.branch].depth || !is_existing_level(pos.id)) { canned_msg(MSG_OK); redraw_map = true; break; } le.go_to(pos.id); new_level = true; break; } case CMD_MAP_JUMP_DOWN_LEFT: move_x = -block_step; move_y = block_step; break; case CMD_MAP_JUMP_DOWN: move_y = block_step; move_x = 0; break; case CMD_MAP_JUMP_UP_RIGHT: move_x = block_step; move_y = -block_step; break; case CMD_MAP_JUMP_UP: move_y = -block_step; move_x = 0; break; case CMD_MAP_JUMP_UP_LEFT: move_y = -block_step; move_x = -block_step; break; case CMD_MAP_JUMP_LEFT: move_x = -block_step; move_y = 0; break; case CMD_MAP_JUMP_DOWN_RIGHT: move_y = block_step; move_x = block_step; break; case CMD_MAP_JUMP_RIGHT: move_x = block_step; move_y = 0; break; case CMD_MAP_SCROLL_DOWN: move_y = 20; move_x = 0; scroll_y = 20; break; case CMD_MAP_SCROLL_UP: move_y = -20; move_x = 0; scroll_y = -20; break; case CMD_MAP_FIND_YOU: if (on_level) { move_x = you.pos().x - (start_x + curs_x - 1); move_y = you.pos().y - (start_y + curs_y - 1); } break; case CMD_MAP_FIND_UPSTAIR: case CMD_MAP_FIND_DOWNSTAIR: case CMD_MAP_FIND_PORTAL: case CMD_MAP_FIND_TRAP: case CMD_MAP_FIND_ALTAR: case CMD_MAP_FIND_EXCLUDED: case CMD_MAP_FIND_F: case CMD_MAP_FIND_WAYPOINT: case CMD_MAP_FIND_STASH: case CMD_MAP_FIND_STASH_REVERSE: { bool forward = (cmd != CMD_MAP_FIND_STASH_REVERSE); int getty; switch (cmd) { case CMD_MAP_FIND_UPSTAIR: getty = '<'; break; case CMD_MAP_FIND_DOWNSTAIR: getty = '>'; break; case CMD_MAP_FIND_PORTAL: getty = '\t'; break; case CMD_MAP_FIND_TRAP: getty = '^'; break; case CMD_MAP_FIND_ALTAR: getty = '_'; break; case CMD_MAP_FIND_EXCLUDED: getty = 'E'; break; case CMD_MAP_FIND_F: getty = 'F'; break; case CMD_MAP_FIND_WAYPOINT: getty = 'W'; break; default: case CMD_MAP_FIND_STASH: case CMD_MAP_FIND_STASH_REVERSE: getty = 'I'; break; } if (anchor_x == -1) { anchor_x = start_x + curs_x - 1; anchor_y = start_y + curs_y - 1; } if (travel_mode) { search_found = _find_feature(features, getty, curs_x, curs_y, start_x, start_y, search_found, &move_x, &move_y, forward); } else { search_found = _find_feature(getty, curs_x, curs_y, start_x, start_y, anchor_x, anchor_y, search_found, &move_x, &move_y); } break; } case CMD_MAP_GOTO_TARGET: { int x = start_x + curs_x - 1, y = start_y + curs_y - 1; if (travel_mode && on_level && x == you.pos().x && y == you.pos().y) { if (you.travel_x > 0 && you.travel_y > 0) { move_x = you.travel_x - x; move_y = you.travel_y - y; } break; } else { spec_place = level_pos(level_id::current(), coord_def(x, y)); map_alive = false; break; } } #ifdef WIZARD case CMD_MAP_WIZARD_TELEPORT: { if (!you.wizard) break; if (!on_level) break; const coord_def pos(start_x + curs_x - 1, start_y + curs_y - 1); if (!in_bounds(pos)) break; you.moveto(pos); map_alive = false; break; } #endif case CMD_MAP_EXIT_MAP: if (allow_esc) { spec_place = level_pos(); map_alive = false; break; } default: if (travel_mode) { map_alive = false; break; } redraw_map = false; continue; } if (!map_alive) break; #ifdef USE_TILE { int new_x = start_x + curs_x + move_x - 1; int new_y = start_y + curs_y + move_y - 1; curs_x += (new_x < 1 || new_x > GXM) ? 0 : move_x; curs_y += (new_y < 1 || new_y > GYM) ? 0 : move_y; } #else if (curs_x + move_x < 1 || curs_x + move_x > crawl_view.termsz.x) move_x = 0; curs_x += move_x; if (num_lines < map_lines) { // Scrolling only happens when we don't have a large enough // display to show the known map. if (scroll_y != 0) { const int old_screen_y = screen_y; screen_y += scroll_y; if (scroll_y < 0) screen_y = std::max(screen_y, min_y + half_screen); else screen_y = std::min(screen_y, max_y - half_screen); curs_y -= (screen_y - old_screen_y); scroll_y = 0; } if (curs_y + move_y < 1) { screen_y += move_y; if (screen_y < min_y + half_screen) { move_y = screen_y - (min_y + half_screen); screen_y = min_y + half_screen; } else move_y = 0; } if (curs_y + move_y > num_lines) { screen_y += move_y; if (screen_y > max_y - half_screen) { move_y = screen_y - (max_y - half_screen); screen_y = max_y - half_screen; } else move_y = 0; } } if (curs_y + move_y < 1 || curs_y + move_y > num_lines) move_y = 0; curs_y += move_y; #endif } le.go_to(original); travel_init_new_level(); travel_cache.update(); } bool emphasise(const coord_def& where) { dungeon_feature_type feat = env.map_knowledge(where).feat(); return (is_unknown_stair(where) && (you.your_level || feat_stair_direction(feat) == CMD_GO_DOWNSTAIRS) && you.where_are_you != BRANCH_VESTIBULE_OF_HELL); } static unsigned _get_travel_colour(const coord_def& p) { #ifdef WIZARD if (you.wizard && testbits(env.pgrid(p), FPROP_HIGHLIGHT)) return (LIGHTGREEN); #endif if (is_waypoint(p)) return LIGHTGREEN; short dist = travel_point_distance[p.x][p.y]; return dist > 0? Options.tc_reachable : dist == PD_EXCLUDED? Options.tc_excluded : dist == PD_EXCLUDED_RADIUS? Options.tc_exclude_circle : dist < 0? Options.tc_dangerous : Options.tc_disconnected; } static bool _travel_colour_override(const coord_def& p) { if (is_waypoint(p) || travel_point_distance[p.x][p.y] == PD_EXCLUDED) return (true); #ifdef WIZARD if (you.wizard && testbits(env.pgrid(p), FPROP_HIGHLIGHT)) return (true); #endif show_type obj = get_map_knowledge_obj(p); return (obj.cls == SH_FEATURE && (obj.feat == DNGN_FLOOR || obj.feat == DNGN_LAVA || obj.feat == DNGN_DEEP_WATER || obj.feat == DNGN_SHALLOW_WATER)); } unsigned get_map_col(const coord_def& p, bool travel) { if (travel && _travel_colour_override(p)) return _get_travel_colour(p); const show_type& obj = env.map_knowledge(p).object; if (obj.cls == SH_FEATURE && emphasise(p)) { const feature_def& fdef = get_feature_def(obj); if (fdef.seen_em_colour) return fdef.seen_em_colour; } return get_map_knowledge_col(p); }