未验证 提交 8d30d672 编写于 作者: K Kevin Granade 提交者: GitHub

Merge pull request #54153 from wapcaplet/w-widget-overmap

Add widget for overmap
[
{
"id": "overmap_full_width",
"type": "widget",
"label": "Overmap",
"var": "overmap_text",
"style": "text",
"width": 0,
"height": 7,
"flags": [ "W_LABEL_NONE" ]
},
{
"id": "overmap_location_desc",
"type": "widget",
"label": "Location",
"var": "overmap_loc_text",
"style": "text",
"flags": [ "W_LABEL_NONE" ]
}
]
......@@ -17,6 +17,8 @@
"compass_all_layout",
"weapon_style_layout",
"place_date_time_layout",
"overmap_full_width",
"overmap_location_desc",
"light_moon_wind_temp_layout",
"vehicle_azimuth_cruise_fuel_layout",
"vital_numbers_layout",
......
......@@ -293,6 +293,14 @@
"bodypart": "arm_l",
"style": "text"
},
{
"id": "test_overmap_3x3_text",
"type": "widget",
"var": "overmap_text",
"style": "text",
"width": 3,
"height": 3
},
{
"id": "test_stat_panel",
"type": "widget",
......
......@@ -168,6 +168,7 @@ next.
Variable widgets define a "var" field, with the name of a predefined widget variable. This tells the
widget what information it should show. Most of the time, these are attributes of the player
character, but they can also be attributes of the world, environment, or vehicle where they are.
See the [Variables](#variables) section for a list of them.
For example, a widget to show the current STR stat would define this "var":
......@@ -551,6 +552,8 @@ Some vars refer to text descriptors. These must use style "text". Examples:
| `moon_phase_text` | Phase of the moon - "New moon", "Waxing gibbous", "Full moon" etc.
| `move_mode_letter` | Movement mode - "W": walking, "R": running, "C": crouching, "P": prone
| `move_mode_text` | Movement mode - "walking", "running", "crouching", "prone"
| `overmap_loc_text` | Overmap coordinates, same as shown in the lower corner of overmap screen
| `overmap_text` | Colored text rendering of the local overmap; may define "width" and "height"
| `pain_text` | "Mild pain", "Distracting pain", "Intense pain", etc.
| `place_text` | Location place name
| `power_text` | Bionic power available
......
......@@ -3,6 +3,8 @@
#include "display.h"
#include "game.h"
#include "options.h"
#include "overmap.h"
#include "overmapbuffer.h"
#include "make_static.h"
#include "map.h"
#include "mood_face.h"
......@@ -33,6 +35,15 @@ static const flag_id json_flag_THERMOMETER( "THERMOMETER" );
static const itype_id fuel_type_muscle( "muscle" );
// Cache for the overmap widget string
static disp_overmap_cache disp_om_cache;
disp_overmap_cache::disp_overmap_cache()
{
_center = overmap::invalid_tripoint;
_mission = overmap::invalid_tripoint;
}
// Get remotely controlled vehicle, or vehicle character is inside of
vehicle *display::vehicle_driven( const Character &u )
{
......@@ -1078,3 +1089,287 @@ std::pair<std::string, nc_color> display::move_mode_text_color( const Character
return std::make_pair( mm_text, mm_color );
}
std::pair<std::string, nc_color> display::overmap_note_symbol_color( const std::string note_text )
{
std::string ter_sym = "N";
nc_color ter_color = c_yellow;
// Parse note text for : (symbol) and ; (color) separators
int symbolIndex = note_text.find( ':' );
int colorIndex = note_text.find( ';' );
bool symbolFirst = symbolIndex < colorIndex;
// Both color and symbol
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 );
}
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;
}
}
}
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 );
const bool vehicle_here = overmap_buffer.has_vehicle( 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 grey # for unseen
ter_sym = "#";
ter_color = c_dark_gray;
} else if( vehicle_here ) {
// Always cyan c for vehicle
ter_color = c_cyan;
ter_sym = "c";
} 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;
map &here = get_map();
// Map is roughly centered around this point
const tripoint_abs_omt &center_xyz = u.global_omt_location();
const tripoint_abs_omt &mission_xyz = u.get_active_mission_target();
// Retrieve cached string instead of constantly rebuilding it
if( disp_om_cache.is_valid_for( center_xyz, mission_xyz ) ) {
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;
// 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 wth?
// 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 );
}
// Append the colorized symbol for this point to the map
overmap_text += colorize( sym_color.first, sym_color.second );
}
overmap_text += "\n";
}
// Rebuild the cache so we can reuse it if nothing changes
disp_om_cache.rebuild( center_xyz, mission_xyz, overmap_text );
return overmap_text;
}
std::string display::overmap_position_text( const tripoint_abs_omt &loc )
{
point_abs_omt abs_omt = loc.xy();
point_abs_om om;
point_om_omt omt;
std::tie( om, omt ) = project_remain<coords::om>( abs_omt );
return string_format( _( "LEVEL %i, %d'%d, %d'%d" ), loc.z(), om.x(), omt.x(), om.y(), omt.y() );
}
// Return (x, y) position of mission target, relative to avatar location, within an overmap of the
// given width and height.
point display::mission_arrow_offset( const avatar &you, int width, int height )
{
// FIXME: Use tripoint for curs
const point_abs_omt curs = you.global_omt_location().xy();
const tripoint_abs_omt targ = you.get_active_mission_target();
const point mid( width / 2, height / 2 );
// If x-coordinates are the same, mission is either due north or due south
// Use an extreme slope rather than dividing by zero
double slope = curs.x() == targ.x() ? 1000 :
static_cast<double>( targ.y() - curs.y() ) / ( targ.x() - curs.x() );
if( std::fabs( slope ) > 12 ) {
// For any near-vertical slope, center the marker
if( targ.y() > curs.y() ) {
// Target is due south
return point( mid.x, height - 1 );
//mvwputch( w_minimap, point( mid.x + start_x, height - 1 + start_y ), c_red, '*' );
} else if( targ.y() < curs.y() ) {
// Target is due north
return point( mid.x, 1 );
//mvwputch( w_minimap, point( mid.x + start_x, 1 + start_y ), c_red, '*' );
} else {
// Target is right here
return mid;
}
} else {
// For non-vertical slope, calculate where it intersects the edge of the map
point arrow( point_north_west );
if( std::fabs( slope ) >= 1. ) {
// If target to the north or south, arrow on top or bottom edge of minimap
if( targ.y() > curs.y() ) {
arrow.x = static_cast<int>( ( 1. + ( 1. / slope ) ) * mid.x );
arrow.y = height - 1;
} else {
arrow.x = static_cast<int>( ( 1. - ( 1. / slope ) ) * mid.x );
arrow.y = 0;
}
// Clip to left/right edges
arrow.x = std::max( arrow.x, 0 );
arrow.x = std::min( arrow.x, width - 1 );
} else {
// If target to the east or west, arrow on left or right edge of minimap
if( targ.x() > curs.x() ) {
arrow.x = width - 1;
arrow.y = static_cast<int>( ( 1. + slope ) * mid.y );
} else {
arrow.x = 0;
arrow.y = static_cast<int>( ( 1. - slope ) * mid.y );
}
// Clip to top/bottom edges
arrow.y = std::max( arrow.y, 0 );
arrow.y = std::min( arrow.y, height - 1 );
}
return arrow;
//mvwputch( w_minimap, arrow + point( start_x, start_y ), c_red, glyph );
}
}
......@@ -9,6 +9,36 @@
class avatar;
class Character;
struct disp_overmap_cache {
private:
tripoint_abs_omt _center;
tripoint_abs_omt _mission;
std::string _om_wgt_str;
public:
disp_overmap_cache();
// Returns true if the stored overmap string can be used with the given
// center (player) position and mission target.
bool is_valid_for( const tripoint_abs_omt &center, const tripoint_abs_omt &mission ) const {
return _center == center && _mission == mission;
}
// Rebuild the cache using the validation parameters "center" and "mission"
// and store the associated widget string.
void rebuild( const tripoint_abs_omt &center, const tripoint_abs_omt &mission,
const std::string &om_wgt_str ) {
_center = center;
_mission = mission;
_om_wgt_str = om_wgt_str;
}
// Retreive the cached widget string
const std::string &get_val() const {
return _om_wgt_str;
}
};
// The display namespace contains UI string output and colorization functions
// Some return plain strings or translations, some return a (string, color) pair,
// and some return a string with colorization tags embedded.
......@@ -107,6 +137,18 @@ nc_color rad_badge_color( const int rad );
// Highlighted badge color for character's radiation badge, if they have one
std::pair<std::string, nc_color> rad_badge_text_color( const Character &u );
// Colorized symbol for the overmap tile at the given location
std::pair<std::string, nc_color> overmap_tile_symbol_color( const avatar &u,
const tripoint_abs_omt &omt, const bool edge_tile, bool &found_mi );
// Colorized symbol for an overmap note, given its full text
std::pair<std::string, nc_color> overmap_note_symbol_color( const std::string note_text );
// Mission marker position as an offset within an overmap of given width and height
point mission_arrow_offset( const avatar &you, int width, int height );
// Fully colorized newline-separated overmap string of the given size, centered on character
std::string colorized_overmap_text( const avatar &u, const int width, const int height );
// Current overmap position (coordinates)
std::string overmap_position_text( const tripoint_abs_omt &loc );
// Functions returning colorized string
// gets the string that describes your weight
std::string weight_string( const Character &u );
......
......@@ -38,6 +38,7 @@
#include "cuboid_rectangle.h"
#include "cursesdef.h"
#include "cursesport.h"
#include "display.h"
#include "enums.h"
#include "game.h"
#include "game_constants.h"
......@@ -1178,12 +1179,8 @@ static void draw_om_sidebar(
print_hint( "QUIT" );
}
point_abs_omt abs_omt = center.xy();
point_abs_om om;
point_om_omt omt;
std::tie( om, omt ) = project_remain<coords::om>( abs_omt );
mvwprintz( wbar, point( 1, getmaxy( wbar ) - 1 ), c_red,
_( "LEVEL %i, %d'%d, %d'%d" ), center.z(), om.x(), omt.x(), om.y(), omt.y() );
const std::string coords = display::overmap_position_text( center );
mvwprintz( wbar, point( 1, getmaxy( wbar ) - 1 ), c_red, coords );
wnoutrefresh( wbar );
}
......
......@@ -177,6 +177,7 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const
const tripoint_abs_omt &global_omt, const point &start_input,
const int width, const int height )
{
// 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;
......@@ -186,108 +187,39 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const
map &here = get_map();
const int sight_points = you.overmap_sight_range( g->light_level( you.posz() ) );
// i scans across width, with 0 in the middle(ish)
// -(w/2) ... w-(w/2)-1
// w:9 -4 ... 4
// w:10 -5 ... 4
// w:11 -5 ... 5
// w:12 -6 ... 5
// w:13 -6 ... 6
for( int i = -( width / 2 ); i <= width - ( width / 2 ) - 1; i++ ) {
// j scans across height, with 0 in the middle(ish)
// (same algorithm)
for( int j = -( height / 2 ); j <= height - ( height / 2 ) - 1; 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 );
const bool vehicle_here = overmap_buffer.has_vehicle( omp );
if( overmap_buffer.has_note( omp ) ) {
const std::string &note_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 );
}
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;
}
}
}
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 grey # for unseen
ter_sym = "#";
ter_color = c_dark_gray;
} else if( vehicle_here ) {
// Always cyan c for vehicle
ter_color = c_cyan;
ter_sym = "c";
} 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 ) ) {
......@@ -305,6 +237,7 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const
ter_color = red_background( ter_color );
}
}
// 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 );
......@@ -325,55 +258,18 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const
}
}
// Print arrow to mission if we have one!
// 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 ) {
// Use an extreme slope rather than dividing by zero
double slope = curs.x() == targ.x() ? 1000 :
static_cast<double>( targ.y() - curs.y() ) / ( targ.x() - curs.x() );
if( std::fabs( slope ) > 12 ) {
// For any near-vertical slope, center the marker
if( targ.y() > curs.y() ) {
mvwputch( w_minimap, point( mid.x + start_x, height - 1 + start_y ), c_red, '*' );
} else {
mvwputch( w_minimap, point( mid.x + start_x, 1 + start_y ), c_red, '*' );
}
} else {
point arrow( point_north_west );
if( std::fabs( slope ) >= 1. ) {
// If target to the north or south, arrow on top or bottom edge of minimap
if( targ.y() > curs.y() ) {
arrow.x = static_cast<int>( ( 1. + ( 1. / slope ) ) * mid.x );
arrow.y = height - 1;
} else {
arrow.x = static_cast<int>( ( 1. - ( 1. / slope ) ) * mid.x );
arrow.y = 0;
}
// Clip to left/right edges
arrow.x = std::max( arrow.x, 0 );
arrow.x = std::min( arrow.x, width - 1 );
} else {
// If target to the east or west, arrow on left or right edge of minimap
if( targ.x() > curs.x() ) {
arrow.x = width - 1;
arrow.y = static_cast<int>( ( 1. + slope ) * mid.y );
} else {
arrow.x = 0;
arrow.y = static_cast<int>( ( 1. - slope ) * mid.y );
}
// Clip to top/bottom edges
arrow.y = std::max( arrow.y, 0 );
arrow.y = std::min( arrow.y, height - 1 );
}
char glyph = '*';
if( targ.z() > you.posz() ) {
glyph = '^';
} else if( targ.z() < you.posz() ) {
glyph = 'v';
}
mvwputch( w_minimap, arrow + point( start_x, start_y ), c_red, glyph );
char glyph = '*';
if( targ.z() > you.posz() ) {
glyph = '^';
} else if( targ.z() < you.posz() ) {
glyph = 'v';
}
const point arrow = display::mission_arrow_offset( you, width, height );
mvwputch( w_minimap, arrow + point( start_x, start_y ), c_red, glyph );
}
}
......@@ -2327,8 +2223,6 @@ static std::vector<window_panel> initialize_default_custom_panels( const widget
ret.emplace_back( window_panel( draw_compass_padding_compact, "Compass",
to_translation( "Compass" ),
5, width, false ) );
ret.emplace_back( window_panel( draw_overmap, "Overmap", to_translation( "Overmap" ),
7, width, false ) );
return ret;
}
......
......@@ -141,6 +141,10 @@ std::string enum_to_string<widget_var>( widget_var data )
return "move_mode_text";
case widget_var::pain_text:
return "pain_text";
case widget_var::overmap_loc_text:
return "overmap_loc_text";
case widget_var::overmap_text:
return "overmap_text";
case widget_var::place_text:
return "place_text";
case widget_var::power_text:
......@@ -484,7 +488,7 @@ window_panel widget::get_window_panel( const int width, const int req_height )
for( const widget_id &wid : _widgets ) {
height += wid->_height > 0 ? wid->_height : 1;
}
} else if( _style == "widget" ) {
} else if( _style == "widget" || _style == "text" ) {
height = _height > 1 ? _height : req_height;
}
// Minimap and log do not have a predetermined height
......@@ -515,6 +519,8 @@ bool widget::uses_text_function()
case widget_var::move_mode_letter:
case widget_var::move_mode_text:
case widget_var::pain_text:
case widget_var::overmap_loc_text:
case widget_var::overmap_text:
case widget_var::place_text:
case widget_var::power_text:
case widget_var::rad_badge_text:
......@@ -541,10 +547,15 @@ bool widget::uses_text_function()
std::string widget::color_text_function_string( const avatar &ava, unsigned int max_width )
{
std::string ret;
bool apply_color = true;
// Most text variables have both a string and a color.
// The string and color in `desc` will be converted to colorized text with markup.
std::pair<std::string, nc_color> desc;
// Give a default color (some widget_vars do not define one)
// Set a default color
desc.second = c_light_gray;
// By default, colorize the string in desc.first with the color in desc.second.
bool apply_color = true;
// Some helper display:: functions do their own internal colorization of the string.
// For those, desc.first is the already-colorized string, and apply_color is set to false.
switch( _var ) {
case widget_var::activity_text:
desc = display::activity_text_color( ava );
......@@ -589,6 +600,13 @@ std::string widget::color_text_function_string( const avatar &ava, unsigned int
case widget_var::pain_text:
desc = display::pain_text_color( ava );
break;
case widget_var::overmap_loc_text:
desc.first = display::overmap_position_text( ava.global_omt_location() );
break;
case widget_var::overmap_text:
desc.first = display::colorized_overmap_text( ava, _width == 0 ? max_width : _width, _height );
apply_color = false;
break;
case widget_var::place_text:
desc.first = overmap_buffer.ter( ava.global_omt_location() )->get_name();
break;
......@@ -787,6 +805,10 @@ static std::string append_line( const std::string &line, bool first_row, unsigne
std::string ret;
// Width used by label, ": " and value, using utf8_width to ignore color tags
unsigned int used_width = utf8_width( line, true );
// utf8_width subtracts 1 for each newline; add it back for multiline widgets
if( !line.empty() && line.back() == '\n' ) {
used_width += 1;
}
if( first_row ) {
const std::string tlabel = label.translated();
// If label is empty or omitted, don't reserve space for it
......
......@@ -59,6 +59,8 @@ enum class widget_var : int {
moon_phase_text,// Current phase of the moon
move_mode_letter, // Movement mode, color letter (W/R/C/P)
move_mode_text, // Movement mode, color text (walking/running/crouching/prone)
overmap_loc_text,// Local overmap position, pseudo latitude/longitude with Z-level
overmap_text, // Local overmap and mission marker, multi-line color string
pain_text, // Pain description text, color string
place_text, // Place name in world where character is
power_text, // Remaining power from bionics, color string
......
......@@ -2,9 +2,13 @@
#include "game.h"
#include "player_helpers.h"
#include "map.h"
#include "map_helpers.h"
#include "mission.h"
#include "monster.h"
#include "morale.h"
#include "overmap.h"
#include "overmapbuffer.h"
#include "options_helpers.h"
#include "weather.h"
#include "weather_type.h"
......@@ -77,6 +81,7 @@ static const widget_id widget_test_move_cost_num( "test_move_cost_num" );
static const widget_id widget_test_move_mode_letter( "test_move_mode_letter" );
static const widget_id widget_test_move_mode_text( "test_move_mode_text" );
static const widget_id widget_test_move_num( "test_move_num" );
static const widget_id widget_test_overmap_3x3_text( "test_overmap_3x3_text" );
static const widget_id widget_test_per_num( "test_per_num" );
static const widget_id widget_test_pool_graph( "test_pool_graph" );
static const widget_id widget_test_rad_badge_text( "test_rad_badge_text" );
......@@ -946,6 +951,82 @@ TEST_CASE( "widgets showing weather conditions", "[widget][weather]" )
}
}
// Fill a 3x3 overmap area around the avatar with a given overmap terrain
static void fill_overmap_area( const avatar &ava, const oter_id &oter )
{
const tripoint_abs_omt &ava_pos = ava.global_omt_location();
for( int x = -1; x <= 1; ++x ) {
for( int y = -1; y <= 1; ++y ) {
const tripoint offset( x, y, 0 );
overmap_buffer.ter_set( ava_pos + offset, oter );
overmap_buffer.set_seen( ava_pos + offset, true );
}
}
}
TEST_CASE( "multi-line overmap text widget", "[widget][overmap]" )
{
widget overmap_w = widget_test_overmap_3x3_text.obj();
avatar &ava = get_avatar();
mission msn;
// Use mission target to invalidate the om cache
msn.set_target( ava.global_omt_location() + tripoint( 5, 0, 0 ) );
clear_avatar();
clear_map();
ava.on_mission_assignment( msn );
// Mission marker is a red asterisk when it's along the border
const std::string red_star = "<color_c_red>*</color>";
SECTION( "field" ) {
const std::string brown_dot = "<color_c_brown>.</color>";
const std::string h_brown_dot = "<color_h_brown>.</color>";
fill_overmap_area( ava, oter_id( "field" ) );
// Mission marker to the north of avatar position (y - 2)
msn.set_target( ava.global_omt_location() + tripoint( 0, -2, 0 ) );
// (red star in top center of the map)
const std::vector<std::string> field_3x3 = {
brown_dot, red_star, brown_dot, "\n",
brown_dot, h_brown_dot, brown_dot, "\n",
brown_dot, brown_dot, brown_dot, "\n"
};
CHECK( overmap_w.layout( ava ) == join( field_3x3, "" ) );
}
SECTION( "forest" ) {
const std::string green_F = "<color_c_green>F</color>";
const std::string h_green_F = "<color_h_green>F</color>";
fill_overmap_area( ava, oter_id( "forest" ) );
// Mission marker to the east of avatar position (x + 2)
msn.set_target( ava.global_omt_location() + tripoint( 2, 0, 0 ) );
// (red star on the right edge of the map)
const std::vector<std::string> forest_3x3 = {
green_F, green_F, green_F, "\n",
green_F, h_green_F, red_star, "\n",
green_F, green_F, green_F, "\n"
};
CHECK( overmap_w.layout( ava ) == join( forest_3x3, "" ) );
}
SECTION( "central lab" ) {
const std::string blue_L = "<color_c_light_blue>L</color>";
const std::string h_blue_L = "<color_h_light_blue>L</color>";
//const std::string blue_L_red = "<color_c_light_blue_red>L</color>";
fill_overmap_area( ava, oter_id( "central_lab" ) );
// Mission marker southwest of avatar position (x-2, y+2)
msn.set_target( ava.global_omt_location() + tripoint( -2, 2, 0 ) );
// (red star on lower left corner of map)
const std::vector<std::string> lab_3x3 = {
blue_L, blue_L, blue_L, "\n",
blue_L, h_blue_L, blue_L, "\n",
red_star, blue_L, blue_L, "\n"
};
CHECK( overmap_w.layout( ava ) == join( lab_3x3, "" ) );
}
// TODO: Horde indicators
}
TEST_CASE( "Custom widget height and multiline formatting", "[widget]" )
{
const int cols = 32;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册