Skip to content

Commit

Permalink
Consolidate overmap terrain and symbol code
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ehughsbaird committed Jun 23, 2024
1 parent 352b387 commit 7ef7a79
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 277 deletions.
98 changes: 17 additions & 81 deletions src/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,74 +1104,6 @@ std::pair<std::string, nc_color> 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<std::string, nc_color> 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 &note_text = overmap_buffer.note( omt );
std::pair<std::string, nc_color> 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<tripoint_abs_omt> 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;
Expand All @@ -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<std::string, nc_color> 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<std::string, nc_color> 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 );
Expand Down
83 changes: 83 additions & 0 deletions src/overmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<oter_id, oter_t const *>, cache_size> cache;
size_t cache_next = 0;

std::pair<std::string, nc_color> 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<tripoint_abs_omt> mission_target = std::nullopt;
// Locations of NPCs to draw
std::unordered_map<tripoint_abs_omt, npc_coloring> npc_color;
// NPC/player paths to draw
std::unordered_set<tripoint_abs_omt> npc_path_route;
std::unordered_map<point_abs_omt, int> 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<std::string, nc_color> 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 );
Expand Down
Loading

0 comments on commit 7ef7a79

Please sign in to comment.