Skip to content
Permalink
Tree: e50236488f
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1223 lines (1100 sloc) 55.4 KB
#include "player.h"
#include "addiction.h"
#include "bionics.h"
#include "effect.h"
#include "game.h"
#include "input.h"
#include "mutation.h"
#include "options.h"
#include "output.h"
#include "profession.h"
#include "skill.h"
#include "string_formatter.h"
#include "units.h"
#include "weather.h"
#include <algorithm>
const skill_id skill_swimming( "swimming" );
// use this instead of having to type out 26 spaces like before
static const std::string header_spaces( 26, ' ' );
// Rescale temperature value to one that the player sees
int temperature_print_rescaling( int temp )
{
return ( temp / 100.0 ) * 2 - 100;
}
bool should_combine_bps( const player &p, size_t l, size_t r )
{
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] );
}
void player::print_encumbrance( const catacurses::window &win, int line,
item *selected_clothing ) const
{
const int height = getmaxy( win );
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;
}
if( off < 0 ) {
off = -off;
} else {
off = -off - 1;
}
} while( off > -num_bp && static_cast<int>( parts.size() ) < height - 1 );
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 ) {
const encumbrance_data &e = enc_data[bp];
bool highlighted = ( selected_clothing == nullptr ) ? false :
( selected_clothing->covers( static_cast<body_part>( bp ) ) );
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 );
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;
}
// 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 ) ?
( highlighted ? h_green : h_light_gray ) :
( highlighted ? c_green : c_light_gray );
mvwprintz( win, row, 1, limb_color, out.c_str() );
// accumulated encumbrance from clothing, plus extra encumbrance from layering
wprintz( win, encumb_color( e.encumbrance ), string_format( "%3d", e.armor_encumbrance ).c_str() );
// separator in low toned color
wprintz( win, c_light_gray, "+" );
// take into account the new encumbrance system for layers
wprintz( win, encumb_color( e.encumbrance ), string_format( "%-3d",
e.layer_penalty ).c_str() );
// print warmth, tethered to right hand side of the window
out = string_format( "(% 3d)", temperature_print_rescaling( temp_conv[bp] ) );
mvwprintz( win, row, getmaxx( win ) - 6, bodytemp_color( bp ), out.c_str() );
row++;
}
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 );
}
}
std::string swim_cost_text( int moves )
{
return string_format( ngettext( "Swimming costs %+d movement point. ",
"Swimming costs %+d movement points. ",
moves ),
moves );
}
std::string run_cost_text( int moves )
{
return string_format( ngettext( "Running costs %+d movement point. ",
"Running costs %+d movement points. ",
moves ),
moves );
}
std::string reload_cost_text( int moves )
{
return string_format( ngettext( "Reloading costs %+d movement point. ",
"Reloading costs %+d movement points. ",
moves ),
moves );
}
std::string melee_cost_text( int moves )
{
return string_format( ngettext( "Melee and thrown attacks cost %+d movement point. ",
"Melee and thrown attacks cost %+d movement points. ",
moves ),
moves );
}
std::string dodge_skill_text( double mod )
{
return string_format( _( "Dodge skill %+.1f. " ), mod );
}
int get_encumbrance( const player &p, body_part bp, bool combine )
{
// Body parts that can't combine with anything shouldn't print double values on combine
// This shouldn't happen, but handle this, just in case
bool combines_with_other = static_cast<int>( bp_aiOther[bp] ) != bp;
return p.encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 );
}
std::string get_encumbrance_description( const player &p, body_part bp, bool combine )
{
std::string s;
const int eff_encumbrance = get_encumbrance( p, bp, combine );
switch( bp ) {
case bp_torso: {
const int melee_roll_pen = std::max( -eff_encumbrance, -80 );
s += string_format( _( "Melee attack rolls %+d%%; " ), melee_roll_pen );
s += dodge_skill_text( - ( eff_encumbrance / 10 ) );
s += swim_cost_text( ( eff_encumbrance / 10 ) * ( 80 - p.get_skill_level( skill_swimming ) * 3 ) );
s += melee_cost_text( eff_encumbrance );
break;
}
case bp_head:
s += _( "Head encumbrance has no effect; it simply limits how much you can put on." );
break;
case bp_eyes:
s += string_format( _( "Perception %+d when checking traps or firing ranged weapons;\n"
"Dispersion %+d when throwing items." ),
-( eff_encumbrance / 10 ),
eff_encumbrance * 10 );
break;
case bp_mouth:
s += _( "Covering your mouth will make it more difficult to breathe and catch your breath." );
break;
case bp_arm_l:
case bp_arm_r:
s += _( "Arm encumbrance affects stamina cost of melee attacks and accuracy with ranged weapons." );
break;
case bp_hand_l:
case bp_hand_r:
s += _( "Reduces the speed at which you can handle or manipulate items\n" );
s += reload_cost_text( ( eff_encumbrance / 10 ) * 15 );
s += string_format( _( "Dexterity %+.1f when throwing items;\n" ), -( eff_encumbrance / 10.0f ) );
s += melee_cost_text( eff_encumbrance / 2 );
s += "\n";
s += string_format( _( "Reduces aim speed of guns by %.1f." ), p.aim_speed_encumbrance_modifier() );
break;
case bp_leg_l:
case bp_leg_r:
s += run_cost_text( int( eff_encumbrance * 0.15 ) );
s += swim_cost_text( ( eff_encumbrance / 10 ) * ( 50 - p.get_skill_level(
skill_swimming ) * 2 ) / 2 );
s += dodge_skill_text( -eff_encumbrance / 10.0 / 4.0 );
break;
case bp_foot_l:
case bp_foot_r:
s += run_cost_text( int( eff_encumbrance * 0.25 ) );
break;
case num_bp:
break;
}
return s;
}
void player::disp_info()
{
unsigned line;
std::vector<std::string> effect_name;
std::vector<std::string> effect_text;
std::string tmp;
for( auto &elem : *effects ) {
for( auto &_effect_it : elem.second ) {
tmp = _effect_it.second.disp_name();
if( !tmp.empty() ) {
effect_name.push_back( tmp );
effect_text.push_back( _effect_it.second.disp_desc() );
}
}
}
if( get_perceived_pain() > 0 ) {
effect_name.push_back( _( "Pain" ) );
const auto ppen = get_pain_penalty();
std::stringstream pain_text;
if( ppen.strength > 0 ) {
pain_text << _( "Strength" ) << " -" << ppen.strength << " ";
}
if( ppen.dexterity > 0 ) {
pain_text << _( "Dexterity" ) << " -" << ppen.dexterity << " ";
}
if( ppen.intelligence > 0 ) {
pain_text << _( "Intelligence" ) << " -" << ppen.intelligence << " ";
}
if( ppen.perception > 0 ) {
pain_text << _( "Perception" ) << " -" << ppen.perception << " ";
}
if( ppen.speed > 0 ) {
pain_text << _( "Speed" ) << " -" << ppen.speed << "% ";
}
effect_text.push_back( pain_text.str() );
}
int starvation_base_penalty = get_starvation() + 300;
if( starvation_base_penalty > 300 ) {
std::stringstream starvation_text;
if( starvation_base_penalty > 1400 ) {
effect_name.push_back( _( "Severely Malnourished" ) );
starvation_text <<
_( "Your body is severely weakened by starvation. You might die if you don't start eating regular meals!\n \n" );
} else {
effect_name.push_back( _( "Malnourished" ) );
starvation_text <<
_( "Your body is weakened by starvation. Only time and regular meals will help you recover.\n \n" );
}
if( starvation_base_penalty > 500 ) {
starvation_text << _( "Strength" ) << " -" << int( starvation_base_penalty / 500 ) << " ";
if( starvation_base_penalty > 1000 ) {
starvation_text << _( "Dexterity" ) << " -" << int( starvation_base_penalty / 1000 ) << " ";
starvation_text << _( "Intelligence" ) << " -" << int( starvation_base_penalty / 1000 ) << " ";
}
}
int starvation_speed_penalty = abs( hunger_speed_penalty( get_starvation() + get_hunger() ) );
if( get_hunger() + get_starvation() > 100 ) {
starvation_text << _( "Speed" ) << " -" << starvation_speed_penalty << "% ";
}
effect_text.push_back( starvation_text.str() );
}
if( ( has_trait( trait_id( "TROGLO" ) ) && g->is_in_sunlight( pos() ) &&
g->weather == WEATHER_SUNNY ) ||
( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) &&
g->weather != WEATHER_SUNNY ) ) {
effect_name.push_back( _( "In Sunlight" ) );
effect_text.push_back( _( "The sunlight irritates you.\n\
Strength - 1; Dexterity - 1; Intelligence - 1; Perception - 1" ) );
} else if( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) ) {
effect_name.push_back( _( "In Sunlight" ) );
effect_text.push_back( _( "The sunlight irritates you badly.\n\
Strength - 2; Dexterity - 2; Intelligence - 2; Perception - 2" ) );
} else if( has_trait( trait_id( "TROGLO3" ) ) && g->is_in_sunlight( pos() ) ) {
effect_name.push_back( _( "In Sunlight" ) );
effect_text.push_back( _( "The sunlight irritates you terribly.\n\
Strength - 4; Dexterity - 4; Intelligence - 4; Perception - 4" ) );
}
for( auto &elem : addictions ) {
if( elem.sated < 0_turns && elem.intensity >= MIN_ADDICTION_LEVEL ) {
effect_name.push_back( addiction_name( elem ) );
effect_text.push_back( addiction_text( elem ) );
}
}
unsigned maxy = unsigned( TERMY );
unsigned effect_win_size_y = 1 + unsigned( effect_name.size() );
std::vector<trait_id> traitslist = get_mutations();
unsigned trait_win_size_y = 1 + unsigned( traitslist.size() );
std::vector<bionic> bionicslist = *my_bionics;
unsigned bionics_win_size_y = 2 + bionicslist.size();
const auto skillslist = Skill::get_skills_sorted_by( [&]( Skill const & a, Skill const & b ) {
int const level_a = get_skill_level_object( a.ident() ).exercised_level();
int const level_b = get_skill_level_object( b.ident() ).exercised_level();
return level_a > level_b || ( level_a == level_b && a.name() < b.name() );
} );
unsigned skill_win_size_y = 1 + skillslist.size();
unsigned info_win_size_y = 6;
unsigned infooffsetytop = 11;
unsigned infooffsetybottom = infooffsetytop + 1 + info_win_size_y;
if( ( bionics_win_size_y + trait_win_size_y + infooffsetybottom ) > maxy ) {
// maximum space for either window if they're both the same size
unsigned max_shared_y = ( maxy - infooffsetybottom ) / 2;
// both are larger than the shared size
if( std::min( bionics_win_size_y, trait_win_size_y ) > max_shared_y ) {
bionics_win_size_y = max_shared_y;
// trait window is less than the shared size, so give space to bionics
} else if( trait_win_size_y <= max_shared_y ) {
bionics_win_size_y = maxy - infooffsetybottom - trait_win_size_y;
}
// fall through if bionics is smaller
trait_win_size_y = maxy - infooffsetybottom - bionics_win_size_y;
}
if( skill_win_size_y + infooffsetybottom > maxy ) {
skill_win_size_y = maxy - infooffsetybottom;
}
catacurses::window w_grid_top = catacurses::newwin( infooffsetybottom, FULL_SCREEN_WIDTH + 1,
VIEW_OFFSET_Y, VIEW_OFFSET_X );
catacurses::window w_grid_skill = catacurses::newwin( skill_win_size_y + 1, 27,
infooffsetybottom + VIEW_OFFSET_Y, 0 + VIEW_OFFSET_X );
catacurses::window w_grid_trait = catacurses::newwin( trait_win_size_y + 1, 27,
infooffsetybottom + VIEW_OFFSET_Y, 27 + VIEW_OFFSET_X );
catacurses::window w_grid_bionics = catacurses::newwin( bionics_win_size_y + 1, 27,
infooffsetybottom + VIEW_OFFSET_Y + trait_win_size_y + 1,
27 + VIEW_OFFSET_X );
catacurses::window w_grid_effect = catacurses::newwin( effect_win_size_y + 1, 28,
infooffsetybottom + VIEW_OFFSET_Y, 53 + VIEW_OFFSET_X );
catacurses::window w_tip = catacurses::newwin( 1, FULL_SCREEN_WIDTH, VIEW_OFFSET_Y,
0 + VIEW_OFFSET_X );
catacurses::window w_stats = catacurses::newwin( 9, 26, 1 + VIEW_OFFSET_Y, 0 + VIEW_OFFSET_X );
catacurses::window w_traits = catacurses::newwin( trait_win_size_y, 26,
infooffsetybottom + VIEW_OFFSET_Y, 27 + VIEW_OFFSET_X );
catacurses::window w_bionics = catacurses::newwin( bionics_win_size_y, 26,
infooffsetybottom + VIEW_OFFSET_Y + trait_win_size_y + 1,
27 + VIEW_OFFSET_X );
catacurses::window w_encumb = catacurses::newwin( 9, 26, 1 + VIEW_OFFSET_Y, 27 + VIEW_OFFSET_X );
catacurses::window w_effects = catacurses::newwin( effect_win_size_y, 26,
infooffsetybottom + VIEW_OFFSET_Y, 54 + VIEW_OFFSET_X );
catacurses::window w_speed = catacurses::newwin( 9, 26, 1 + VIEW_OFFSET_Y, 54 + VIEW_OFFSET_X );
catacurses::window w_skills = catacurses::newwin( skill_win_size_y, 26,
infooffsetybottom + VIEW_OFFSET_Y, 0 + VIEW_OFFSET_X );
catacurses::window w_info = catacurses::newwin( info_win_size_y, FULL_SCREEN_WIDTH,
infooffsetytop + VIEW_OFFSET_Y, 0 + VIEW_OFFSET_X );
unsigned upper_info_border = 10;
unsigned lower_info_border = 1 + upper_info_border + info_win_size_y;
for( unsigned i = 0; i < unsigned( FULL_SCREEN_WIDTH + 1 ); i++ ) {
//Horizontal line top grid
mvwputch( w_grid_top, upper_info_border, i, BORDER_COLOR, LINE_OXOX );
mvwputch( w_grid_top, lower_info_border, i, BORDER_COLOR, LINE_OXOX );
//Vertical line top grid
if( i <= infooffsetybottom ) {
mvwputch( w_grid_top, i, 26, BORDER_COLOR, LINE_XOXO );
mvwputch( w_grid_top, i, 53, BORDER_COLOR, LINE_XOXO );
mvwputch( w_grid_top, i, FULL_SCREEN_WIDTH, BORDER_COLOR, LINE_XOXO );
}
//Horizontal line skills
if( i <= 26 ) {
mvwputch( w_grid_skill, skill_win_size_y, i, BORDER_COLOR, LINE_OXOX );
}
//Vertical line skills
if( i <= skill_win_size_y ) {
mvwputch( w_grid_skill, i, 26, BORDER_COLOR, LINE_XOXO );
}
//Horizontal line traits
if( i <= 26 ) {
mvwputch( w_grid_trait, trait_win_size_y, i, BORDER_COLOR, LINE_OXOX );
}
//Vertical line traits
if( i <= trait_win_size_y ) {
mvwputch( w_grid_trait, i, 26, BORDER_COLOR, LINE_XOXO );
}
//Horizontal line bionics
if( i <= 26 ) {
mvwputch( w_grid_bionics, bionics_win_size_y, i, BORDER_COLOR, LINE_OXOX );
}
//Vertical line bionics
if( i <= bionics_win_size_y ) {
mvwputch( w_grid_bionics, i, 26, BORDER_COLOR, LINE_XOXO );
}
//Horizontal line effects
if( i <= 27 ) {
mvwputch( w_grid_effect, effect_win_size_y, i, BORDER_COLOR, LINE_OXOX );
}
//Vertical line effects
if( i <= effect_win_size_y ) {
mvwputch( w_grid_effect, i, 0, BORDER_COLOR, LINE_XOXO );
mvwputch( w_grid_effect, i, 27, BORDER_COLOR, LINE_XOXO );
}
}
//Intersections top grid
mvwputch( w_grid_top, lower_info_border, 26, BORDER_COLOR, LINE_OXXX ); // T
mvwputch( w_grid_top, lower_info_border, 53, BORDER_COLOR, LINE_OXXX ); // T
mvwputch( w_grid_top, upper_info_border, 26, BORDER_COLOR, LINE_XXOX ); // _|_
mvwputch( w_grid_top, upper_info_border, 53, BORDER_COLOR, LINE_XXOX ); // _|_
mvwputch( w_grid_top, upper_info_border, FULL_SCREEN_WIDTH, BORDER_COLOR, LINE_XOXX ); // -|
mvwputch( w_grid_top, lower_info_border, FULL_SCREEN_WIDTH, BORDER_COLOR, LINE_XOXX ); // -|
wrefresh( w_grid_top );
mvwputch( w_grid_skill, skill_win_size_y, 26, BORDER_COLOR, LINE_XOOX ); // _|
if( skill_win_size_y > trait_win_size_y ) {
mvwputch( w_grid_skill, trait_win_size_y, 26, BORDER_COLOR, LINE_XXXO ); // |-
} else if( skill_win_size_y == trait_win_size_y ) {
mvwputch( w_grid_skill, trait_win_size_y, 26, BORDER_COLOR, LINE_XXOX ); // _|_
}
mvwputch( w_grid_trait, trait_win_size_y, 26, BORDER_COLOR, LINE_XOXX ); // -|
if( trait_win_size_y > effect_win_size_y ) {
mvwputch( w_grid_trait, effect_win_size_y, 26, BORDER_COLOR, LINE_XXXO ); // |-
} else if( trait_win_size_y == effect_win_size_y ) {
mvwputch( w_grid_trait, effect_win_size_y, 26, BORDER_COLOR, LINE_XXOX ); // _|_
} else if( trait_win_size_y < effect_win_size_y ) {
mvwputch( w_grid_trait, trait_win_size_y, 26, BORDER_COLOR, LINE_XOXX ); // -|
mvwputch( w_grid_trait, effect_win_size_y, 26, BORDER_COLOR, LINE_XXOO ); // |_
}
if( ( trait_win_size_y + bionics_win_size_y ) > effect_win_size_y ) {
mvwputch( w_grid_bionics, bionics_win_size_y, 26, BORDER_COLOR, LINE_XOOX ); // _|
} else if( ( trait_win_size_y + bionics_win_size_y ) == effect_win_size_y ) {
mvwputch( w_grid_bionics, effect_win_size_y, 26, BORDER_COLOR, LINE_XXOX ); // _|_
} else if( ( trait_win_size_y + bionics_win_size_y ) < effect_win_size_y ) {
mvwputch( w_grid_bionics, bionics_win_size_y, 26, BORDER_COLOR, LINE_XOXX ); // -|
mvwputch( w_grid_bionics, effect_win_size_y, 26, BORDER_COLOR, LINE_XXOO ); // |_
}
mvwputch( w_grid_effect, effect_win_size_y, 0, BORDER_COLOR, LINE_XXOO ); // |_
mvwputch( w_grid_effect, effect_win_size_y, 27, BORDER_COLOR, LINE_XOOX ); // _|
wrefresh( w_grid_skill );
wrefresh( w_grid_effect );
wrefresh( w_grid_trait );
wrefresh( w_grid_bionics );
//-1 for header
trait_win_size_y--;
bionics_win_size_y--;
skill_win_size_y--;
effect_win_size_y--;
// Print name and header
// Post-humanity trumps your pre-Cataclysm life.
if( crossed_threshold() ) {
std::string race;
for( auto &mut : my_mutations ) {
const auto &mdata = mut.first.obj();
if( mdata.threshold ) {
race = mdata.name();
break;
}
}
//~ player info window: 1s - name, 2s - gender, 3s - Prof or Mutation name
mvwprintw( w_tip, 0, 0, _( "%1$s | %2$s | %3$s" ), name.c_str(),
male ? _( "Male" ) : _( "Female" ), race.c_str() );
} else if( prof == nullptr || prof == prof->generic() ) {
// Regular person. Nothing interesting.
//~ player info window: 1s - name, 2s - gender, '|' - field separator.
mvwprintw( w_tip, 0, 0, _( "%1$s | %2$s" ), name.c_str(),
male ? _( "Male" ) : _( "Female" ) );
} else {
mvwprintw( w_tip, 0, 0, _( "%1$s | %2$s | %3$s" ), name.c_str(),
male ? _( "Male" ) : _( "Female" ), prof->gender_appropriate_name( male ).c_str() );
}
input_context ctxt( "PLAYER_INFO" );
ctxt.register_updown();
ctxt.register_action( "NEXT_TAB", _( "Cycle to next category" ) );
ctxt.register_action( "QUIT" );
ctxt.register_action( "CONFIRM", _( "Toggle skill training" ) );
ctxt.register_action( "HELP_KEYBINDINGS" );
std::string action;
std::string help_msg = string_format( _( "Press %s for help." ),
ctxt.get_desc( "HELP_KEYBINDINGS" ).c_str() );
mvwprintz( w_tip, 0, FULL_SCREEN_WIDTH - utf8_width( help_msg ), c_light_red, help_msg.c_str() );
help_msg.clear();
wrefresh( w_tip );
// First! Default STATS screen.
const std::string title_STATS = _( "STATS" );
center_print( w_stats, 0, c_light_gray, title_STATS );
// Stats
const auto display_stat = [&w_stats]( const char *name, int cur, int max, int line_n ) {
nc_color cstatus;
if( cur <= 0 ) {
cstatus = c_dark_gray;
} else if( cur < max / 2 ) {
cstatus = c_red;
} else if( cur < max ) {
cstatus = c_light_red;
} else if( cur == max ) {
cstatus = c_white;
} else if( cur < max * 1.5 ) {
cstatus = c_light_green;
} else {
cstatus = c_green;
}
mvwprintz( w_stats, line_n, 1, c_light_gray, name );
mvwprintz( w_stats, line_n, 18, cstatus, "%2d", cur );
mvwprintz( w_stats, line_n, 21, c_light_gray, "(%2d)", max );
};
display_stat( _( "Strength:" ), str_cur, str_max, 2 );
display_stat( _( "Dexterity:" ), dex_cur, dex_max, 3 );
display_stat( _( "Intelligence:" ), int_cur, int_max, 4 );
display_stat( _( "Perception:" ), per_cur, per_max, 5 );
wrefresh( w_stats );
// Next, draw encumbrance.
const std::string title_ENCUMB = _( "ENCUMBRANCE AND WARMTH" );
center_print( w_encumb, 0, c_light_gray, title_ENCUMB );
print_encumbrance( w_encumb );
wrefresh( w_encumb );
// Next, draw traits.
const std::string title_TRAITS = _( "TRAITS" );
center_print( w_traits, 0, c_light_gray, title_TRAITS );
std::sort( traitslist.begin(), traitslist.end(), trait_display_sort );
for( size_t i = 0; i < traitslist.size() && i < trait_win_size_y; i++ ) {
const auto &mdata = traitslist[i].obj();
const auto color = mdata.get_display_color();
trim_and_print( w_traits, int( i ) + 1, 1, getmaxx( w_traits ) - 1, color, mdata.name() );
}
wrefresh( w_traits );
// Next, draw bionics
const std::string title_BIONICS = _( "BIONICS" );
center_print( w_bionics, 0, c_light_gray, title_BIONICS );
trim_and_print( w_bionics, 1, 1, getmaxx( w_bionics ) - 1, c_white,
string_format( _( "Bionic Power: %1$d" ), max_power_level ) );
for( size_t i = 0; i < bionicslist.size() && i < bionics_win_size_y; i++ ) {
trim_and_print( w_bionics, int( i ) + 2, 1, getmaxx( w_bionics ) - 1, c_white,
bionicslist[i].info().name );
}
wrefresh( w_bionics );
// Next, draw effects.
const std::string title_EFFECTS = _( "EFFECTS" );
center_print( w_effects, 0, c_light_gray, title_EFFECTS );
for( size_t i = 0; i < effect_name.size() && i < effect_win_size_y; i++ ) {
trim_and_print( w_effects, int( i ) + 1, 0, getmaxx( w_effects ) - 1, c_light_gray,
effect_name[i] );
}
wrefresh( w_effects );
// Next, draw skills.
line = 1;
const std::string title_SKILLS = _( "SKILLS" );
center_print( w_skills, 0, c_light_gray, title_SKILLS );
for( auto &elem : skillslist ) {
const SkillLevel &level = get_skill_level_object( elem->ident() );
// Default to not training and not rusting
nc_color text_color = c_blue;
bool not_capped = level.can_train();
bool training = level.isTraining();
bool rusting = level.isRusting();
if( training && rusting ) {
text_color = c_light_red;
} else if( training && not_capped ) {
text_color = c_light_blue;
} else if( rusting ) {
text_color = c_red;
} else if( !not_capped ) {
text_color = c_white;
}
int level_num = level.level();
int exercise = level.exercise();
// TODO: this skill list here is used in other places as well. Useless redundancy and
// dependency. Maybe change it into a flag of the skill that indicates it's a skill used
// by the bionic?
static const std::array<skill_id, 5> cqb_skills = { {
skill_id( "melee" ), skill_id( "unarmed" ), skill_id( "cutting" ),
skill_id( "bashing" ), skill_id( "stabbing" ),
}
};
if( has_active_bionic( bionic_id( "bio_cqb" ) ) &&
std::find( cqb_skills.begin(), cqb_skills.end(), elem->ident() ) != cqb_skills.end() ) {
level_num = 5;
exercise = 0;
text_color = c_yellow;
}
if( line < skill_win_size_y + 1 ) {
mvwprintz( w_skills, line, 1, text_color, "%s:", ( elem )->name().c_str() );
if( ( elem )->ident() == skill_id( "dodge" ) ) {
mvwprintz( w_skills, line, 14, text_color, "%4.1f/%-2d(%2d%%)",
get_dodge(), level_num, exercise < 0 ? 0 : exercise );
} else {
mvwprintz( w_skills, line, 19, text_color, "%-2d(%2d%%)", level_num,
( exercise < 0 ? 0 : exercise ) );
}
line++;
}
}
wrefresh( w_skills );
// Finally, draw speed.
const std::string title_SPEED = _( "SPEED" );
center_print( w_speed, 0, c_light_gray, title_SPEED );
mvwprintz( w_speed, 1, 1, c_light_gray, _( "Base Move Cost:" ) );
mvwprintz( w_speed, 2, 1, c_light_gray, _( "Current Speed:" ) );
int newmoves = get_speed();
int pen = 0;
line = 3;
if( weight_carried() > weight_capacity() ) {
pen = 25 * ( weight_carried() - weight_capacity() ) / ( weight_capacity() );
mvwprintz( w_speed, line, 1, c_red, _( "Overburdened -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
pen = get_pain_penalty().speed;
if( pen >= 1 ) {
mvwprintz( w_speed, line, 1, c_red, _( "Pain -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
if( get_thirst() > 40 ) {
pen = abs( thirst_speed_penalty( get_thirst() ) );
mvwprintz( w_speed, line, 1, c_red, _( "Thirst -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
if( get_hunger() + get_starvation() > 100 ) {
pen = abs( hunger_speed_penalty( get_hunger() + get_starvation() ) );
mvwprintz( w_speed, line, 1, c_red, _( "Inanition -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
if( has_trait( trait_id( "SUNLIGHT_DEPENDENT" ) ) && !g->is_in_sunlight( pos() ) ) {
pen = ( g->light_level( posz() ) >= 12 ? 5 : 10 );
mvwprintz( w_speed, line, 1, c_red, _( "Out of Sunlight -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
/* Cache result of calculation, possibly used multiple times later. */
const auto player_local_temp = g->get_temperature( pos() );
if( has_trait( trait_id( "COLDBLOOD4" ) ) && player_local_temp > 65 ) {
pen = ( player_local_temp - 65 ) / 2;
mvwprintz( w_speed, line, 1, c_green, _( "Cold-Blooded +%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
if( ( has_trait( trait_id( "COLDBLOOD" ) ) || has_trait( trait_id( "COLDBLOOD2" ) ) ||
has_trait( trait_id( "COLDBLOOD3" ) ) || has_trait( trait_id( "COLDBLOOD4" ) ) ) &&
player_local_temp < 65 ) {
if( has_trait( trait_id( "COLDBLOOD3" ) ) || has_trait( trait_id( "COLDBLOOD4" ) ) ) {
pen = ( 65 - player_local_temp ) / 2;
} else if( has_trait( trait_id( "COLDBLOOD2" ) ) ) {
pen = ( 65 - player_local_temp ) / 3;
} else {
pen = ( 65 - player_local_temp ) / 5;
}
mvwprintz( w_speed, line, 1, c_red, _( "Cold-Blooded -%s%d%%" ),
( pen < 10 ? " " : "" ), pen );
line++;
}
std::map<std::string, int> speed_effects;
for( auto &elem : *effects ) {
for( auto &_effect_it : elem.second ) {
auto &it = _effect_it.second;
bool reduced = resists_effect( it );
int move_adjust = it.get_mod( "SPEED", reduced );
if( move_adjust != 0 ) {
const std::string dis_text = it.get_speed_name();
speed_effects[dis_text] += move_adjust;
}
}
}
for( auto &speed_effect : speed_effects ) {
nc_color col = ( speed_effect.second > 0 ? c_green : c_red );
mvwprintz( w_speed, line, 1, col, "%s", speed_effect.first );
mvwprintz( w_speed, line, 21, col, ( speed_effect.second > 0 ? "+" : "-" ) );
mvwprintz( w_speed, line, ( abs( speed_effect.second ) >= 10 ? 22 : 23 ), col, "%d%%",
abs( speed_effect.second ) );
line++;
}
int quick_bonus = int( newmoves - ( newmoves / 1.1 ) );
int bio_speed_bonus = quick_bonus;
if( has_trait( trait_id( "QUICK" ) ) && has_bionic( bionic_id( "bio_speed" ) ) ) {
bio_speed_bonus = int( newmoves / 1.1 - ( newmoves / 1.1 / 1.1 ) );
std::swap( quick_bonus, bio_speed_bonus );
}
if( has_trait( trait_id( "QUICK" ) ) ) {
mvwprintz( w_speed, line, 1, c_green, _( "Quick +%s%d%%" ),
( quick_bonus < 10 ? " " : "" ), quick_bonus );
line++;
}
if( has_bionic( bionic_id( "bio_speed" ) ) ) {
mvwprintz( w_speed, line, 1, c_green, _( "Bionic Speed +%s%d%%" ),
( bio_speed_bonus < 10 ? " " : "" ), bio_speed_bonus );
}
int runcost = run_cost( 100 );
nc_color col = ( runcost <= 100 ? c_green : c_red );
mvwprintz( w_speed, 1, ( runcost >= 100 ? 21 : ( runcost < 10 ? 23 : 22 ) ), col,
"%d", runcost );
col = ( newmoves >= 100 ? c_green : c_red );
mvwprintz( w_speed, 2, ( newmoves >= 100 ? 21 : ( newmoves < 10 ? 23 : 22 ) ), col,
"%d", newmoves );
wrefresh( w_speed );
catacurses::refresh();
int curtab = 1;
size_t min, max;
line = 0;
bool done = false;
size_t half_y = 0;
size_t bionics_useful_size_y = bionics_win_size_y - 1;
// Initial printing is DONE. Now we give the player a chance to scroll around
// and "hover" over different items for more info.
do {
werase( w_info );
switch( curtab ) {
case 1: // Stats tab
mvwprintz( w_stats, 0, 0, h_light_gray, header_spaces.c_str() );
center_print( w_stats, 0, h_light_gray, title_STATS );
// Clear bonus/penalty menu.
mvwprintz( w_stats, 6, 0, c_light_gray, "%26s", "" );
mvwprintz( w_stats, 7, 0, c_light_gray, "%26s", "" );
mvwprintz( w_stats, 8, 0, c_light_gray, "%26s", "" );
if( line == 0 ) {
// Display information on player strength in appropriate window
mvwprintz( w_stats, 2, 1, h_light_gray, _( "Strength:" ) );
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta,
_( "Strength affects your melee damage, the amount of weight you can carry, your total HP, "
"your resistance to many diseases, and the effectiveness of actions which require brute force." ) );
mvwprintz( w_info, 3, 1, c_magenta, _( "Base HP:" ) );
mvwprintz( w_info, 3, 22, c_magenta, "%3d", hp_max[1] );
if( get_option<std::string>( "USE_METRIC_WEIGHTS" ) == "kg" ) {
mvwprintz( w_info, 4, 1, c_magenta, _( "Carry weight(kg):" ) );
} else {
mvwprintz( w_info, 4, 1, c_magenta, _( "Carry weight(lbs):" ) );
}
mvwprintz( w_info, 4, 21, c_magenta, "%4.1f", convert_weight( weight_capacity() ) );
mvwprintz( w_info, 5, 1, c_magenta, _( "Melee damage:" ) );
mvwprintz( w_info, 5, 22, c_magenta, "%3.1f", bonus_damage( false ) );
} else if( line == 1 ) {
// Display information on player dexterity in appropriate window
mvwprintz( w_stats, 3, 1, h_light_gray, _( "Dexterity:" ) );
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta,
_( "Dexterity affects your chance to hit in melee combat, helps you steady your "
"gun for ranged combat, and enhances many actions that require finesse." ) );
mvwprintz( w_info, 3, 1, c_magenta, _( "Melee to-hit bonus:" ) );
mvwprintz( w_info, 3, 38, c_magenta, "%+.1lf", get_hit_base() );
mvwprintz( w_info, 4, 1, c_magenta, _( "Ranged penalty:" ) );
mvwprintz( w_info, 4, 38, c_magenta, "%+3d", -( abs( ranged_dex_mod() ) ) );
mvwprintz( w_info, 5, 1, c_magenta, _( "Throwing penalty per target's dodge:" ) );
mvwprintz( w_info, 5, 38, c_magenta, "%+3d", throw_dispersion_per_dodge( false ) );
} else if( line == 2 ) {
// Display information on player intelligence in appropriate window
mvwprintz( w_stats, 4, 1, h_light_gray, _( "Intelligence:" ) );
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta,
_( "Intelligence is less important in most situations, but it is vital for more complex tasks like "
"electronics crafting. It also affects how much skill you can pick up from reading a book." ) );
mvwprintz( w_info, 3, 1, c_magenta, _( "Read times:" ) );
mvwprintz( w_info, 3, 21, c_magenta, "%3d%%", read_speed( false ) );
mvwprintz( w_info, 4, 1, c_magenta, _( "Skill rust:" ) );
mvwprintz( w_info, 4, 22, c_magenta, "%2d%%", rust_rate( false ) );
mvwprintz( w_info, 5, 1, c_magenta, _( "Crafting bonus:" ) );
mvwprintz( w_info, 5, 22, c_magenta, "%2d%%", get_int() );
} else if( line == 3 ) {
// Display information on player perception in appropriate window
mvwprintz( w_stats, 5, 1, h_light_gray, _( "Perception:" ) );
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta,
_( "Perception is the most important stat for ranged combat. It's also used for "
"detecting traps and other things of interest." ) );
mvwprintz( w_info, 4, 1, c_magenta, _( "Trap detection level:" ) );
mvwprintz( w_info, 4, 23, c_magenta, "%2d", get_per() );
if( ranged_per_mod() > 0 ) {
mvwprintz( w_info, 5, 1, c_magenta, _( "Aiming penalty:" ) );
mvwprintz( w_info, 5, 21, c_magenta, "%+4d", -ranged_per_mod() );
}
}
wrefresh( w_stats );
wrefresh( w_info );
action = ctxt.handle_input();
if( action == "DOWN" ) {
line++;
if( line == 4 ) {
line = 0;
}
} else if( action == "UP" ) {
if( line == 0 ) {
line = 3;
} else {
line--;
}
} else if( action == "NEXT_TAB" ) {
mvwprintz( w_stats, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_stats, 0, c_light_gray, title_STATS );
wrefresh( w_stats );
line = 0;
curtab++;
} else if( action == "QUIT" ) {
done = true;
}
mvwprintz( w_stats, 2, 1, c_light_gray, _( "Strength:" ) );
mvwprintz( w_stats, 3, 1, c_light_gray, _( "Dexterity:" ) );
mvwprintz( w_stats, 4, 1, c_light_gray, _( "Intelligence:" ) );
mvwprintz( w_stats, 5, 1, c_light_gray, _( "Perception:" ) );
wrefresh( w_stats );
break;
case 2: { // Encumbrance tab
werase( w_encumb );
center_print( w_encumb, 0, h_light_gray, title_ENCUMB );
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( *this, line, bp_aiOther[line] );
s += get_encumbrance_description( *this, bp, combined_here );
fold_and_print( w_info, 0, 1, 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
}
}
} else if( action == "UP" ) {
if( line > 0 ) {
if( bp_aiOther[line] == line - 1 && // second of a pair
should_combine_bps( *this, 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
}
}
} else if( action == "NEXT_TAB" ) {
mvwprintz( w_encumb, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_encumb, 0, c_light_gray, title_ENCUMB );
wrefresh( w_encumb );
line = 0;
curtab++;
} else if( action == "QUIT" ) {
done = true;
}
break;
}
case 4: // Traits tab
werase( w_traits );
mvwprintz( w_traits, 0, 0, h_light_gray, header_spaces );
center_print( w_traits, 0, h_light_gray, title_TRAITS );
if( line <= ( trait_win_size_y - 1 ) / 2 ) {
min = 0;
max = trait_win_size_y;
if( traitslist.size() < max ) {
max = traitslist.size();
}
} else if( line >= traitslist.size() - ( trait_win_size_y + 1 ) / 2 ) {
min = ( traitslist.size() < trait_win_size_y ? 0 : traitslist.size() - trait_win_size_y );
max = traitslist.size();
} else {
min = line - ( trait_win_size_y - 1 ) / 2;
max = line + trait_win_size_y / 2 + 1;
if( traitslist.size() < max ) {
max = traitslist.size();
}
}
for( size_t i = min; i < max; i++ ) {
const auto &mdata = traitslist[i].obj();
const auto color = mdata.get_display_color();
trim_and_print( w_traits, int( 1 + i - min ), 1, getmaxx( w_traits ) - 1,
i == line ? hilite( color ) : color, mdata.name() );
}
if( line < traitslist.size() ) {
const auto &mdata = traitslist[line].obj();
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta, string_format(
"<color_%s>%s</color>: %s", string_from_color( mdata.get_display_color() ),
mdata.name(), traitslist[line]->desc() ) );
}
wrefresh( w_traits );
wrefresh( w_info );
action = ctxt.handle_input();
if( action == "DOWN" ) {
if( line < traitslist.size() - 1 ) {
line++;
}
break;
} else if( action == "UP" ) {
if( line > 0 ) {
line--;
}
} else if( action == "NEXT_TAB" ) {
mvwprintz( w_traits, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_traits, 0, c_light_gray, title_TRAITS );
for( size_t i = 0; i < traitslist.size() && i < trait_win_size_y; i++ ) {
const auto &mdata = traitslist[i].obj();
mvwprintz( w_traits, int( i + 1 ), 1, c_black, " " );
const auto color = mdata.get_display_color();
trim_and_print( w_traits, int( i + 1 ), 1, getmaxx( w_traits ) - 1,
color, mdata.name() );
}
wrefresh( w_traits );
line = 0;
curtab++;
} else if( action == "QUIT" ) {
done = true;
}
break;
case 5: // Bionics tab
werase( w_bionics );
mvwprintz( w_bionics, 0, 0, h_light_gray, header_spaces );
center_print( w_bionics, 0, h_light_gray, title_BIONICS );
trim_and_print( w_bionics, 1, 1, getmaxx( w_bionics ) - 1, c_white,
string_format( _( "Bionic Power: %1$d" ), max_power_level ) );
if( line <= ( ( bionics_useful_size_y - 1 ) / 2 ) ) {
min = 0;
max = std::min( bionicslist.size(), bionics_useful_size_y );
} else if( line >= ( bionicslist.size() - ( bionics_useful_size_y + 1 ) / 2 ) ) {
min = ( bionicslist.size() < bionics_useful_size_y ? 0 : bionicslist.size() - bionics_useful_size_y
+ 1 );
max = bionicslist.size();
} else {
min = line - ( bionics_useful_size_y - 1 ) / 2;
max = std::min( bionicslist.size(), static_cast<size_t>( 1 + line + bionics_useful_size_y / 2 ) );
}
for( size_t i = min; i < max; i++ ) {
trim_and_print( w_bionics, int( 2 + i - min ), 1, getmaxx( w_bionics ) - 1,
i == line ? hilite( c_white ) : c_white, bionicslist[i].info().name );
}
if( line < bionicslist.size() ) {
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_white,
bionicslist[line].info().description );
}
wrefresh( w_bionics );
wrefresh( w_info );
action = ctxt.handle_input();
if( action == "DOWN" ) {
if( line < bionicslist.size() - 1 ) {
line++;
}
break;
} else if( action == "UP" ) {
if( line > 0 ) {
line--;
}
} else if( action == "NEXT_TAB" ) {
mvwprintz( w_bionics, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_bionics, 0, c_light_gray, title_BIONICS );
trim_and_print( w_bionics, 1, 1, getmaxx( w_bionics ) - 1, c_white,
string_format( _( "Bionic Power: %1$d" ), max_power_level ) );
for( size_t i = 0; i < bionicslist.size() && i < bionics_win_size_y; i++ ) {
mvwprintz( w_bionics, int( i + 2 ), 1, c_black, " " );
trim_and_print( w_bionics, int( i + 2 ), 1, getmaxx( w_bionics ) - 1,
c_white, bionicslist[i].info().name );
}
wrefresh( w_bionics );
line = 0;
curtab++;
} else if( action == "QUIT" ) {
done = true;
}
break;
case 6: // Effects tab
mvwprintz( w_effects, 0, 0, h_light_gray, header_spaces.c_str() );
center_print( w_effects, 0, h_light_gray, title_EFFECTS );
half_y = effect_win_size_y / 2;
if( line <= half_y ) {
min = 0;
max = effect_win_size_y;
if( effect_name.size() < max ) {
max = effect_name.size();
}
} else if( line >= effect_name.size() - half_y ) {
min = ( effect_name.size() < effect_win_size_y ? 0 : effect_name.size() - effect_win_size_y );
max = effect_name.size();
} else {
min = line - half_y;
max = line - half_y + effect_win_size_y;
if( effect_name.size() < max ) {
max = effect_name.size();
}
}
for( size_t i = min; i < max; i++ ) {
trim_and_print( w_effects, int( 1 + i - min ), 0, getmaxx( w_effects ) - 1,
i == line ? h_light_gray : c_light_gray, effect_name[i] );
}
if( line < effect_text.size() ) {
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta, effect_text[line] );
}
wrefresh( w_effects );
wrefresh( w_info );
action = ctxt.handle_input();
if( action == "DOWN" ) {
if( line < effect_name.size() - 1 ) {
line++;
}
break;
} else if( action == "UP" ) {
if( line > 0 ) {
line--;
}
} else if( action == "NEXT_TAB" ) {
mvwprintz( w_effects, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_effects, 0, c_light_gray, title_EFFECTS );
for( size_t i = 0; i < effect_name.size() && i < 7; i++ ) {
mvwprintz( w_effects, int( i + 1 ), 0, c_light_gray, effect_name[i] );
}
wrefresh( w_effects );
line = 0;
curtab = 1;
} else if( action == "QUIT" ) {
done = true;
}
break;
case 3: // Skills tab
mvwprintz( w_skills, 0, 0, h_light_gray, header_spaces.c_str() );
center_print( w_skills, 0, h_light_gray, title_SKILLS );
half_y = skill_win_size_y / 2;
if( line <= half_y ) {
min = 0;
max = skill_win_size_y;
if( skillslist.size() < max ) {
max = skillslist.size();
}
} else if( line >= skillslist.size() - half_y ) {
min = ( skillslist.size() < size_t( skill_win_size_y ) ? 0 : skillslist.size() - skill_win_size_y );
max = skillslist.size();
} else {
min = line - half_y;
max = line - half_y + skill_win_size_y;
if( skillslist.size() < max ) {
max = skillslist.size();
}
}
const Skill *selectedSkill = nullptr;
for( size_t i = min; i < max; i++ ) {
const Skill *aSkill = skillslist[i];
const SkillLevel &level = get_skill_level_object( aSkill->ident() );
const bool can_train = level.can_train();
const bool training = level.isTraining();
const bool rusting = level.isRusting();
const int exercise = level.exercise();
nc_color cstatus;
if( i == line ) {
selectedSkill = aSkill;
if( !can_train ) {
cstatus = rusting ? h_light_red : h_white;
} else if( exercise >= 100 ) {
cstatus = training ? h_pink : h_magenta;
} else if( rusting ) {
cstatus = training ? h_light_red : h_red;
} else {
cstatus = training ? h_light_blue : h_blue;
}
} else {
if( rusting ) {
cstatus = training ? c_light_red : c_red;
} else if( !can_train ) {
cstatus = c_white;
} else {
cstatus = training ? c_light_blue : c_blue;
}
}
mvwprintz( w_skills, int( 1 + i - min ), 1, c_light_gray, " " );
mvwprintz( w_skills, int( 1 + i - min ), 1, cstatus, "%s:", aSkill->name().c_str() );
if( aSkill->ident() == skill_id( "dodge" ) ) {
mvwprintz( w_skills, int( 1 + i - min ), 14, cstatus, "%4.1f/%-2d(%2d%%)",
get_dodge(), level.level(), exercise < 0 ? 0 : exercise );
} else {
mvwprintz( w_skills, int( 1 + i - min ), 19, cstatus, "%-2d(%2d%%)", level.level(),
( exercise < 0 ? 0 : exercise ) );
}
}
draw_scrollbar( w_skills, line, skill_win_size_y, int( skillslist.size() ), 1 );
wrefresh( w_skills );
werase( w_info );
if( line < skillslist.size() ) {
fold_and_print( w_info, 0, 1, FULL_SCREEN_WIDTH - 2, c_magenta, selectedSkill->description() );
}
wrefresh( w_info );
action = ctxt.handle_input();
if( action == "DOWN" ) {
if( size_t( line ) < skillslist.size() - 1 ) {
line++;
}
} else if( action == "UP" ) {
if( line > 0 ) {
line--;
}
} else if( action == "NEXT_TAB" ) {
werase( w_skills );
mvwprintz( w_skills, 0, 0, c_light_gray, header_spaces.c_str() );
center_print( w_skills, 0, c_light_gray, title_SKILLS );
for( size_t i = 0; i < skillslist.size() && i < size_t( skill_win_size_y ); i++ ) {
const Skill *thisSkill = skillslist[i];
const SkillLevel &level = get_skill_level_object( thisSkill->ident() );
bool can_train = level.can_train();
bool isLearning = level.isTraining();
bool rusting = level.isRusting();
nc_color cstatus;
if( rusting ) {
cstatus = isLearning ? c_light_red : c_red;
} else if( !can_train ) {
cstatus = c_white;
} else {
cstatus = isLearning ? c_light_blue : c_blue;
}
mvwprintz( w_skills, i + 1, 1, cstatus, "%s:", thisSkill->name().c_str() );
if( thisSkill->ident() == skill_id( "dodge" ) ) {
mvwprintz( w_skills, i + 1, 14, cstatus, "%4.1f/%-2d(%2d%%)",
get_dodge(), level.level(), level.exercise() < 0 ? 0 : level.exercise() );
} else {
mvwprintz( w_skills, i + 1, 19, cstatus, "%-2d(%2d%%)", level.level(),
( level.exercise() < 0 ? 0 : level.exercise() ) );
}
}
wrefresh( w_skills );
line = 0;
curtab++;
} else if( action == "CONFIRM" ) {
get_skill_level_object( selectedSkill->ident() ).toggleTraining();
} else if( action == "QUIT" ) {
done = true;
}
}
} while( !done );
g->refresh_all();
}
You can’t perform that action at this time.