void inventory_selector::print_right_column() const { if (right_column_width == 0) { return; } player &u = g->u; int drp_line = 1; drop_map::const_iterator dit = dropping.find(-1); if (dit != dropping.end()) { std::string item_name = u.weapname(); if (dit->second == -1) { item_name.insert(0, "+ "); } else { item_name = string_format("# %s {%d}", item_name.c_str(), dit->second); } const char invlet = invlet_or_space(u.weapon); trim_and_print(w_inv, drp_line, right_column_offset, right_column_width - 4, c_ltblue, "%c %s", invlet, item_name.c_str()); drp_line++; } auto iter = u.worn.begin(); for (size_t k = 0; k < u.worn.size(); k++, ++iter) { // worn items can not be dropped partially if (dropping.count(player::worn_position_to_index(k)) == 0) { continue; } const char invlet = invlet_or_space(*iter); trim_and_print(w_inv, drp_line, right_column_offset, right_column_width - 4, c_cyan, "%c + %s", invlet, iter->display_name().c_str()); drp_line++; } for( const auto &elem : dropping ) { if( elem.first < 0 ) { // worn or wielded item, already displayed above continue; } const std::list<item> &stack = u.inv.const_stack( elem.first ); const item &it = stack.front(); const char invlet = invlet_or_space(it); const int count = elem.second; const int display_count = (count == -1) ? (it.charges >= 0) ? it.charges : stack.size() : count; const nc_color col = it.color_in_inventory(); std::string item_name = it.tname( display_count ); if (count == -1) { if (stack.size() > 1) { item_name = string_format("%d %s", stack.size(), item_name.c_str()); } else { item_name.insert(0, "+ "); } } else { item_name = string_format("# %s {%d}", item_name.c_str(), count); } trim_and_print(w_inv, drp_line, right_column_offset, right_column_width - 2, col, "%c %s", invlet, item_name.c_str()); drp_line++; } }
void inventory_selector::draw( WINDOW *w ) const { mvwprintw( w, 0, 0, title.c_str() ); // Position of inventory columns is adaptive. They're aligned to the left if they occupy less than 2/3 of the screen. // Otherwise they're aligned symmetrically to the center of the screen. static const float min_ratio_to_center = 1.f / 3; const int free_space = getmaxx( w ) - get_columns_width(); const bool center_align = std::abs( float( free_space ) / getmaxx( w ) ) <= min_ratio_to_center; const int max_gap = ( columns.size() > 1 ) ? free_space / ( int( columns.size() ) - 1 ) : 0; const int gap = center_align ? max_gap : std::min<int>( max_gap, 4 ); const int gap_rounding_error = ( center_align && columns.size() > 1 ) ? free_space % ( columns.size() - 1 ) : 0; size_t x = 1; size_t y = 2; size_t active_x = 0; for( const auto &column : columns ) { if( &column == &columns.back() ) { x += gap_rounding_error; } if( !is_active_column( *column ) ) { column->draw( w, x, y ); } else { active_x = x; } if( column->pages_count() > 1 ) { mvwprintw( w, getmaxy( w ) - 2, x, _( "Page %d/%d" ), column->page_index() + 1, column->pages_count() ); } x += column->get_width() + gap; } get_active_column().draw( w, active_x, y ); if( empty() ) { center_print( w, getmaxy( w ) / 2, c_dkgray, _( "Your inventory is empty." ) ); } const std::string msg_str = ( navigation == navigation_mode::CATEGORY ) ? _( "Category selection; [TAB] switches mode, arrows select." ) : _( "Item selection; [TAB] switches mode, arrows select." ); const nc_color msg_color = ( navigation == navigation_mode::CATEGORY ) ? h_white : c_ltgray; if( center_align ) { center_print( w, getmaxy( w ) - 1, msg_color, msg_str.c_str() ); } else { trim_and_print( w, getmaxy( w ) - 1, 1, getmaxx( w ), msg_color, msg_str.c_str() ); } }
void inventory_column::draw( WINDOW *win, size_t x, size_t y ) const { for( size_t i = page_offset, line = 0; i < entries.size() && line < entries_per_page; ++i, ++line ) { const auto &entry = entries[i]; if( entry.is_null() ) { continue; } trim_and_print( win, y + line, x + get_entry_indent( entry ), getmaxx( win ) - x, get_entry_color( entry ), "%s", get_entry_text( entry ).c_str() ); if( entry.is_item() && entry.it->invlet != '\0' ) { const nc_color invlet_color = g->u.assigned_invlet.count( entry.it->invlet ) ? c_yellow : c_white; mvwputch( win, y + line, x, invlet_color, entry.it->invlet ); } } }
void inventory_selector::print_column(const itemstack_vector &items, size_t y, size_t w, size_t selected, size_t current_page_offset) const { nc_color selected_line_color = inCategoryMode ? c_white_red : h_white; if ((&items == &this->items) != in_inventory) { selected_line_color = inCategoryMode ? c_ltgray_red : h_ltgray; } int cur_line = 2; for (size_t a = 0; a + current_page_offset < items.size() && a < items_per_page; a++, cur_line++) { const itemstack_or_category &cur_entry = items[a + current_page_offset]; if (cur_entry.category == NULL) { continue; } if (cur_entry.it == NULL) { const std::string name = trim_to(cur_entry.category->name, w); mvwprintz(w_inv, cur_line, y, c_magenta, "%s", name.c_str()); continue; } const item &it = *cur_entry.it; std::string item_name = it.display_name(); if (cur_entry.slice != NULL) { const size_t count = cur_entry.slice->size(); if (count > 1) { item_name = string_format("%d %s", count, it.display_name(count).c_str()); } } nc_color name_color = it.color_in_inventory(); nc_color invlet_color = c_white; if (g->u.assigned_invlet.count(it.invlet)) { invlet_color = c_yellow; } if (a + current_page_offset == selected) { name_color = selected_line_color; invlet_color = selected_line_color; } item_name = get_drop_icon(dropping.find(cur_entry.item_pos)) + item_name; if (it.invlet != 0) { mvwputch(w_inv, cur_line, y, invlet_color, it.invlet); } if (OPTIONS["ITEM_SYMBOLS"]) { item_name = string_format("%c %s", it.symbol(), item_name.c_str()); } trim_and_print(w_inv, cur_line, y + 2, w - 2, name_color, "%s", item_name.c_str()); } }
/** * Prints a list of all parts to the screen inside of a boxed window, possibly * highlighting a selected one. * @param win The window to draw in. * @param y1 The y-coordinate to start drawing at. * @param max_y Draw no further than this y-coordinate. * @param width The width of the window. * @param p The index of the part being examined. * @param hl The index of the part to highlight (if any). */ int vehicle::print_part_list( const catacurses::window &win, int y1, const int max_y, int width, int p, int hl /*= -1*/ ) const { if( p < 0 || p >= ( int )parts.size() ) { return y1; } std::vector<int> pl = this->parts_at_relative( parts[p].mount.x, parts[p].mount.y ); int y = y1; for( size_t i = 0; i < pl.size(); i++ ) { if( y >= max_y ) { mvwprintz( win, y, 1, c_yellow, _( "More parts here..." ) ); ++y; break; } const vehicle_part &vp = parts[ pl [ i ] ]; nc_color col_cond = vp.is_broken() ? c_dark_gray : vp.base.damage_color(); std::string partname = vp.name(); if( vp.is_fuel_store() && vp.ammo_current() != "null" ) { partname += string_format( " (%s)", item::nname( vp.ammo_current() ).c_str() ); } if( part_flag( pl[i], "CARGO" ) ) { //~ used/total volume of a cargo vehicle part partname += string_format( _( " (vol: %s/%s %s)" ), format_volume( stored_volume( pl[i] ) ).c_str(), format_volume( max_volume( pl[i] ) ).c_str(), volume_units_abbr() ); } bool armor = part_flag( pl[i], "ARMOR" ); std::string left_sym; std::string right_sym; if( armor ) { left_sym = "("; right_sym = ")"; } else if( part_info( pl[i] ).location == part_location_structure ) { left_sym = "["; right_sym = "]"; } else { left_sym = "-"; right_sym = "-"; } nc_color sym_color = ( int )i == hl ? hilite( c_light_gray ) : c_light_gray; mvwprintz( win, y, 1, sym_color, left_sym ); trim_and_print( win, y, 2, getmaxx( win ) - 4, ( int )i == hl ? hilite( col_cond ) : col_cond, partname ); wprintz( win, sym_color, right_sym ); if( i == 0 && vpart_position( const_cast<vehicle &>( *this ), pl[i] ).is_inside() ) { //~ indicates that a vehicle part is inside mvwprintz( win, y, width - 2 - utf8_width( _( "Interior" ) ), c_light_gray, _( "Interior" ) ); } else if( i == 0 ) { //~ indicates that a vehicle part is outside mvwprintz( win, y, width - 2 - utf8_width( _( "Exterior" ) ), c_light_gray, _( "Exterior" ) ); } y++; } // print the label for this location const cata::optional<std::string> label = vpart_position( const_cast<vehicle &>( *this ), p ).get_label(); if( label && y <= max_y ) { mvwprintz( win, y++, 1, c_light_red, _( "Label: %s" ), label->c_str() ); } return y; }
hp_part Character::body_window( const std::string &menu_header, bool show_all, bool precise, int normal_bonus, int head_bonus, int torso_bonus, bool bleed, bool bite, bool infect ) const { WINDOW *hp_window = newwin(10, 31, (TERMY - 10) / 2, (TERMX - 31) / 2); draw_border(hp_window); trim_and_print( hp_window, 1, 1, getmaxx(hp_window) - 2, c_ltred, menu_header.c_str() ); const int y_off = 2; // 1 for border, 1 for header /* This struct estabiles some kind of connection between the hp_part (which can be healed and * have HP) and the body_part. Note that there are more body_parts than hp_parts. For example: * Damage to bp_head, bp_eyes and bp_mouth is all applied on the HP of hp_head. */ struct healable_bp { mutable bool allowed; body_part bp; hp_part hp; std::string name; // Translated name as it appears in the menu. int bonus; }; /* The array of the menu entries show to the player. The entries are displayed in this order, * it may be changed here. */ std::array<healable_bp, num_hp_parts> parts = { { { false, bp_head, hp_head, _("Head"), head_bonus }, { false, bp_torso, hp_torso, _("Torso"), torso_bonus }, { false, bp_arm_l, hp_arm_l, _("Left Arm"), normal_bonus }, { false, bp_arm_r, hp_arm_r, _("Right Arm"), normal_bonus }, { false, bp_leg_l, hp_leg_l, _("Left Leg"), normal_bonus }, { false, bp_leg_r, hp_leg_r, _("Right Leg"), normal_bonus }, } }; for( size_t i = 0; i < parts.size(); i++ ) { const auto &e = parts[i]; const body_part bp = e.bp; const hp_part hp = e.hp; const int maximal_hp = hp_max[hp]; const int current_hp = hp_cur[hp]; const int bonus = e.bonus; // This will c_ltgray if the part does not have any effects cured by the item // (e.g. it cures only bites, but the part does not have a bite effect) const nc_color state_col = limb_color( bp, bleed, bite, infect ); const bool has_curable_effect = state_col != c_ltgray; // The same as in the main UI sidebar. Independent of the capability of the healing item! const nc_color all_state_col = limb_color( bp, true, true, true ); const bool has_any_effect = all_state_col != c_ltgray; // Broken means no HP can be restored, it requires surgical attention. const bool limb_is_broken = current_hp == 0; // This considers only the effects that can *not* be removed. const nc_color new_state_col = limb_color( bp, !bleed, !bite, !infect ); if( show_all ) { e.allowed = true; } else if( has_curable_effect ) { e.allowed = true; } else if( limb_is_broken ) { continue; } else if( current_hp < maximal_hp && e.bonus != 0 ) { e.allowed = true; } else { continue; } const int line = i + y_off; const nc_color color = show_all ? c_green : state_col; mvwprintz( hp_window, line, 1, color, "%d: %s", i + 1, e.name.c_str() ); const auto print_hp = [&]( const int x, const nc_color col, const int hp ) { const auto bar = get_hp_bar( hp, maximal_hp, false ); if( hp == 0 ) { mvwprintz( hp_window, line, x, col, "-----" ); } else if( precise ) { mvwprintz( hp_window, line, x, col, "%5d", hp ); } else { mvwprintz( hp_window, line, x, col, bar.first.c_str() ); } }; if( !limb_is_broken ) { // Drop the bar color, use the state color instead const nc_color color = has_any_effect ? all_state_col : c_green; print_hp( 15, color, current_hp ); } else { // But still could be infected or bleeding const nc_color color = has_any_effect ? all_state_col : c_dkgray; print_hp( 15, color, 0 ); } if( !limb_is_broken ) { const int new_hp = std::max( 0, std::min( maximal_hp, current_hp + bonus ) ); if( new_hp == current_hp && !has_curable_effect ) { // Nothing would change continue; } mvwprintz( hp_window, line, 20, c_dkgray, " -> " ); const nc_color color = has_any_effect ? new_state_col : c_green; print_hp( 24, color, new_hp ); } else { const nc_color color = has_any_effect ? new_state_col : c_dkgray; mvwprintz( hp_window, line, 20, c_dkgray, " -> " ); print_hp( 24, color, 0 ); } } mvwprintz( hp_window, parts.size() + y_off, 1, c_ltgray, _("%d: Exit"), parts.size() + 1 ); wrefresh(hp_window); char ch; hp_part healed_part = num_hp_parts; do { ch = getch(); const size_t index = ch - '1'; if( index < parts.size() && parts[index].allowed ) { healed_part = parts[index].hp; break; } else if( index == parts.size() || ch == KEY_ESCAPE) { healed_part = num_hp_parts; break; } } while (ch < '1' || ch > '7'); werase(hp_window); wrefresh(hp_window); delwin(hp_window); refresh(); return healed_part; }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int veh_root_part = 0; int cargo_part = -1; vehicle *veh = g->m.veh_at( pos, veh_root_part ); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, veh_root_part ) ) { case DONE: return; case ITEMS_FROM_CARGO: cargo_part = veh->part_with_feature( veh_root_part, "CARGO", false ); from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. if( g->m.has_flag( "SEALED", pos ) ) { return; } break; } } if( !from_vehicle ) { bool isEmpty = ( g->m.i_at( pos ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here // but water. if( ( !isEmpty ) && g->m.furn( pos ) == f_toilet ) { isEmpty = true; for( auto maybe_water : g->m.i_at( pos ) ) { if( maybe_water.typeId() != "water" ) { isEmpty = false; break; } } } if( isEmpty && ( min != -1 || !OPTIONS["AUTO_PICKUP_ADJACENT"] ) ) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items( cargo_part ); here.resize( vehitems.size() ); std::copy( vehitems.rbegin(), vehitems.rend(), here.begin() ); } else { auto mapitems = g->m.i_at( pos ); here.resize( mapitems.size() ); std::copy( mapitems.rbegin(), mapitems.rend(), here.begin() ); } if( min == -1 ) { if( g->check_zone( "NO_AUTO_PICKUP", pos ) ) { here.clear(); } // Recursively pick up adjacent items if that option is on. if( OPTIONS["AUTO_PICKUP_ADJACENT"] && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; if( g->m.has_flag( "SEALED", apos ) ) { continue; } if( g->check_zone( "NO_AUTO_PICKUP", apos ) ) { continue; } pick_up( apos, min ); } } } // Not many items, just grab them if( ( int )here.size() <= min && min != -1 ) { g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } if( min != -1 ) { // don't bother if we're just autopickup-ing g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy( g->w_messages ) - 3; int itemsH = std::min( 25, TERMY / 2 ); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if( maxmaxitems > TERMY - minleftover ) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<pickup_count> getitem( here.size() ); int maxitems = here.size(); maxitems = ( maxitems < minmaxitems ? minmaxitems : ( maxitems > maxmaxitems ? maxmaxitems : maxitems ) ); int itemcount = 0; if( min == -1 ) { //Auto Pickup, select matching items if( !select_autopickup_items( here, getitem ) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx( g->w_messages ); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx( g->w_messages ); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; WINDOW *w_pickup = newwin( pickupH, pickupW, pickupY, pickupX ); WINDOW *w_item_info = newwin( itemsH, itemsW, itemsY, itemsX ); WINDOW_PTR w_pickupptr( w_pickup ); WINDOW_PTR w_item_infoptr( w_item_info ); std::string action; long raw_input_char = ' '; input_context ctxt( "PICKUP" ); ctxt.register_action( "UP" ); ctxt.register_action( "DOWN" ); ctxt.register_action( "RIGHT" ); ctxt.register_action( "LEFT" ); ctxt.register_action( "NEXT_TAB", _( "Next page" ) ); ctxt.register_action( "PREV_TAB", _( "Previous page" ) ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "SELECT_ALL" ); ctxt.register_action( "QUIT", _( "Cancel" ) ); ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); int start = 0, cur_it; player pl_copy = g->u; pl_copy.set_fake( true ); bool update = true; mvwprintw( w_pickup, 0, 0, _( "PICK UP" ) ); int selected = 0; int iScrollPos = 0; if( g->was_fullscreen ) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { const std::string pickup_chars = ctxt.get_available_single_char_hotkeys( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;" ); int idx = -1; for( int i = 1; i < pickupH; i++ ) { mvwprintw( w_pickup, i, 0, " " ); } if( action == "ANY_INPUT" && raw_input_char >= '0' && raw_input_char <= '9' ) { int raw_input_char_value = ( char )raw_input_char - '0'; itemcount *= 10; itemcount += raw_input_char_value; if( itemcount < 0 ) { itemcount = 0; } } else if( action == "SCROLL_UP" ) { iScrollPos--; } else if( action == "SCROLL_DOWN" ) { iScrollPos++; } else if( action == "PREV_TAB" ) { if( start > 0 ) { start -= maxitems; } else { start = ( int )( ( here.size() - 1 ) / maxitems ) * maxitems; } selected = start; mvwprintw( w_pickup, maxitems + 2, 0, " " ); } else if( action == "NEXT_TAB" ) { if( start + maxitems < ( int )here.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw( w_pickup, maxitems + 2, pickupH, " " ); } else if( action == "UP" ) { selected--; iScrollPos = 0; if( selected < 0 ) { selected = here.size() - 1; start = ( int )( here.size() / maxitems ) * maxitems; if( start >= ( int )here.size() ) { start -= maxitems; } } else if( selected < start ) { start -= maxitems; } } else if( action == "DOWN" ) { selected++; iScrollPos = 0; if( selected >= ( int )here.size() ) { selected = 0; start = 0; } else if( selected >= start + maxitems ) { start += maxitems; } } else if( selected >= 0 && ( ( action == "RIGHT" && !getitem[selected] ) || ( action == "LEFT" && getitem[selected] ) ) ) { idx = selected; } else if( action == "ANY_INPUT" && raw_input_char == '`' ) { std::string ext = string_input_popup( _( "Enter 2 letters (case sensitive):" ), 3, "", "", "", 2 ); if( ext.size() == 2 ) { int p1 = pickup_chars.find( ext.at( 0 ) ); int p2 = pickup_chars.find( ext.at( 1 ) ); if( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else if( action == "ANY_INPUT" ) { idx = ( raw_input_char <= 127 ) ? pickup_chars.find( raw_input_char ) : -1; iScrollPos = 0; } if( idx >= 0 && idx < ( int )here.size() ) { if( getitem[idx] ) { if( here[idx].count_by_charges() ) { if( getitem[idx].count == 0 ) { pl_copy.inv.find_item( getitem[idx].position ).charges -= here[idx].charges; } else { pl_copy.inv.find_item( getitem[idx].position ).charges -= getitem[idx].count; } } else { unsigned stack_size = pl_copy.inv.const_stack( getitem[idx].position ).size(); pl_copy.i_rem( getitem[idx].position ); //if the stack_was emptied, removing the item invalidated later positions- fix them if( stack_size == 1 ) { for( unsigned i = 0; i < here.size(); i++ ) { if( getitem[i] && getitem[i].position > getitem[idx].position ) { getitem[i].position--; } } } } } //end if getitem[idx] if( itemcount != 0 || getitem[idx].count == 0 ) { if( itemcount >= here[idx].charges || !here[idx].count_by_charges() ) { // Ignore the count if we pickup the whole stack anyway // or something that is not counted by charges (tools) itemcount = 0; } getitem[idx].count = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[idx].pick = ( action == "RIGHT" ? true : ( action == "LEFT" ? false : !getitem[idx] ) ); if( action != "RIGHT" && action != "LEFT" ) { selected = idx; start = ( int )( idx / maxitems ) * maxitems; } if( getitem[idx] ) { item temp = here[idx]; if( getitem[idx].count != 0 && getitem[idx].count < here[idx].charges ) { temp.charges = getitem[idx].count; } item *added = &( pl_copy.i_add( temp ) ); getitem[idx].position = pl_copy.inv.position_by_item( added ); } else { getitem[idx].count = 0; } update = true; } werase( w_item_info ); if( selected >= 0 && selected <= ( int )here.size() - 1 ) { std::vector<iteminfo> vThisItem, vDummy; here[selected].info( true, vThisItem ); draw_item_info( w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true ); } draw_custom_border( w_item_info, false ); mvwprintw( w_item_info, 0, 2, "< " ); trim_and_print( w_item_info, 0, 4, itemsW - 8, c_white, "%s >", here[selected].display_name().c_str() ); wrefresh( w_item_info ); if( action == "SELECT_ALL" ) { int count = 0; for( size_t i = 0; i < here.size(); i++ ) { if( getitem[i] ) { count++; } else { item *added = &( pl_copy.i_add( here[i] ) ); getitem[i].position = pl_copy.inv.position_by_item( added ); } getitem[i].pick = true; } if( count == ( int )here.size() ) { for( size_t i = 0; i < here.size(); i++ ) { getitem[i].pick = false; } pl_copy = g->u; pl_copy.set_fake( true ); } update = true; } for( cur_it = start; cur_it < start + maxitems; cur_it++ ) { mvwprintw( w_pickup, 1 + ( cur_it % maxitems ), 0, " " ); if( cur_it < ( int )here.size() ) { nc_color icolor = here[cur_it].color_in_inventory(); if( cur_it == selected ) { icolor = hilite( icolor ); } if( cur_it < ( int )pickup_chars.size() ) { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, char( pickup_chars[cur_it] ) ); } else if( cur_it < ( int )pickup_chars.size() + ( int )pickup_chars.size() * ( int )pickup_chars.size() ) { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, "`%c%c", char( pickup_chars[p1] ), char( pickup_chars[p2] ) ); } else { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, ' ' ); } if( getitem[cur_it] ) { if( getitem[cur_it].count == 0 ) { wprintz( w_pickup, c_ltblue, " + " ); } else { wprintz( w_pickup, c_ltblue, " # " ); } } else { wprintw( w_pickup, " - " ); } std::string item_name = here[cur_it].display_name(); if( OPTIONS["ITEM_SYMBOLS"] ) { item_name = string_format( "%s %s", here[cur_it].symbol().c_str(), item_name.c_str() ); } trim_and_print( w_pickup, 1 + ( cur_it % maxitems ), 6, pickupW - 4, icolor, "%s", item_name.c_str() ); } } mvwprintw( w_pickup, maxitems + 1, 0, _( "[%s] Unmark" ), ctxt.get_desc( "LEFT", 1 ).c_str() ); center_print( w_pickup, maxitems + 1, c_ltgray, _( "[%s] Help" ), ctxt.get_desc( "HELP_KEYBINDINGS", 1 ).c_str() ); right_print( w_pickup, maxitems + 1, 0, c_ltgray, _( "[%s] Mark" ), ctxt.get_desc( "RIGHT", 1 ).c_str() ); mvwprintw( w_pickup, maxitems + 2, 0, _( "[%s] Prev" ), ctxt.get_desc( "PREV_TAB", 1 ).c_str() ); center_print( w_pickup, maxitems + 2, c_ltgray, _( "[%s] All" ), ctxt.get_desc( "SELECT_ALL", 1 ).c_str() ); right_print( w_pickup, maxitems + 2, 0, c_ltgray, _( "[%s] Next" ), ctxt.get_desc( "NEXT_TAB", 1 ).c_str() ); if( update ) { // Update weight & volume information update = false; for( int i = 9; i < pickupW; ++i ) { mvwaddch( w_pickup, 0, i, ' ' ); } mvwprintz( w_pickup, 0, 9, ( pl_copy.weight_carried() > g->u.weight_capacity() ? c_red : c_white ), _( "Wgt %.1f" ), convert_weight( pl_copy.weight_carried() ) + 0.05 ); // +0.05 to round up wprintz( w_pickup, c_white, "/%.1f", convert_weight( g->u.weight_capacity() ) ); mvwprintz( w_pickup, 0, 24, ( pl_copy.volume_carried() > g->u.volume_capacity() ? c_red : c_white ), _( "Vol %d" ), pl_copy.volume_carried() ); wprintz( w_pickup, c_white, "/%d", g->u.volume_capacity() ); } wrefresh( w_pickup ); action = ctxt.handle_input(); raw_input_char = ctxt.get_raw_input().get_first_input(); } while( action != "QUIT" && action != "CONFIRM" ); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection ) { item_selected = true; } } if( action != "CONFIRM" || !item_selected ) { w_pickupptr.reset(); w_item_infoptr.reset(); add_msg( _( "Never mind." ) ); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } std::reverse( getitem.begin(), getitem.end() ); for( size_t i = 0; i < here.size(); i++ ) { if( getitem[i] ) { g->u.activity.values.push_back( i ); g->u.activity.values.push_back( getitem[i].count ); } } g->reenter_fullscreen(); }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int veh_root_part = 0; int cargo_part = -1; vehicle *veh = g->m.veh_at (pos, veh_root_part); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, veh_root_part ) ) { case DONE: return; case ITEMS_FROM_CARGO: cargo_part = veh->part_with_feature( veh_root_part, "CARGO", false ); from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. break; } } if (g->m.has_flag("SEALED", pos)) { return; } //min == -1 is Autopickup if (!g->u.can_pickup(min != -1)) { // no message on autopickup (-1) return; } if( !from_vehicle ) { bool isEmpty = (g->m.i_at(pos).empty()); // Hide the pickup window if this is a toilet and there's nothing here // but water. if ((!isEmpty) && g->m.furn(pos) == f_toilet) { isEmpty = true; for( auto maybe_water : g->m.i_at(pos) ) { if( maybe_water.typeId() != "water") { isEmpty = false; break; } } } if (isEmpty && (min != -1 || !OPTIONS["AUTO_PICKUP_ADJACENT"] )) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items(cargo_part); here.resize( vehitems.size() ); std::copy( vehitems.begin(), vehitems.end(), here.begin() ); } else { auto mapitems = g->m.i_at(pos); here.resize( mapitems.size() ); std::copy( mapitems.begin(), mapitems.end(), here.begin() ); } if (min == -1) { if( g->check_zone( "NO_AUTO_PICKUP", pos ) ) { here.clear(); } // Recursively pick up adjacent items if that option is on. if( OPTIONS["AUTO_PICKUP_ADJACENT"] && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; if( g->m.has_flag( "SEALED", apos ) ) { continue; } if( g->check_zone( "NO_AUTO_PICKUP", apos ) ) { continue; } pick_up( apos, min ); } } } // Not many items, just grab them if ((int)here.size() <= min && min != -1) { g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } if(min != -1) { // don't bother if we're just autopickup-ing g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy(g->w_messages) - 3; int itemsH = std::min(25, TERMY / 2); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if(maxmaxitems > TERMY - minleftover) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<bool> getitem; getitem.resize(here.size(), false); int maxitems = here.size(); maxitems = (maxitems < minmaxitems ? minmaxitems : (maxitems > maxmaxitems ? maxmaxitems : maxitems )); int itemcount = 0; std::map<int, unsigned int> pickup_count; // Count of how many we'll pick up from each stack if (min == -1) { //Auto Pickup, select matching items if( !select_autopickup_items( here, getitem) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx(g->w_messages); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx(g->w_messages); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; WINDOW *w_pickup = newwin(pickupH, pickupW, pickupY, pickupX); WINDOW *w_item_info = newwin(itemsH, itemsW, itemsY, itemsX); WINDOW_PTR w_pickupptr( w_pickup ); WINDOW_PTR w_item_infoptr( w_item_info ); int ch = ' '; int start = 0, cur_it; int new_weight = g->u.weight_carried(), new_volume = g->u.volume_carried(); bool update = true; mvwprintw(w_pickup, 0, 0, _("PICK UP")); int selected = 0; int iScrollPos = 0; if(g->was_fullscreen) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { static const std::string pickup_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;"; int idx = -1; for (int i = 1; i < pickupH; i++) { mvwprintw(w_pickup, i, 0, " "); } if (ch >= '0' && ch <= '9') { ch = (char)ch - '0'; itemcount *= 10; itemcount += ch; if( itemcount < 0 ) { itemcount = 0; } } else if ( ch == KEY_PPAGE) { iScrollPos--; } else if ( ch == KEY_NPAGE) { iScrollPos++; } else if ( ch == '<' ) { if ( start > 0 ) { start -= maxitems; } else { start = (int)( (here.size()-1) / maxitems ) * maxitems; } selected = start; mvwprintw(w_pickup, maxitems + 2, 0, " "); } else if ( ch == '>' ) { if ( start + maxitems < (int)here.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw(w_pickup, maxitems + 2, pickupH, " "); } else if ( ch == KEY_UP ) { selected--; iScrollPos = 0; if ( selected < 0 ) { selected = here.size() - 1; start = (int)( here.size() / maxitems ) * maxitems; if (start >= (int)here.size()) { start -= maxitems; } } else if ( selected < start ) { start -= maxitems; } } else if ( ch == KEY_DOWN ) { selected++; iScrollPos = 0; if ( selected >= (int)here.size() ) { selected = 0; start = 0; } else if ( selected >= start + maxitems ) { start += maxitems; } } else if ( selected >= 0 && ( ( ch == KEY_RIGHT && !getitem[selected]) || ( ch == KEY_LEFT && getitem[selected] ) ) ) { idx = selected; } else if ( ch == '`' ) { std::string ext = string_input_popup( _("Enter 2 letters (case sensitive):"), 3, "", "", "", 2); if(ext.size() == 2) { int p1 = pickup_chars.find(ext.at(0)); int p2 = pickup_chars.find(ext.at(1)); if ( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else { idx = ( ch <= 127 ) ? pickup_chars.find(ch) : -1; iScrollPos = 0; } if( idx >= 0 && idx < (int)here.size()) { if( getitem[idx] ) { if( pickup_count[idx] != 0 && (int)pickup_count[idx] < here[idx].charges ) { item temp = here[idx]; temp.charges = pickup_count[idx]; new_weight -= temp.weight(); new_volume -= temp.volume(); } else { new_weight -= here[idx].weight(); new_volume -= here[idx].volume(); } } if (itemcount != 0 || pickup_count[idx] == 0) { if (itemcount >= here[idx].charges || !here[idx].count_by_charges()) { // Ignore the count if we pickup the whole stack anyway // or something that is not counted by charges (tools) itemcount = 0; } pickup_count[idx] = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[idx] = ( ch == KEY_RIGHT ? true : ( ch == KEY_LEFT ? false : !getitem[idx] ) ); if ( ch != KEY_RIGHT && ch != KEY_LEFT) { selected = idx; start = (int)( idx / maxitems ) * maxitems; } if (getitem[idx]) { if (pickup_count[idx] != 0 && (int)pickup_count[idx] < here[idx].charges) { item temp = here[idx]; temp.charges = pickup_count[idx]; new_weight += temp.weight(); new_volume += temp.volume(); } else { new_weight += here[idx].weight(); new_volume += here[idx].volume(); } } else { pickup_count[idx] = 0; } update = true; } werase(w_item_info); if ( selected >= 0 && selected <= (int)here.size() - 1 ) { std::vector<iteminfo> vThisItem, vDummy; here[selected].info(true, vThisItem); draw_item_info(w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true); } draw_custom_border(w_item_info, false); mvwprintw(w_item_info, 0, 2, "< "); trim_and_print(w_item_info, 0, 4, itemsW - 8, c_white, "%s >", here[selected].display_name().c_str()); wrefresh(w_item_info); if (ch == ',') { int count = 0; for (size_t i = 0; i < here.size(); i++) { if (getitem[i]) { count++; } else { new_weight += here[i].weight(); new_volume += here[i].volume(); } getitem[i] = true; } if (count == (int)here.size()) { for (size_t i = 0; i < here.size(); i++) { getitem[i] = false; } new_weight = g->u.weight_carried(); new_volume = g->u.volume_carried(); } update = true; } for (cur_it = start; cur_it < start + maxitems; cur_it++) { mvwprintw(w_pickup, 1 + (cur_it % maxitems), 0, " "); if (cur_it < (int)here.size()) { nc_color icolor = here[cur_it].color_in_inventory(); if (cur_it == selected) { icolor = hilite(icolor); } if (cur_it < (int)pickup_chars.size() ) { mvwputch(w_pickup, 1 + (cur_it % maxitems), 0, icolor, char(pickup_chars[cur_it])); } else { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz(w_pickup, 1 + (cur_it % maxitems), 0, icolor, "`%c%c", char(pickup_chars[p1]), char(pickup_chars[p2])); } if (getitem[cur_it]) { if (pickup_count[cur_it] == 0) { wprintz(w_pickup, c_ltblue, " + "); } else { wprintz(w_pickup, c_ltblue, " # "); } } else { wprintw(w_pickup, " - "); } std::string item_name = here[cur_it].display_name(); if (OPTIONS["ITEM_SYMBOLS"]) { item_name = string_format("%c %s", here[cur_it].symbol(), item_name.c_str()); } trim_and_print(w_pickup, 1 + (cur_it % maxitems), 6, pickupW - 4, icolor, "%s", item_name.c_str()); } } int pw = pickupW; const char *unmark = _("[left] Unmark"); const char *scroll = _("[up/dn] Scroll"); const char *mark = _("[right] Mark"); mvwprintw(w_pickup, maxitems + 1, 0, unmark); mvwprintw(w_pickup, maxitems + 1, (pw - std::strlen(scroll)) / 2, scroll); mvwprintw(w_pickup, maxitems + 1, pw - std::strlen(mark), mark); const char *prev = _("[<] Prev"); const char *all = _("[,] All"); const char *next = _("[>] Next"); mvwprintw(w_pickup, maxitems + 2, 0, prev); mvwprintw(w_pickup, maxitems + 2, (pw - std::strlen(all)) / 2, all); mvwprintw(w_pickup, maxitems + 2, pw - std::strlen(next), next); if (update) { // Update weight & volume information update = false; for (int i = 9; i < pickupW; ++i) { mvwaddch(w_pickup, 0, i, ' '); } mvwprintz(w_pickup, 0, 9, (new_weight > g->u.weight_capacity() ? c_red : c_white), _("Wgt %.1f"), g->u.convert_weight(new_weight) + 0.05); // +0.05 to round up wprintz(w_pickup, c_white, "/%.1f", g->u.convert_weight(g->u.weight_capacity())); mvwprintz(w_pickup, 0, 24, (new_volume > g->u.volume_capacity() ? c_red : c_white), _("Vol %d"), new_volume); wprintz(w_pickup, c_white, "/%d", g->u.volume_capacity()); } wrefresh(w_pickup); ch = (int)getch(); } while (ch != ' ' && ch != '\n' && ch != KEY_ENTER && ch != KEY_ESCAPE); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection ) { item_selected = true; } } if( (ch != '\n' && ch != KEY_ENTER) || !item_selected ) { w_pickupptr.reset(); w_item_infoptr.reset(); add_msg(_("Never mind.")); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( ACT_PICKUP, 0 ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } for (size_t i = 0; i < here.size(); i++) { if( getitem[i] ) { g->u.activity.values.push_back( i ); g->u.activity.values.push_back( pickup_count[i] ); } } g->reenter_fullscreen(); }
void player::power_bionics() { std::vector <bionic *> passive = filtered_bionics( *my_bionics, TAB_PASSIVE ); std::vector <bionic *> active = filtered_bionics( *my_bionics, TAB_ACTIVE ); bionic *bio_last = NULL; bionic_tab_mode tab_mode = TAB_ACTIVE; //added title_tab_height for the tabbed bionic display int TITLE_HEIGHT = 2; int TITLE_TAB_HEIGHT = 3; // Main window /** Total required height is: * top frame line: + 1 * height of title window: + TITLE_HEIGHT * height of tabs: + TITLE_TAB_HEIGHT * height of the biggest list of active/passive bionics: + bionic_count * bottom frame line: + 1 * TOTAL: TITLE_HEIGHT + TITLE_TAB_HEIGHT + bionic_count + 2 */ const int HEIGHT = std::min( TERMY, std::max( FULL_SCREEN_HEIGHT, TITLE_HEIGHT + TITLE_TAB_HEIGHT + ( int )my_bionics->size() + 2 ) ); const int WIDTH = FULL_SCREEN_WIDTH + ( TERMX - FULL_SCREEN_WIDTH ) / 2; const int START_X = ( TERMX - WIDTH ) / 2; const int START_Y = ( TERMY - HEIGHT ) / 2; //wBio is the entire bionic window catacurses::window wBio = catacurses::newwin( HEIGHT, WIDTH, START_Y, START_X ); const int LIST_HEIGHT = HEIGHT - TITLE_HEIGHT - TITLE_TAB_HEIGHT - 2; const int DESCRIPTION_WIDTH = WIDTH - 2 - 40; const int DESCRIPTION_START_Y = START_Y + TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; const int DESCRIPTION_START_X = START_X + 1 + 40; //w_description is the description panel that is controlled with ! key catacurses::window w_description = catacurses::newwin( LIST_HEIGHT, DESCRIPTION_WIDTH, DESCRIPTION_START_Y, DESCRIPTION_START_X ); // Title window const int TITLE_START_Y = START_Y + 1; const int HEADER_LINE_Y = TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; catacurses::window w_title = catacurses::newwin( TITLE_HEIGHT, WIDTH - 2, TITLE_START_Y, START_X + 1 ); const int TAB_START_Y = TITLE_START_Y + 2; //w_tabs is the tab bar for passive and active bionic groups catacurses::window w_tabs = catacurses::newwin( TITLE_TAB_HEIGHT, WIDTH - 2, TAB_START_Y, START_X + 1 ); int scroll_position = 0; int cursor = 0; //generate the tab title string and a count of the bionics owned bionic_menu_mode menu_mode = ACTIVATING; // offset for display: bionic with index i is drawn at y=list_start_y+i // drawing the bionics starts with bionic[scroll_position] const int list_start_y = HEADER_LINE_Y;// - scroll_position; int half_list_view_location = LIST_HEIGHT / 2; int max_scroll_position = std::max( 0, ( int )active.size() ); input_context ctxt( "BIONICS" ); ctxt.register_updown(); ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "TOGGLE_EXAMINE" ); ctxt.register_action( "REASSIGN" ); ctxt.register_action( "REMOVE" ); ctxt.register_action( "NEXT_TAB" ); ctxt.register_action( "PREV_TAB" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "HELP_KEYBINDINGS" ); bool recalc = false; bool redraw = true; for( ;; ) { if( recalc ) { passive = filtered_bionics( *my_bionics, TAB_PASSIVE ); active = filtered_bionics( *my_bionics, TAB_ACTIVE ); if( active.empty() && !passive.empty() ) { tab_mode = TAB_PASSIVE; } if( --cursor < 0 ) { cursor = 0; } if( scroll_position > max_scroll_position && cursor - scroll_position < LIST_HEIGHT - half_list_view_location ) { scroll_position--; } recalc = false; // bionics were modified, so it's necessary to redraw the screen redraw = true; } //track which list we are looking at std::vector<bionic *> *current_bionic_list = ( tab_mode == TAB_ACTIVE ? &active : &passive ); max_scroll_position = std::max( 0, ( int )current_bionic_list->size() - LIST_HEIGHT ); if( redraw ) { redraw = false; werase( wBio ); draw_border( wBio, BORDER_COLOR, _( " BIONICS " ) ); // Draw symbols to connect additional lines to border mvwputch( wBio, HEADER_LINE_Y - 1, 0, BORDER_COLOR, LINE_XXXO ); // |- mvwputch( wBio, HEADER_LINE_Y - 1, WIDTH - 1, BORDER_COLOR, LINE_XOXX ); // -| int max_width = 0; std::vector<std::string>bps; for( int i = 0; i < num_bp; ++i ) { const body_part bp = bp_aBodyPart[i]; const int total = get_total_bionics_slots( bp ); const std::string s = string_format( "%s: %d/%d", body_part_name_as_heading( bp, 1 ).c_str(), total - get_free_bionics_slots( bp ), total ); bps.push_back( s ); max_width = std::max( max_width, utf8_width( s ) ); } const int pos_x = WIDTH - 2 - max_width; if( g->u.has_trait( trait_id( "DEBUG_CBM_SLOTS" ) ) ) { for( int i = 0; i < num_bp; ++i ) { mvwprintz( wBio, i + list_start_y, pos_x, c_light_gray, bps[i] ); } } if( current_bionic_list->empty() ) { std::string msg; switch( tab_mode ) { case TAB_ACTIVE: msg = _( "No activatable bionics installed." ); break; case TAB_PASSIVE: msg = _( "No passive bionics installed." ); break; } fold_and_print( wBio, list_start_y, 2, pos_x - 1, c_light_gray, msg ); } else { for( size_t i = scroll_position; i < current_bionic_list->size(); i++ ) { if( list_start_y + static_cast<int>( i ) - scroll_position == HEIGHT - 1 ) { break; } const bool is_highlighted = cursor == static_cast<int>( i ); const nc_color col = get_bionic_text_color( *( *current_bionic_list )[i], is_highlighted ); const std::string desc = string_format( "%c %s", ( *current_bionic_list )[i]->invlet, build_bionic_powerdesc_string( *( *current_bionic_list )[i] ).c_str() ); trim_and_print( wBio, list_start_y + i - scroll_position, 2, WIDTH - 3, col, desc ); if( is_highlighted && menu_mode != EXAMINING && g->u.has_trait( trait_id( "DEBUG_CBM_SLOTS" ) ) ) { const bionic_id bio_id = ( *current_bionic_list )[i]->id; draw_connectors( wBio, list_start_y + i - scroll_position, utf8_width( desc ) + 3, pos_x - 2, bio_id ); // redraw highlighted (occupied) body parts for( auto &elem : bio_id->occupied_bodyparts ) { const int i = static_cast<int>( elem.first ); mvwprintz( wBio, i + list_start_y, pos_x, c_yellow, bps[i] ); } } } } draw_scrollbar( wBio, cursor, LIST_HEIGHT, current_bionic_list->size(), list_start_y ); } wrefresh( wBio ); draw_bionics_tabs( w_tabs, active.size(), passive.size(), tab_mode ); draw_bionics_titlebar( w_title, this, menu_mode ); if( menu_mode == EXAMINING && !current_bionic_list->empty() ) { draw_description( w_description, *( *current_bionic_list )[cursor] ); } const std::string action = ctxt.handle_input(); const long ch = ctxt.get_raw_input().get_first_input(); bionic *tmp = NULL; bool confirmCheck = false; if( action == "DOWN" ) { redraw = true; if( static_cast<size_t>( cursor ) < current_bionic_list->size() - 1 ) { cursor++; } else { cursor = 0; } if( scroll_position < max_scroll_position && cursor - scroll_position > LIST_HEIGHT - half_list_view_location ) { scroll_position++; } if( scroll_position > 0 && cursor - scroll_position < half_list_view_location ) { scroll_position = std::max( cursor - half_list_view_location, 0 ); } } else if( action == "UP" ) { redraw = true; if( cursor > 0 ) { cursor--; } else { cursor = current_bionic_list->size() - 1; } if( scroll_position > 0 && cursor - scroll_position < half_list_view_location ) { scroll_position--; } if( scroll_position < max_scroll_position && cursor - scroll_position > LIST_HEIGHT - half_list_view_location ) { scroll_position = std::max( std::min<int>( current_bionic_list->size() - LIST_HEIGHT, cursor - half_list_view_location ), 0 ); } } else if( menu_mode == REASSIGNING ) { menu_mode = ACTIVATING; if( action == "CONFIRM" && !current_bionic_list->empty() ) { auto &bio_list = tab_mode == TAB_ACTIVE ? active : passive; tmp = bio_list[cursor]; } else { tmp = bionic_by_invlet( ch ); } if( tmp == nullptr ) { // Selected an non-existing bionic (or Escape, or ...) continue; } redraw = true; const long newch = popup_getkey( _( "%s; enter new letter. Space to clear. Esc to cancel." ), tmp->id->name.c_str() ); wrefresh( wBio ); if( newch == ch || newch == KEY_ESCAPE ) { continue; } if( newch == ' ' ) { tmp->invlet = ' '; continue; } if( !bionic_chars.valid( newch ) ) { popup( _( "Invalid bionic letter. Only those characters are valid:\n\n%s" ), bionic_chars.get_allowed_chars().c_str() ); continue; } bionic *otmp = bionic_by_invlet( newch ); if( otmp != nullptr ) { std::swap( tmp->invlet, otmp->invlet ); } else { tmp->invlet = newch; } // TODO: show a message like when reassigning a key to an item? } else if( action == "NEXT_TAB" ) { redraw = true; scroll_position = 0; cursor = 0; if( tab_mode == TAB_ACTIVE ) { tab_mode = TAB_PASSIVE; } else { tab_mode = TAB_ACTIVE; } } else if( action == "PREV_TAB" ) { redraw = true; scroll_position = 0; cursor = 0; if( tab_mode == TAB_PASSIVE ) { tab_mode = TAB_ACTIVE; } else { tab_mode = TAB_PASSIVE; } } else if( action == "REASSIGN" ) { menu_mode = REASSIGNING; } else if( action == "TOGGLE_EXAMINE" ) { // switches between activation and examination menu_mode = menu_mode == ACTIVATING ? EXAMINING : ACTIVATING; redraw = true; } else if( action == "REMOVE" ) { menu_mode = REMOVING; redraw = true; } else if( action == "HELP_KEYBINDINGS" ) { redraw = true; } else if( action == "CONFIRM" ) { confirmCheck = true; } else { confirmCheck = true; } //confirmation either occurred by pressing enter where the bionic cursor is, or the hotkey was selected if( confirmCheck ) { auto &bio_list = tab_mode == TAB_ACTIVE ? active : passive; if( action == "CONFIRM" && !current_bionic_list->empty() ) { tmp = bio_list[cursor]; } else { tmp = bionic_by_invlet( ch ); if( tmp && tmp != bio_last ) { // new bionic selected, update cursor and scroll position int temp_cursor = 0; for( temp_cursor = 0; temp_cursor < ( int )bio_list.size(); temp_cursor++ ) { if( bio_list[temp_cursor] == tmp ) { break; } } // if bionic is not found in current list, ignore the attempt to view/activate if( temp_cursor >= ( int )bio_list.size() ) { continue; } //relocate cursor to the bionic that was found cursor = temp_cursor; scroll_position = 0; while( scroll_position < max_scroll_position && cursor - scroll_position > LIST_HEIGHT - half_list_view_location ) { scroll_position++; } } } if( !tmp ) { // entered a key that is not mapped to any bionic, // -> leave screen break; } bio_last = tmp; const bionic_id &bio_id = tmp->id; const bionic_data &bio_data = bio_id.obj(); if( menu_mode == REMOVING ) { recalc = uninstall_bionic( bio_id ); redraw = true; continue; } if( menu_mode == ACTIVATING ) { if( bio_data.activated ) { int b = tmp - &( *my_bionics )[0]; if( tmp->powered ) { deactivate_bionic( b ); } else { activate_bionic( b ); // Clear the menu if we are firing a bionic gun if( tmp->info().gun_bionic ) { break; } } // update message log and the menu g->refresh_all(); redraw = true; if( moves < 0 ) { return; } continue; } else { popup( _( "You can not activate %s!\n" "To read a description of %s, press '!', then '%c'." ), bio_data.name.c_str(), bio_data.name.c_str(), tmp->invlet ); redraw = true; } } else if( menu_mode == EXAMINING ) { // Describing bionics, allow user to jump to description key redraw = true; if( action != "CONFIRM" ) { for( size_t i = 0; i < active.size(); i++ ) { if( active[i] == tmp ) { tab_mode = TAB_ACTIVE; cursor = static_cast<int>( i ); int max_scroll_check = std::max( 0, ( int )active.size() - LIST_HEIGHT ); if( static_cast<int>( i ) > max_scroll_check ) { scroll_position = max_scroll_check; } else { scroll_position = i; } break; } } for( size_t i = 0; i < passive.size(); i++ ) { if( passive[i] == tmp ) { tab_mode = TAB_PASSIVE; cursor = static_cast<int>( i ); int max_scroll_check = std::max( 0, ( int )passive.size() - LIST_HEIGHT ); if( static_cast<int>( i ) > max_scroll_check ) { scroll_position = max_scroll_check; } else { scroll_position = i; } break; } } } } } } }
/* * Generate and refresh output */ void uimenu::show() { if (!started) { setup(); } std::string padspaces = std::string(w_width - 2 - pad_left - pad_right, ' '); const int text_lines = textformatted.size(); for ( int i = 0; i < text_lines; i++ ) { trim_and_print(window, 1 + i, 2, getmaxx(window) - 4, text_color, "%s", textformatted[i].c_str()); } mvwputch(window, text_lines + 1, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, text_lines + 1, i, border_color, LINE_OXOX); } mvwputch(window, text_lines + 1, w_width - 1, border_color, LINE_XOXX); int estart = text_lines + 2; calcStartPos( vshift, fselected, vmax, fentries.size() ); for ( int fei = vshift, si = 0; si < vmax; fei++, si++ ) { if ( fei < (int)fentries.size() ) { int ei = fentries [ fei ]; nc_color co = ( ei == selected ? hilight_color : ( entries[ ei ].enabled ? entries[ ei ].text_color : disabled_color ) ); if ( hilight_full ) { mvwprintz(window, estart + si, pad_left + 1, co , "%s", padspaces.c_str()); } if(entries[ ei ].enabled && entries[ ei ].hotkey >= 33 && entries[ ei ].hotkey < 126 ) { mvwprintz( window, estart + si, pad_left + 2, ( ei == selected ) ? hilight_color : hotkey_color , "%c", entries[ ei ].hotkey ); } if( padspaces.size() > 3 ) { // padspaces's length indicates the maximal width of the entry, it is used above to // activate the highlighting, it is used to override previous text there, but in both // cases printeing starts at pad_left+1, here it starts at pad_left+4, so 3 cells less // to be used. const auto entry = utf8_wrapper( entries[ ei ].txt ); trim_and_print( window, estart + si, pad_left + 4, w_width - 2 - pad_left - pad_right, co, "%s", entry.c_str() ); } if ( !entries[ei].extratxt.txt.empty() ) { mvwprintz( window, estart + si, pad_left + 1 + entries[ ei ].extratxt.left, entries[ ei ].extratxt.color, "%s", entries[ ei ].extratxt.txt.c_str() ); } if ( entries[ei].extratxt.sym != 0 ) { mvwputch ( window, estart + si, pad_left + 1 + entries[ ei ].extratxt.left, entries[ ei ].extratxt.color, entries[ ei ].extratxt.sym ); } if ( callback != NULL && ei == selected ) { callback->select(ei, this); } } else { mvwprintz(window, estart + si, pad_left + 1, c_ltgray , "%s", padspaces.c_str()); } } if ( desc_enabled ) { // draw border mvwputch(window, w_height - desc_lines - 2, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, w_height - desc_lines - 2, i, border_color, LINE_OXOX); } mvwputch(window, w_height - desc_lines - 2, w_width - 1, border_color, LINE_XOXX); // clear previous desc the ugly way for ( int y = desc_lines + 1; y > 1; --y ) { for ( int x = 2; x < w_width - 2; ++x) { mvwputch(window, w_height - y, x, text_color, " "); } } // draw description fold_and_print(window, w_height - desc_lines - 1, 2, w_width - 4, text_color, entries[selected].desc.c_str()); } if ( !filter.empty() ) { mvwprintz( window, w_height - 1, 2, border_color, "< %s >", filter.c_str() ); mvwprintz( window, w_height - 1, 4, text_color, "%s", filter.c_str() ); } apply_scrollbar(); this->refresh(true); }
/** * Generate and refresh output */ void uimenu::show() { if (!started) { setup(); } werase(window); draw_border(window, border_color); if( !title.empty() ) { mvwprintz(window, 0, 1, border_color, "< "); wprintz( window, title_color, title ); wprintz(window, border_color, " >"); } std::string padspaces = std::string(w_width - 2 - pad_left - pad_right, ' '); const int text_lines = textformatted.size(); int estart = 1; if( !textformatted.empty() ) { for ( int i = 0; i < text_lines; i++ ) { trim_and_print( window, 1 + i, 2, getmaxx( window ) - 4, text_color, textformatted[i] ); } mvwputch(window, text_lines + 1, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, text_lines + 1, i, border_color, LINE_OXOX); } mvwputch(window, text_lines + 1, w_width - 1, border_color, LINE_XOXX); estart += text_lines + 1; // +1 for the horizontal line. } calcStartPos( vshift, fselected, vmax, fentries.size() ); for ( int fei = vshift, si = 0; si < vmax; fei++, si++ ) { if ( fei < (int)fentries.size() ) { int ei = fentries [ fei ]; nc_color co = ( ei == selected ? hilight_color : ( entries[ ei ].enabled || entries[ei].force_color ? entries[ ei ].text_color : disabled_color ) ); if ( hilight_full ) { mvwprintz( window, estart + si, pad_left + 1, co, padspaces ); } if( entries[ ei ].hotkey >= 33 && entries[ ei ].hotkey < 126 ) { const nc_color hotkey_co = ei == selected ? hilight_color : hotkey_color; mvwprintz( window, estart + si, pad_left + 2, entries[ ei ].enabled ? hotkey_co : co, "%c", entries[ ei ].hotkey ); } if( padspaces.size() > 3 ) { // padspaces's length indicates the maximal width of the entry, it is used above to // activate the highlighting, it is used to override previous text there, but in both // cases printing starts at pad_left+1, here it starts at pad_left+4, so 3 cells less // to be used. const auto entry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].txt ) : entries[ ei ].txt ); trim_and_print( window, estart + si, pad_left + 4, max_entry_len, co, "%s", entry.c_str() ); if( max_column_len && !entries[ ei ].ctxt.empty() ) { const auto centry = utf8_wrapper( ei == selected ? remove_color_tags( entries[ ei ].ctxt ) : entries[ ei ].ctxt ); trim_and_print( window, estart + si, getmaxx( window ) - max_column_len - 2, max_column_len, co, "%s", centry.c_str() ); } } mvwzstr menu_entry_extra_text = entries[ei].extratxt; if ( !menu_entry_extra_text.txt.empty() ) { mvwprintz( window, estart + si, pad_left + 1 + menu_entry_extra_text.left, menu_entry_extra_text.color, menu_entry_extra_text.txt ); } if ( menu_entry_extra_text.sym != 0 ) { mvwputch ( window, estart + si, pad_left + 1 + menu_entry_extra_text.left, menu_entry_extra_text.color, menu_entry_extra_text.sym ); } if ( callback != NULL && ei == selected ) { callback->select(ei, this); } } else { mvwprintz( window, estart + si, pad_left + 1, c_light_gray, padspaces ); } } if ( desc_enabled ) { // draw border mvwputch(window, w_height - desc_lines - 2, 0, border_color, LINE_XXXO); for ( int i = 1; i < w_width - 1; ++i) { mvwputch(window, w_height - desc_lines - 2, i, border_color, LINE_OXOX); } mvwputch(window, w_height - desc_lines - 2, w_width - 1, border_color, LINE_XOXX); // clear previous desc the ugly way for ( int y = desc_lines + 1; y > 1; --y ) { for ( int x = 2; x < w_width - 2; ++x) { mvwputch(window, w_height - y, x, text_color, " "); } } if( static_cast<size_t>( selected ) < entries.size() ){ fold_and_print( window, w_height - desc_lines - 1, 2, w_width - 4, text_color, entries[selected].desc ); } } if ( !filter.empty() ) { mvwprintz( window, w_height - 1, 2, border_color, "< %s >", filter.c_str() ); mvwprintz( window, w_height - 1, 4, text_color, filter ); } apply_scrollbar(); this->refresh(true); }
void inventory_selector::display( const std::string &title, selector_mode mode ) const { werase(w_inv); mvwprintw(w_inv, 0, 0, title.c_str()); // Position of inventory columns is adaptive. They're aligned to the left if they occupy less than 2/3 of the screen. // Otherwise they're aligned symmetrically to the center of the screen. static const float min_ratio_to_center = 1.f / 3; const int free_space = getmaxx( w_inv ) - get_columns_width(); const bool center_align = std::abs( float( free_space ) / getmaxx( w_inv ) ) <= min_ratio_to_center; const int max_gap = ( columns.size() > 1 ) ? free_space / ( columns.size() - 1 ) : 0; const int gap = center_align ? max_gap : std::min<int>( max_gap, 4 ); const int gap_rounding_error = ( center_align && columns.size() > 1 ) ? free_space % ( columns.size() - 1 ) : 0; size_t x = 1; size_t y = 2; size_t active_x = 0; for( const auto &column : columns ) { if( &column == &columns.back() ) { x += gap_rounding_error; } if( !is_active_column( *column ) ) { column->draw( w_inv, x, y ); } else { active_x = x; } if( column->pages_count() > 1 ) { mvwprintw( w_inv, getmaxy( w_inv ) - 2, x, _( "Page %d/%d" ), column->page_index() + 1, column->pages_count() ); } x += column->get_width() + gap; } get_active_column().draw( w_inv, active_x, y ); if( mode == SM_PICK ) { mvwprintw(w_inv, 1, 61, _("Hotkeys: %d/%d "), u.allocated_invlets().size(), inv_chars.size()); } if (mode == SM_MULTIDROP) { // Make copy, remove to be dropped items from that // copy and let the copy recalculate the volume capacity // (can be affected by various traits). player tmp = u; // first round: remove weapon & worn items, start with larges worn index for( const auto &elem : dropping ) { if( elem.first == -1 && elem.second == -1 ) { tmp.remove_weapon(); } else if( elem.first == -1 && elem.second != -1 ) { tmp.weapon.charges -= elem.second; } else if( elem.first < 0 ) { tmp.i_rem( elem.first ); } } remove_dropping_items(tmp); print_inv_weight_vol(tmp.weight_carried(), tmp.volume_carried(), tmp.volume_capacity()); mvwprintw(w_inv, 1, 0, _("To drop x items, type a number and then the item hotkey.")); } else { print_inv_weight_vol(u.weight_carried(), u.volume_carried(), u.volume_capacity()); } if( empty() ) { center_print( w_inv, getmaxy( w_inv ) / 2, c_dkgray, _( "Your inventory is empty." ) ); } const std::string msg_str = ( navigation == navigation_mode::CATEGORY ) ? _( "Category selection; [TAB] switches mode, arrows select." ) : _( "Item selection; [TAB] switches mode, arrows select." ); const nc_color msg_color = ( navigation == navigation_mode::CATEGORY ) ? h_white : c_ltgray; if( center_align ) { center_print( w_inv, getmaxy( w_inv ) - 1, msg_color, msg_str.c_str() ); } else { trim_and_print( w_inv, getmaxy( w_inv ) - 1, 1, getmaxx( w_inv ), msg_color, msg_str.c_str() ); } wrefresh(w_inv); }
void string_input_popup::create_window() { nc_color title_color = c_light_red; nc_color desc_color = c_green; int titlesize = utf8_width( _title ); // Occupied horizontal space if( _max_length <= 0 ) { _max_length = _width; } // 2 for border (top and bottom) and 1 for the input text line. int w_height = 2 + 1; // |"w_width = width + titlesize (this text) + 5": _____ | int w_width = FULL_SCREEN_WIDTH; if( _width <= 0 ) { _width = std::max( 5, FULL_SCREEN_WIDTH - titlesize - 5 ); // Default if unspecified } else { _width = std::min( FULL_SCREEN_WIDTH - 20, _width ); w_width = _width + titlesize + 5; } std::vector<std::string> title_split = { _title }; if( w_width > FULL_SCREEN_WIDTH ) { // Out of horizontal space- wrap the title titlesize = FULL_SCREEN_WIDTH - _width - 5; w_width = FULL_SCREEN_WIDTH; for( int wraplen = w_width - 2; wraplen >= titlesize; wraplen-- ) { title_split = foldstring( _title, wraplen ); if( int( title_split.back().size() ) <= titlesize ) { break; } } w_height += int( title_split.size() ) - 1; } std::vector<std::string> descformatted; if( !_description.empty() ) { const int twidth = std::min( utf8_width( remove_color_tags( _description ) ), w_width - 4 ); descformatted = foldstring( _description, twidth ); w_height += descformatted.size(); } // length of title + border (left) + space _startx = titlesize + 2; // Below the description and below the top border _starty = 1 + descformatted.size(); if( _max_length <= 0 ) { _max_length = 1024; } _endx = w_width - 3; _position = -1; const int w_y = ( TERMY - w_height ) / 2; const int w_x = std::max( ( TERMX - w_width ) / 2, 0 ); w = catacurses::newwin( w_height, w_width, w_y, w_x ); draw_border( w ); for( size_t i = 0; i < descformatted.size(); ++i ) { trim_and_print( w, 1 + i, 1, w_width - 2, desc_color, descformatted[i] ); } for( int i = 0; i < int( title_split.size() ) - 1; i++ ) { mvwprintz( w, _starty++, i + 1, title_color, title_split[i] ); } right_print( w, _starty, w_width - titlesize - 1, title_color, title_split.back() ); _starty = w_height - 2; // The ____ looks better at the bottom right when the title folds }
void construction_menu() { static bool hide_unconstructable = false; // only display constructions the player can theoretically perform std::vector<std::string> available; std::map<std::string, std::vector<std::string>> cat_available; load_available_constructions( available, cat_available, hide_unconstructable ); if( available.empty() ) { popup( _( "You can not construct anything here." ) ); return; } int w_height = TERMY; if( ( int )available.size() + 2 < w_height ) { w_height = available.size() + 2; } if( w_height < FULL_SCREEN_HEIGHT ) { w_height = FULL_SCREEN_HEIGHT; } const int w_width = std::max( FULL_SCREEN_WIDTH, TERMX * 2 / 3); const int w_y0 = ( TERMY > w_height ) ? ( TERMY - w_height ) / 2 : 0; const int w_x0 = ( TERMX > w_width ) ? ( TERMX - w_width ) / 2 : 0; WINDOW_PTR w_con_ptr {newwin( w_height, w_width, w_y0, w_x0 )}; WINDOW *const w_con = w_con_ptr.get(); const int w_list_width = int( .375 * w_width ); const int w_list_height = w_height - 4; const int w_list_x0 = 1; WINDOW_PTR w_list_ptr {newwin( w_list_height, w_list_width, w_y0 + 3, w_x0 + w_list_x0 )}; WINDOW *const w_list = w_list_ptr.get(); draw_grid( w_con, w_list_width + w_list_x0 ); //tabcount needs to be increased to add more categories int tabcount = 10; std::string construct_cat[] = {_( "All" ), _( "Constructions" ), _( "Furniture" ), _( "Digging and Mining" ), _( "Repairing" ), _( "Reinforcing" ), _( "Decorative" ), _( "Farming and Woodcutting" ), _( "Others" ), _( "Filter" ) }; bool update_info = true; bool update_cat = true; bool isnew = true; int tabindex = 0; int select = 0; int offset = 0; bool exit = false; std::string category_name = ""; std::vector<std::string> constructs; //storage for the color text so it can be scrolled std::vector< std::vector < std::string > > construct_buffers; std::vector<std::string> full_construct_buffer; std::vector<int> construct_buffer_breakpoints; int total_project_breakpoints = 0; int current_construct_breakpoint = 0; bool previous_hide_unconstructable = false; //track the cursor to determine when to refresh the list of construction recipes int previous_tabindex = -1; int previous_select = -1; const inventory &total_inv = g->u.crafting_inventory(); input_context ctxt( "CONSTRUCTION" ); ctxt.register_action( "UP", _( "Move cursor up" ) ); ctxt.register_action( "DOWN", _( "Move cursor down" ) ); ctxt.register_action( "RIGHT", _( "Move tab right" ) ); ctxt.register_action( "LEFT", _( "Move tab left" ) ); ctxt.register_action( "PAGE_UP" ); ctxt.register_action( "PAGE_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ); ctxt.register_action( "QUIT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "FILTER" ); std::string filter; int previous_index = 0; do { if( update_cat ) { update_cat = false; switch( tabindex ) { case 0: category_name = "ALL"; break; case 1: category_name = "CONSTRUCT"; break; case 2: category_name = "FURN"; break; case 3: category_name = "DIG"; break; case 4: category_name = "REPAIR"; break; case 5: category_name = "REINFORCE"; break; case 6: category_name = "DECORATE"; break; case 7: category_name = "FARM_WOOD"; break; case 8: category_name = "OTHER"; break; case 9: category_name = "FILTER"; break; } if( category_name == "ALL" ) { constructs = available; previous_index = tabindex; } else if( category_name == "FILTER" ) { constructs.clear(); std::copy_if( available.begin(), available.end(), std::back_inserter( constructs ), [&](const std::string &a){ return lcmatch(a, filter); } ); } else { constructs = cat_available[category_name]; previous_index = tabindex; } if( isnew ){ if( !uistate.last_construction.empty() ){ select = std::distance(constructs.begin(), std::find( constructs.begin(), constructs.end(), uistate.last_construction )); } filter = uistate.construction_filter; } } // Erase existing tab selection & list of constructions mvwhline( w_con, 1, 1, ' ', w_list_width ); werase( w_list ); // Print new tab listing mvwprintz( w_con, 1, 1, c_yellow, "<< %s >>", construct_cat[tabindex].c_str() ); // Determine where in the master list to start printing calcStartPos( offset, select, w_list_height, constructs.size() ); // Print the constructions between offset and max (or how many will fit) for( size_t i = 0; ( int )i < w_list_height && ( i + offset ) < constructs.size(); i++ ) { int current = i + offset; std::string con_name = constructs[current]; bool highlight = ( current == select ); trim_and_print( w_list, i, 0, w_list_width, construction_color( con_name, highlight ), "%s", con_name.c_str() ); } if( update_info ) { update_info = false; // Clear out lines for tools & materials const int pos_x = w_list_width + w_list_x0 + 2; const int available_window_width = w_width - pos_x - 1; for( int i = 1; i < w_height - 1; i++ ) { mvwhline( w_con, i, pos_x, ' ', available_window_width ); } nc_color color_stage = c_white; std::vector<std::string> notes; notes.push_back( string_format( _( "Press %s or %s to tab." ), ctxt.get_desc( "LEFT" ).c_str(), ctxt.get_desc( "RIGHT" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to search." ), ctxt.get_desc( "FILTER" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to toggle unavailable constructions." ), ctxt.get_desc( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to view and edit key-bindings." ), ctxt.get_desc( "HELP_KEYBINDINGS" ).c_str() ) ); //leave room for top and bottom UI text const int available_buffer_height = w_height - 3 - 3 - (int)notes.size(); // print the hotkeys regardless of if there are constructions for( size_t i = 0; i < notes.size(); ++i ) { trim_and_print( w_con, w_height - 1 - (int)notes.size() + (int)i, pos_x, available_window_width, c_white, "%s", notes[i].c_str() ); } if( !constructs.empty() ) { if( select >= (int) constructs.size() ){ select = 0; } std::string current_desc = constructs[select]; // Print construction name trim_and_print( w_con, 1, pos_x, available_window_width, c_white, "%s", current_desc.c_str() ); //only reconstruct the project list when moving away from the current item, or when changing the display mode if( previous_select != select || previous_tabindex != tabindex || previous_hide_unconstructable != hide_unconstructable ) { previous_select = select; previous_tabindex = tabindex; previous_hide_unconstructable = hide_unconstructable; //construct the project list buffer // Print stages and their requirement. std::vector<construction *> options = constructions_by_desc( current_desc ); construct_buffers.clear(); total_project_breakpoints = 0; current_construct_breakpoint = 0; construct_buffer_breakpoints.clear(); full_construct_buffer.clear(); int stage_counter = 0; for( std::vector<construction *>::iterator it = options.begin(); it != options.end(); ++it ) { stage_counter++; construction *current_con = *it; if( hide_unconstructable && !can_construct( *current_con ) ) { continue; } // Update the cached availability of components and tools in the requirement object current_con->requirements->can_make_with_inventory( total_inv ); std::vector<std::string> current_buffer; std::ostringstream current_line; // display result only if more than one step. // Assume single stage constructions should be clear // in their description what their result is. if( current_con->post_terrain != "" && options.size() > 1 ) { //also print out stage number when multiple stages are available current_line << _( "Stage #" ) << stage_counter; current_buffer.push_back( current_line.str() ); current_line.str( "" ); std::string result_string; if( current_con->post_is_furniture ) { result_string = furn_str_id( current_con->post_terrain ).obj().name; } else { result_string = ter_str_id( current_con->post_terrain ).obj().name; } current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format( _( "Result: %s" ), result_string.c_str() ) << "</color>"; std::vector<std::string> folded_result_string = foldstring( current_line.str(), available_window_width ); current_buffer.insert( current_buffer.end(), folded_result_string.begin(), folded_result_string.end() ); } current_line.str( "" ); // display required skill and difficulty int pskill = g->u.get_skill_level( current_con->skill ); int diff = ( current_con->difficulty > 0 ) ? current_con->difficulty : 0; current_line << "<color_" << string_from_color( ( pskill >= diff ? c_white : c_red ) ) << ">" << string_format( _( "Skill Req: %d (%s)" ), diff, current_con->skill.obj().name().c_str() ) << "</color>"; current_buffer.push_back( current_line.str() ); // TODO: Textify pre_flags to provide a bit more information. // Example: First step of dig pit could say something about // requiring diggable ground. current_line.str( "" ); if( current_con->pre_terrain != "" ) { std::string require_string; if( current_con->pre_is_furniture ) { require_string = furn_str_id( current_con->pre_terrain ).obj().name; } else { require_string = ter_str_id( current_con->pre_terrain ).obj().name; } current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format( _( "Requires: %s" ), require_string.c_str() ) << "</color>"; std::vector<std::string> folded_result_string = foldstring( current_line.str(), available_window_width ); current_buffer.insert( current_buffer.end(), folded_result_string.begin(), folded_result_string.end() ); } // get pre-folded versions of the rest of the construction project to be displayed later // get time needed std::vector<std::string> folded_time = current_con->get_folded_time_string( available_window_width ); current_buffer.insert( current_buffer.end(), folded_time.begin(), folded_time.end() ); std::vector<std::string> folded_tools = current_con->requirements->get_folded_tools_list( available_window_width, color_stage, total_inv ); current_buffer.insert( current_buffer.end(), folded_tools.begin(), folded_tools.end() ); std::vector<std::string> folded_components = current_con->requirements->get_folded_components_list( available_window_width, color_stage, total_inv ); current_buffer.insert( current_buffer.end(), folded_components.begin(), folded_components.end() ); construct_buffers.push_back( current_buffer ); } //determine where the printing starts for each project, so it can be scrolled to those points size_t current_buffer_location = 0; for( size_t i = 0; i < construct_buffers.size(); i++ ) { construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location ) ); full_construct_buffer.insert( full_construct_buffer.end(), construct_buffers[i].begin(), construct_buffers[i].end() ); //handle text too large for one screen if( construct_buffers[i].size() > static_cast<size_t>( available_buffer_height ) ) { construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location + static_cast<size_t>( available_buffer_height ) ) ); } current_buffer_location += construct_buffers[i].size(); if( i < construct_buffers.size() - 1 ) { full_construct_buffer.push_back( std::string( "" ) ); current_buffer_location++; } } total_project_breakpoints = static_cast<int>( construct_buffer_breakpoints.size() ); } if( current_construct_breakpoint > 0 ) { // Print previous stage indicator if breakpoint is past the beginning trim_and_print( w_con, 2, pos_x, available_window_width, c_white, _( "Press %s to show previous stage(s)." ), ctxt.get_desc( "PAGE_UP" ).c_str() ); } if( static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] + available_buffer_height ) < full_construct_buffer.size() ) { // Print next stage indicator if more breakpoints are remaining after screen height trim_and_print( w_con, w_height - 2 - (int)notes.size(), pos_x, available_window_width, c_white, _( "Press %s to show next stage(s)." ), ctxt.get_desc( "PAGE_DOWN" ).c_str() ); } // Leave room for above/below indicators int ypos = 3; nc_color stored_color = color_stage; for( size_t i = static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] ); i < full_construct_buffer.size(); i++ ) { //the value of 3 is from leaving room at the top of window if( ypos > available_buffer_height + 3 ) { break; } print_colored_text( w_con, ypos++, ( w_list_width + w_list_x0 + 2 ), stored_color, color_stage, full_construct_buffer[i] ); } } } // Finished updating draw_scrollbar( w_con, select, w_list_height, constructs.size(), 3 ); wrefresh( w_con ); wrefresh( w_list ); const std::string action = ctxt.handle_input(); if( action == "FILTER" ){ filter = string_input_popup( _( "Search" ), 50, filter, "", _( "Filter" ), 100, false ); if( !filter.empty() ){ update_info = true; update_cat = true; tabindex = 9; select = 0; }else if( previous_index !=9 ){ tabindex = previous_index; update_info = true; update_cat = true; select = 0; } uistate.construction_filter = filter; } else if( action == "DOWN" ) { update_info = true; if( select < ( int )constructs.size() - 1 ) { select++; } else { select = 0; } } else if( action == "UP" ) { update_info = true; if( select > 0 ) { select--; } else { select = constructs.size() - 1; } } else if( action == "LEFT" ) { update_info = true; update_cat = true; select = 0; tabindex--; if( tabindex < 0 ) { tabindex = tabcount - 1; } } else if( action == "RIGHT" ) { update_info = true; update_cat = true; select = 0; tabindex = ( tabindex + 1 ) % tabcount; } else if( action == "PAGE_UP" ) { update_info = true; if( current_construct_breakpoint > 0 ) { current_construct_breakpoint--; } if( current_construct_breakpoint < 0 ) { current_construct_breakpoint = 0; } } else if( action == "PAGE_DOWN" ) { update_info = true; if( current_construct_breakpoint < total_project_breakpoints - 1 ) { current_construct_breakpoint++; } if( current_construct_breakpoint >= total_project_breakpoints ) { current_construct_breakpoint = total_project_breakpoints - 1; } } else if( action == "QUIT" ) { exit = true; } else if( action == "HELP_KEYBINDINGS" ) { draw_grid( w_con, w_list_width + w_list_x0 ); } else if( action == "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ) { update_info = true; update_cat = true; hide_unconstructable = !hide_unconstructable; select = 0; offset = 0; load_available_constructions( available, cat_available, hide_unconstructable ); } else if( action == "CONFIRM" ) { if( constructs.empty() || select >= (int) constructs.size() ){ continue;// Nothing to be done here } if( player_can_build( g->u, total_inv, constructs[select] ) ) { place_construction( constructs[select] ); uistate.last_construction = constructs[select]; exit = true; } else { popup( _( "You can't build that!" ) ); draw_grid( w_con, w_list_width + w_list_x0 ); update_info = true; } } } while( !exit ); w_list_ptr.reset(); w_con_ptr.reset(); g->refresh_all(); }
void player_morale::display( double focus_gain ) { // Create and draw the window itself. WINDOW *w = newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, ( TERMY > FULL_SCREEN_HEIGHT ) ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0, ( TERMX > FULL_SCREEN_WIDTH ) ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0 ); draw_border( w ); // Figure out how wide the name column needs to be. int name_column_width = 18; for( auto &i : points ) { int length = utf8_width( i.get_name() ); if( length > name_column_width ) { name_column_width = length; // If it's too wide, truncate. if( name_column_width >= 72 ) { name_column_width = 72; break; } } } // Header mvwprintz( w, 1, 1, c_white, _( "Morale Modifiers:" ) ); mvwprintz( w, 2, 1, c_ltgray, _( "Name" ) ); mvwprintz( w, 2, name_column_width + 2, c_ltgray, _( "Value" ) ); // Start printing the number right after the name column. // We'll right-justify it later. int number_pos = name_column_width + 1; const morale_mult mult = get_temper_mult(); // Print out the morale entries. for( size_t i = 0; i < points.size(); i++ ) { const std::string name = points[i].get_name(); const int bonus = points[i].get_net_bonus( mult ); const nc_color bonus_color = ( bonus < 0 ? c_red : c_green ); // Print out the name. trim_and_print( w, i + 3, 1, name_column_width, bonus_color, name.c_str() ); // Print out the number, right-justified. mvwprintz( w, i + 3, number_pos, bonus_color, "% 6d", bonus ); } // Print out the total morale, right-justified. const nc_color level_color = ( get_level() < 0 ? c_red : c_green ); mvwprintz( w, 20, 1, level_color, _( "Total:" ) ); mvwprintz( w, 20, number_pos, level_color, "% 6d", get_level() ); // Print out the focus gain rate, right-justified. const nc_color gain_color = ( focus_gain < 0 ? c_red : c_green ); mvwprintz( w, 22, 1, gain_color, _( "Focus gain:" ) ); mvwprintz( w, 22, number_pos - 3, gain_color, _( "%6.2f per minute" ), focus_gain ); // Make sure the changes are shown. wrefresh( w ); // Wait for any keystroke. getch(); // Close the window. werase( w ); delwin( w ); }
// Pick up items at (pos). void Pickup::pick_up( const tripoint &pos, int min ) { int cargo_part = -1; const optional_vpart_position vp = g->m.veh_at( pos ); vehicle *const veh = veh_pointer_or_null( vp ); bool from_vehicle = false; if( min != -1 ) { switch( interact_with_vehicle( veh, pos, vp ? vp->part_index() : -1 ) ) { case DONE: return; case ITEMS_FROM_CARGO: { const cata::optional<vpart_reference> carg = vp.part_with_feature( "CARGO", false ); cargo_part = carg ? carg->part_index() : -1; } from_vehicle = cargo_part >= 0; break; case ITEMS_FROM_GROUND: // Nothing to change, default is to pick from ground anyway. if( g->m.has_flag( "SEALED", pos ) ) { return; } break; } } if( !from_vehicle ) { bool isEmpty = ( g->m.i_at( pos ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here // but water. if( ( !isEmpty ) && g->m.furn( pos ) == f_toilet ) { isEmpty = true; for( auto maybe_water : g->m.i_at( pos ) ) { if( maybe_water.typeId() != "water" ) { isEmpty = false; break; } } } if( isEmpty && ( min != -1 || !get_option<bool>( "AUTO_PICKUP_ADJACENT" ) ) ) { return; } } // which items are we grabbing? std::vector<item> here; if( from_vehicle ) { auto vehitems = veh->get_items( cargo_part ); here.resize( vehitems.size() ); std::copy( vehitems.begin(), vehitems.end(), here.begin() ); } else { auto mapitems = g->m.i_at( pos ); here.resize( mapitems.size() ); std::copy( mapitems.begin(), mapitems.end(), here.begin() ); } if( min == -1 ) { // Recursively pick up adjacent items if that option is on. if( get_option<bool>( "AUTO_PICKUP_ADJACENT" ) && g->u.pos() == pos ) { //Autopickup adjacent direction adjacentDir[8] = {NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}; for( auto &elem : adjacentDir ) { tripoint apos = tripoint( direction_XY( elem ), 0 ); apos += pos; pick_up( apos, min ); } } // Bail out if this square cannot be auto-picked-up if( g->check_zone( zone_type_id( "NO_AUTO_PICKUP" ), pos ) ) { return; } else if( g->m.has_flag( "SEALED", pos ) ) { return; } } // Not many items, just grab them if( ( int )here.size() <= min && min != -1 ) { g->u.assign_activity( activity_id( "ACT_PICKUP" ) ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); // Only one item means index is 0. g->u.activity.values.push_back( 0 ); // auto-pickup means pick up all. g->u.activity.values.push_back( 0 ); return; } std::vector<std::list<item_idx>> stacked_here; for( size_t i = 0; i < here.size(); i++ ) { item &it = here[i]; bool found_stack = false; for( auto &stack : stacked_here ) { if( stack.begin()->_item.stacks_with( it ) ) { item_idx el = { it, i }; stack.push_back( el ); found_stack = true; break; } } if( !found_stack ) { std::list<item_idx> newstack; newstack.push_back( { it, i } ); stacked_here.push_back( newstack ); } } std::reverse( stacked_here.begin(), stacked_here.end() ); if( min != -1 ) { // don't bother if we're just autopickuping g->temp_exit_fullscreen(); } bool sideStyle = use_narrow_sidebar(); // Otherwise, we have Autopickup, 2 or more items and should list them, etc. int maxmaxitems = sideStyle ? TERMY : getmaxy( g->w_messages ) - 3; int itemsH = std::min( 25, TERMY / 2 ); int pickupBorderRows = 3; // The pickup list may consume the entire terminal, minus space needed for its // header/footer and the item info window. int minleftover = itemsH + pickupBorderRows; if( maxmaxitems > TERMY - minleftover ) { maxmaxitems = TERMY - minleftover; } const int minmaxitems = sideStyle ? 6 : 9; std::vector<pickup_count> getitem( stacked_here.size() ); int maxitems = stacked_here.size(); maxitems = ( maxitems < minmaxitems ? minmaxitems : ( maxitems > maxmaxitems ? maxmaxitems : maxitems ) ); int itemcount = 0; if( min == -1 ) { //Auto Pickup, select matching items if( !select_autopickup_items( stacked_here, getitem ) ) { // If we didn't find anything, bail out now. return; } } else { int pickupH = maxitems + pickupBorderRows; int pickupW = getmaxx( g->w_messages ); int pickupY = VIEW_OFFSET_Y; int pickupX = getbegx( g->w_messages ); int itemsW = pickupW; int itemsY = sideStyle ? pickupY + pickupH : TERMY - itemsH; int itemsX = pickupX; catacurses::window w_pickup = catacurses::newwin( pickupH, pickupW, pickupY, pickupX ); catacurses::window w_item_info = catacurses::newwin( itemsH, itemsW, itemsY, itemsX ); std::string action; long raw_input_char = ' '; input_context ctxt( "PICKUP" ); ctxt.register_action( "UP" ); ctxt.register_action( "DOWN" ); ctxt.register_action( "RIGHT" ); ctxt.register_action( "LEFT" ); ctxt.register_action( "NEXT_TAB", _( "Next page" ) ); ctxt.register_action( "PREV_TAB", _( "Previous page" ) ); ctxt.register_action( "SCROLL_UP" ); ctxt.register_action( "SCROLL_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "SELECT_ALL" ); ctxt.register_action( "QUIT", _( "Cancel" ) ); ctxt.register_action( "ANY_INPUT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "FILTER" ); int start = 0; int cur_it = 0; bool update = true; mvwprintw( w_pickup, 0, 0, _( "PICK UP" ) ); int selected = 0; int iScrollPos = 0; std::string filter; std::string new_filter; std::vector<int> matches;//Indexes of items that match the filter bool filter_changed = true; if( g->was_fullscreen ) { g->draw_ter(); } // Now print the two lists; those on the ground and about to be added to inv // Continue until we hit return or space do { const std::string pickup_chars = ctxt.get_available_single_char_hotkeys( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;" ); int idx = -1; for( int i = 1; i < pickupH; i++ ) { mvwprintw( w_pickup, i, 0, " " ); } if( action == "ANY_INPUT" && raw_input_char >= '0' && raw_input_char <= '9' ) { int raw_input_char_value = ( char )raw_input_char - '0'; itemcount *= 10; itemcount += raw_input_char_value; if( itemcount < 0 ) { itemcount = 0; } } else if( action == "SCROLL_UP" ) { iScrollPos--; } else if( action == "SCROLL_DOWN" ) { iScrollPos++; } else if( action == "PREV_TAB" ) { if( start > 0 ) { start -= maxitems; } else { start = ( int )( ( matches.size() - 1 ) / maxitems ) * maxitems; } selected = start; mvwprintw( w_pickup, maxitems + 2, 0, " " ); } else if( action == "NEXT_TAB" ) { if( start + maxitems < ( int )matches.size() ) { start += maxitems; } else { start = 0; } iScrollPos = 0; selected = start; mvwprintw( w_pickup, maxitems + 2, pickupH, " " ); } else if( action == "UP" ) { selected--; iScrollPos = 0; if( selected < 0 ) { selected = matches.size() - 1; start = ( int )( matches.size() / maxitems ) * maxitems; if( start >= ( int )matches.size() ) { start -= maxitems; } } else if( selected < start ) { start -= maxitems; } } else if( action == "DOWN" ) { selected++; iScrollPos = 0; if( selected >= ( int )matches.size() ) { selected = 0; start = 0; } else if( selected >= start + maxitems ) { start += maxitems; } } else if( selected >= 0 && selected < int( matches.size() ) && ( ( action == "RIGHT" && !getitem[matches[selected]].pick ) || ( action == "LEFT" && getitem[matches[selected]].pick ) ) ) { idx = selected; } else if( action == "FILTER" ) { new_filter = filter; string_input_popup popup; popup .title( _( "Set filter" ) ) .width( 30 ) .edit( new_filter ); if( !popup.canceled() ) { filter_changed = true; } else { wrefresh( g->w_terrain ); } } else if( action == "ANY_INPUT" && raw_input_char == '`' ) { std::string ext = string_input_popup() .title( _( "Enter 2 letters (case sensitive):" ) ) .width( 3 ) .max_length( 2 ) .query_string(); if( ext.size() == 2 ) { int p1 = pickup_chars.find( ext.at( 0 ) ); int p2 = pickup_chars.find( ext.at( 1 ) ); if( p1 != -1 && p2 != -1 ) { idx = pickup_chars.size() + ( p1 * pickup_chars.size() ) + p2; } } } else if( action == "ANY_INPUT" ) { idx = ( raw_input_char <= 127 ) ? pickup_chars.find( raw_input_char ) : -1; iScrollPos = 0; } if( idx >= 0 && idx < ( int )matches.size() ) { size_t true_idx = matches[idx]; if( itemcount != 0 || getitem[true_idx].count == 0 ) { item &temp = stacked_here[true_idx].begin()->_item; int amount_available = temp.count_by_charges() ? temp.charges : stacked_here[true_idx].size(); if( itemcount >= amount_available ) { itemcount = 0; } getitem[true_idx].count = itemcount; itemcount = 0; } // Note: this might not change the value of getitem[idx] at all! getitem[true_idx].pick = ( action == "RIGHT" ? true : ( action == "LEFT" ? false : !getitem[true_idx].pick ) ); if( action != "RIGHT" && action != "LEFT" ) { selected = idx; start = ( int )( idx / maxitems ) * maxitems; } if( !getitem[true_idx].pick ) { getitem[true_idx].count = 0; } update = true; } if( filter_changed ) { matches.clear(); while( matches.empty() ) { auto filter_func = item_filter_from_string( new_filter ); for( size_t index = 0; index < stacked_here.size(); index++ ) { if( filter_func( stacked_here[index].begin()->_item ) ) { matches.push_back( index ); } } if( matches.empty() ) { popup( _( "Your filter returned no results" ) ); wrefresh( g->w_terrain ); // The filter must have results, or simply be emptied or canceled, // as this screen can't be reached without there being // items available string_input_popup popup; popup .title( _( "Set filter" ) ) .width( 30 ) .edit( new_filter ); if( popup.canceled() ) { new_filter = filter; filter_changed = false; } } } if( filter_changed ) { filter = new_filter; filter_changed = false; selected = 0; start = 0; iScrollPos = 0; } wrefresh( g->w_terrain ); } item &selected_item = stacked_here[matches[selected]].begin()->_item; werase( w_item_info ); if( selected >= 0 && selected <= ( int )stacked_here.size() - 1 ) { std::vector<iteminfo> vThisItem; std::vector<iteminfo> vDummy; selected_item.info( true, vThisItem ); draw_item_info( w_item_info, "", "", vThisItem, vDummy, iScrollPos, true, true ); } draw_custom_border( w_item_info, false ); mvwprintw( w_item_info, 0, 2, "< " ); trim_and_print( w_item_info, 0, 4, itemsW - 8, c_white, "%s >", selected_item.display_name().c_str() ); wrefresh( w_item_info ); if( action == "SELECT_ALL" ) { int count = 0; for( auto i : matches ) { if( getitem[i].pick ) { count++; } getitem[i].pick = true; } if( count == ( int )stacked_here.size() ) { for( size_t i = 0; i < stacked_here.size(); i++ ) { getitem[i].pick = false; } } update = true; } for( cur_it = start; cur_it < start + maxitems; cur_it++ ) { mvwprintw( w_pickup, 1 + ( cur_it % maxitems ), 0, " " ); if( cur_it < ( int )matches.size() ) { int true_it = matches[cur_it]; item &this_item = stacked_here[ true_it ].begin()->_item; nc_color icolor = this_item.color_in_inventory(); if( cur_it == selected ) { icolor = hilite( icolor ); } if( cur_it < ( int )pickup_chars.size() ) { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, char( pickup_chars[cur_it] ) ); } else if( cur_it < ( int )pickup_chars.size() + ( int )pickup_chars.size() * ( int )pickup_chars.size() ) { int p = cur_it - pickup_chars.size(); int p1 = p / pickup_chars.size(); int p2 = p % pickup_chars.size(); mvwprintz( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, "`%c%c", char( pickup_chars[p1] ), char( pickup_chars[p2] ) ); } else { mvwputch( w_pickup, 1 + ( cur_it % maxitems ), 0, icolor, ' ' ); } if( getitem[true_it].pick ) { if( getitem[true_it].count == 0 ) { wprintz( w_pickup, c_light_blue, " + " ); } else { wprintz( w_pickup, c_light_blue, " # " ); } } else { wprintw( w_pickup, " - " ); } std::string item_name; if( stacked_here[true_it].begin()->_item.ammo_type() == "money" ) { //Count charges //TODO: transition to the item_location system used for the inventory unsigned long charges_total = 0; for( const auto item : stacked_here[true_it] ) { charges_total += item._item.charges; } //Picking up none or all the cards in a stack if( !getitem[true_it].pick || getitem[true_it].count == 0 ) { item_name = stacked_here[true_it].begin()->_item.display_money( stacked_here[true_it].size(), charges_total ); } else { unsigned long charges = 0; int c = getitem[true_it].count; for( auto it = stacked_here[true_it].begin(); it != stacked_here[true_it].end() && c > 0; ++it, --c ) { charges += it->_item.charges; } item_name = string_format( _( "%s of %s" ), stacked_here[true_it].begin()->_item.display_money( getitem[true_it].count, charges ), format_money( charges_total ) ); } } else { item_name = this_item.display_name( stacked_here[true_it].size() ); } if( stacked_here[true_it].size() > 1 ) { item_name = string_format( "%d %s", stacked_here[true_it].size(), item_name.c_str() ); } if( get_option<bool>( "ITEM_SYMBOLS" ) ) { item_name = string_format( "%s %s", this_item.symbol().c_str(), item_name.c_str() ); } trim_and_print( w_pickup, 1 + ( cur_it % maxitems ), 6, pickupW - 4, icolor, item_name ); } } mvwprintw( w_pickup, maxitems + 1, 0, _( "[%s] Unmark" ), ctxt.get_desc( "LEFT", 1 ).c_str() ); center_print( w_pickup, maxitems + 1, c_light_gray, string_format( _( "[%s] Help" ), ctxt.get_desc( "HELP_KEYBINDINGS", 1 ).c_str() ) ); right_print( w_pickup, maxitems + 1, 0, c_light_gray, string_format( _( "[%s] Mark" ), ctxt.get_desc( "RIGHT", 1 ).c_str() ) ); mvwprintw( w_pickup, maxitems + 2, 0, _( "[%s] Prev" ), ctxt.get_desc( "PREV_TAB", 1 ).c_str() ); center_print( w_pickup, maxitems + 2, c_light_gray, string_format( _( "[%s] All" ), ctxt.get_desc( "SELECT_ALL", 1 ).c_str() ) ); right_print( w_pickup, maxitems + 2, 0, c_light_gray, string_format( _( "[%s] Next" ), ctxt.get_desc( "NEXT_TAB", 1 ).c_str() ) ); if( update ) { // Update weight & volume information update = false; for( int i = 9; i < pickupW; ++i ) { mvwaddch( w_pickup, 0, i, ' ' ); } units::mass weight_picked_up = 0; units::volume volume_picked_up = 0; for( size_t i = 0; i < getitem.size(); i++ ) { if( getitem[i].pick ) { item temp = stacked_here[i].begin()->_item; if( temp.count_by_charges() && getitem[i].count < temp.charges && getitem[i].count != 0 ) { temp.charges = getitem[i].count; } int num_picked = std::min( stacked_here[i].size(), getitem[i].count == 0 ? stacked_here[i].size() : getitem[i].count ); weight_picked_up += temp.weight() * num_picked; volume_picked_up += temp.volume() * num_picked; } } auto weight_predict = g->u.weight_carried() + weight_picked_up; auto volume_predict = g->u.volume_carried() + volume_picked_up; mvwprintz( w_pickup, 0, 9, weight_predict > g->u.weight_capacity() ? c_red : c_white, _( "Wgt %.1f" ), round_up( convert_weight( weight_predict ), 1 ) ); wprintz( w_pickup, c_white, "/%.1f", round_up( convert_weight( g->u.weight_capacity() ), 1 ) ); std::string fmted_volume_predict = format_volume( volume_predict ); mvwprintz( w_pickup, 0, 24, volume_predict > g->u.volume_capacity() ? c_red : c_white, _( "Vol %s" ), fmted_volume_predict.c_str() ); std::string fmted_volume_capacity = format_volume( g->u.volume_capacity() ); wprintz( w_pickup, c_white, "/%s", fmted_volume_capacity.c_str() ); }; wrefresh( w_pickup ); action = ctxt.handle_input(); raw_input_char = ctxt.get_raw_input().get_first_input(); } while( action != "QUIT" && action != "CONFIRM" ); bool item_selected = false; // Check if we have selected an item. for( auto selection : getitem ) { if( selection.pick ) { item_selected = true; } } if( action != "CONFIRM" || !item_selected ) { w_pickup = catacurses::window(); w_item_info = catacurses::window(); add_msg( _( "Never mind." ) ); g->reenter_fullscreen(); g->refresh_all(); return; } } // At this point we've selected our items, register an activity to pick them up. g->u.assign_activity( activity_id( "ACT_PICKUP" ) ); g->u.activity.placement = pos - g->u.pos(); g->u.activity.values.push_back( from_vehicle ); if( min == -1 ) { // Auto pickup will need to auto resume since there can be several of them on the stack. g->u.activity.auto_resume = true; } std::vector<std::pair<int, int>> pick_values; for( size_t i = 0; i < stacked_here.size(); i++ ) { const auto &selection = getitem[i]; if( !selection.pick ) { continue; } const auto &stack = stacked_here[i]; // Note: items can be both charged and stacked // For robustness, let's assume they can be both in the same stack bool pick_all = selection.count == 0; size_t count = selection.count; for( const item_idx &it : stack ) { if( !pick_all && count == 0 ) { break; } if( it._item.count_by_charges() ) { size_t num_picked = std::min( ( size_t )it._item.charges, count ); pick_values.push_back( { static_cast<int>( it.idx ), static_cast<int>( num_picked ) } ); count -= num_picked; } else { size_t num_picked = 1; pick_values.push_back( { static_cast<int>( it.idx ), 0 } ); count -= num_picked; } } } // The pickup activity picks up items last-to-first from its values list, so make sure the // higher indices are at the end. std::sort( pick_values.begin(), pick_values.end() ); for( auto &it : pick_values ) { g->u.activity.values.push_back( it.first ); g->u.activity.values.push_back( it.second ); } g->reenter_fullscreen(); }
hp_part Character::body_window( const std::string &menu_header, bool show_all, bool precise, int normal_bonus, int head_bonus, int torso_bonus, int bleed, int bite, int infect ) const { WINDOW *hp_window = newwin(10, 31, (TERMY - 10) / 2, (TERMX - 31) / 2); draw_border(hp_window); trim_and_print( hp_window, 1, 1, getmaxx(hp_window) - 2, c_ltred, menu_header.c_str() ); nc_color color = c_ltgray; bool allowed_result[num_hp_parts] = { false }; const auto check_part = [&]( hp_part part, std::string part_name, int heal_val, int line_num ) { body_part bp = player::hp_to_bp( part ); if( show_all || hp_cur[part] < hp_max[part] || has_effect("infected", bp) || has_effect("bite", bp) || has_effect("bleed", bp) ) { nc_color color = show_all ? c_green : limb_color( bp, bleed, bite, infect ); if( color != c_ltgray || heal_val != 0 ) { mvwprintz( hp_window, line_num, 1, color, part_name.c_str() ); allowed_result[part] = true; } } }; check_part( hp_head, _("1: Head"), head_bonus, 2 ); check_part( hp_torso, _("2: Torso"), torso_bonus, 3 ); check_part( hp_arm_l, _("3: Left Arm"), normal_bonus, 4 ); check_part( hp_arm_r, _("4: Right Arm"), normal_bonus, 5 ); check_part( hp_leg_l, _("5: Left Leg"), normal_bonus, 6 ); check_part( hp_leg_r, _("6: Right Leg"), normal_bonus, 7 ); mvwprintz( hp_window, 8, 1, c_ltgray, _("7: Exit") ); std::string health_bar; for( int i = 0; i < num_hp_parts; i++ ) { if( !allowed_result[i] ) { continue; } body_part bp = body_part( i ); // Have printed the name of the body part, can select it int current_hp = hp_cur[i]; if( current_hp != 0 ) { std::tie( health_bar, color ) = get_hp_bar(current_hp, hp_max[i], false); // Drop the bar color, use the state color instead const nc_color state_col = limb_color( bp, true, true, true ); color = state_col != c_ltgray ? state_col : c_green; if( precise ) { mvwprintz(hp_window, i + 2, 15, color, "%5d", current_hp); } else { mvwprintz(hp_window, i + 2, 15, color, health_bar.c_str()); } } else { // curhp is 0; requires surgical attention // But still could be infected or bleeding const nc_color state_col = limb_color( bp, true, true, true ); color = state_col != c_ltgray ? state_col : c_dkgray; mvwprintz(hp_window, i + 2, 15, color, "-----"); } if( current_hp != 0 ) { switch( hp_part( i ) ) { case hp_head: current_hp += head_bonus; break; case hp_torso: current_hp += torso_bonus; break; default: current_hp += normal_bonus; break; } if( current_hp > hp_max[i] ) { current_hp = hp_max[i]; } else if (current_hp < 0) { current_hp = 0; } if( current_hp == hp_cur[i] && ( infect <= 0 || !has_effect( "infected", bp ) ) && ( bite <= 0 || !has_effect( "bite", bp ) ) && ( bleed <= 0 || !has_effect( "bleed", bp ) ) ) { // Nothing would change continue; } mvwprintz( hp_window, i + 2, 20, c_dkgray, " -> " ); std::tie( health_bar, color ) = get_hp_bar( current_hp, hp_max[i], false ); const nc_color state_col = limb_color( bp, bleed > 0, bite > 0, infect > 0 ); color = state_col != c_ltgray ? state_col : c_green; if( precise ) { mvwprintz( hp_window, i + 2, 24, color, "%5d", current_hp ); } else { mvwprintz( hp_window, i + 2, 24, color, health_bar.c_str() ); } } else { // curhp is 0; requires surgical attention const nc_color state_col = limb_color( bp, bleed > 0, bite > 0, infect > 0 ); color = state_col != c_ltgray ? state_col : c_dkgray; mvwprintz(hp_window, i + 2, 24, color, "-----"); } } wrefresh(hp_window); char ch; hp_part healed_part = num_hp_parts; do { ch = getch(); if (ch == '1') { healed_part = hp_head; } else if (ch == '2') { healed_part = hp_torso; } else if (ch == '3') { healed_part = hp_arm_l; } else if (ch == '4') { healed_part = hp_arm_r; } else if (ch == '5') { healed_part = hp_leg_l; } else if (ch == '6') { healed_part = hp_leg_r; } else if (ch == '7' || ch == KEY_ESCAPE) { healed_part = num_hp_parts; break; } } while (ch < '1' || ch > '7'); werase(hp_window); wrefresh(hp_window); delwin(hp_window); refresh(); return healed_part; }