From 352b387495a0e7783dfafd0988cbf678ef43db76 Mon Sep 17 00:00:00 2001 From: ehughsbaird <44244083+ehughsbaird@users.noreply.github.com> Date: Mon, 10 Jun 2024 06:24:31 +0000 Subject: [PATCH 1/2] Remove unused game::draw_minimap --- src/game.cpp | 206 --------------------------------------------------- src/game.h | 1 - 2 files changed, 207 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index bc65aff2d41d2..d351ef39e061d 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 56ea6185b4a94..298a663c6e6a4 100644 --- a/src/game.h +++ b/src/game.h @@ -1027,7 +1027,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 From 7ef7a798d2bb6b5d54d7ac0c70768823ca0184f5 Mon Sep 17 00:00:00 2001 From: ehughsbaird <44244083+ehughsbaird@users.noreply.github.com> Date: Mon, 10 Jun 2024 18:11:41 +0000 Subject: [PATCH 2/2] Consolidate overmap terrain and symbol code Make the three different places that draw overmap displays use a single function to get the color and symbol to draw at a point, instead of them each doing it their own way. To support the slightly different ways each of them do it, as well as the extreme degree of options presented by the main overmap view, this function takes two different structs as arguments. The first struct is the immediate "arguments" of the function (things that change for each tile), while the second struct outlines the options this display wants and does not change between calls. A pointer to another struct can be provided for a minor LRU optimization. --- src/display.cpp | 98 +++------------ src/overmap.h | 83 +++++++++++++ src/overmap_ui.cpp | 302 +++++++++++++++++++++++++-------------------- src/panels.cpp | 87 ++++--------- 4 files changed, 293 insertions(+), 277 deletions(-) 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/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 e1a8df4e55d09..7cda798240e69 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -545,8 +545,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 @@ -559,16 +557,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(); @@ -590,36 +596,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 @@ -648,14 +624,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 @@ -738,89 +710,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 ) { @@ -855,7 +746,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; @@ -2048,6 +1939,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 ); } }