void zone_manager::add( const std::string &name, const zone_type_id &type, const bool invert, const bool enabled, const tripoint &start, const tripoint &end, std::shared_ptr<zone_options> options ) { zone_data new_zone = zone_data( name, type, invert, enabled, start, end, options ); //the start is a vehicle tile with cargo space if( const cata::optional<vpart_reference> vp = g->m.veh_at( g->m.getlocal( start ) ).part_with_feature( "CARGO", false ) ) { // TODO:Allow for loot zones on vehicles to be larger than 1x1 if( start == end && query_yn( _( "Bind this zone to the cargo part here?" ) ) ) { // TODO: refactor zone options for proper validation code if( type == zone_type_id( "FARM_PLOT" ) ) { popup( _( "You cannot add that type of zone to a vehicle." ), PF_NONE ); return; } create_vehicle_loot_zone( vp->vehicle(), vp->mount(), new_zone ); return; } } //Create a regular zone zones.push_back( new_zone ); cache_data(); }
std::shared_ptr<zone_options> zone_options::create( const zone_type_id &type ) { if( type == zone_type_id( "FARM_PLOT" ) ) { return std::make_shared<plot_options>(); } return std::make_shared<zone_options>(); }
bool zone_options::is_valid( const zone_type_id &type, const zone_options &options ) { if( type == zone_type_id( "FARM_PLOT" ) ) { return dynamic_cast<const plot_options *>( &options ) != nullptr ; } // ensure options is not derived class for the rest of zone types return !options.has_options(); }
bool zone_manager::has_loot_dest_near( const tripoint &where ) const { for( const auto &ztype : get_manager().get_types() ) { const zone_type_id &type = ztype.first; if( type == zone_type_id( "CAMP_FOOD" ) || type == zone_type_id( "FARM_PLOT" ) || type == zone_type_id( "LOOT_UNSORTED" ) || type == zone_type_id( "LOOT_IGNORE" ) || type == zone_type_id( "NO_AUTO_PICKUP" ) || type == zone_type_id( "NO_NPC_PICKUP" ) ) { continue; } if( has_near( type, where ) ) { return true; } } return false; }
void activity_on_turn_move_loot( player_activity &, player &p ) { const activity_id act_move_loot = activity_id( "ACT_MOVE_LOOT" ); auto &mgr = zone_manager::get_manager(); if( g->m.check_vehicle_zones( g->get_levz() ) ) { mgr.cache_vzones(); } const auto abspos = g->m.getabs( p.pos() ); const auto &src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos ); vehicle *src_veh, *dest_veh; int src_part, dest_part; // Nuke the current activity, leaving the backlog alone. p.activity = player_activity(); // sort source tiles by distance const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set ); if( !mgr.is_sorting() ) { mgr.start_sort( src_sorted ); } for( auto &src : src_sorted ) { const auto &src_loc = g->m.getlocal( src ); if( !g->m.inbounds( src_loc ) ) { if( !g->m.inbounds( p.pos() ) ) { // p is implicitly an NPC that has been moved off the map, so reset the activity // and unload them p.assign_activity( act_move_loot ); p.set_moves( 0 ); g->reload_npcs(); mgr.end_sort(); return; } std::vector<tripoint> route; route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), p.get_path_avoid() ); if( route.empty() ) { // can't get there, can't do anything, skip it continue; } p.set_destination( route, player_activity( act_move_loot ) ); mgr.end_sort(); return; } bool is_adjacent_or_closer = square_dist( p.pos(), src_loc ) <= 1; // skip tiles in IGNORE zone and tiles on fire // (to prevent taking out wood off the lit brazier) // and inaccessible furniture, like filled charcoal kiln if( mgr.has( zone_type_id( "LOOT_IGNORE" ), src ) || g->m.get_field( src_loc, fd_fire ) != nullptr || !g->m.can_put_items_ter_furn( src_loc ) ) { continue; } // the boolean in this pair being true indicates the item is from a vehicle storage space auto items = std::vector<std::pair<item *, bool>>(); //Check source for cargo part //map_stack and vehicle_stack are different types but inherit from item_stack // TODO: use one for loop if( const cata::optional<vpart_reference> vp = g->m.veh_at( src_loc ).part_with_feature( "CARGO", false ) ) { src_veh = &vp->vehicle(); src_part = vp->part_index(); for( auto &it : src_veh->get_items( src_part ) ) { items.push_back( std::make_pair( &it, true ) ); } } else { src_veh = nullptr; src_part = -1; } for( auto &it : g->m.i_at( src_loc ) ) { items.push_back( std::make_pair( &it, false ) ); } //Skip items that have already been processed for( auto it = items.begin() + mgr.get_num_processed( src ); it < items.end(); it++ ) { mgr.increment_num_processed( src ); const auto thisitem = it->first; if( thisitem->made_of_from_type( LIQUID ) ) { // skip unpickable liquid continue; } // Only if it's from a vehicle do we use the vehicle source location information. vehicle *this_veh = it->second ? src_veh : nullptr; const int this_part = it->second ? src_part : -1; const auto id = mgr.get_near_zone_type_for_item( *thisitem, abspos ); // checks whether the item is already on correct loot zone or not // if it is, we can skip such item, if not we move the item to correct pile // think empty bag on food pile, after you ate the content if( !mgr.has( id, src ) ) { const auto &dest_set = mgr.get_near( id, abspos ); for( auto &dest : dest_set ) { const auto &dest_loc = g->m.getlocal( dest ); //Check destination for cargo part if( const cata::optional<vpart_reference> vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO", false ) ) { dest_veh = &vp->vehicle(); dest_part = vp->part_index(); } else { dest_veh = nullptr; dest_part = -1; } // skip tiles with inaccessible furniture, like filled charcoal kiln if( !g->m.can_put_items_ter_furn( dest_loc ) ) { continue; } units::volume free_space; // if there's a vehicle with space do not check the tile beneath if( dest_veh ) { free_space = dest_veh->free_volume( dest_part ); } else { free_space = g->m.free_volume( dest_loc ); } // check free space at destination if( free_space >= thisitem->volume() ) { // before we move any item, check if player is at or // adjacent to the loot source tile if( !is_adjacent_or_closer ) { std::vector<tripoint> route; bool adjacent = false; // get either direct route or route to nearest adjacent tile if // source tile is impassable if( g->m.passable( src_loc ) ) { route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), p.get_path_avoid() ); } else { // immpassable source tile (locker etc.), // get route to nerest adjacent tile instead route = route_adjacent( p, src_loc ); adjacent = true; } // check if we found path to source / adjacent tile if( route.empty() ) { add_msg( m_info, _( "%s can't reach the source tile. Try to sort out loot without a cart." ), p.disp_name() ); mgr.end_sort(); return; } // shorten the route to adjacent tile, if necessary if( !adjacent ) { route.pop_back(); } // set the destination and restart activity after player arrives there // we don't need to check for safe mode, // activity will be restarted only if // player arrives on destination tile p.set_destination( route, player_activity( act_move_loot ) ); mgr.end_sort(); return; } move_item( p, *thisitem, thisitem->count(), src_loc, dest_loc, this_veh, this_part ); // moved item away from source so decrement mgr.decrement_num_processed( src ); break; } } if( p.moves <= 0 ) { // Restart activity and break from cycle. p.assign_activity( act_move_loot ); mgr.end_sort(); return; } } } } // If we got here without restarting the activity, it means we're done add_msg( m_info, string_format( _( "%s sorted out every item possible." ), p.disp_name() ) ); mgr.end_sort(); }
// 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(); }
zone_type_id zone_manager::get_near_zone_type_for_item( const item &it, const tripoint &where ) const { auto cat = it.get_category(); if( it.has_flag( "FIREWOOD" ) ) { if( has_near( zone_type_id( "LOOT_WOOD" ), where ) ) { return zone_type_id( "LOOT_WOOD" ); } } if( cat.id() == "food" ) { const bool preserves = it.is_food_container() && it.type->container->preserves; const auto &it_food = it.is_food_container() ? it.contents.front() : it; if( it_food.is_food() ) { // skip food without comestible, like MREs if( it_food.get_comestible()->comesttype == "DRINK" ) { if( !preserves && it_food.goes_bad() && has_near( zone_type_id( "LOOT_PDRINK" ), where ) ) { return zone_type_id( "LOOT_PDRINK" ); } else if( has_near( zone_type_id( "LOOT_DRINK" ), where ) ) { return zone_type_id( "LOOT_DRINK" ); } } if( !preserves && it_food.goes_bad() && has_near( zone_type_id( "LOOT_PFOOD" ), where ) ) { return zone_type_id( "LOOT_PFOOD" ); } } return zone_type_id( "LOOT_FOOD" ); } if( cat.id() == "guns" ) { return zone_type_id( "LOOT_GUNS" ); } if( cat.id() == "magazines" ) { return zone_type_id( "LOOT_MAGAZINES" ); } if( cat.id() == "ammo" ) { return zone_type_id( "LOOT_AMMO" ); } if( cat.id() == "weapons" ) { return zone_type_id( "LOOT_WEAPONS" ); } if( cat.id() == "tools" ) { return zone_type_id( "LOOT_TOOLS" ); } if( cat.id() == "clothing" ) { if( it.is_filthy() && has_near( zone_type_id( "LOOT_FCLOTHING" ), where ) ) { return zone_type_id( "LOOT_FCLOTHING" ); } return zone_type_id( "LOOT_CLOTHING" ); } if( cat.id() == "drugs" ) { return zone_type_id( "LOOT_DRUGS" ); } if( cat.id() == "books" ) { return zone_type_id( "LOOT_BOOKS" ); } if( cat.id() == "mods" ) { return zone_type_id( "LOOT_MODS" ); } if( cat.id() == "mutagen" ) { return zone_type_id( "LOOT_MUTAGENS" ); } if( cat.id() == "bionics" ) { return zone_type_id( "LOOT_BIONICS" ); } if( cat.id() == "veh_parts" ) { return zone_type_id( "LOOT_VEHICLE_PARTS" ); } if( cat.id() == "other" ) { return zone_type_id( "LOOT_OTHER" ); } if( cat.id() == "fuel" ) { return zone_type_id( "LOOT_FUEL" ); } if( cat.id() == "seeds" ) { return zone_type_id( "LOOT_SEEDS" ); } if( cat.id() == "chems" ) { return zone_type_id( "LOOT_CHEMICAL" ); } if( cat.id() == "spare_parts" ) { return zone_type_id( "LOOT_SPARE_PARTS" ); } if( cat.id() == "artifacts" ) { return zone_type_id( "LOOT_ARTIFACTS" ); } if( cat.id() == "armor" ) { if( it.is_filthy() && has_near( zone_type_id( "LOOT_FARMOR" ), where ) ) { return zone_type_id( "LOOT_FARMOR" ); } return zone_type_id( "LOOT_ARMOR" ); } return zone_type_id(); }
zone_manager::zone_manager() { types.emplace( zone_type_id( "NO_AUTO_PICKUP" ), zone_type( translate_marker( "No Auto Pickup" ), translate_marker( "You won't auto-pickup items inside the zone." ) ) ); types.emplace( zone_type_id( "NO_NPC_PICKUP" ), zone_type( translate_marker( "No NPC Pickup" ), translate_marker( "Friendly NPCs don't pickup items inside the zone." ) ) ); types.emplace( zone_type_id( "NPC_RETREAT" ), zone_type( translate_marker( "NPC Retreat" ), translate_marker( "When fleeing, friendly NPCs will attempt to retreat toward this zone if it is within 60 tiles." ) ) ); types.emplace( zone_type_id( "NPC_NO_INVESTIGATE" ), zone_type( translate_marker( "NPC Ignore Sounds" ), translate_marker( "Friendly NPCs won't investigate unseen sounds coming from this zone." ) ) ); types.emplace( zone_type_id( "NPC_INVESTIGATE_ONLY" ), zone_type( translate_marker( "NPC Investigation Area" ), translate_marker( "Friendly NPCs will investigate unseen sounds only if they come from inside this area." ) ) ); types.emplace( zone_type_id( "LOOT_UNSORTED" ), zone_type( translate_marker( "Loot: Unsorted" ), translate_marker( "Place to drop unsorted loot. You can use \"sort out loot\" zone-action to sort items inside. It can overlap with Loot zones of different types." ) ) ); types.emplace( zone_type_id( "LOOT_FOOD" ), zone_type( translate_marker( "Loot: Food" ), translate_marker( "Destination for comestibles. If more specific food zone is not defined, all food is moved here." ) ) ); types.emplace( zone_type_id( "LOOT_PFOOD" ), zone_type( translate_marker( "Loot: P.Food" ), translate_marker( "Destination for perishable comestibles. Does include perishable drinks if such zone is not specified." ) ) ); types.emplace( zone_type_id( "LOOT_DRINK" ), zone_type( translate_marker( "Loot: Drink" ), translate_marker( "Destination for drinks. Does include perishable drinks if such zone is not specified." ) ) ); types.emplace( zone_type_id( "LOOT_PDRINK" ), zone_type( translate_marker( "Loot: P.Drink" ), translate_marker( "Destination for perishable drinks." ) ) ); types.emplace( zone_type_id( "LOOT_GUNS" ), zone_type( translate_marker( "Loot: Guns" ), translate_marker( "Destination for guns, bows and similar weapons." ) ) ); types.emplace( zone_type_id( "LOOT_MAGAZINES" ), zone_type( translate_marker( "Loot: Magazines" ), translate_marker( "Destination for gun magazines." ) ) ); types.emplace( zone_type_id( "LOOT_AMMO" ), zone_type( translate_marker( "Loot: Ammo" ), translate_marker( "Destination for ammo." ) ) ); types.emplace( zone_type_id( "LOOT_WEAPONS" ), zone_type( translate_marker( "Loot: Weapons" ), translate_marker( "Destination for melee weapons." ) ) ); types.emplace( zone_type_id( "LOOT_TOOLS" ), zone_type( translate_marker( "Loot: Tools" ), translate_marker( "Destination for tools." ) ) ); types.emplace( zone_type_id( "LOOT_CLOTHING" ), zone_type( translate_marker( "Loot: Clothing" ), translate_marker( "Destination for clothing. Does include filthy clothing if such zone is not specified." ) ) ); types.emplace( zone_type_id( "LOOT_FCLOTHING" ), zone_type( translate_marker( "Loot: F.Clothing" ), translate_marker( "Destination for filthy clothing." ) ) ); types.emplace( zone_type_id( "LOOT_DRUGS" ), zone_type( translate_marker( "Loot: Drugs" ), translate_marker( "Destination for drugs and other medical items." ) ) ); types.emplace( zone_type_id( "LOOT_BOOKS" ), zone_type( translate_marker( "Loot: Books" ), translate_marker( "Destination for books and magazines." ) ) ); types.emplace( zone_type_id( "LOOT_MODS" ), zone_type( translate_marker( "Loot: Mods" ), translate_marker( "Destination for firearm modifications and similar items." ) ) ); types.emplace( zone_type_id( "LOOT_MUTAGENS" ), zone_type( translate_marker( "Loot: Mutagens" ), translate_marker( "Destination for mutagens, serums, and purifiers." ) ) ); types.emplace( zone_type_id( "LOOT_BIONICS" ), zone_type( translate_marker( "Loot: Bionics" ), translate_marker( "Destination for Compact Bionics Modules aka CBMs." ) ) ); types.emplace( zone_type_id( "LOOT_VEHICLE_PARTS" ), zone_type( translate_marker( "Loot: V.Parts" ), translate_marker( "Destination for vehicle parts." ) ) ); types.emplace( zone_type_id( "LOOT_OTHER" ), zone_type( translate_marker( "Loot: Other" ), translate_marker( "Destination for other miscellaneous items." ) ) ); types.emplace( zone_type_id( "LOOT_FUEL" ), zone_type( translate_marker( "Loot: Fuel" ), translate_marker( "Destination for gasoline, diesel, lamp oil and other substances used as a fuel." ) ) ); types.emplace( zone_type_id( "LOOT_SEEDS" ), zone_type( translate_marker( "Loot: Seeds" ), translate_marker( "Destination for seeds, stems and similar items." ) ) ); types.emplace( zone_type_id( "LOOT_CHEMICAL" ), zone_type( translate_marker( "Loot: Chemical" ), translate_marker( "Destination for chemicals." ) ) ); types.emplace( zone_type_id( "LOOT_SPARE_PARTS" ), zone_type( translate_marker( "Loot: S.Parts" ), translate_marker( "Destination for spare parts." ) ) ); types.emplace( zone_type_id( "LOOT_ARTIFACTS" ), zone_type( translate_marker( "Loot: Artifacts" ), translate_marker( "Destination for artifacts" ) ) ); types.emplace( zone_type_id( "LOOT_ARMOR" ), zone_type( translate_marker( "Loot: Armor" ), translate_marker( "Destination for armor. Does include filthy armor if such zone is not specified." ) ) ); types.emplace( zone_type_id( "LOOT_FARMOR" ), zone_type( translate_marker( "Loot: F.Armor" ), translate_marker( "Destination for filthy armor." ) ) ); types.emplace( zone_type_id( "LOOT_WOOD" ), zone_type( translate_marker( "Loot: Wood" ), translate_marker( "Destination for firewood and items that can be used as such." ) ) ); types.emplace( zone_type_id( "LOOT_IGNORE" ), zone_type( translate_marker( "Loot: Ignore" ), translate_marker( "Items inside of this zone are ignored by \"sort out loot\" zone-action." ) ) ); types.emplace( zone_type_id( "FARM_PLOT" ), zone_type( translate_marker( "Farm: Plot" ), translate_marker( "Designate a farm plot for tilling and planting." ) ) ); types.emplace( zone_type_id( "CAMP_FOOD" ), zone_type( translate_marker( "Basecamp: Food" ), translate_marker( "Items in this zone will be added to a basecamp's food supply in the Distribute Food mission." ) ) ); }