From 506bcd6838c26f87fd389cabdf1b91a65dd267a1 Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 3 Sep 2019 22:18:04 -0400 Subject: [PATCH 1/5] new menu for armor sorting get_encumbrance_description is now part of player.h --- data/raw/keybindings.json | 66 +- src/armor_layers.cpp | 1221 +++++++++++++++++++------------------ src/player.h | 1 + src/player_display.cpp | 16 +- 4 files changed, 665 insertions(+), 639 deletions(-) diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index f4181f461f507..c5a8c0b9cfcd4 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -419,20 +419,6 @@ "name": "Enable safemode option", "bindings": [ { "input_method": "keyboard", "key": "s" }, { "input_method": "keyboard", "key": "S" } ] }, - { - "type": "keybinding", - "id": "USAGE_HELP", - "category": "SORT_ARMOR", - "name": "Display Usage Help", - "bindings": [ { "input_method": "keyboard", "key": "F1" } ] - }, - { - "type": "keybinding", - "id": "MOVE_ARMOR", - "category": "SORT_ARMOR", - "name": "Select armor for moving", - "bindings": [ { "input_method": "keyboard", "key": "s" }, { "input_method": "keyboard", "key": "RETURN" } ] - }, { "type": "keybinding", "id": "MOVE_PANEL", @@ -449,45 +435,73 @@ }, { "type": "keybinding", - "id": "CHANGE_SIDE", + "id": "USAGE_HELP", "category": "SORT_ARMOR", - "name": "Change side armor is worn on", - "bindings": [ { "input_method": "keyboard", "key": "c" } ] + "name": "Display Usage Help", + "bindings": [ { "input_method": "keyboard", "key": "F1" } ] }, { "type": "keybinding", - "id": "ASSIGN_INVLETS", + "id": "MOVE_ARMOR", "category": "SORT_ARMOR", - "name": "Assign invlets to armor", - "bindings": [ { "input_method": "keyboard", "key": "r" } ] + "name": "Select armor for moving", + "bindings": [ { "input_method": "keyboard", "key": "RETURN" } ] + }, + { + "type": "keybinding", + "id": "CHANGE_SIDE", + "category": "SORT_ARMOR", + "name": "Change side armor is worn on", + "bindings": [ { "input_method": "keyboard", "key": "c" } ] }, { "type": "keybinding", "id": "SORT_ARMOR", "category": "SORT_ARMOR", "name": "Sort armor into natural layer order", - "bindings": [ { "input_method": "keyboard", "key": "S" } ] + "bindings": [ { "input_method": "keyboard", "key": "s" }, { "input_method": "keyboard", "key": "S" } ] }, { "type": "keybinding", "id": "EQUIP_ARMOR", "category": "SORT_ARMOR", "name": "Equip armor from inventory", - "bindings": [ { "input_method": "keyboard", "key": "e" } ] + "bindings": [ { "input_method": "keyboard", "key": "w" }, { "input_method": "keyboard", "key": "W" } ] }, { "type": "keybinding", "id": "EQUIP_ARMOR_HERE", "category": "SORT_ARMOR", "name": "Equip armor from inventory at this position", - "bindings": [ { "input_method": "keyboard", "key": "E" } ] + "bindings": [ { "input_method": "keyboard", "key": "e" }, { "input_method": "keyboard", "key": "E" } ] }, { "type": "keybinding", - "id": "REMOVE_ARMOR", + "id": "TAKE_OFF", "category": "SORT_ARMOR", - "name": "Unequip selected armor", - "bindings": [ { "input_method": "keyboard", "key": "u" } ] + "name": "Take off selected armor", + "bindings": [ { "input_method": "keyboard", "key": "t" }, { "input_method": "keyboard", "key": "T" } ] + }, + { + "type": "keybinding", + "id": "DROP", + "category": "SORT_ARMOR", + "name": "Drop selected armor", + "bindings": [ { "input_method": "keyboard", "key": "d" }, { "input_method": "keyboard", "key": "D" } ] + }, + { + "type": "keybinding", + "id": "PAGE_UP", + "category": "SORT_ARMOR", + "name": "Select previous category", + "bindings": [ { "input_method": "keyboard", "key": "PPAGE" } ] + }, + { + "type": "keybinding", + "id": "PAGE_DOWN", + "category": "SORT_ARMOR", + "name": "Select next category", + "bindings": [ { "input_method": "keyboard", "key": "NPAGE" } ] }, { "type": "keybinding", diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 8b98e7b33f2fe..b6f7adc8116df 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -22,12 +22,6 @@ namespace { -std::string clothing_layer( const item &worn_item ); -std::vector clothing_properties( - const item &worn_item, int width, const Character & ); -std::vector clothing_protection( const item &worn_item, int width ); -std::vector clothing_flags_description( const item &worn_item ); - struct item_penalties { std::vector body_parts_with_stacking_penalty; std::vector body_parts_with_out_of_order_penalty; @@ -112,380 +106,416 @@ item_penalties get_item_penalties( std::list::const_iterator worn_item_it, std::move( lists_of_bad_items_within[0] ) }; } -std::string body_part_names( const std::vector &parts ) -{ - if( parts.empty() ) { - debugmsg( "Asked for names of empty list" ); - return {}; - } - - std::vector names; - names.reserve( parts.size() ); - for( size_t i = 0; i < parts.size(); ++i ) { - const body_part part = parts[i]; - if( i + 1 < parts.size() && parts[i + 1] == static_cast( bp_aiOther[part] ) ) { - // Can combine two body parts (e.g. arms) - names.push_back( body_part_name_accusative( part, 2 ) ); - ++i; - } else { - names.push_back( body_part_name_accusative( part ) ); - } - } - - return enumerate_as_string( names ); -} - -void draw_mid_pane( const catacurses::window &w_sort_middle, - std::list::const_iterator const worn_item_it, - const Character &c, int tabindex ) -{ - const int win_width = getmaxx( w_sort_middle ); - const size_t win_height = static_cast( getmaxy( w_sort_middle ) ); - // NOLINTNEXTLINE(cata-use-named-point-constants) - size_t i = fold_and_print( w_sort_middle, point( 1, 0 ), win_width - 1, c_white, - worn_item_it->type_name( 1 ) ) - 1; - std::vector props = clothing_properties( *worn_item_it, win_width - 3, c ); - nc_color color = c_light_gray; - for( std::string &iter : props ) { - print_colored_text( w_sort_middle, point( 2, ++i ), color, c_light_gray, iter ); - } - - std::vector prot = clothing_protection( *worn_item_it, win_width - 3 ); - if( i + prot.size() < win_height ) { - for( std::string &iter : prot ) { - print_colored_text( w_sort_middle, point( 2, ++i ), color, c_light_gray, iter ); - } - } else { - return; - } - - i++; - std::vector layer_desc = foldstring( clothing_layer( *worn_item_it ), win_width ); - if( i + layer_desc.size() < win_height && !clothing_layer( *worn_item_it ).empty() ) { - for( std::string &iter : layer_desc ) { - mvwprintz( w_sort_middle, point( 0, ++i ), c_light_blue, iter ); - } - } - - i++; - std::vector desc = clothing_flags_description( *worn_item_it ); - if( !desc.empty() ) { - for( size_t j = 0; j < desc.size() && i + j < win_height; ++j ) { - i += fold_and_print( w_sort_middle, point( 0, i ), win_width, c_light_blue, desc[j] ); - } - } - - const item_penalties penalties = get_item_penalties( worn_item_it, c, tabindex ); - - if( !penalties.body_parts_with_stacking_penalty.empty() ) { - std::string layer_description = [&]() { - switch( worn_item_it->get_layer() ) { - case PERSONAL_LAYER: - return _( "in your personal aura" ); - case UNDERWEAR_LAYER: - return _( "close to your skin" ); - case REGULAR_LAYER: - return _( "of normal clothing" ); - case WAIST_LAYER: - return _( "on your waist" ); - case OUTER_LAYER: - return _( "of outer clothing" ); - case BELTED_LAYER: - return _( "strapped to you" ); - case AURA_LAYER: - return _( "an aura around you" ); - default: - debugmsg( "Unexpected layer" ); - return ""; - } - } - (); - std::string body_parts = - body_part_names( penalties.body_parts_with_stacking_penalty ); - std::string message = - string_format( - ngettext( "Wearing multiple items %s on your " - "%s is adding encumbrance there.", - "Wearing multiple items %s on your " - "%s is adding encumbrance there.", - penalties.body_parts_with_stacking_penalty.size() ), - layer_description, body_parts - ); - i += fold_and_print( w_sort_middle, point( 0, i ), win_width, c_light_gray, message ); - } - - if( !penalties.body_parts_with_out_of_order_penalty.empty() ) { - std::string body_parts = - body_part_names( penalties.body_parts_with_out_of_order_penalty ); - std::string message; - - if( penalties.bad_items_within.empty() ) { - message = string_format( - ngettext( "Wearing this outside items it would normally be beneath " - "is adding encumbrance to your %s.", - "Wearing this outside items it would normally be beneath " - "is adding encumbrance to your %s.", - penalties.body_parts_with_out_of_order_penalty.size() ), - body_parts - ); - } else { - std::string bad_item_name = *penalties.bad_items_within.begin(); - message = string_format( - ngettext( "Wearing this outside your %s " - "is adding encumbrance to your %s.", - "Wearing this outside your %s " - "is adding encumbrance to your %s.", - penalties.body_parts_with_out_of_order_penalty.size() ), - bad_item_name, body_parts - ); - } - // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - i += fold_and_print( w_sort_middle, point( 0, i ), win_width, c_light_gray, message ); - } -} std::string clothing_layer( const item &worn_item ) { std::string layer; if( worn_item.has_flag( "PERSONAL" ) ) { - layer = _( "This is in your personal aura." ); + layer = _( "(p.aur)" ); } else if( worn_item.has_flag( "SKINTIGHT" ) ) { - layer = _( "This is worn next to the skin." ); + layer = _( "(skin)" ); } else if( worn_item.has_flag( "WAIST" ) ) { - layer = _( "This is worn on or around your waist." ); + layer = _( "(waist)" ); } else if( worn_item.has_flag( "OUTER" ) ) { - layer = _( "This is worn over your other clothes." ); + layer = _( "(outer)" ); } else if( worn_item.has_flag( "BELTED" ) ) { - layer = _( "This is strapped onto you." ); + layer = _( "(strap)" ); } else if( worn_item.has_flag( "AURA" ) ) { - layer = _( "This is an aura around you." ); + layer = _( "(aura)" ); } return layer; } - -std::vector clothing_properties( - const item &worn_item, const int width, const Character &c ) -{ - std::vector props; - props.reserve( 5 ); - - const std::string space = " "; - props.push_back( string_format( "[%s]", _( "Properties" ) ) ); - props.push_back( name_and_value( space + _( "Coverage:" ), - string_format( "%3d", worn_item.get_coverage() ), width ) ); - props.push_back( name_and_value( space + _( "Encumbrance:" ), - string_format( "%3d", worn_item.get_encumber( c ) ), - width ) ); - props.push_back( name_and_value( space + _( "Warmth:" ), - string_format( "%3d", worn_item.get_warmth() ), width ) ); - props.push_back( name_and_value( space + string_format( _( "Storage (%s):" ), volume_units_abbr() ), - format_volume( worn_item.get_storage() ), width ) ); - return props; -} - -std::vector clothing_protection( const item &worn_item, const int width ) +enum sort_armor_columns : int { + column_cond, + column_encumb, + column_storage, + column_warmth, + column_divider, + column_coverage, + column_bash, + column_cut, + column_acid, + column_fire, + column_env, + column_num_entries +}; +struct sort_armor_col_data { + int width; + std::string name; + std::string description; +}; +sort_armor_col_data get_col_data( int col_idx ) { - std::vector prot; - prot.reserve( 4 ); - - const std::string space = " "; - prot.push_back( string_format( "[%s]", _( "Protection" ) ) ); - prot.push_back( name_and_value( space + _( "Bash:" ), - string_format( "%3d", static_cast( worn_item.bash_resist() ) ), width ) ); - prot.push_back( name_and_value( space + _( "Cut:" ), - string_format( "%3d", static_cast( worn_item.cut_resist() ) ), width ) ); - prot.push_back( name_and_value( space + _( "Environmental:" ), - string_format( "%3d", static_cast( worn_item.get_env_resist() ) ), width ) ); - return prot; + static std::array< sort_armor_col_data, column_num_entries> col_data = { { + { + 3, _( "cond" ), _( + string_format( "clothing conditions:\n\tx - poor fit\n\tf - filthy", + string_from_color( c_red ), string_from_color( c_cyan ) ) + ) + }, + {8, _( "encum " ), ( "encumberance." )}, + {7, _( "strge" ), _( "storage." )}, + {4, _( "wrm" ), _( "warmth." )}, + {3, "", ""}, + {3, _( "cvr" ), _( "coverage" )}, + {4, _( "bsh" ), _( "bash protection." )}, + {4, _( "cut" ), _( "cut protection." )}, + {4, _( "acd" ), _( "acid protection." )}, + {4, _( "fre" ), _( "fire protection." )}, + {4, _( "env" ), _( "environmental protection." )}, + } + }; + return col_data[col_idx]; } - -std::vector clothing_flags_description( const item &worn_item ) +class layering_item_info { - std::vector description_stack; - - if( worn_item.has_flag( "FIT" ) ) { - description_stack.push_back( _( "It fits you well." ) ); - } else if( worn_item.has_flag( "VARSIZE" ) ) { - description_stack.push_back( _( "It could be refitted." ) ); - } - - if( worn_item.has_flag( "HOOD" ) ) { - description_stack.push_back( _( "It has a hood." ) ); - } - if( worn_item.has_flag( "POCKETS" ) ) { - description_stack.push_back( _( "It has pockets." ) ); - } - if( worn_item.has_flag( "WATERPROOF" ) ) { - description_stack.push_back( _( "It is waterproof." ) ); - } - if( worn_item.has_flag( "WATER_FRIENDLY" ) ) { - description_stack.push_back( _( "It is water friendly." ) ); - } - if( worn_item.has_flag( "FANCY" ) ) { - description_stack.push_back( _( "It looks fancy." ) ); - } - if( worn_item.has_flag( "SUPER_FANCY" ) ) { - description_stack.push_back( _( "It looks really fancy." ) ); - } - if( worn_item.has_flag( "FLOTATION" ) ) { - description_stack.push_back( _( "You will not drown today." ) ); - } - if( worn_item.has_flag( "OVERSIZE" ) ) { - description_stack.push_back( _( "It is very bulky." ) ); - } - if( worn_item.has_flag( "SWIM_GOGGLES" ) ) { - description_stack.push_back( _( "It helps you to see clearly underwater." ) ); - } - if( worn_item.has_flag( "SEMITANGIBLE" ) ) { - description_stack.push_back( _( "It can occupy the same space as other things." ) ); - } - - return description_stack; -} + protected: + item_penalties penalties; + int encumber; + std::string name; + std::list::iterator itm_iter; + + public: + layering_item_info( std::list::iterator itm_i, player &p, body_part bp ) { + penalties = get_item_penalties( itm_i, p, bp ); + encumber = itm_i->get_encumber( p ); + name = itm_i->tname(); + itm_iter = itm_i; + } -} //namespace + std::list::iterator get_iter() { + return itm_iter; + } -struct layering_item_info { - item_penalties penalties; - int encumber; - std::string name; + bool operator ==( const layering_item_info &o ) const { + return itm_iter == o.itm_iter; + } - // Operator overload required to leverage vector equality operator. - bool operator ==( const layering_item_info &o ) const { - // This is used to merge e.g. both arms into one entry when their items - // are equivalent. For that purpose we don't care about the exact - // penalities because they will list different body parts; we just - // check that the badness is the same (which is all that matters for - // rendering the right-hand list). - return this->penalties.badness() == o.penalties.badness() && - this->encumber == o.encumber && - this->name == o.name; - } + int get_col_value( sort_armor_columns col ) { + item itm = *itm_iter; + int ret_val; + switch( col ) { + case column_warmth: + ret_val = itm.get_warmth(); + break; + case column_coverage: + ret_val = itm.get_coverage(); + break; + case column_bash: + ret_val = itm.bash_resist(); + break; + case column_cut: + ret_val = itm.cut_resist(); + break; + case column_acid: + ret_val = itm.acid_resist(); + break; + case column_fire: + ret_val = itm.fire_resist(); + break; + case column_env: + ret_val = itm.get_env_resist(); + break; + default: + ret_val = 0; + break; + } + return ret_val; + } + void print_columnns( bool is_selected, const catacurses::window &w, int right_bound, + int cur_print_y ) { + item itm = *itm_iter; + nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; + for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { + int col_w = get_col_data( col_idx ).width; + right_bound -= col_w; + switch( col_idx ) { + case column_cond: { + nc_color print_col2 = is_selected ? hilite( c_red ) : c_red; + if( is_selected ) { //fill whole line with color + mvwprintz( w, point( right_bound, cur_print_y ), print_col2, "%*s", col_w, "" ); + } + int pos_x = right_bound + col_w - 1; + char cond; + + print_col2 = is_selected ? hilite( c_cyan ) : c_cyan; + cond = ( itm.is_filthy() ? 'f' : ' ' ); + mvwprintz( w, point( pos_x, cur_print_y ), print_col2, "%c", cond ); + pos_x--; + + print_col2 = is_selected ? hilite( c_red ) : c_red; + cond = ( itm.has_flag( "FIT" ) || !itm.has_flag( "VARSIZE" ) ) ? ' ' : 'x'; + mvwprintz( w, point( pos_x, cur_print_y ), print_col2, "%c", cond ); + pos_x--; + } + break; + case column_encumb: { + nc_color print_col2 = penalties.color_for_stacking_badness(); + if( is_selected ) { + print_col2 = hilite( print_col2 ); + } + std::string s = string_format( "%d%c", encumber, penalties.badness() > 0 ? '+' : ' ' ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col2, "%*s", col_w, s ); + } + break; + case column_storage: { + units::volume storage = itm.get_storage(); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, + ( storage > 0_ml ? format_volume( itm.get_storage() ) : "" ) ); + } + break; + case column_divider: { + if( is_selected ) { //fill whole line with color + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, "" ); + } + nc_color print_col2 = is_selected ? hilite( c_light_gray ) : c_light_gray; + mvwputch( w, point( right_bound + col_w - 1, cur_print_y ), print_col2, LINE_XOXO ); + } + break; + default: { + int val = get_col_value( static_cast( col_idx ) ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, val ); + } + break; + } + } + nc_color name_col = penalties.color_for_stacking_badness(); + if( is_selected ) { + name_col = hilite( name_col ); + } + mvwprintz( w, point( 2, cur_print_y ), c_dark_gray, clothing_layer( itm ) ); + if( is_selected ) { //fill whole line with color + mvwprintz( w, point( 9, cur_print_y ), name_col, "%*s", right_bound - 9, "" ); + } + trim_and_print( w, point( 9, cur_print_y ), right_bound - 9, name_col, name ); + } + void set_aggregate_data( std::array &agg ) { + for( size_t i = 0; i < column_num_entries; i++ ) { + if( i == column_warmth ) { + agg[i] += get_col_value( static_cast( i ) ); + } else { + agg[i] += get_col_value( static_cast( i ) ) * get_col_value( column_coverage ); + } + } + } }; -static std::vector items_cover_bp( const Character &c, int bp ) +class layering_group_info { - std::vector s; - for( auto elem_it = c.worn.begin(); elem_it != c.worn.end(); ++elem_it ) { - if( elem_it->covers( static_cast( bp ) ) ) { - s.push_back( { get_item_penalties( elem_it, c, bp ), - elem_it->get_encumber( c ), - elem_it->tname() - } ); + private: + std::string name; + std::array header_data; + std::list items; + body_part bp; + player *p; + int start_line; + + int divider_posn; + + //printing constatnts + int max_x; + int max_y; + //printing vars + int list_min_line; + int list_selected_line; + bool is_moving; + + public: + bool is_skipped = false; + + layering_group_info( body_part bp, player *p ) { + this->bp = bp; + this->p = p; + } + void add_item( std::list::iterator itm_i ) { + items.push_back( layering_item_info( itm_i, *p, bp ) ); + } + // same for the duration of the menu + void set_print_constats( int right_bound, int max_y ) { + this->max_x = right_bound; + this->max_y = max_y; + } + // depend on user input + void set_print_vars( int list_selected_line, bool is_moving, int list_min_line ) { + this->list_selected_line = list_selected_line; + this->is_moving = is_moving; + this->list_min_line = list_min_line; } - } - return s; -} -static void draw_grid( const catacurses::window &w, int left_pane_w, int mid_pane_w ) -{ - const int win_w = getmaxx( w ); - const int win_h = getmaxy( w ); - - draw_border( w ); - mvwhline( w, point( 1, 2 ), 0, win_w - 2 ); - mvwvline( w, point( left_pane_w + 1, 3 ), 0, win_h - 4 ); - mvwvline( w, point( left_pane_w + mid_pane_w + 2, 3 ), 0, win_h - 4 ); - - // intersections - mvwputch( w, point( 0, 2 ), BORDER_COLOR, LINE_XXXO ); - mvwputch( w, point( win_w - 1, 2 ), BORDER_COLOR, LINE_XOXX ); - mvwputch( w, point( left_pane_w + 1, 2 ), BORDER_COLOR, LINE_OXXX ); - mvwputch( w, point( left_pane_w + 1, win_h - 1 ), BORDER_COLOR, LINE_XXOX ); - mvwputch( w, point( left_pane_w + mid_pane_w + 2, 2 ), BORDER_COLOR, LINE_OXXX ); - mvwputch( w, point( left_pane_w + mid_pane_w + 2, win_h - 1 ), BORDER_COLOR, LINE_XXOX ); - - wrefresh( w ); -} + void set_combined( bool combined ) { + this->name = body_part_name_as_heading( all_body_parts[bp], combined ? 2 : 1 ); + is_skipped = false; + } -void player::sort_armor() -{ - /* Define required height of the right pane: - * + 3 - horizontal lines; - * + 1 - caption line; - * + 2 - innermost/outermost string lines; - * + num_bp - sub-categories (torso, head, eyes, etc.); - * + 1 - gap; - * number of lines required for displaying all items is calculated dynamically, - * because some items can have multiple entries (i.e. cover a few parts of body). - */ - - int req_right_h = 3 + 1 + 2 + num_bp + 1; - for( const body_part cover : all_body_parts ) { - for( const item &elem : worn ) { - if( elem.covers( cover ) ) { - req_right_h++; + void insert_item( std::list &worn, int from_line, int to_line ) { + int from_idx = from_line - min_selectable(); + int to_idx = to_line - min_selectable(); + + auto from_it = items.begin(); + std::advance( from_it, from_idx ); + + auto to_it = items.begin(); + std::advance( to_it, to_idx ); + + std::list::iterator to = ( *to_it ).get_iter(); + if( to_idx > from_idx ) { + ++to; } + worn.splice( to, worn, ( *from_it ).get_iter() ); } - } - /* Define required height of the mid pane: - * + 3 - horizontal lines; - * + 1 - caption line; - * + 8 - general properties - * + 13 - ASSUMPTION: max possible number of flags @ item - * + num_bp+1 - warmth & enc block - */ - const int req_mid_h = 3 + 1 + 8 + 13 + num_bp + 1; - - const int win_h = std::min( TERMY, std::max( FULL_SCREEN_HEIGHT, - std::max( req_right_h, req_mid_h ) ) ); - const int win_w = FULL_SCREEN_WIDTH + ( TERMX - FULL_SCREEN_WIDTH ) * 3 / 4; - const int win_x = TERMX / 2 - win_w / 2; - const int win_y = TERMY / 2 - win_h / 2; + layering_item_info get_cur_item_info() { + int cur_itm_idx = list_selected_line - min_selectable(); + auto itm_iter = items.begin(); + std::advance( itm_iter, cur_itm_idx ); + return *itm_iter; + } - int cont_h = win_h - 4; - int left_w = ( win_w - 4 ) / 3; - int right_w = left_w; - int middle_w = ( win_w - 4 ) - left_w - right_w; + size_t num_items() { + return items.size(); + } + size_t height() { + return num_items() + 2; + } + void clear() { + items.clear(); + } + int min_selectable() { + return start_line + 1; + } + int max_selectable() { + return min_selectable() + num_items() - 1; + } + body_part get_bp() { + return bp; + } + bool operator ==( const layering_group_info &o ) const { + return items == o.items; + } - int tabindex = num_bp; - const int tabcount = num_bp + 1; + void print_header( bool is_selected, const catacurses::window &w, int cur_print_y ) { + int right_bound = max_x; + nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; + for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { + int col_w = get_col_data( col_idx ).width; + right_bound -= col_w; + switch( col_idx ) { + //case column_fits: + //case column_filthy: + case column_cond: + case column_storage: + case column_coverage: + //skip, but keep highlight + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, "" ); + break; + case column_encumb: { + int size_hack = 0; + right_bound -= size_hack; + + const auto enc_data = p->get_encumbrance(); + const encumbrance_data &e = enc_data[bp]; + nc_color print_col2 = encumb_color( e.encumbrance ); + if( is_selected ) { + print_col2 = hilite( print_col2 ); + } + //mvwprintz(w, point(right_bound, cur_print_y), print_col2, "%3d+%-3d", col_w, 15, 0); + std::string s = string_format( "%d+%d ", e.armor_encumbrance, e.layer_penalty ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col2, "%*s", col_w + size_hack, s ); + break; + } + case column_divider: { + if( is_selected ) { //fill whole line with color + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, "" ); + } + nc_color print_col2 = is_selected ? hilite( c_light_gray ) : c_light_gray; + divider_posn = right_bound + col_w - 1; + mvwputch( w, point( divider_posn, cur_print_y ), print_col2, LINE_XOXO ); + } + break; + case column_warmth: + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, header_data[col_idx] ); + break; + default: { + double val = static_cast( header_data[col_idx] ) / 100; + int val_i = static_cast( std::round( val ) ); + //std::string s = string_format("~%d", val_i); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, val_i ); + } + break; + } + } + nc_color name_col = is_selected ? hilite( c_white ) : c_white; + if( is_selected ) { //fill whole line with color + mvwprintz( w, point( 2, cur_print_y ), name_col, "%*s", right_bound - 2, "" ); + } + mvwprintz( w, point( 2, cur_print_y ), name_col, "%s:", name ); + } + void print_data( int &cur_print_line, int &print_y, const catacurses::window &w ) { + int header_y = print_y; + header_data.fill( 0 ); + bool is_selected = false; + bool can_print_header = false; + + start_line = cur_print_line; + if( cur_print_line >= list_min_line && print_y < max_y ) { + can_print_header = true; + print_y++; + } + cur_print_line++; - int leftListIndex = 0; - int leftListOffset = 0; - int selected = -1; + for( layering_item_info &elem : items ) { + nc_color print_col = c_light_gray; + if( list_selected_line == cur_print_line ) { + if( is_moving ) { + mvwprintz( w, point( 1, print_y ), c_yellow, ">" ); + } + print_col = hilite( c_white ); + is_selected = true; + } + if( cur_print_line >= list_min_line && print_y < max_y ) { + elem.print_columnns( ( list_selected_line == cur_print_line ), w, max_x, print_y ); + print_y++; + } + cur_print_line++; + + elem.set_aggregate_data( header_data ); + } - int rightListOffset = 0; + if( can_print_header ) { //need to print header after we aggreate data + print_header( is_selected, w, header_y ); + } - std::vector::iterator> tmp_worn; - std::array armor_cat = {{ - _( "Torso" ), _( "Head" ), _( "Eyes" ), _( "Mouth" ), _( "L. Arm" ), _( "R. Arm" ), - _( "L. Hand" ), _( "R. Hand" ), _( "L. Leg" ), _( "R. Leg" ), _( "L. Foot" ), - _( "R. Foot" ), _( "All" ) + if( cur_print_line >= list_min_line && print_y < max_y ) { + mvwhline( w, point( 1, print_y ), 0, max_x - 1 ); + mvwputch( w, point( divider_posn, print_y ), c_light_gray, LINE_XXXX ); + print_y++; + } + cur_print_line++; } - }; +}; - // Layout window - catacurses::window w_sort_armor = catacurses::newwin( win_h, win_w, point( win_x, win_y ) ); - draw_grid( w_sort_armor, left_w, middle_w ); - // Subwindows (between lines) - catacurses::window w_sort_cat = catacurses::newwin( 1, win_w - 4, point( win_x + 2, win_y + 1 ) ); - catacurses::window w_sort_left = catacurses::newwin( cont_h, left_w, point( win_x + 1, - win_y + 3 ) ); - catacurses::window w_sort_middle = catacurses::newwin( cont_h - num_bp - 1, middle_w, - point( win_x + left_w + 2, win_y + 3 ) ); - catacurses::window w_sort_right = catacurses::newwin( cont_h, right_w, - point( win_x + left_w + middle_w + 3, win_y + 3 ) ); - catacurses::window w_encumb = catacurses::newwin( num_bp + 1, middle_w, - point( win_x + left_w + 2, win_y + 3 + cont_h - num_bp - 1 ) ); +} //namespace +void player::sort_armor() +{ input_context ctxt( "SORT_ARMOR" ); - ctxt.register_cardinal(); - ctxt.register_action( "QUIT" ); - ctxt.register_action( "PREV_TAB" ); - ctxt.register_action( "NEXT_TAB" ); + ctxt.register_updown(); + ctxt.register_action( "PAGE_UP" ); + ctxt.register_action( "PAGE_DOWN" ); + ctxt.register_action( "MOVE_ARMOR" ); ctxt.register_action( "CHANGE_SIDE" ); - ctxt.register_action( "ASSIGN_INVLETS" ); ctxt.register_action( "SORT_ARMOR" ); ctxt.register_action( "EQUIP_ARMOR" ); ctxt.register_action( "EQUIP_ARMOR_HERE" ); - ctxt.register_action( "REMOVE_ARMOR" ); + ctxt.register_action( "TAKE_OFF" ); + ctxt.register_action( "DROP" ); + ctxt.register_action( "USAGE_HELP" ); ctxt.register_action( "HELP_KEYBINDINGS" ); + ctxt.register_action( "QUIT" ); auto do_return_entry = []() { g->u.assign_activity( activity_id( "ACT_ARMOR_LAYERS" ), 0 ); @@ -493,6 +523,64 @@ void player::sort_armor() g->u.activity.moves_left = INT_MAX; }; + const int name_start_idx = 11; + int max_name_w = 0; + + const int header_h = 3; + const int footer_h = 6; + + //find required height and width + int req_data_h = 0; + for( int bp_idx = 0; bp_idx < num_bp; bp_idx++ ) { + req_data_h += 2; // title + divider line + + bool same = true; + bool check_same = ( bp_idx > 3 && bp_idx % 2 == 0 ); + for( const item &elem : worn ) { + bool covers = elem.covers( static_cast( bp_idx ) ); + if( covers ) { + req_data_h++; + } + if( check_same && + ( covers != elem.covers( static_cast( bp_idx + 1 ) ) ) ) { + same = false; + } + + //something wrong with using tname, results are too long + int elem_w = utf8_width( elem.nname( elem.typeId() ) ); + if( elem_w > max_name_w ) { + max_name_w = elem_w; + } + } + if( check_same && same ) { + bp_idx++; + } + } + int columns_w = 0; + for( size_t i = 0; i < column_num_entries; i++ ) { + columns_w += get_col_data( i ).width; + } + + const int win_h = std::min( TERMY, header_h + req_data_h + footer_h ); + const int win_w = std::min( name_start_idx + max_name_w + columns_w + 1, TERMX ); + const int win_x = TERMX / 2 - win_w / 2; + const int win_y = TERMY / 2 - win_h / 2; + catacurses::window w_sort_armor = catacurses::newwin( win_h, win_w, point( win_x, win_y ) ); + + int data_max_y = win_h - footer_h; + + std::vector items_by_category; + items_by_category.reserve( num_bp ); + for( size_t bp = 0; bp < num_bp; bp++ ) { + items_by_category.push_back( layering_group_info( static_cast( bp ), this ) ); + items_by_category[bp].set_print_constats( win_w - 1, data_max_y ); + } + + layering_group_info *cur_grp = &items_by_category[0]; + int min_print_line = 0; + int selected_line = 1; + bool is_moving = false; + bool exit = false; while( !exit ) { if( is_player() ) { @@ -512,222 +600,165 @@ void player::sort_armor() return; } } - werase( w_sort_cat ); - werase( w_sort_left ); - werase( w_sort_middle ); - werase( w_sort_right ); - werase( w_encumb ); - - // top bar - wprintz( w_sort_cat, c_white, _( "Sort Armor" ) ); - wprintz( w_sort_cat, c_yellow, " << %s >>", armor_cat[tabindex] ); - right_print( w_sort_cat, 0, 0, c_white, string_format( - _( "Press %s for help. Press %s to change keybindings." ), - ctxt.get_desc( "USAGE_HELP" ), - ctxt.get_desc( "HELP_KEYBINDINGS" ) ) ); - - // Create ptr list of items to display - tmp_worn.clear(); - if( tabindex == num_bp ) { // All - for( auto it = worn.begin(); it != worn.end(); ++it ) { - tmp_worn.push_back( it ); - } - } else { // bp_* - body_part bp = static_cast( tabindex ); - for( auto it = worn.begin(); it != worn.end(); ++it ) { - if( it->covers( bp ) ) { - tmp_worn.push_back( it ); + + // set up data + for( size_t bp = 0; bp < num_bp; bp++ ) { + items_by_category[bp].clear(); + } + for( auto elem_it = worn.begin(); elem_it != worn.end(); ++elem_it ) { + for( size_t bp = 0; bp < num_bp; bp++ ) { + if( !elem_it->covers( static_cast( bp ) ) ) { + continue; } + items_by_category[bp].add_item( elem_it ); } } - int leftListSize = ( static_cast( tmp_worn.size() ) < cont_h - 2 ) ? static_cast - ( tmp_worn.size() ) : cont_h - 2; - // Ensure leftListIndex is in bounds - int new_index_upper_bound = std::max( 0, static_cast( tmp_worn.size() ) - 1 ); - leftListIndex = std::min( leftListIndex, new_index_upper_bound ); + //window border + werase( w_sort_armor ); + draw_border( w_sort_armor ); + mvwprintz( w_sort_armor, point( 1, 0 ), c_white, _( "Sort Armor" ) ); + int print_x = win_w; + std::string msg = string_format( _( "<[%s] Columns Info>" ), ctxt.get_desc( "USAGE_HELP" ) ); + print_x -= utf8_width( msg ) + 1; + mvwprintz( w_sort_armor, point( print_x, 0 ), c_white, msg ); + + //column names + int print_y = 1; + int divider_posn = 0; + for( size_t col_idx = column_num_entries, right_bound = win_w - 1; col_idx-- > 0; ) { + sort_armor_col_data col_data = get_col_data( col_idx ); + int col_w = col_data.width; + right_bound -= col_w; + if( col_idx == column_divider ) { + divider_posn = right_bound + col_w - 1; + mvwputch( w_sort_armor, point( divider_posn, print_y ), c_light_gray, LINE_XOXO ); + } else { + mvwprintz( w_sort_armor, point( right_bound, print_y ), c_light_gray, "%*s", col_w, col_data.name ); + } + } + print_y += 1; - // Left header - mvwprintz( w_sort_left, point_zero, c_light_gray, _( "(Innermost)" ) ); - right_print( w_sort_left, 0, 0, c_light_gray, string_format( _( "Storage (%s)" ), - volume_units_abbr() ) ); - // Left list - for( int drawindex = 0; drawindex < leftListSize; drawindex++ ) { - int itemindex = leftListOffset + drawindex; + // data top + mvwhline( w_sort_armor, point( 1, print_y ), 0, win_w - 2 ); //middle + mvwputch( w_sort_armor, point( 0, print_y ), c_light_gray, LINE_XXXO ); //left + mvwputch( w_sort_armor, point( win_w - 1, print_y ), c_light_gray, LINE_XOXX ); //right + mvwputch( w_sort_armor, point( divider_posn, print_y ), c_light_gray, LINE_XXXX ); //divider + mvwprintz( w_sort_armor, point( 1, print_y ), c_light_gray, _( "(Innermost)" ) ); + print_y += 1; - if( itemindex == leftListIndex ) { - mvwprintz( w_sort_left, point( 0, drawindex + 1 ), c_yellow, ">>" ); - } + // data + int cur_print_line = 0; + const int data_start_y = print_y; - std::string worn_armor_name = tmp_worn[itemindex]->tname(); - item_penalties const penalties = - get_item_penalties( tmp_worn[itemindex], *this, tabindex ); + for( int bp_idx = 0; bp_idx < num_bp; bp_idx++ ) { + layering_group_info *l = &items_by_category[bp_idx]; - const int offset_x = ( itemindex == selected ) ? 3 : 2; - trim_and_print( w_sort_left, point( offset_x, drawindex + 1 ), left_w - offset_x - 3, - penalties.color_for_stacking_badness(), worn_armor_name ); - right_print( w_sort_left, drawindex + 1, 0, c_light_gray, - format_volume( tmp_worn[itemindex]->get_storage() ) ); + bool combined = false; + if( bp_idx > 3 && bp_idx % 2 == 0 && *l == items_by_category[bp_idx + 1] ) { + combined = true; + bp_idx++; + items_by_category[bp_idx].is_skipped = true; + } + l->set_combined( combined ); + l->set_print_vars( selected_line, is_moving, min_print_line ); + l->print_data( cur_print_line, print_y, w_sort_armor ); } - // Left footer - mvwprintz( w_sort_left, point( 0, cont_h - 1 ), c_light_gray, _( "(Outermost)" ) ); - if( leftListSize > static_cast( tmp_worn.size() ) ) { - // TODO: replace it by right_print() - mvwprintz( w_sort_left, point( left_w - utf8_width( _( "" ) ), cont_h - 1 ), - c_light_blue, _( "" ) ); - } - if( leftListSize == 0 ) { + // data bottom + const int footer_y = std::min( print_y - 1, data_max_y ); + mvwhline( w_sort_armor, point( 1, footer_y ), 0, win_w - 2 ); //middle + mvwputch( w_sort_armor, point( 0, footer_y ), c_light_gray, LINE_XXXO ); //left + mvwputch( w_sort_armor, point( win_w - 1, footer_y ), c_light_gray, LINE_XOXX ); //right + mvwputch( w_sort_armor, point( divider_posn, footer_y ), c_light_gray, LINE_XXOX ); //divider + + mvwprintz( w_sort_armor, point( 1, footer_y ), c_light_gray, _( "(Outermost)" ) ); + if( cur_print_line >= footer_y ) { // TODO: replace it by right_print() - mvwprintz( w_sort_left, point( left_w - utf8_width( _( "" ) ), cont_h - 1 ), - c_light_blue, _( "" ) ); + mvwprintz( w_sort_armor, point( 17, footer_y ), c_light_blue, _( "" ) ); } - // Items stats - if( leftListSize > 0 ) { - draw_mid_pane( w_sort_middle, tmp_worn[leftListIndex], *this, tabindex ); - } else { - // NOLINTNEXTLINE(cata-use-named-point-constants) - fold_and_print( w_sort_middle, point( 1, 0 ), middle_w - 1, c_white, - _( "Nothing to see here!" ) ); - } + // footer + const std::string s = get_encumbrance_description( cur_grp->get_bp(), false ); + fold_and_print( w_sort_armor, point( 1, footer_y + 1 ), win_w - 2, c_magenta, s ); - mvwprintz( w_encumb, point_east, c_white, _( "Encumbrance and Warmth" ) ); - print_encumbrance( w_encumb, -1, ( leftListSize > 0 ) ? &*tmp_worn[leftListIndex] : nullptr ); + wrefresh( w_sort_armor ); - // Right header - mvwprintz( w_sort_right, point_zero, c_light_gray, _( "(Innermost)" ) ); - right_print( w_sort_right, 0, 0, c_light_gray, _( "Encumbrance" ) ); + //helper functions - // Right list - int rightListSize = 0; - for( int cover = 0, pos = 1; cover < num_bp; cover++ ) { - bool combined = false; - if( cover > 3 && cover % 2 == 0 && - items_cover_bp( *this, cover ) == items_cover_bp( *this, cover + 1 ) ) { - combined = true; + // Get prev/next valid group - wrap if over boundaries and skip if combined. + std::function get_neighbour_grp = [&]( + layering_group_info * grp, int direction ) { + int cur_idx = grp->get_bp() + direction; + if( cur_idx >= static_cast( items_by_category.size() ) ) { + cur_idx = 0; + } else if( cur_idx < 0 ) { + cur_idx = items_by_category.size() - 1; } - if( rightListSize >= rightListOffset && pos <= cont_h - 2 ) { - mvwprintz( w_sort_right, point( 1, pos ), ( cover == tabindex ? c_yellow : c_white ), - "%s:", body_part_name_as_heading( all_body_parts[cover], combined ? 2 : 1 ) ); - pos++; - } - rightListSize++; - for( layering_item_info &elem : items_cover_bp( *this, cover ) ) { - if( rightListSize >= rightListOffset && pos <= cont_h - 2 ) { - nc_color color = elem.penalties.color_for_stacking_badness(); - trim_and_print( w_sort_right, point( 2, pos ), right_w - 5, color, - elem.name ); - char plus = elem.penalties.badness() > 0 ? '+' : ' '; - mvwprintz( w_sort_right, point( right_w - 4, pos ), c_light_gray, "%3d%c", - elem.encumber, plus ); - pos++; - } - rightListSize++; + + grp = &items_by_category[cur_idx]; + if( grp->is_skipped ) { + return get_neighbour_grp( grp, direction ); + } else { + return grp; } - if( combined ) { - cover++; + }; + //check if need move the list (== change min_print_line) + auto recalculate_top_line = [&]() { + if( selected_line < min_print_line ) { + min_print_line = selected_line; + } else if( selected_line >= min_print_line + ( data_max_y - data_start_y ) ) { + min_print_line = selected_line - ( data_max_y - data_start_y ) + 1; } - } - - // Right footer - mvwprintz( w_sort_right, point( 0, cont_h - 1 ), c_light_gray, _( "(Outermost)" ) ); - if( rightListSize > cont_h - 2 ) { - // TODO: replace it by right_print() - mvwprintz( w_sort_right, point( right_w - utf8_width( _( "" ) ), cont_h - 1 ), c_light_blue, - _( "" ) ); - } - // F5 - wrefresh( w_sort_cat ); - wrefresh( w_sort_left ); - wrefresh( w_sort_middle ); - wrefresh( w_sort_right ); - wrefresh( w_encumb ); + //if we select top of the list and it happens to be top of the group, print the title as well + if( selected_line == min_print_line && selected_line == cur_grp->min_selectable() ) { + min_print_line -= 1; + } + }; const std::string action = ctxt.handle_input(); - if( is_npc() && action == "ASSIGN_INVLETS" ) { - // It doesn't make sense to assign invlets to NPC items - continue; - } - - // Helper function for moving items in the list - auto shift_selected_item = [&]() { - if( selected >= 0 ) { - std::list::iterator to = tmp_worn[leftListIndex]; - if( leftListIndex > selected ) { - ++to; + if( action == "UP" ) { + int prev_line = selected_line--; + if( selected_line < cur_grp->min_selectable() ) { + if( is_moving ) { + selected_line = cur_grp->max_selectable(); + } else { + cur_grp = get_neighbour_grp( cur_grp, -1 ); + selected_line = cur_grp->max_selectable(); } - worn.splice( to, worn, tmp_worn[selected] ); - selected = leftListIndex; - reset_encumbrance(); } - }; - if( action == "UP" && leftListSize > 0 ) { - leftListIndex--; - if( leftListIndex < 0 ) { - leftListIndex = tmp_worn.size() - 1; - } - - // Scrolling logic - leftListOffset = ( leftListIndex < leftListOffset ) ? leftListIndex : leftListOffset; - if( !( ( leftListIndex >= leftListOffset ) && - ( leftListIndex < leftListOffset + leftListSize ) ) ) { - leftListOffset = leftListIndex - leftListSize + 1; - leftListOffset = ( leftListOffset > 0 ) ? leftListOffset : 0; - } - - shift_selected_item(); - } else if( action == "DOWN" && leftListSize > 0 ) { - leftListIndex = ( leftListIndex + 1 ) % tmp_worn.size(); - - // Scrolling logic - if( !( ( leftListIndex >= leftListOffset ) && - ( leftListIndex < leftListOffset + leftListSize ) ) ) { - leftListOffset = leftListIndex - leftListSize + 1; - leftListOffset = ( leftListOffset > 0 ) ? leftListOffset : 0; - } - - shift_selected_item(); - } else if( action == "LEFT" ) { - tabindex--; - if( tabindex < 0 ) { - tabindex = tabcount - 1; - } - leftListIndex = leftListOffset = 0; - selected = -1; - } else if( action == "RIGHT" ) { - tabindex = ( tabindex + 1 ) % tabcount; - leftListIndex = leftListOffset = 0; - selected = -1; - } else if( action == "NEXT_TAB" ) { - rightListOffset++; - if( rightListOffset + cont_h - 2 > rightListSize ) { - rightListOffset = rightListSize - cont_h + 2; - } - } else if( action == "PREV_TAB" ) { - rightListOffset--; - if( rightListOffset < 0 ) { - rightListOffset = 0; - } - } else if( action == "MOVE_ARMOR" ) { - if( selected >= 0 ) { - selected = -1; - } else { - selected = leftListIndex; + recalculate_top_line(); + if( is_moving ) { + cur_grp->insert_item( worn, prev_line, selected_line ); + reset_encumbrance(); } - } else if( action == "CHANGE_SIDE" ) { - if( leftListIndex < static_cast( tmp_worn.size() ) && tmp_worn[leftListIndex]->is_sided() ) { - if( g->u.query_yn( _( "Swap side for %s?" ), - colorize( tmp_worn[leftListIndex]->tname(), - tmp_worn[leftListIndex]->color_in_inventory() ) ) ) { - change_side( *tmp_worn[leftListIndex] ); - wrefresh( w_sort_armor ); + } else if( action == "DOWN" ) { + int prev_line = selected_line++; + if( selected_line > cur_grp->max_selectable() ) { + if( is_moving ) { + selected_line = cur_grp->min_selectable(); + } else { + cur_grp = get_neighbour_grp( cur_grp, 1 ); + selected_line = cur_grp->min_selectable(); } } + + recalculate_top_line(); + if( is_moving ) { + cur_grp->insert_item( worn, prev_line, selected_line ); + reset_encumbrance(); + } + } else if( action == "PAGE_UP" ) { + cur_grp = get_neighbour_grp( cur_grp, -1 ); + selected_line = cur_grp->min_selectable(); + } else if( action == "PAGE_DOWN" ) { + cur_grp = get_neighbour_grp( cur_grp, 1 ); + selected_line = cur_grp->min_selectable(); + } else if( action == "MOVE_ARMOR" ) { + is_moving = !is_moving; } else if( action == "SORT_ARMOR" ) { - // Copy to a vector because stable_sort requires random-access - // iterators + // Copy to a vector because stable_sort requires random-access iterators std::vector worn_copy( worn.begin(), worn.end() ); std::stable_sort( worn_copy.begin(), worn_copy.end(), []( const item & l, const item & r ) { @@ -736,6 +767,15 @@ void player::sort_armor() ); std::copy( worn_copy.begin(), worn_copy.end(), worn.begin() ); reset_encumbrance(); + } else if( action == "CHANGE_SIDE" ) { + auto cur_item_iter = cur_grp->get_cur_item_info().get_iter(); + if( cur_item_iter->is_sided() ) { + if( g->u.query_yn( _( "Swap side for %s?" ), + colorize( cur_item_iter->tname(), + cur_item_iter->color_in_inventory() ) ) ) { + change_side( *cur_item_iter ); + } + } } else if( action == "EQUIP_ARMOR" ) { // filter inventory for all items that are armor/clothing item_location loc = game_menus::inv::wear( *this ); @@ -746,24 +786,12 @@ void player::sort_armor() cata::optional::iterator> new_equip_it = wear( this->i_at( loc.obtain( *this ) ) ); if( new_equip_it ) { - body_part bp = static_cast( tabindex ); - if( tabindex == num_bp || ( **new_equip_it ).covers( bp ) ) { - // Set ourselves up to be pointing at the new item - // TODO: This doesn't work yet because we don't save our - // state through other activities, but that's a thing - // that would be nice to do. - leftListIndex = - std::count_if( worn.begin(), *new_equip_it, - [&]( const item & i ) { - return tabindex == num_bp || i.covers( bp ); - } ); - } + //Do nothing } else if( is_npc() ) { // TODO: Pass the reason here popup( _( "Can't put this on!" ) ); } } - draw_grid( w_sort_armor, left_w, middle_w ); } else if( action == "EQUIP_ARMOR_HERE" ) { // filter inventory for all items that are armor/clothing item_location loc = game_menus::inv::wear( *this ); @@ -771,89 +799,72 @@ void player::sort_armor() // only equip if something valid selected! if( loc ) { // wear the item - if( cata::optional::iterator> new_equip_it = - wear( this->i_at( loc.obtain( *this ) ) ) ) { + cata::optional::iterator> new_equip_it = + wear( this->i_at( loc.obtain( *this ) ) ); + if( new_equip_it ) { // save iterator to cursor's position - std::list::iterator cursor_it = tmp_worn[leftListIndex]; + auto cur_item_iter = cur_grp->get_cur_item_info().get_iter(); // reorder `worn` vector to place new item at cursor - worn.splice( cursor_it, worn, *new_equip_it ); + worn.splice( cur_item_iter, worn, *new_equip_it ); } else if( is_npc() ) { // TODO: Pass the reason here popup( _( "Can't put this on!" ) ); } } - draw_grid( w_sort_armor, left_w, middle_w ); - } else if( action == "REMOVE_ARMOR" ) { - // query (for now) - if( leftListIndex < static_cast( tmp_worn.size() ) ) { - if( g->u.query_yn( _( "Remove selected armor?" ) ) ) { - do_return_entry(); - // remove the item, asking to drop it if necessary - takeoff( *tmp_worn[leftListIndex] ); - if( !g->u.has_activity( activity_id( "ACT_ARMOR_LAYERS" ) ) ) { - // An activity has been created to take off the item; - // we must surrender control until it is done. - return; - } - g->u.cancel_activity(); - draw_grid( w_sort_armor, left_w, middle_w ); - wrefresh( w_sort_armor ); - selected = -1; + } else if( action == "TAKE_OFF" ) { + if( g->u.query_yn( _( "Take off selected armor?" ) ) ) { + do_return_entry(); + // remove the item, asking to drop it if necessary + auto cur_item_iter = cur_grp->get_cur_item_info().get_iter(); + takeoff( *cur_item_iter ); + if( !g->u.has_activity( activity_id( "ACT_ARMOR_LAYERS" ) ) ) { + // An activity has been created to take off the item; + // we must surrender control until it is done. + return; } + g->u.cancel_activity(); } - } else if( action == "ASSIGN_INVLETS" ) { - // prompt first before doing this (yes, yes, more popups...) - if( query_yn( _( "Reassign invlets for armor?" ) ) ) { - // Start with last armor (the most unimportant one?) - auto iiter = inv_chars.rbegin(); - auto witer = worn.rbegin(); - while( witer != worn.rend() && iiter != inv_chars.rend() ) { - const char invlet = *iiter; - item &w = *witer; - if( invlet == w.invlet ) { - ++witer; - } else if( invlet_to_position( invlet ) != INT_MIN ) { - ++iiter; - } else { - inv.reassign_item( w, invlet ); - ++witer; - ++iiter; - } + } else if( action == "DROP" ) { //same as TAKE_OFF + if( g->u.query_yn( _( "Drop selected armor?" ) ) ) { + do_return_entry(); + auto cur_item_iter = cur_grp->get_cur_item_info().get_iter(); + drop( get_item_position( &*cur_item_iter ), pos() ); + if( !g->u.has_activity( activity_id( "ACT_ARMOR_LAYERS" ) ) ) { + return; } + g->u.cancel_activity(); } - } else if( action == "USAGE_HELP" ) { - popup_getkey( _( "\ -Use the arrow- or keypad keys to navigate the left list.\n\ -[%s] to select highlighted armor for reordering.\n\ -[%s] / [%s] to scroll the right list.\n\ -[%s] to assign special inventory letters to clothing.\n\ -[%s] to change the side on which item is worn.\n\ -[%s] to sort armor into natural layer order.\n\ -[%s] to equip a new item.\n\ -[%s] to equip a new item at the currently selected position.\n\ -[%s] to remove selected armor from oneself.\n\ - \n\ -[Encumbrance and Warmth] explanation:\n\ -The first number is the summed encumbrance from all clothing on that bodypart.\n\ -The second number is an additional encumbrance penalty caused by wearing multiple items \ -on one of the bodypart's layers or wearing items outside of other items they would \ -normally be work beneath (e.g. a shirt over a backpack).\n\ -The sum of these values is the effective encumbrance value your character has for that bodypart." ), - ctxt.get_desc( "MOVE_ARMOR" ), - ctxt.get_desc( "PREV_TAB" ), - ctxt.get_desc( "NEXT_TAB" ), - ctxt.get_desc( "ASSIGN_INVLETS" ), - ctxt.get_desc( "CHANGE_SIDE" ), - ctxt.get_desc( "SORT_ARMOR" ), - ctxt.get_desc( "EQUIP_ARMOR" ), - ctxt.get_desc( "EQUIP_ARMOR_HERE" ), - ctxt.get_desc( "REMOVE_ARMOR" ) - ); - draw_grid( w_sort_armor, left_w, middle_w ); - } else if( action == "HELP_KEYBINDINGS" ) { - draw_grid( w_sort_armor, left_w, middle_w ); + } + + else if( action == "USAGE_HELP" ) { + std::string help_str = ""; + std::string head_color = string_from_color( c_white ); + std::string desc_color = string_from_color( c_light_gray ); + + for( size_t i = 0; i < column_num_entries; i++ ) { + sort_armor_col_data col_data = get_col_data( i ); + if( static_cast( i ) == column_divider ) { + help_str += "\n"; + } else { + help_str += string_format( "%s - %s\n", + head_color, col_data.name, desc_color, col_data.description ); + } + + } + help_str += _( string_format( + "\n\ +Encumbrance explanation:\n\ + total encumbrance = clothing encumbrance + additional penalty\n\ +Penalty is caused by wearing multiple items on the same layer or \n\ +wearing inner items over outer ones(e.g.a shirt over a backpack).\n\ +\n\ +Layers order: (skin) -> (normal) -> (waist) -> (outer) -> (strap)", + head_color, desc_color, head_color, desc_color + ) ); + popup( help_str ); } else if( action == "QUIT" ) { exit = true; } + } } diff --git a/src/player.h b/src/player.h index 1455884ade435..5712a8529ccc9 100644 --- a/src/player.h +++ b/src/player.h @@ -240,6 +240,7 @@ class player : public Character /** Handles and displays detailed character info for the '@' screen */ void disp_info(); + std::string get_encumbrance_description( body_part bp, bool combine ); /**Estimate effect duration based on player relevant skill*/ time_duration estimate_effect_dur( const skill_id &relevant_skill, const efftype_id &effect, diff --git a/src/player_display.cpp b/src/player_display.cpp index 1369326a83559..0c296e4142d19 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -189,26 +189,26 @@ static std::string dodge_skill_text( double mod ) return string_format( _( "Dodge skill %+.1f. " ), mod ); } -static int get_encumbrance( const player &p, body_part bp, bool combine ) +static int get_player_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 const bool combines_with_other = static_cast( bp_aiOther[bp] ) != bp; - return p.encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 ); + return p->encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 ); } -static std::string get_encumbrance_description( const player &p, body_part bp, bool combine ) +std::string player::get_encumbrance_description( body_part bp, bool combine ) { std::string s; - const int eff_encumbrance = get_encumbrance( p, bp, combine ); + const int eff_encumbrance = get_player_encumbrance( this, 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.0 ) ); - s += swim_cost_text( ( eff_encumbrance / 10.0 ) * ( 80 - p.get_skill_level( + s += swim_cost_text( ( eff_encumbrance / 10.0 ) * ( 80 - get_skill_level( skill_swimming ) * 3 ) ); s += melee_cost_text( eff_encumbrance ); break; @@ -236,12 +236,12 @@ static std::string get_encumbrance_description( const player &p, body_part bp, b 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() ); + s += string_format( _( "Reduces aim speed of guns by %.1f." ), aim_speed_encumbrance_modifier() ); break; case bp_leg_l: case bp_leg_r: s += run_cost_text( static_cast( eff_encumbrance * 0.15 ) ); - s += swim_cost_text( ( eff_encumbrance / 10 ) * ( 50 - p.get_skill_level( + s += swim_cost_text( ( eff_encumbrance / 10 ) * ( 50 - get_skill_level( skill_swimming ) * 2 ) / 2 ); s += dodge_skill_text( -eff_encumbrance / 10.0 / 4.0 ); break; @@ -407,7 +407,7 @@ static void draw_encumbrance_tab( const catacurses::window &w_encumb, bp = bps[line].first; combined_here = bps[line].second; } - const std::string s = get_encumbrance_description( you, bp, combined_here ); + const std::string s = you.get_encumbrance_description( 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 ); From 54440269e75ea52012b0c22a6f96f55dc7917125 Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 4 Sep 2019 15:23:40 -0400 Subject: [PATCH 2/5] fixing translations being static adding scrollbar (instead of ) travis fixes --- src/armor_layers.cpp | 99 +++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index b6f7adc8116df..7e6bab9e7acbe 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -142,29 +142,43 @@ enum sort_armor_columns : int { column_num_entries }; struct sort_armor_col_data { - int width; - std::string name; - std::string description; + private: + int width; + int cut_off; + std::string name; + std::string description; + public: + sort_armor_col_data( int width, int cut_off, std::string name, std::string description ): + width( width ), cut_off( cut_off ), name( name ), description( description ) { + } + int get_width() { + return width; + } + std::string get_name() { + return ( _( name ) ).substr( 0, cut_off ); + } + std::string get_description() { + return _( description ); + } }; sort_armor_col_data get_col_data( int col_idx ) { static std::array< sort_armor_col_data, column_num_entries> col_data = { { { - 3, _( "cond" ), _( - string_format( "clothing conditions:\n\tx - poor fit\n\tf - filthy", - string_from_color( c_red ), string_from_color( c_cyan ) ) - ) + 3, 4, "condition", + string_format( "clothing conditions:\n\tx - poor fit\n\tf - filthy", + string_from_color( c_red ), string_from_color( c_cyan ) ) }, - {8, _( "encum " ), ( "encumberance." )}, - {7, _( "strge" ), _( "storage." )}, - {4, _( "wrm" ), _( "warmth." )}, - {3, "", ""}, - {3, _( "cvr" ), _( "coverage" )}, - {4, _( "bsh" ), _( "bash protection." )}, - {4, _( "cut" ), _( "cut protection." )}, - {4, _( "acd" ), _( "acid protection." )}, - {4, _( "fre" ), _( "fire protection." )}, - {4, _( "env" ), _( "environmental protection." )}, + {8, 5, "encumberance", "encumberance."}, + {7, 5, "storage", "storage." }, + {4, 3, "warmth", "warmth." }, + {3, 3, "", ""}, + {3, 3, "coverage", "coverage" }, + {4, 3, "bash", "bash protection." }, + {4, 3, "cut", "cut protection." }, + {4, 3, "acid", "acid protection." }, + {4, 3, "fire", "fire protection." }, + {4, 3, "environmental", "environmental protection." } } }; return col_data[col_idx]; @@ -229,7 +243,7 @@ class layering_item_info item itm = *itm_iter; nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { - int col_w = get_col_data( col_idx ).width; + int col_w = get_col_data( col_idx ).get_width(); right_bound -= col_w; switch( col_idx ) { case column_cond: { @@ -315,7 +329,7 @@ class layering_group_info int divider_posn; //printing constatnts - int max_x; + int x_bound; int max_y; //printing vars int list_min_line; @@ -334,7 +348,7 @@ class layering_group_info } // same for the duration of the menu void set_print_constats( int right_bound, int max_y ) { - this->max_x = right_bound; + this->x_bound = right_bound; this->max_y = max_y; } // depend on user input @@ -396,10 +410,10 @@ class layering_group_info } void print_header( bool is_selected, const catacurses::window &w, int cur_print_y ) { - int right_bound = max_x; + int right_bound = x_bound; nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { - int col_w = get_col_data( col_idx ).width; + int col_w = get_col_data( col_idx ).get_width(); right_bound -= col_w; switch( col_idx ) { //case column_fits: @@ -475,7 +489,7 @@ class layering_group_info is_selected = true; } if( cur_print_line >= list_min_line && print_y < max_y ) { - elem.print_columnns( ( list_selected_line == cur_print_line ), w, max_x, print_y ); + elem.print_columnns( ( list_selected_line == cur_print_line ), w, x_bound, print_y ); print_y++; } cur_print_line++; @@ -488,7 +502,7 @@ class layering_group_info } if( cur_print_line >= list_min_line && print_y < max_y ) { - mvwhline( w, point( 1, print_y ), 0, max_x - 1 ); + mvwhline( w, point( 1, print_y ), 0, x_bound - 1 ); mvwputch( w, point( divider_posn, print_y ), c_light_gray, LINE_XXXX ); print_y++; } @@ -547,7 +561,7 @@ void player::sort_armor() } //something wrong with using tname, results are too long - int elem_w = utf8_width( elem.nname( elem.typeId() ) ); + int elem_w = utf8_width( item::nname( elem.typeId() ) ); if( elem_w > max_name_w ) { max_name_w = elem_w; } @@ -558,15 +572,19 @@ void player::sort_armor() } int columns_w = 0; for( size_t i = 0; i < column_num_entries; i++ ) { - columns_w += get_col_data( i ).width; + columns_w += get_col_data( i ).get_width(); } - - const int win_h = std::min( TERMY, header_h + req_data_h + footer_h ); + const int requested_h = header_h + req_data_h + footer_h; + const int win_h = std::min( TERMY, requested_h ); const int win_w = std::min( name_start_idx + max_name_w + columns_w + 1, TERMX ); const int win_x = TERMX / 2 - win_w / 2; const int win_y = TERMY / 2 - win_h / 2; catacurses::window w_sort_armor = catacurses::newwin( win_h, win_w, point( win_x, win_y ) ); + scrollbar s; + s.viewport_size( win_h - header_h - footer_h ); + s.offset_y( header_h ); + int data_max_y = win_h - footer_h; std::vector items_by_category; @@ -617,7 +635,7 @@ void player::sort_armor() //window border werase( w_sort_armor ); draw_border( w_sort_armor ); - mvwprintz( w_sort_armor, point( 1, 0 ), c_white, _( "Sort Armor" ) ); + mvwprintz( w_sort_armor, point_east, c_white, _( "Sort Armor" ) ); int print_x = win_w; std::string msg = string_format( _( "<[%s] Columns Info>" ), ctxt.get_desc( "USAGE_HELP" ) ); print_x -= utf8_width( msg ) + 1; @@ -628,13 +646,14 @@ void player::sort_armor() int divider_posn = 0; for( size_t col_idx = column_num_entries, right_bound = win_w - 1; col_idx-- > 0; ) { sort_armor_col_data col_data = get_col_data( col_idx ); - int col_w = col_data.width; + int col_w = col_data.get_width(); right_bound -= col_w; if( col_idx == column_divider ) { divider_posn = right_bound + col_w - 1; mvwputch( w_sort_armor, point( divider_posn, print_y ), c_light_gray, LINE_XOXO ); } else { - mvwprintz( w_sort_armor, point( right_bound, print_y ), c_light_gray, "%*s", col_w, col_data.name ); + mvwprintz( w_sort_armor, point( right_bound, print_y ), c_light_gray, "%*s", col_w, + col_data.get_name() ); } } print_y += 1; @@ -666,18 +685,20 @@ void player::sort_armor() } // data bottom - const int footer_y = std::min( print_y - 1, data_max_y ); + int footer_y = data_max_y; + if( requested_h > win_h ) { + s.content_size( cur_print_line - 1 ); + s.viewport_pos( min_print_line ); + s.apply( w_sort_armor ); + } else { + footer_y = print_y - 1; + } mvwhline( w_sort_armor, point( 1, footer_y ), 0, win_w - 2 ); //middle mvwputch( w_sort_armor, point( 0, footer_y ), c_light_gray, LINE_XXXO ); //left mvwputch( w_sort_armor, point( win_w - 1, footer_y ), c_light_gray, LINE_XOXX ); //right mvwputch( w_sort_armor, point( divider_posn, footer_y ), c_light_gray, LINE_XXOX ); //divider mvwprintz( w_sort_armor, point( 1, footer_y ), c_light_gray, _( "(Outermost)" ) ); - if( cur_print_line >= footer_y ) { - // TODO: replace it by right_print() - mvwprintz( w_sort_armor, point( 17, footer_y ), c_light_blue, _( "" ) ); - } - // footer const std::string s = get_encumbrance_description( cur_grp->get_bp(), false ); fold_and_print( w_sort_armor, point( 1, footer_y + 1 ), win_w - 2, c_magenta, s ); @@ -837,7 +858,7 @@ void player::sort_armor() } else if( action == "USAGE_HELP" ) { - std::string help_str = ""; + std::string help_str; std::string head_color = string_from_color( c_white ); std::string desc_color = string_from_color( c_light_gray ); @@ -847,7 +868,7 @@ void player::sort_armor() help_str += "\n"; } else { help_str += string_format( "%s - %s\n", - head_color, col_data.name, desc_color, col_data.description ); + head_color, col_data.get_name(), desc_color, col_data.get_description() ); } } From 34d81ce2d3cfcbaed595e256a40d4889db7c9a7e Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 5 Sep 2019 13:28:42 -0400 Subject: [PATCH 3/5] printing fill names as column titles, adding tabs for small res BevapDin code fixes --- src/armor_layers.cpp | 212 ++++++++++++++++++++++++++--------------- src/player_display.cpp | 6 +- 2 files changed, 136 insertions(+), 82 deletions(-) diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 7e6bab9e7acbe..20da781dc4f0b 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -144,18 +144,17 @@ enum sort_armor_columns : int { struct sort_armor_col_data { private: int width; - int cut_off; std::string name; std::string description; public: - sort_armor_col_data( int width, int cut_off, std::string name, std::string description ): - width( width ), cut_off( cut_off ), name( name ), description( description ) { + sort_armor_col_data( int width, std::string name, std::string description ): + width( width ), name( name ), description( description ) { } int get_width() { - return width; + return std::max( utf8_width( _( name ) ) + 1, width ); } std::string get_name() { - return ( _( name ) ).substr( 0, cut_off ); + return _( name ); } std::string get_description() { return _( description ); @@ -165,20 +164,20 @@ sort_armor_col_data get_col_data( int col_idx ) { static std::array< sort_armor_col_data, column_num_entries> col_data = { { { - 3, 4, "condition", - string_format( "clothing conditions:\n\tx - poor fit\n\tf - filthy", + 3, translate_marker( "condition" ), + string_format( translate_marker( "clothing conditions:\n\tx - poor fit\n\tf - filthy" ), string_from_color( c_red ), string_from_color( c_cyan ) ) }, - {8, 5, "encumberance", "encumberance."}, - {7, 5, "storage", "storage." }, - {4, 3, "warmth", "warmth." }, - {3, 3, "", ""}, - {3, 3, "coverage", "coverage" }, - {4, 3, "bash", "bash protection." }, - {4, 3, "cut", "cut protection." }, - {4, 3, "acid", "acid protection." }, - {4, 3, "fire", "fire protection." }, - {4, 3, "environmental", "environmental protection." } + {8, translate_marker( "encumberance" ), translate_marker( "encumberance." )}, + {7, translate_marker( "storage" ), translate_marker( "storage." ) }, + {4, translate_marker( "warmth" ), translate_marker( "warmth." ) }, + {3, translate_marker( " " ), translate_marker( "" )}, + {3, translate_marker( "coverage" ), translate_marker( "coverage." ) }, + {4, translate_marker( "bash" ), translate_marker( "bash protection." ) }, + {4, translate_marker( "cut" ), translate_marker( "cut protection." ) }, + {4, translate_marker( "acid" ), translate_marker( "acid protection." ) }, + {4, translate_marker( "fire" ), translate_marker( "fire protection." ) }, + {4, translate_marker( "environmental" ), translate_marker( "environmental protection." ) } } }; return col_data[col_idx]; @@ -208,41 +207,41 @@ class layering_item_info } int get_col_value( sort_armor_columns col ) { - item itm = *itm_iter; - int ret_val; + const item &itm = *itm_iter; switch( col ) { case column_warmth: - ret_val = itm.get_warmth(); - break; + return itm.get_warmth(); case column_coverage: - ret_val = itm.get_coverage(); - break; + return itm.get_coverage(); case column_bash: - ret_val = itm.bash_resist(); - break; + return itm.bash_resist(); case column_cut: - ret_val = itm.cut_resist(); - break; + return itm.cut_resist(); case column_acid: - ret_val = itm.acid_resist(); - break; + return itm.acid_resist(); case column_fire: - ret_val = itm.fire_resist(); - break; + return itm.fire_resist(); case column_env: - ret_val = itm.get_env_resist(); - break; + return itm.get_env_resist(); default: - ret_val = 0; - break; + return 0; } - return ret_val; } void print_columnns( bool is_selected, const catacurses::window &w, int right_bound, - int cur_print_y ) { + int cur_print_y, bool is_compact, int compact_display_tab ) { item itm = *itm_iter; nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; + int cur_tab = 1; for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { + if( is_compact ) { + if( col_idx == column_divider ) { + cur_tab--; + continue; + } + if( cur_tab != compact_display_tab ) { + continue; + } + } int col_w = get_col_data( col_idx ).get_width(); right_bound -= col_w; switch( col_idx ) { @@ -277,7 +276,7 @@ class layering_item_info case column_storage: { units::volume storage = itm.get_storage(); mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, - ( storage > 0_ml ? format_volume( itm.get_storage() ) : "" ) ); + storage > 0_ml ? format_volume( itm.get_storage() ) : "" ); } break; case column_divider: { @@ -288,6 +287,12 @@ class layering_item_info mvwputch( w, point( right_bound + col_w - 1, cur_print_y ), print_col2, LINE_XOXO ); } break; + case column_env: { + int val = get_col_value( static_cast( col_idx ) ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, "" ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", 5, val ); + } + break; default: { int val = get_col_value( static_cast( col_idx ) ); mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, val ); @@ -331,35 +336,39 @@ class layering_group_info //printing constatnts int x_bound; int max_y; + bool is_compact; + //printing vars int list_min_line; int list_selected_line; bool is_moving; + int compact_display_tab; public: bool is_skipped = false; - layering_group_info( body_part bp, player *p ) { - this->bp = bp; - this->p = p; - } + layering_group_info( body_part bp, player *p ) : bp( bp ), p( p ) { } + void add_item( std::list::iterator itm_i ) { - items.push_back( layering_item_info( itm_i, *p, bp ) ); + items.emplace_back( itm_i, *p, bp ); } // same for the duration of the menu - void set_print_constats( int right_bound, int max_y ) { + void set_print_constats( int right_bound, int max_y, bool is_compact ) { this->x_bound = right_bound; this->max_y = max_y; + this->is_compact = is_compact; } // depend on user input - void set_print_vars( int list_selected_line, bool is_moving, int list_min_line ) { + void set_print_vars( int list_selected_line, bool is_moving, int list_min_line, + int compact_display_tab ) { this->list_selected_line = list_selected_line; this->is_moving = is_moving; this->list_min_line = list_min_line; + this->compact_display_tab = compact_display_tab; } void set_combined( bool combined ) { - this->name = body_part_name_as_heading( all_body_parts[bp], combined ? 2 : 1 ); + name = body_part_name_as_heading( all_body_parts[bp], combined ? 2 : 1 ); is_skipped = false; } @@ -412,12 +421,20 @@ class layering_group_info void print_header( bool is_selected, const catacurses::window &w, int cur_print_y ) { int right_bound = x_bound; nc_color print_col = is_selected ? hilite( c_white ) : c_light_gray; + int cur_tab = 1; for( size_t col_idx = column_num_entries; col_idx-- > 0; ) { + if( is_compact ) { + if( col_idx == column_divider ) { + cur_tab--; + continue; + } + if( cur_tab != compact_display_tab ) { + continue; + } + } int col_w = get_col_data( col_idx ).get_width(); right_bound -= col_w; switch( col_idx ) { - //case column_fits: - //case column_filthy: case column_cond: case column_storage: case column_coverage: @@ -448,13 +465,19 @@ class layering_group_info mvwputch( w, point( divider_posn, cur_print_y ), print_col2, LINE_XOXO ); } break; + case column_env: { + double val = static_cast( header_data[col_idx] ) / 100; + int val_i = static_cast( std::round( val ) ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*s", col_w, "" ); + mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", 5, val_i ); + } + break; case column_warmth: mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, header_data[col_idx] ); break; default: { double val = static_cast( header_data[col_idx] ) / 100; int val_i = static_cast( std::round( val ) ); - //std::string s = string_format("~%d", val_i); mvwprintz( w, point( right_bound, cur_print_y ), print_col, "%*d", col_w, val_i ); } break; @@ -489,7 +512,8 @@ class layering_group_info is_selected = true; } if( cur_print_line >= list_min_line && print_y < max_y ) { - elem.print_columnns( ( list_selected_line == cur_print_line ), w, x_bound, print_y ); + elem.print_columnns( ( list_selected_line == cur_print_line ), w, x_bound, print_y, is_compact, + compact_display_tab ); print_y++; } cur_print_line++; @@ -518,6 +542,7 @@ void player::sort_armor() ctxt.register_updown(); ctxt.register_action( "PAGE_UP" ); ctxt.register_action( "PAGE_DOWN" ); + ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "MOVE_ARMOR" ); ctxt.register_action( "CHANGE_SIDE" ); @@ -576,11 +601,15 @@ void player::sort_armor() } const int requested_h = header_h + req_data_h + footer_h; const int win_h = std::min( TERMY, requested_h ); - const int win_w = std::min( name_start_idx + max_name_w + columns_w + 1, TERMX ); + const int requested_w = name_start_idx + max_name_w + columns_w + 1; + const int win_w = std::min( requested_w, TERMX ); const int win_x = TERMX / 2 - win_w / 2; const int win_y = TERMY / 2 - win_h / 2; catacurses::window w_sort_armor = catacurses::newwin( win_h, win_w, point( win_x, win_y ) ); + bool is_compact = requested_w > win_w; + int compact_display_tab = 0; + scrollbar s; s.viewport_size( win_h - header_h - footer_h ); s.offset_y( header_h ); @@ -589,9 +618,9 @@ void player::sort_armor() std::vector items_by_category; items_by_category.reserve( num_bp ); - for( size_t bp = 0; bp < num_bp; bp++ ) { - items_by_category.push_back( layering_group_info( static_cast( bp ), this ) ); - items_by_category[bp].set_print_constats( win_w - 1, data_max_y ); + for( const body_part bp : all_body_parts ) { + items_by_category.push_back( layering_group_info( bp, this ) ); + items_by_category[bp].set_print_constats( win_w - 1, data_max_y, is_compact ); } layering_group_info *cur_grp = &items_by_category[0]; @@ -620,12 +649,12 @@ void player::sort_armor() } // set up data - for( size_t bp = 0; bp < num_bp; bp++ ) { + for( const body_part bp : all_body_parts ) { items_by_category[bp].clear(); } for( auto elem_it = worn.begin(); elem_it != worn.end(); ++elem_it ) { - for( size_t bp = 0; bp < num_bp; bp++ ) { - if( !elem_it->covers( static_cast( bp ) ) ) { + for( const body_part bp : all_body_parts ) { + if( !elem_it->covers( bp ) ) { continue; } items_by_category[bp].add_item( elem_it ); @@ -644,7 +673,18 @@ void player::sort_armor() //column names int print_y = 1; int divider_posn = 0; + int cur_tab = 1; for( size_t col_idx = column_num_entries, right_bound = win_w - 1; col_idx-- > 0; ) { + if( is_compact ) { + if( col_idx == column_divider ) { + cur_tab--; + continue; + } + if( cur_tab != compact_display_tab ) { + continue; + } + } + sort_armor_col_data col_data = get_col_data( col_idx ); int col_w = col_data.get_width(); right_bound -= col_w; @@ -656,6 +696,17 @@ void player::sort_armor() col_data.get_name() ); } } + + //compact tab name + if( is_compact ) { + std::string tab_name; + if( compact_display_tab == 0 ) { + tab_name = _( "Info" ); + } else { + tab_name = _( "Protection" ); + } + mvwprintz( w_sort_armor, point( 3, print_y ), c_yellow, "<< %s >>", tab_name ); + } print_y += 1; // data top @@ -671,17 +722,17 @@ void player::sort_armor() const int data_start_y = print_y; for( int bp_idx = 0; bp_idx < num_bp; bp_idx++ ) { - layering_group_info *l = &items_by_category[bp_idx]; + layering_group_info &l = items_by_category[bp_idx]; bool combined = false; - if( bp_idx > 3 && bp_idx % 2 == 0 && *l == items_by_category[bp_idx + 1] ) { + if( bp_idx > 3 && bp_idx % 2 == 0 && l == items_by_category[bp_idx + 1] ) { combined = true; bp_idx++; items_by_category[bp_idx].is_skipped = true; } - l->set_combined( combined ); - l->set_print_vars( selected_line, is_moving, min_print_line ); - l->print_data( cur_print_line, print_y, w_sort_armor ); + l.set_combined( combined ); + l.set_print_vars( selected_line, is_moving, min_print_line, compact_display_tab ); + l.print_data( cur_print_line, print_y, w_sort_armor ); } // data bottom @@ -776,6 +827,8 @@ void player::sort_armor() } else if( action == "PAGE_DOWN" ) { cur_grp = get_neighbour_grp( cur_grp, 1 ); selected_line = cur_grp->min_selectable(); + } else if( action == "NEXT_TAB" ) { + compact_display_tab = ( compact_display_tab + 1 ) % 2; } else if( action == "MOVE_ARMOR" ) { is_moving = !is_moving; } else if( action == "SORT_ARMOR" ) { @@ -805,7 +858,7 @@ void player::sort_armor() if( loc ) { // wear the item cata::optional::iterator> new_equip_it = - wear( this->i_at( loc.obtain( *this ) ) ); + wear( i_at( loc.obtain( *this ) ) ); if( new_equip_it ) { //Do nothing } else if( is_npc() ) { @@ -821,7 +874,7 @@ void player::sort_armor() if( loc ) { // wear the item cata::optional::iterator> new_equip_it = - wear( this->i_at( loc.obtain( *this ) ) ); + wear( i_at( loc.obtain( *this ) ) ); if( new_equip_it ) { // save iterator to cursor's position auto cur_item_iter = cur_grp->get_cur_item_info().get_iter(); @@ -859,29 +912,30 @@ void player::sort_armor() else if( action == "USAGE_HELP" ) { std::string help_str; - std::string head_color = string_from_color( c_white ); - std::string desc_color = string_from_color( c_light_gray ); - for( size_t i = 0; i < column_num_entries; i++ ) { sort_armor_col_data col_data = get_col_data( i ); if( static_cast( i ) == column_divider ) { help_str += "\n"; } else { - help_str += string_format( "%s - %s\n", - head_color, col_data.get_name(), desc_color, col_data.get_description() ); + help_str += colorize( col_data.get_name(), c_white ); + help_str += " - "; + help_str += colorize( col_data.get_description(), c_light_gray ); + help_str += "\n"; } } - help_str += _( string_format( - "\n\ -Encumbrance explanation:\n\ - total encumbrance = clothing encumbrance + additional penalty\n\ -Penalty is caused by wearing multiple items on the same layer or \n\ -wearing inner items over outer ones(e.g.a shirt over a backpack).\n\ -\n\ -Layers order: (skin) -> (normal) -> (waist) -> (outer) -> (strap)", - head_color, desc_color, head_color, desc_color - ) ); + help_str += "\n"; + help_str += colorize( _( "Encumbrance explanation:\n" ), c_white ); + help_str += colorize( _( "\ttotal encumbrance = clothing encumbrance + additional penalty\n" ), + c_light_gray ); + help_str += colorize( _( "Penalty is caused by wearing multiple items on the same layer or\n" ), + c_light_gray ); + help_str += colorize( _( "wearing inner items over outer ones(e.g.a shirt over a backpack).\n" ), + c_light_gray ); + help_str += "\n"; + help_str += colorize( _( "Layers order" ), c_white ); + help_str += colorize( _( ": (skin) -> (normal) -> (waist) -> (outer) -> (strap)" ), c_light_gray ); + popup( help_str ); } else if( action == "QUIT" ) { exit = true; diff --git a/src/player_display.cpp b/src/player_display.cpp index 0c296e4142d19..97df8f80b5917 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -189,19 +189,19 @@ static std::string dodge_skill_text( double mod ) return string_format( _( "Dodge skill %+.1f. " ), mod ); } -static int get_player_encumbrance( const player *p, body_part bp, bool combine ) +static int get_player_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 const bool combines_with_other = static_cast( bp_aiOther[bp] ) != bp; - return p->encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 ); + return p.encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 ); } std::string player::get_encumbrance_description( body_part bp, bool combine ) { std::string s; - const int eff_encumbrance = get_player_encumbrance( this, bp, combine ); + const int eff_encumbrance = get_player_encumbrance( *this, bp, combine ); switch( bp ) { case bp_torso: { From 758a48bc171083789c0a0437fd301cad3e651784 Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 6 Sep 2019 16:36:05 -0400 Subject: [PATCH 4/5] fixing item name width not calculating correctly --- src/armor_layers.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 20da781dc4f0b..efc4caca93ff1 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -585,8 +585,7 @@ void player::sort_armor() same = false; } - //something wrong with using tname, results are too long - int elem_w = utf8_width( item::nname( elem.typeId() ) ); + int elem_w = utf8_width( elem.tname(), true ); if( elem_w > max_name_w ) { max_name_w = elem_w; } From 6d990f71cf64dde48af2cc06bc63b5ac561186bd Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 25 Oct 2019 20:31:05 -0400 Subject: [PATCH 5/5] fixing issues when one of the layers don't have any items on it fixing issues when taking clothes off npc adding waterproof condition --- src/armor_layers.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index efc4caca93ff1..647feb83d5d8c 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -165,8 +165,7 @@ sort_armor_col_data get_col_data( int col_idx ) static std::array< sort_armor_col_data, column_num_entries> col_data = { { { 3, translate_marker( "condition" ), - string_format( translate_marker( "clothing conditions:\n\tx - poor fit\n\tf - filthy" ), - string_from_color( c_red ), string_from_color( c_cyan ) ) + string_format( translate_marker( "clothing conditions:\n\tx - poor fit\n\tf - filthy\n\tw - waterproof" ) ) }, {8, translate_marker( "encumberance" ), translate_marker( "encumberance." )}, {7, translate_marker( "storage" ), translate_marker( "storage." ) }, @@ -253,7 +252,7 @@ class layering_item_info int pos_x = right_bound + col_w - 1; char cond; - print_col2 = is_selected ? hilite( c_cyan ) : c_cyan; + print_col2 = is_selected ? hilite( c_red ) : c_red; cond = ( itm.is_filthy() ? 'f' : ' ' ); mvwprintz( w, point( pos_x, cur_print_y ), print_col2, "%c", cond ); pos_x--; @@ -262,6 +261,11 @@ class layering_item_info cond = ( itm.has_flag( "FIT" ) || !itm.has_flag( "VARSIZE" ) ) ? ' ' : 'x'; mvwprintz( w, point( pos_x, cur_print_y ), print_col2, "%c", cond ); pos_x--; + + print_col2 = is_selected ? hilite( c_cyan ) : c_cyan; + cond = itm.has_flag( "WATERPROOF" ) ? 'w' : ' '; + mvwprintz( w, point( pos_x, cur_print_y ), print_col2, "%c", cond ); + pos_x--; } break; case column_encumb: { @@ -622,9 +626,9 @@ void player::sort_armor() items_by_category[bp].set_print_constats( win_w - 1, data_max_y, is_compact ); } - layering_group_info *cur_grp = &items_by_category[0]; + layering_group_info *cur_grp = nullptr; int min_print_line = 0; - int selected_line = 1; + int selected_line = -1; bool is_moving = false; bool exit = false; @@ -729,11 +733,23 @@ void player::sort_armor() bp_idx++; items_by_category[bp_idx].is_skipped = true; } + + if( cur_grp == nullptr && l.num_items() > 0 ) { + cur_grp = &l; + selected_line = cur_print_line + 1; + } + l.set_combined( combined ); l.set_print_vars( selected_line, is_moving, min_print_line, compact_display_tab ); l.print_data( cur_print_line, print_y, w_sort_armor ); } + if( cur_grp == nullptr || cur_grp->num_items() == 0 ) { + add_msg_if_player( _( "You are not wearing any clothes." ) ); + add_msg_if_npc( _( "%s is not wearing any clothes." ), name ); + return; + } + // data bottom int footer_y = data_max_y; if( requested_h > win_h ) { @@ -768,7 +784,9 @@ void player::sort_armor() } grp = &items_by_category[cur_idx]; - if( grp->is_skipped ) { + if( grp == cur_grp ) { //did a full wrap around without finding anythig + return grp; + } else if( grp->is_skipped || grp->num_items() < 1 ) { return get_neighbour_grp( grp, direction ); } else { return grp; @@ -896,6 +914,9 @@ void player::sort_armor() return; } g->u.cancel_activity(); + // Reset when interacting with npc, this will mimic behavior as player + cur_grp = nullptr; + selected_line = 1; } } else if( action == "DROP" ) { //same as TAKE_OFF if( g->u.query_yn( _( "Drop selected armor?" ) ) ) { @@ -906,6 +927,8 @@ void player::sort_armor() return; } g->u.cancel_activity(); + cur_grp = nullptr; + selected_line = 1; } } @@ -925,7 +948,7 @@ void player::sort_armor() } help_str += "\n"; help_str += colorize( _( "Encumbrance explanation:\n" ), c_white ); - help_str += colorize( _( "\ttotal encumbrance = clothing encumbrance + additional penalty\n" ), + help_str += colorize( _( " total encumbrance = clothing encumbrance + additional penalty\n" ), c_light_gray ); help_str += colorize( _( "Penalty is caused by wearing multiple items on the same layer or\n" ), c_light_gray );