Skip to content

Commit

Permalink
Fix encumbrance window in character info and armor layer screens (Cle…
Browse files Browse the repository at this point in the history
…verRaven#33276)

* Fix names of paired bodyparts (such as arms, legs, etc) cannot be translated for languages without plural forms

* Fix encumbrance window in character info and armor layer screens

* Apply suggestion from review
  • Loading branch information
Qrox authored and misterprimus committed Sep 21, 2019
1 parent 7561da5 commit 4f59536
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 87 deletions.
6 changes: 5 additions & 1 deletion lang/extract_json_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,12 @@ def extract_bodypart(item):
outfile = get_outfile("bodypart")
writestr(outfile, item["name"])
writestr(outfile, item["name"], context="bodypart_accusative")
if "name_plural" in item:
writestr(outfile, item["name_plural"])
writestr(outfile, item["name_plural"], context="bodypart_accusative")
writestr(outfile, item["encumbrance_text"])
writestr(outfile, item["heading_singular"], item["heading_plural"])
writestr(outfile, item["heading_singular"])
writestr(outfile, item["heading_plural"])
if "hp_bar_ui_text" in item:
writestr(outfile, item["hp_bar_ui_text"])

Expand Down
17 changes: 10 additions & 7 deletions src/bodypart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,26 +233,29 @@ void body_part_struct::check() const
}
}

// Some languages do not have plural forms, so we don't use ngettext()/npgettext()
// in the 3 following methods so the singular and plural strings can be properly
// translated for these languages.
std::string body_part_name( body_part bp, int number )
{
const auto &bdy = get_bp( bp );
return ngettext( bdy.name.c_str(),
bdy.name_multiple.c_str(), number );
return number > 1 ? _( bdy.name_multiple ) : _( bdy.name );
}

std::string body_part_name_accusative( body_part bp, int number )
{
const auto &bdy = get_bp( bp );
return npgettext( "bodypart_accusative",
bdy.name.c_str(),
bdy.name_multiple.c_str(), number );
if( number > 1 ) {
return pgettext( "bodypart_accusative", bdy.name_multiple.c_str() );
} else {
return pgettext( "bodypart_accusative", bdy.name.c_str() );
}
}

std::string body_part_name_as_heading( body_part bp, int number )
{
const auto &bdy = get_bp( bp );
return ngettext( bdy.name_as_heading_singular.c_str(), bdy.name_as_heading_multiple.c_str(),
number );
return number > 1 ? _( bdy.name_as_heading_multiple ) : _( bdy.name_as_heading_singular );
}

std::string body_part_hp_bar_ui_text( body_part bp )
Expand Down
2 changes: 1 addition & 1 deletion src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -1743,7 +1743,7 @@ class player : public Character

// formats and prints encumbrance info to specified window
void print_encumbrance( const catacurses::window &win, int line = -1,
item *selected_limb = nullptr ) const;
const item *selected_limb = nullptr ) const;

// Prints message(s) about current health
void print_health() const;
Expand Down
167 changes: 89 additions & 78 deletions src/player_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "addiction.h"
#include "avatar.h"
#include "bionics.h"
#include "cata_utility.h"
#include "effect.h"
#include "game.h"
#include "input.h"
Expand Down Expand Up @@ -45,93 +46,110 @@ static int temperature_print_rescaling( int temp )
return ( temp / 100.0 ) * 2 - 100;
}

static bool should_combine_bps( const player &p, size_t l, size_t r )
static body_part other_part( body_part bp )
{
return static_cast<body_part>( bp_aiOther[bp] );
}

static bool should_combine_bps( const player &p, body_part l, body_part r,
const item *selected_clothing )
{
const auto enc_data = p.get_encumbrance();
return enc_data[l] == enc_data[r] &&
temperature_print_rescaling( p.temp_conv[l] ) == temperature_print_rescaling( p.temp_conv[r] );
return l != r && // are different parts
l == other_part( r ) && r == other_part( l ) && // are complementary parts
// same encumberance & temperature
enc_data[l] == enc_data[r] &&
temperature_print_rescaling( p.temp_conv[l] ) == temperature_print_rescaling( p.temp_conv[r] ) &&
// selected_clothing covers both or neither parts
( !selected_clothing || ( selected_clothing->covers( l ) == selected_clothing->covers( r ) ) );
}

void player::print_encumbrance( const catacurses::window &win, int line,
item *selected_clothing ) const
static std::vector<std::pair<body_part, bool>> list_and_combine_bps( const player &p,
const item *selected_clothing )
{
const int height = getmaxy( win );
const int orig_line = line;

// fill a set with the indices of the body parts to display
line = std::max( 0, line );
std::set<int> parts;
// check and optionally enqueue line+0, -1, +1, -2, +2, ...
int off = 0; // offset from line
int skip[2] = {}; // how far to skip on next neg/pos jump
do {
if( !skip[off > 0] && line + off >= 0 && line + off < num_bp ) { // line+off is in bounds
parts.insert( line + off );
if( line + off != static_cast<int>( bp_aiOther[line + off] ) &&
should_combine_bps( *this, line + off, bp_aiOther[line + off] ) ) { // part of a pair
skip[static_cast<int>( bp_aiOther[line + off] ) > line + off ] =
1; // skip the next candidate in this direction
}
} else {
skip[off > 0] = 0;
// bool represents whether the part has been combined with its other half
std::vector<std::pair<body_part, bool>> bps;
for( auto bp : all_body_parts ) {
// assuming that a body part has at most one other half
if( other_part( other_part( bp ) ) != bp ) {
debugmsg( "Bodypart %d has more than one other half!", bp );
}
if( off < 0 ) {
off = -off;
if( should_combine_bps( p, bp, other_part( bp ), selected_clothing ) ) {
if( bp < other_part( bp ) ) {
// only add the earlier one
bps.emplace_back( bp, true );
}
} else {
off = -off - 1;
bps.emplace_back( bp, false );
}
} while( off > -num_bp && static_cast<int>( parts.size() ) < height - 1 );
}
return bps;
}

void player::print_encumbrance( const catacurses::window &win, const int line,
const item *const selected_clothing ) const
{
// bool represents whether the part has been combined with its other half
const std::vector<std::pair<body_part, bool>> bps = list_and_combine_bps( *this,
selected_clothing );

// width/height excluding title & scrollbar
const int height = getmaxy( win ) - 1;
bool draw_scrollbar = height < static_cast<int>( bps.size() );
const int width = getmaxx( win ) - ( draw_scrollbar ? 1 : 0 );
// index of the first printed bodypart from `bps`
const int firstline = clamp( line - height / 2, 0, std::max( 0,
static_cast<int>( bps.size() ) - height ) );

std::string out;
/*** I chose to instead only display X+Y instead of X+Y=Z. More room was needed ***
*** for displaying triple digit encumbrance, due to new encumbrance system. ***
*** If the player wants to see the total without having to do them maths, the ***
*** armor layers ui shows everything they want :-) -Davek ***/
int row = 1;
const auto enc_data = get_encumbrance();
for( auto bp : parts ) {
for( int i = 0; i < height; ++i ) {
int thisline = firstline + i;
if( thisline < 0 ) {
continue;
}
if( static_cast<size_t>( thisline ) >= bps.size() ) {
break;
}
const body_part bp = bps[thisline].first;
const bool combine = bps[thisline].second;
const encumbrance_data &e = enc_data[bp];
const bool highlighted = ( selected_clothing == nullptr ) ? false :
( selected_clothing->covers( static_cast<body_part>( bp ) ) );
const bool combine = should_combine_bps( *this, bp, bp_aiOther[bp] );
out.clear();
// limb, and possible color highlighting
// TODO: utf8 aware printf would be nice... this works well enough for now
out = body_part_name_as_heading( all_body_parts[bp], combine ? 2 : 1 );

const int len = 7 - utf8_width( out );
switch( sgn( len ) ) {
case -1:
out = utf8_truncate( out, 7 );
break;
case 1:
out = out + std::string( len, ' ' );
break;
const bool highlighted = selected_clothing ? selected_clothing->covers( bp ) : false;
std::string out = body_part_name_as_heading( bp, combine ? 2 : 1 );
if( utf8_width( out ) > 7 ) {
out = utf8_truncate( out, 7 );
}

// Two different highlighting schemes, highlight if the line is selected as per line being set.
// Make the text green if this part is covered by the passed in item.
nc_color limb_color = ( orig_line == bp ) ?
nc_color limb_color = thisline == line ?
( highlighted ? h_green : h_light_gray ) :
( highlighted ? c_green : c_light_gray );
mvwprintz( win, point( 1, row ), limb_color, out );
mvwprintz( win, point( 1, 1 + i ), limb_color, "%s", out );
// accumulated encumbrance from clothing, plus extra encumbrance from layering
wprintz( win, encumb_color( e.encumbrance ), string_format( "%3d", e.armor_encumbrance ) );
mvwprintz( win, point( 8, 1 + i ), encumb_color( e.encumbrance ), "%3d", e.armor_encumbrance );
// separator in low toned color
wprintz( win, c_light_gray, "+" );
mvwprintz( win, point( 11, 1 + i ), c_light_gray, "+" );
// take into account the new encumbrance system for layers
wprintz( win, encumb_color( e.encumbrance ), string_format( "%-3d", e.layer_penalty ) );
mvwprintz( win, point( 12, 1 + i ), encumb_color( e.encumbrance ), "%-3d", e.layer_penalty );
// print warmth, tethered to right hand side of the window
out = string_format( "(% 3d)", temperature_print_rescaling( temp_conv[bp] ) );
mvwprintz( win, point( getmaxx( win ) - 6, row ), bodytemp_color( bp ), out );
row++;
mvwprintz( win, point( width - 6, 1 + i ), bodytemp_color( bp ), "(% 3d)",
temperature_print_rescaling( temp_conv[bp] ) );
}

if( off > -num_bp ) { // not every body part fit in the window
// TODO: account for skipped paired body parts in scrollbar math
draw_scrollbar( win, std::max( orig_line, 0 ), height - 1, num_bp, 1 );
if( draw_scrollbar ) {
scrollbar().
offset_x( width ).
offset_y( 1 ).
content_size( bps.size() ).
viewport_pos( firstline ).
viewport_size( height ).
scroll_to_last( false ).
apply( win );
}

}

static std::string swim_cost_text( int moves )
Expand Down Expand Up @@ -375,40 +393,33 @@ static void draw_encumbrance_tab( const catacurses::window &w_encumb,
const catacurses::window &w_info, player &you, unsigned int &line, int &curtab, input_context &ctxt,
bool &done, std::string &action )
{
const std::vector<std::pair<body_part, bool>> bps = list_and_combine_bps( you, nullptr );

werase( w_encumb );
center_print( w_encumb, 0, h_light_gray, _( title_ENCUMB ) );
you.print_encumbrance( w_encumb, line );
wrefresh( w_encumb );

werase( w_info );
std::string s;

body_part bp = line <= 11 ? all_body_parts[line] : num_bp;
bool combined_here = ( bp_aiOther[line] == line + 1 ||
bp_aiOther[line] == line - 1 ) && // first of a pair
should_combine_bps( you, line, bp_aiOther[line] );
s += get_encumbrance_description( you, bp, combined_here );
body_part bp = num_bp;
bool combined_here = false;
if( line < bps.size() ) {
bp = bps[line].first;
combined_here = bps[line].second;
}
const std::string s = get_encumbrance_description( you, bp, combined_here );
// NOLINTNEXTLINE(cata-use-named-point-constants)
fold_and_print( w_info, point( 1, 0 ), FULL_SCREEN_WIDTH - 2, c_magenta, s );
wrefresh( w_info );

action = ctxt.handle_input();
if( action == "DOWN" ) {
if( line < num_bp - 1 ) {
if( combined_here ) {
line += ( line < num_bp - 2 ) ? 2 : 0; // skip a line if we aren't at the last pair
} else {
line++; // unpaired or unequal
}
if( line + 1 < bps.size() ) {
++line;
}
} else if( action == "UP" ) {
if( line > 0 ) {
if( bp_aiOther[line] == line - 1 && // second of a pair
should_combine_bps( you, line, bp_aiOther[line] ) ) {
line -= ( line > 1 ) ? 2 : 0; // skip a line if we aren't at the first pair
} else {
line--; // unpaired or unequal
}
--line;
}
} else if( action == "NEXT_TAB" || action == "PREV_TAB" ) {
mvwprintz( w_encumb, point_zero, c_light_gray, header_spaces );
Expand Down

0 comments on commit 4f59536

Please sign in to comment.