diff --git a/src/display.cpp b/src/display.cpp index 39c91072550ba..0ce1fe4d6e83d 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -1104,74 +1104,6 @@ std::pair display::overmap_note_symbol_color( const std:: return std::make_pair( ter_sym, ter_color ); } -// Return an overmap tile symbol and color for an omt relatively near the avatar's position. -// The edge_tile flag says this omt is at the edge of the map and may point to an off-map mission. -// The found_mi (reference) is set to true to tell the calling function if a mission marker was found. -std::pair display::overmap_tile_symbol_color( const avatar &u, - const tripoint_abs_omt &omt, const bool edge_tile, bool &found_mi ) -{ - std::string ter_sym; - nc_color ter_color = c_light_gray; - - // Terrain color and symbol to use for this point - const bool seen = overmap_buffer.seen( omt ); - if( overmap_buffer.has_note( omt ) ) { - const std::string ¬e_text = overmap_buffer.note( omt ); - std::pair sym_color = display::overmap_note_symbol_color( note_text ); - ter_sym = sym_color.first; - ter_color = sym_color.second; - } else if( !seen ) { - // Always gray # for unseen - ter_sym = "#"; - ter_color = c_dark_gray; - } else if( overmap_buffer.has_vehicle( omt ) ) { - ter_color = c_cyan; - ter_sym = overmap_buffer.get_vehicle_ter_sym( omt ); - } else { - // Otherwise, get symbol and color appropriate for the terrain - const oter_id &cur_ter = overmap_buffer.ter( omt ); - ter_sym = cur_ter->get_symbol(); - if( overmap_buffer.is_explored( omt ) ) { - ter_color = c_dark_gray; - } else { - ter_color = cur_ter->get_color(); - } - } - const tripoint_abs_omt target = u.get_active_mission_target(); - const tripoint_abs_omt u_loc = u.global_omt_location(); - - // Check if there is a valid mission target, and avatar is not there already - if( target != overmap::invalid_tripoint && target.xy() != u_loc.xy() ) { - // highlight it with a red background (if on-map) - // or point towards it with a red asterisk (if off-map) - if( target.xy() == omt.xy() ) { - ter_color = red_background( ter_color ); - found_mi = true; - } else if( edge_tile ) { - std::vector plist = line_to( u_loc, target ); - if( std::find( plist.begin(), plist.end(), omt ) != plist.end() ) { - ter_color = c_red; - ter_sym = "*"; - found_mi = true; - } - } - } - - // Show hordes on minimap, leaving a one-tile space around the player - if( std::abs( u_loc.x() - omt.x() ) > 1 || std::abs( u_loc.y() - omt.y() ) > 1 ) { - const int horde_size = overmap_buffer.get_horde_size( omt ); - const int sight_points = u.overmap_sight_range( g->light_level( u.posz() ) ); - if( horde_size >= HORDE_VISIBILITY_SIZE && overmap_buffer.seen( omt ) && - u.overmap_los( omt, sight_points ) ) { - // Draw green Z or z - ter_sym = horde_size > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z'; - ter_color = c_green; - } - } - - return std::make_pair( ter_sym, ter_color ); -} - std::string display::colorized_overmap_text( const avatar &u, const int width, const int height ) { std::string overmap_text; @@ -1185,33 +1117,37 @@ std::string display::colorized_overmap_text( const avatar &u, const int width, c return disp_om_cache.get_val(); } - // Remember when mission indicator is found, so we don't draw it more than once - bool found_mi = false; // Figure out extents of the map area, so we know where the edges are const int left = -( width / 2 ); const int right = width + left - 1; const int top = -( height / 2 ); const int bottom = height + top - 1; + + oter_display_options opts( center_xyz, u.overmap_sight_range( g->light_level( u.posz() ) ) ); + opts.showhordes = true; + if( mission_xyz != overmap::invalid_tripoint ) { + opts.mission_target = mission_xyz; + } + opts.mission_inbounds = ( mission_xyz.x() >= center_xyz.x() + left && + mission_xyz.x() <= center_xyz.x() + right && + mission_xyz.y() >= center_xyz.y() + top && + mission_xyz.y() <= center_xyz.y() + bottom ); + opts.hilite_pc = true; + opts.hilite_mission = true; + // Scan each row of overmap tiles for( int row = top; row <= bottom; row++ ) { // Scan across the width of the row for( int col = left; col <= right; col++ ) { // Is this point along the border of the overmap text area we have to work with? // If so, overmap_tile_symbol_color may draw a mission indicator at this point. - const bool edge = !found_mi && !( mission_xyz.x() >= center_xyz.x() + left && - mission_xyz.x() <= center_xyz.x() + right && - mission_xyz.y() >= center_xyz.y() + top && - mission_xyz.y() <= center_xyz.y() + bottom ) && - ( row == top || row == bottom || col == left || col == right ); // Get colorized symbol for this point const tripoint_abs_omt omt( center_xyz.xy() + point( col, row ), here.get_abs_sub().z() ); - std::pair sym_color = display::overmap_tile_symbol_color( u, omt, edge, - found_mi ); - // Highlight player character location in the center - if( row == 0 && col == 0 ) { - sym_color.second = hilite( sym_color.second ); - } + oter_display_args args( overmap_buffer.seen( omt ) ); + args.edge_tile = ( row == top || row == bottom || col == left || col == right ); + + std::pair sym_color = oter_symbol_and_color( omt, args, opts ); // Append the colorized symbol for this point to the map overmap_text += colorize( sym_color.first, sym_color.second ); diff --git a/src/game.cpp b/src/game.cpp index 4b823fd67194f..79bdeaee3d534 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4223,212 +4223,6 @@ void game::draw_veh_dir_indicator( bool next ) } } -void game::draw_minimap() -{ - - // Draw the box - werase( w_minimap ); - draw_border( w_minimap ); - - const tripoint_abs_omt curs = u.global_omt_location(); - const point_abs_omt curs2( curs.xy() ); - const tripoint_abs_omt targ = u.get_active_mission_target(); - bool drew_mission = targ == overmap::invalid_tripoint; - - const int levz = m.get_abs_sub().z(); - for( int i = -2; i <= 2; i++ ) { - for( int j = -2; j <= 2; j++ ) { - const point_abs_omt om( curs2 + point( i, j ) ); - nc_color ter_color; - tripoint_abs_omt omp( om, levz ); - std::string ter_sym; - const bool seen = overmap_buffer.seen( omp ); - if( overmap_buffer.has_note( omp ) ) { - - const std::string ¬e_text = overmap_buffer.note( omp ); - - ter_color = c_yellow; - ter_sym = "N"; - - int symbolIndex = note_text.find( ':' ); - int colorIndex = note_text.find( ';' ); - - bool symbolFirst = symbolIndex < colorIndex; - - if( colorIndex > -1 && symbolIndex > -1 ) { - if( symbolFirst ) { - if( colorIndex > 4 ) { - colorIndex = -1; - } - if( symbolIndex > 1 ) { - symbolIndex = -1; - colorIndex = -1; - } - } else { - if( symbolIndex > 4 ) { - symbolIndex = -1; - } - if( colorIndex > 2 ) { - colorIndex = -1; - } - } - } else if( colorIndex > 2 ) { - colorIndex = -1; - } else if( symbolIndex > 1 ) { - symbolIndex = -1; - } - - if( symbolIndex > -1 ) { - int symbolStart = 0; - if( colorIndex > -1 && !symbolFirst ) { - symbolStart = colorIndex + 1; - } - ter_sym = note_text.substr( symbolStart, symbolIndex - symbolStart ).c_str()[0]; - } - - if( colorIndex > -1 ) { - - int colorStart = 0; - - if( symbolIndex > -1 && symbolFirst ) { - colorStart = symbolIndex + 1; - } - - std::string sym = note_text.substr( colorStart, colorIndex - colorStart ); - - if( sym.length() == 2 ) { - if( sym == "br" ) { - ter_color = c_brown; - } else if( sym == "lg" ) { - ter_color = c_light_gray; - } else if( sym == "dg" ) { - ter_color = c_dark_gray; - } - } else { - char colorID = sym.c_str()[0]; - if( colorID == 'r' ) { - ter_color = c_light_red; - } else if( colorID == 'R' ) { - ter_color = c_red; - } else if( colorID == 'g' ) { - ter_color = c_light_green; - } else if( colorID == 'G' ) { - ter_color = c_green; - } else if( colorID == 'b' ) { - ter_color = c_light_blue; - } else if( colorID == 'B' ) { - ter_color = c_blue; - } else if( colorID == 'W' ) { - ter_color = c_white; - } else if( colorID == 'C' ) { - ter_color = c_cyan; - } else if( colorID == 'c' ) { - ter_color = c_light_cyan; - } else if( colorID == 'P' ) { - ter_color = c_pink; - } else if( colorID == 'm' ) { - ter_color = c_magenta; - } - } - } - } else if( !seen ) { - ter_sym = " "; - ter_color = c_black; - } else if( overmap_buffer.has_vehicle( omp ) ) { - ter_color = c_cyan; - ter_sym = overmap_buffer.get_vehicle_ter_sym( omp ); - } else { - const oter_id &cur_ter = overmap_buffer.ter( omp ); - ter_sym = cur_ter->get_symbol(); - if( overmap_buffer.is_explored( omp ) ) { - ter_color = c_dark_gray; - } else { - ter_color = cur_ter->get_color(); - } - } - if( !drew_mission && targ.xy() == omp.xy() ) { - // If there is a mission target, and it's not on the same - // overmap terrain as the player character, mark it. - // TODO: Inform player if the mission is above or below - drew_mission = true; - if( i != 0 || j != 0 ) { - ter_color = red_background( ter_color ); - } - } - if( i == 0 && j == 0 ) { - mvwputch_hi( w_minimap, point( 3, 3 ), ter_color, ter_sym ); - } else { - mvwputch( w_minimap, point( 3 + i, 3 + j ), ter_color, ter_sym ); - } - } - } - - // Print arrow to mission if we have one! - if( !drew_mission ) { - double slope = curs2.x() != targ.x() ? - static_cast( targ.y() - curs2.y() ) / ( targ.x() - curs2.x() ) : 4; - - if( curs2.x() == targ.x() || std::fabs( slope ) > 3.5 ) { // Vertical slope - if( targ.y() > curs2.y() ) { - mvwputch( w_minimap, point( 3, 6 ), c_red, "*" ); - } else { - mvwputch( w_minimap, point( 3, 0 ), c_red, "*" ); - } - } else { - point arrow( point_north_west ); - if( std::fabs( slope ) >= 1. ) { // y diff is bigger! - arrow.y = targ.y() > curs2.y() ? 6 : 0; - arrow.x = - static_cast( 3 + 3 * ( targ.y() > curs2.y() ? slope : ( 0 - slope ) ) ); - if( arrow.x < 0 ) { - arrow.x = 0; - } - if( arrow.x > 6 ) { - arrow.x = 6; - } - } else { - arrow.x = targ.x() > curs2.x() ? 6 : 0; - arrow.y = static_cast( 3 + 3 * ( targ.x() > curs2.x() ? slope : -slope ) ); - if( arrow.y < 0 ) { - arrow.y = 0; - } - if( arrow.y > 6 ) { - arrow.y = 6; - } - } - char glyph = '*'; - if( targ.z() > u.posz() ) { - glyph = '^'; - } else if( targ.z() < u.posz() ) { - glyph = 'v'; - } - - mvwputch( w_minimap, arrow, c_red, glyph ); - } - } - - Character &player_character = get_player_character(); - const int sight_points = player_character.overmap_sight_range( g->light_level( - player_character.posz() ) ); - for( int i = -3; i <= 3; i++ ) { - for( int j = -3; j <= 3; j++ ) { - if( i > -3 && i < 3 && j > -3 && j < 3 ) { - continue; // only do hordes on the border, skip inner map - } - const tripoint_abs_omt omp( curs2 + point( i, j ), levz ); - if( overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE ) { - if( overmap_buffer.seen( omp ) - && player_character.overmap_los( omp, sight_points ) ) { - mvwputch( w_minimap, point( i + 3, j + 3 ), c_green, - overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); - } - } - } - } - - wnoutrefresh( w_minimap ); -} - float game::natural_light_level( const int zlev ) const { // ignore while underground or above limits diff --git a/src/game.h b/src/game.h index 9a11e3c118a6c..2a01fc37574f6 100644 --- a/src/game.h +++ b/src/game.h @@ -1031,7 +1031,6 @@ class game bool is_game_over(); // Returns true if the player quit or died void bury_screen() const;// Bury a dead character (record their last words) void death_screen(); // Display our stats, "GAME OVER BOO HOO" - void draw_minimap(); // Draw the 5x5 minimap public: /** * If there is a robot (that can be disabled), query the player diff --git a/src/overmap.h b/src/overmap.h index 3ab8fc824c087..f634b372b93ce 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -568,6 +568,89 @@ class overmap oter_id get_or_migrate_oter( const std::string &oterid ); }; +// A small LRU cache: most oter_id's occur in clumps like forests of swamps. +// This cache helps avoid much more costly lookups in the full hashmap. +struct oter_display_lru { + static constexpr size_t cache_size = 8; // used below to calculate the next index + std::array, cache_size> cache; + size_t cache_next = 0; + + std::pair get_symbol_and_color( const oter_id &cur_ter ); +}; + +// "arguments" to oter_symbol_and_color that do not change between calls in a batch +struct oter_display_options { + struct npc_coloring { + nc_color color; + size_t count = 0; + }; + + oter_display_options( tripoint_abs_omt orig, int sight_range ) : + center( orig ), sight_points( sight_range ) {} + + // Needs a better name + // Whether or not to draw all sorts of overlays that can blink on the overmap + bool blink = true; + // Show debug scent symbols + bool debug_scent = false; + // Show mission location by drawing a red background instead of overwriting the tile + bool hilite_mission = false; + // Draw the PC location with `hilite()` (blue bg-ish) on terrain at loc instead of '@' + bool hilite_pc = false; + // If false, and there is a mission, draw an an indicator towards the mission target on an edge + bool mission_inbounds = true; + // Darken explored tiles + bool show_explored = true; + bool showhordes = true; + // Hilight oters revealed by map use + bool show_map_revealed = false; + // Draw anything for the PC (assumed to be at center) + bool show_pc = true; + // Draw the weather tiles instead of terrain + bool show_weather = false; + + // Where the PC is/the center of the map + tripoint_abs_omt center; + // How far the PC can see + int sight_points; + + std::optional mission_target = std::nullopt; + // Locations of NPCs to draw + std::unordered_map npc_color; + // NPC/player paths to draw + std::unordered_set npc_path_route; + std::unordered_map player_path_route; + + // Zone to draw and location + std::string sZoneName; + tripoint_abs_omt tripointZone = tripoint_abs_omt( -1, -1, -1 ); + + // Internal bookkeeping value - only draw edge mission indicator once + mutable bool drawn_mission = false; +}; + +// arguments for oter_symbol_and_color pertaining to a single point +struct oter_display_args { + + explicit oter_display_args( bool seen ) : see( seen ) {} + + // Can the/has the PC seen this tile + bool see; + // If this tile is on the edge of the drawn tiles, we may draw a mission indicator on it + bool edge_tile = false; + // Check if location is within player line-of-sight + // These ints are treated as unassigned booleans. Use get_and_assign_los() to reference + // This allows for easy re-use of these variables without the unnecessary lookups if they aren't used + int los = -1; + int los_sky = -1; +}; + +// Symbol and color to display a overmap tile as - depending on notes, overlays, etc... +// args are updated per call, opts are generated before a batch of calls. +// A pointer to lru may be provided for possible speed improvements. +std::pair oter_symbol_and_color( const tripoint_abs_omt &omp, + oter_display_args &args, const oter_display_options &opts, oter_display_lru *lru = nullptr ); + bool is_river( const oter_id &ter ); bool is_water_body( const oter_id &ter ); bool is_lake_or_river( const oter_id &ter ); diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index 95bab8ccae9b8..ba499f6d35370 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -546,8 +546,6 @@ static void draw_ascii( const int om_map_height = OVERMAP_WINDOW_HEIGHT; const int om_half_width = om_map_width / 2; const int om_half_height = om_map_height / 2; - const bool viewing_weather = - ( uistate.overmap_debug_weather || uistate.overmap_visible_weather ) && center.z() == 10; avatar &player_character = get_avatar(); // Target of current mission @@ -560,16 +558,24 @@ static void draw_ascii( const int sight_points = !has_debug_vision ? player_character.overmap_sight_range( g->light_level( player_character.posz() ) ) : 100; - // Whether showing hordes is currently enabled - const bool showhordes = uistate.overmap_show_hordes; - const bool show_map_revealed = uistate.overmap_show_revealed_omts; - const oter_id forest = oter_forest.id(); - - std::string sZoneName; - tripoint_abs_omt tripointZone( -1, -1, -1 ); + oter_display_lru lru_cache; + oter_display_options oter_opts( orig, sight_points ); + oter_opts.blink = blink; + oter_opts.show_weather = ( uistate.overmap_debug_weather || uistate.overmap_visible_weather ) && + center.z() == 10; + oter_opts.show_pc = true; + oter_opts.debug_scent = data.debug_scent; + oter_opts.show_map_revealed = uistate.overmap_show_revealed_omts; + oter_opts.showhordes = uistate.overmap_show_hordes; + oter_opts.show_explored = show_explored; + + std::string &sZoneName = oter_opts.sZoneName; + tripoint_abs_omt &tripointZone = oter_opts.tripointZone; const zone_manager &zones = zone_manager::get_manager(); + oter_opts.mission_target = target; + if( data.iZoneIndex != -1 ) { const zone_data &zone = zones.get_zones()[data.iZoneIndex].get(); sZoneName = zone.get_name(); @@ -591,36 +597,6 @@ static void draw_ascii( } } - // A small LRU cache: most oter_id's occur in clumps like forests of swamps. - // This cache helps avoid much more costly lookups in the full hashmap. - constexpr size_t cache_size = 8; // used below to calculate the next index - std::array, cache_size> cache; - size_t cache_next = 0; - - const auto set_color_and_symbol = [&]( const oter_id & cur_ter, const tripoint_abs_omt & omp, - std::string & ter_sym, nc_color & ter_color ) { - // First see if we have the oter_t cached - oter_t const *info = nullptr; - for( const auto &c : cache ) { - if( c.first == cur_ter ) { - info = c.second; - break; - } - } - // Nope, look in the hash map next - if( !info ) { - info = &cur_ter.obj(); - cache[cache_next] = std::make_pair( cur_ter, info ); - cache_next = ( cache_next + 1 ) % cache_size; - } - // Ok, we found something - if( info ) { - const bool explored = show_explored && overmap_buffer.is_explored( omp ); - ter_color = explored ? c_dark_gray : info->get_color( uistate.overmap_show_land_use_codes ); - ter_sym = info->get_symbol( uistate.overmap_show_land_use_codes ); - } - }; - const tripoint_abs_omt corner = center - point( om_half_width, om_half_height ); // For use with place_special: cache the color and symbol of each submap @@ -649,14 +625,10 @@ static void draw_ascii( } // Cache NPCs since time to draw them is linear (per seen tile) with their count - struct npc_coloring { - nc_color color; - size_t count = 0; - }; - std::unordered_set npc_path_route; - std::unordered_set &revealed_highlights = get_avatar().map_revealed_omts; - std::unordered_map player_path_route; - std::unordered_map npc_color; + std::unordered_set &npc_path_route = oter_opts.npc_path_route; + std::unordered_map &player_path_route = oter_opts.player_path_route; + std::unordered_map &npc_color = + oter_opts.npc_color; auto npcs_near_player = overmap_buffer.get_npcs_near_player( sight_points ); if( blink ) { // get seen NPCs @@ -744,89 +716,8 @@ static void draw_ascii( cur_ter = overmap_buffer.ter( omp ); } - // Check if location is within player line-of-sight - // These ints are treated as unassigned booleans. Use get_and_assign_los() to reference - // This allows for easy re-use of these variables without the unnecessary lookups if they aren't used - int los = -1; - int los_sky = -1; - if( blink && omp == orig ) { - // Display player pos, should always be visible - ter_color = player_character.symbol_color(); - ter_sym = "@"; - } else if( viewing_weather && ( uistate.overmap_debug_weather || - get_and_assign_los( los_sky, player_character, omp, sight_points * 2 ) ) ) { - const weather_type_id type = get_weather_at_point( omp ); - ter_color = type->map_color; - ter_sym = type->get_symbol(); - } else if( data.debug_scent && get_scent_glyph( omp, ter_color, ter_sym ) ) { - // get_scent_glyph has changed ter_color and ter_sym if omp has a scent - } else if( blink && overmap_buffer.is_marked_dangerous( omp ) ) { - ter_color = c_red; - ter_sym = "X"; - } else if( blink && has_target && omp.xy() == target.xy() ) { - // Mission target, display always, player should know where it is anyway. - ter_color = c_red; - ter_sym = "*"; - if( target.z() > center.z() ) { - ter_sym = "^"; - } else if( target.z() < center.z() ) { - ter_sym = "v"; - } - } else if( blink && uistate.overmap_show_map_notes && overmap_buffer.has_note( omp ) ) { - // Display notes in all situations, even when not seen - std::tie( ter_sym, ter_color, std::ignore ) = - get_note_display_info( overmap_buffer.note( omp ) ); - } else if( !see ) { - // All cases above ignore the seen-status, - ter_color = oter_unexplored.obj().get_color(); - ter_sym = oter_unexplored.obj().get_symbol(); - // All cases below assume that see is true. - } else if( blink && npc_color.count( omp ) != 0 ) { - // Visible NPCs are cached already - ter_color = npc_color[omp].color; - ter_sym = "@"; - } else if( blink && player_path_route.find( omp.xy() ) != player_path_route.end() ) { - // player path - ter_color = c_blue; - const int player_path_z = player_path_route[omp.xy()]; - if( player_path_z == omp.z() ) { - ter_sym = "!"; - } else if( player_path_z > omp.z() ) { - ter_sym = "^"; - } else { - ter_sym = "v"; - } - } else if( blink && npc_path_route.find( omp ) != npc_path_route.end() ) { - // npc path - ter_color = c_red; - ter_sym = "!"; - } else if( blink && show_map_revealed && - revealed_highlights.find( omp ) != revealed_highlights.end() ) { - // Revealed map tiles - ter_color = c_magenta; - ter_sym = "&"; - } else if( blink && showhordes && - overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE && - ( get_and_assign_los( los, player_character, omp, sight_points ) || - uistate.overmap_debug_mongroup || player_character.has_trait( trait_DEBUG_CLAIRVOYANCE ) ) ) { - // Display Hordes only when within player line-of-sight - ter_color = c_green; - ter_sym = overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; - } else if( blink && overmap_buffer.has_vehicle( omp ) ) { - ter_color = c_cyan; - ter_sym = overmap_buffer.get_vehicle_ter_sym( omp ); - } else if( !sZoneName.empty() && tripointZone.xy() == omp.xy() ) { - ter_color = c_yellow; - ter_sym = "Z"; - } else if( !uistate.overmap_show_forest_trails && cur_ter && - ( cur_ter->get_type_id() == oter_type_forest_trail ) ) { - // If forest trails shouldn't be displayed, and this is a forest trail, then - // instead render it like a forest. - set_color_and_symbol( forest, omp, ter_sym, ter_color ); - } else { - // Nothing special, but is visible to the player. - set_color_and_symbol( cur_ter, omp, ter_sym, ter_color ); - } + oter_display_args oter_args( see ); + std::tie( ter_sym, ter_color ) = oter_symbol_and_color( omp, oter_args, oter_opts, &lru_cache ); // Are we debugging monster groups? if( blink && uistate.overmap_debug_mongroup ) { @@ -861,7 +752,7 @@ static void draw_ascii( } // Set the color only if we encountered an eligible group. if( ter_sym == "+" || ter_sym == "-" ) { - if( get_and_assign_los( los, player_character, omp, sight_points ) ) { + if( get_and_assign_los( oter_args.los, player_character, omp, sight_points ) ) { ter_color = c_light_blue; } else { ter_color = c_blue; @@ -2075,6 +1966,155 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig, } // namespace overmap_ui +std::pair oter_display_lru::get_symbol_and_color( const oter_id &cur_ter ) +{ + std::pair ret = {"?", c_red}; + // First see if we have the oter_t cached + oter_t const *info = nullptr; + for( const auto &c : cache ) { + if( c.first == cur_ter ) { + info = c.second; + break; + } + } + // Nope, look in the hash map next + if( !info ) { + info = &cur_ter.obj(); + cache[cache_next] = std::make_pair( cur_ter, info ); + cache_next = ( cache_next + 1 ) % cache_size; + } + // Ok, we found something + if( info ) { + ret.second = info->get_color( uistate.overmap_show_land_use_codes ); + ret.first = info->get_symbol( uistate.overmap_show_land_use_codes ); + } + return ret; +} + +std::pair oter_symbol_and_color( const tripoint_abs_omt &omp, + oter_display_args &args, const oter_display_options &opts, oter_display_lru *lru ) +{ + std::pair ret; + + oter_id cur_ter = oter_str_id::NULL_ID(); + avatar &player_character = get_avatar(); + std::vector plist; + + if( opts.blink && !opts.mission_inbounds && opts.mission_target ) { + plist = line_to( opts.center.xy(), opts.mission_target->xy() ); + } + + if( args.see ) { + cur_ter = overmap_buffer.ter( omp ); + } + + if( opts.blink && opts.show_pc && !opts.hilite_pc && omp == opts.center ) { + // Display player pos, should always be visible + ret.second = player_character.symbol_color(); + ret.first = "@"; + } else if( opts.show_weather && ( uistate.overmap_debug_weather || + overmap_ui::get_and_assign_los( args.los_sky, player_character, omp, opts.sight_points * 2 ) ) ) { + const weather_type_id type = overmap_ui::get_weather_at_point( omp ); + ret.second = type->map_color; + ret.first = type->get_symbol(); + } else if( opts.debug_scent && overmap_ui::get_scent_glyph( omp, ret.second, ret.first ) ) { + // get_scent_glyph has changed ret.second and ret.first if omp has a scent + } else if( opts.blink && overmap_buffer.is_marked_dangerous( omp ) ) { + ret.second = c_red; + ret.first = "X"; + } else if( opts.blink && opts.mission_inbounds && opts.mission_target && !opts.hilite_mission && + omp.xy() == opts.mission_target->xy() ) { + // Mission target, display always, player should know where it is anyway. + ret.second = c_red; + ret.first = "*"; + if( opts.mission_target->z() > opts.center.z() ) { + ret.first = "^"; + } else if( opts.mission_target->z() < opts.center.z() ) { + ret.first = "v"; + } + } else if( !opts.mission_inbounds && !opts.drawn_mission && args.edge_tile && + std::find( plist.begin(), plist.end(), omp.xy() ) != plist.end() ) { + ret.first = "*"; + ret.second = c_red; + opts.drawn_mission = true; + } else if( opts.blink && uistate.overmap_show_map_notes && overmap_buffer.has_note( omp ) ) { + // Display notes in all situations, even when not seen + std::tie( ret.first, ret.second, + std::ignore ) = overmap_ui::get_note_display_info( overmap_buffer.note( omp ) ); + } else if( !args.see ) { + // All cases above ignore the seen-status, + ret.second = oter_unexplored.obj().get_color(); + ret.first = oter_unexplored.obj().get_symbol(); + // All cases below assume that see is true. + } else if( opts.blink && opts.npc_color.count( omp ) != 0 ) { + // Visible NPCs are cached already + ret.second = opts.npc_color.at( omp ).color; + ret.first = "@"; + } else if( opts.blink && opts.player_path_route.find( omp.xy() ) != opts.player_path_route.end() ) { + // player path + ret.second = c_blue; + const int player_path_z = opts.player_path_route.at( omp.xy() ); + if( player_path_z == omp.z() ) { + ret.first = "!"; + } else if( player_path_z > omp.z() ) { + ret.first = "^"; + } else { + ret.first = "v"; + } + } else if( opts.blink && opts.npc_path_route.find( omp ) != opts.npc_path_route.end() ) { + // npc path + ret.second = c_red; + ret.first = "!"; + } else if( opts.blink && opts.show_map_revealed && + player_character.map_revealed_omts.find( omp ) != player_character.map_revealed_omts.end() ) { + // Revealed map tiles + ret.second = c_magenta; + ret.first = "&"; + } else if( opts.blink && opts.showhordes && + overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE && + ( overmap_ui::get_and_assign_los( args.los, player_character, omp, opts.sight_points ) || + uistate.overmap_debug_mongroup || player_character.has_trait( trait_DEBUG_CLAIRVOYANCE ) ) ) { + // Display Hordes only when within player line-of-sight + ret.second = c_green; + ret.first = overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; + } else if( opts.blink && overmap_buffer.has_vehicle( omp ) ) { + ret.second = c_cyan; + ret.first = overmap_buffer.get_vehicle_ter_sym( omp ); + } else if( !opts.sZoneName.empty() && opts.tripointZone.xy() == omp.xy() ) { + ret.second = c_yellow; + ret.first = "Z"; + } else if( !uistate.overmap_show_forest_trails && cur_ter && + ( cur_ter->get_type_id() == oter_type_forest_trail ) ) { + // If forest trails shouldn't be displayed, and this is a forest trail, then + // instead render it like a forest. + ret = lru ? lru->get_symbol_and_color( oter_forest.id() ) : std::pair { + oter_forest->get_symbol( uistate.overmap_show_land_use_codes ), + oter_forest->get_color( uistate.overmap_show_land_use_codes ) + }; + if( opts.show_explored && overmap_buffer.is_explored( omp ) ) { + ret.second = c_dark_gray; + } + } else { + // Nothing special, but is visible to the player. + ret = lru ? lru->get_symbol_and_color( cur_ter ) : std::pair { + cur_ter->get_symbol( uistate.overmap_show_land_use_codes ), + cur_ter->get_color( uistate.overmap_show_land_use_codes ) + }; + if( opts.show_explored && overmap_buffer.is_explored( omp ) ) { + ret.second = c_dark_gray; + } + } + + if( opts.hilite_mission && opts.mission_target && opts.mission_target->xy() == omp.xy() ) { + ret.second = red_background( ret.second ); + } + if( opts.hilite_pc && opts.show_pc && opts.center.xy() == omp.xy() ) { + ret.second = hilite( ret.second ); + } + + return ret; +} + void ui::omap::display() { overmap_ui::display( get_player_character().global_omt_location(), overmap_ui::draw_data_t() ); diff --git a/src/panels.cpp b/src/panels.cpp index ed303d835e886..6480a18e63a0d 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -167,13 +167,19 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const // Map is centered on curs - typically player's global_omt_location const point_abs_omt curs = global_omt.xy(); const tripoint_abs_omt targ = you.get_active_mission_target(); - bool drew_mission = targ == overmap::invalid_tripoint; const int start_y = start_input.y; const int start_x = start_input.x; const point mid( width / 2, height / 2 ); map &here = get_map(); const int sight_points = you.overmap_sight_range( g->light_level( you.posz() ) ); + oter_display_options opts( global_omt, sight_points ); + if( targ != overmap::invalid_tripoint ) { + opts.mission_target = targ; + } + opts.hilite_pc = true; + opts.hilite_mission = true; + // i scans across width, with 0 in the middle(ish) // -(w/2) ... w-(w/2)-1 // w:9 -4 ... 4 @@ -181,80 +187,31 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const // w:11 -5 ... 5 // w:12 -6 ... 5 // w:13 -6 ... 6 - for( int i = -( width / 2 ); i <= width - ( width / 2 ) - 1; i++ ) { + int left = -( width / 2 ); + int right = width - ( width / 2 ) + 1; + int top = -( height / 2 ); + int bottom = height - ( height / 2 ); + + opts.mission_inbounds = targ.x() >= left + curs.x() && targ.x() <= right + curs.x() && + targ.y() >= top + curs.y() && targ.y() <= bottom + curs.y(); + + for( int i = left; i <= right; i++ ) { // j scans across height, with 0 in the middle(ish) // (same algorithm) - for( int j = -( height / 2 ); j <= height - ( height / 2 ) - 1; j++ ) { + for( int j = top; j <= bottom; j++ ) { // omp is the current overmap point, at the current z-level const tripoint_abs_omt omp( curs + point( i, j ), here.get_abs_sub().z() ); // Terrain color and symbol to use for this point nc_color ter_color; std::string ter_sym; + const bool seen = overmap_buffer.seen( omp ); - if( overmap_buffer.has_note( omp ) ) { - const std::string ¬e_text = overmap_buffer.note( omp ); - std::pair sym_color = display::overmap_note_symbol_color( note_text ); - ter_sym = sym_color.first; - ter_color = sym_color.second; - } else if( !seen ) { - // Always gray # for unseen - ter_sym = "#"; - ter_color = c_dark_gray; - } else if( overmap_buffer.has_vehicle( omp ) ) { - ter_color = c_cyan; - ter_sym = overmap_buffer.get_vehicle_ter_sym( omp ); - } else { - // Otherwise, get symbol and color appropriate for the terrain - const oter_id &cur_ter = overmap_buffer.ter( omp ); - ter_sym = cur_ter->get_symbol(); - if( overmap_buffer.is_explored( omp ) ) { - ter_color = c_dark_gray; - } else { - ter_color = cur_ter->get_color(); - } - } - if( !drew_mission && targ.xy() == omp.xy() ) { - // If there is a mission target, and it's not on the same - // overmap terrain as the player character, mark it. - // TODO: Inform player if the mission is above or below - drew_mission = true; - if( i != 0 || j != 0 ) { - ter_color = red_background( ter_color ); - } - } + oter_display_args args( seen ); + args.edge_tile = i == left || i == right || j == top || j == bottom; + std::tie( ter_sym, ter_color ) = oter_symbol_and_color( omp, args, opts ); // TODO: Build colorized string instead of writing directly to window - if( i == 0 && j == 0 ) { - // Highlight player character position in center of minimap - mvwputch_hi( w_minimap, mid + point( start_x, start_y ), ter_color, ter_sym ); - } else { - mvwputch( w_minimap, mid + point( i + start_x, j + start_y ), ter_color, - ter_sym ); - } - - if( i < -1 || i > 1 || j < -1 || j > 1 ) { - // Show hordes on minimap, leaving a one-tile space around the player - int horde_size = overmap_buffer.get_horde_size( omp ); - if( horde_size >= HORDE_VISIBILITY_SIZE && - overmap_buffer.seen( omp ) && you.overmap_los( omp, sight_points ) ) { - mvwputch( w_minimap, mid + point( i + start_x, j + start_y ), c_green, - horde_size > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); - } - } - } - } - - // When the mission marker is not visible within the current overmap extents, - // draw an arrow at the edge of the map pointing in the general mission direction. - // TODO: Replace `drew_mission` with a function like `is_mission_on_map` - if( !drew_mission ) { - char glyph = '*'; - if( targ.z() > you.posz() ) { - glyph = '^'; - } else if( targ.z() < you.posz() ) { - glyph = 'v'; + mvwputch( w_minimap, mid + point( i + start_x, j + start_y ), ter_color, ter_sym ); } - const point arrow = display::mission_arrow_offset( you, width, height ); - mvwputch( w_minimap, arrow + point( start_x, start_y ), c_red, glyph ); } }