diff options
author | Robert Vollmert <rvollmert@gmx.net> | 2009-11-04 22:45:50 +0100 |
---|---|---|
committer | Robert Vollmert <rvollmert@gmx.net> | 2009-11-04 22:45:50 +0100 |
commit | 64c38d95bfd6b24e34bc86f7d4ce7ba345c5a963 (patch) | |
tree | 9aaba99b80cd44b032cdd30e40f8b6625508ee89 /crawl-ref/source/viewmap.cc | |
parent | ce5c888daccae3cf40be8e28176f415e54b26650 (diff) | |
download | crawl-ref-64c38d95bfd6b24e34bc86f7d4ce7ba345c5a963.tar.gz crawl-ref-64c38d95bfd6b24e34bc86f7d4ce7ba345c5a963.zip |
Split up view.cc.
Diffstat (limited to 'crawl-ref/source/viewmap.cc')
-rw-r--r-- | crawl-ref/source/viewmap.cc | 1188 |
1 files changed, 1188 insertions, 0 deletions
diff --git a/crawl-ref/source/viewmap.cc b/crawl-ref/source/viewmap.cc new file mode 100644 index 0000000000..b7d2f066e1 --- /dev/null +++ b/crawl-ref/source/viewmap.cc @@ -0,0 +1,1188 @@ +/* + * 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 "env.h" +#include "envmap.h" +#include "exclude.h" +#include "feature.h" +#include "files.h" +#include "format.h" +#include "macro.h" +#include "mon-util.h" +#include "options.h" +#include "place.h" +#include "player.h" +#include "stash.h" +#include "stuff.h" +#include "terrain.h" +#include "travel.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(where).object && !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_FEAWN: + 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_envmap_char(where.x, where.y) == (unsigned) feature; + } +} + +static bool _is_feature_fudged(int feature, const coord_def& where) +{ + if (!env.map(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<coord_def>& features, + unsigned char feature, std::vector<coord_def> *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<coord_def>& 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 std::string _level_description_string() +{ + if (you.level_type == LEVEL_PANDEMONIUM) + return "- Pandemonium"; + + if (you.level_type == LEVEL_ABYSS) + return "- The Abyss"; + + if (you.level_type == LEVEL_LABYRINTH) + return "- a Labyrinth"; + + if (you.level_type == LEVEL_PORTAL_VAULT) + { + if (!you.level_type_name.empty()) + return "- " + article_a(upcase_first(you.level_type_name)); + return "- a Portal Chamber"; + } + + // level_type == LEVEL_DUNGEON + char buf[200]; + const int youbranch = you.where_are_you; + if ( branches[youbranch].depth == 1 ) + snprintf(buf, sizeof buf, "- %s", branches[youbranch].longname); + else + { + const int curr_subdungeon_level = player_branch_depth(); + snprintf(buf, sizeof buf, "%d of %s", curr_subdungeon_level, + branches[youbranch].longname); + } + return buf; +} + +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; + if (Options.level_map_title) + { + const formatted_string help = + formatted_string::parse_string("(Press <w>?</w> for help)"); + const int helplen = std::string(help).length(); + + cgotoxy(1, 1); + textcolor(WHITE); + cprintf("%-*s", + get_number_of_cols() - helplen, + ("Level " + _level_description_string()).c_str()); + + textcolor(LIGHTGREY); + cgotoxy(get_number_of_cols() - helplen + 1, 1); + help.display(); + } + + 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++) + { + screen_buffer_t colour = DARKGREY; + + coord_def c(start_x + screen_x, start_y + screen_y); + + if (!map_bounds(c)) + { + buffer2[bufcount2 + 1] = DARKGREY; + buffer2[bufcount2] = 0; + } + else + { + colour = colour_code_map(c, + Options.item_colour, + travel_mode, + on_level); + + buffer2[bufcount2 + 1] = colour; + buffer2[bufcount2] = env.map(c).glyph(); + + 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, 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<coord_def> &features, + bool on_level) +{ + // We now need to redo travel colours. + features.clear(); + + if (on_level) + { + find_travel_pos((on_level ? you.pos() : coord_def()), + 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 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<coord_def> features; + + 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); + } + + 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[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) + { +#ifdef USE_TILE + // Note: Tile versions just center on the current cursor + // location. It silently ignores everything else going + // on in this function. --Enne + coord_def cen(start_x + curs_x - 1, start_y + curs_y - 1); + tiles.load_dungeon(cen); +#else + _draw_level_map(start_x, start_y, travel_mode, on_level); + +#ifdef WIZARD + if (you.wizard) + { + cgotoxy(get_number_of_cols() / 2, 1); + textcolor(WHITE); + cprintf("(%d, %d)", start_x + curs_x - 1, + start_y + curs_y - 1); + + textcolor(LIGHTGREY); + cgotoxy(curs_x, curs_y + top - 1); + } +#endif // WIZARD + +#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); + 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); + break; + } + + case CMD_MAP_CLEAR_EXCLUDES: + clear_excludes(); + _reset_travel_colours(features, on_level); + break; + + 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(); +} + +static char _get_travel_colour( const coord_def& p ) +{ + 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; +} + +bool emphasise(const coord_def& where, dungeon_feature_type feat) +{ + return (is_unknown_stair(where, feat) + && (you.your_level || feat_stair_direction(feat) == CMD_GO_DOWNSTAIRS) + && you.where_are_you != BRANCH_VESTIBULE_OF_HELL); +} + +screen_buffer_t colour_code_map(const coord_def& p, bool item_colour, + bool travel_colour, bool on_level) +{ + if (!is_terrain_known(p)) + return (BLACK); + +#ifdef WIZARD + if (travel_colour && you.wizard + && testbits(env.map(p).property, FPROP_HIGHLIGHT)) + { + return (LIGHTGREEN); + } +#endif + + dungeon_feature_type feat_value = grd(p); + if (!see_cell(p)) + { + const show_type remembered = get_envmap_obj(p); + if (remembered.cls == SH_FEATURE) + feat_value = remembered.feat; + } + + unsigned tc = travel_colour ? _get_travel_colour(p) : DARKGREY; + + if (is_envmap_detected_item(p)) + return real_colour(Options.detected_item_colour); + + if (is_envmap_detected_mons(p)) + { + tc = Options.detected_monster_colour; + return real_colour(tc); + } + + // If this is an important travel square, don't allow the colour + // to be overridden. + if (is_waypoint(p) || travel_point_distance[p.x][p.y] == PD_EXCLUDED) + return real_colour(tc); + + if (item_colour && is_envmap_item(p)) + return get_envmap_col(p); + + int feature_colour = DARKGREY; + const bool terrain_seen = is_terrain_seen(p); + const feature_def &fdef = get_feature_def(feat_value); + feature_colour = terrain_seen ? fdef.seen_colour : fdef.map_colour; + + if (terrain_seen && fdef.seen_em_colour && emphasise(p, feat_value)) + feature_colour = fdef.seen_em_colour; + + if (feature_colour != DARKGREY) + tc = feature_colour; + else if (you.duration[DUR_MESMERISED] && on_level) + { + // If mesmerised, colour the few grids that can be reached anyway + // lightgrey. + const monsters *blocker = monster_at(p); + const bool seen_blocker = blocker && you.can_see(blocker); + if (grd(p) >= DNGN_MINMOVE && !seen_blocker) + { + bool blocked_movement = false; + for (unsigned int i = 0; i < you.mesmerised_by.size(); i++) + { + const monsters& mon = menv[you.mesmerised_by[i]]; + const int olddist = grid_distance(you.pos(), mon.pos()); + const int newdist = grid_distance(p, mon.pos()); + + if (olddist < newdist || !see_cell(env.show_los, p, mon.pos())) + { + blocked_movement = true; + break; + } + } + if (!blocked_movement) + tc = LIGHTGREY; + } + } + + if (Options.feature_item_brand + && is_critical_feature(feat_value) + && igrd(p) != NON_ITEM) + { + tc |= COLFLAG_FEATURE_ITEM; + } + else if (Options.trap_item_brand + && feat_is_trap(feat_value) && igrd(p) != NON_ITEM) + { + // FIXME: this uses the real igrd, which the player shouldn't + // be aware of. + tc |= COLFLAG_TRAP_ITEM; + } + + return real_colour(tc); +} + + |