void pick_up_from_feet( player &p, int pos ) { auto size_before = g->m.i_at( p.pos() ).size(); REQUIRE( size_before > pos ); p.moves = 100; p.assign_activity( activity_id( "ACT_PICKUP" ) ); p.activity.placement = tripoint(0, 0, 0); p.activity.values.push_back( false ); // not from vehicle p.activity.values.push_back( pos ); // index of item to pick up p.activity.values.push_back( 0 ); p.activity.do_turn( p ); REQUIRE( g->m.i_at( p.pos() ).size() == size_before - 1 ); }
void gates::open_gate( const tripoint &pos, player &p ) { const gate_id gid = get_gate_id( pos ); if( !gates_data.is_valid( gid ) ) { p.add_msg_if_player( _( "Nothing happens." ) ); return; } const gate_data &gate = gates_data.obj( gid ); p.add_msg_if_player( gate.pull_message.c_str() ); p.assign_activity( ACT_OPEN_GATE, gate.moves ); p.activity.placement = pos; }
std::list<item> obtain_activity_items( player_activity &act, player &p ) { std::list<item> res; auto items = reorder_for_dropping( p, convert_to_indexes( act ) ); debug_drop_list( items ); while( !items.empty() && ( p.is_npc() || p.moves > 0 || items.front().consumed_moves == 0 ) ) { const auto &ait = items.front(); p.mod_moves( -ait.consumed_moves ); if( p.is_worn( *ait.it ) ) { p.takeoff( *ait.it, &res ); } else if( ait.it->count_by_charges() ) { res.push_back( p.reduce_charges( const_cast<item *>( ait.it ), ait.count ) ); } else { res.push_back( p.i_rem( ait.it ) ); } items.pop_front(); } // Avoid tumbling to the ground. Unload cleanly. const units::volume excessive_volume = p.volume_carried() - p.volume_capacity(); if( excessive_volume > 0 ) { const auto excess = p.inv.remove_randomly_by_volume( excessive_volume ); res.insert( res.begin(), excess.begin(), excess.end() ); } // Load anything that remains (if any) into the activity act.values.clear(); if( !items.empty() ) { for( const auto &drop : convert_to_indexes( p, items ) ) { act.values.push_back( drop.first ); act.values.push_back( drop.second ); } } // And either cancel if it's empty, or restart if it's not. if( act.values.empty() ) { p.cancel_activity(); } else { p.assign_activity( act ); } return res; }
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(); }