bool game::grabbed_veh_move( const tripoint &dp ) { const optional_vpart_position grabbed_vehicle_vp = m.veh_at( u.pos() + u.grab_point ); if( !grabbed_vehicle_vp ) { add_msg( m_info, _( "No vehicle at grabbed point." ) ); u.grab( OBJECT_NONE ); return false; } vehicle *grabbed_vehicle = &grabbed_vehicle_vp->vehicle(); const int grabbed_part = grabbed_vehicle_vp->part_index(); const vehicle *veh_under_player = veh_pointer_or_null( m.veh_at( u.pos() ) ); if( grabbed_vehicle == veh_under_player ) { u.grab_point = -dp; return false; } tripoint dp_veh = -u.grab_point; const tripoint prev_grab = u.grab_point; tripoint next_grab = u.grab_point; bool zigzag = false; if( dp == prev_grab ) { // We are pushing in the direction of vehicle dp_veh = dp; } else if( abs( dp.x + dp_veh.x ) != 2 && abs( dp.y + dp_veh.y ) != 2 ) { // Not actually moving the vehicle, don't do the checks u.grab_point = -( dp + dp_veh ); return false; } else if( ( dp.x == prev_grab.x || dp.y == prev_grab.y ) && next_grab.x != 0 && next_grab.y != 0 ) { // Zig-zag (or semi-zig-zag) pull: player is diagonal to vehicle // and moves away from it, but not directly away dp_veh.x = ( dp.x == -dp_veh.x ) ? 0 : dp_veh.x; dp_veh.y = ( dp.y == -dp_veh.y ) ? 0 : dp_veh.y; next_grab = -dp_veh; zigzag = true; } else { // We are pulling the vehicle next_grab = -dp; } // Make sure the mass and pivot point are correct grabbed_vehicle->invalidate_mass(); //vehicle movement: strength check int mc = 0; int str_req = ( grabbed_vehicle->total_mass() / 25_kilogram ); //strength required to move vehicle. //if vehicle is rollable we modify str_req based on a function of movecost per wheel. // Vehicle just too big to grab & move; 41-45 lets folks have a bit of a window // (Roughly 1.1K kg = danger zone; cube vans are about the max) if( str_req > 45 ) { add_msg( m_info, _( "The %s is too bulky for you to move by hand." ), grabbed_vehicle->name ); return true; // No shoving around an RV. } const auto &wheel_indices = grabbed_vehicle->wheelcache; if( grabbed_vehicle->valid_wheel_config() ) { //determine movecost for terrain touching wheels const tripoint vehpos = grabbed_vehicle->global_pos3(); for( int p : wheel_indices ) { const tripoint wheel_pos = vehpos + grabbed_vehicle->parts[p].precalc[0]; const int mapcost = m.move_cost( wheel_pos, grabbed_vehicle ); mc += ( str_req / wheel_indices.size() ) * mapcost; } //set strength check threshold //if vehicle has many or only one wheel (shopping cart), it is as if it had four. if( wheel_indices.size() > 4 || wheel_indices.size() == 1 ) { str_req = mc / 4 + 1; } else { str_req = mc / wheel_indices.size() + 1; } } else { str_req++; //if vehicle has no wheels str_req make a noise. if( str_req <= u.get_str() ) { sounds::sound( grabbed_vehicle->global_pos3(), str_req * 2, sounds::sound_t::movement, _( "a scraping noise." ), true, "misc", "scraping" ); } } //final strength check and outcomes ///\EFFECT_STR determines ability to drag vehicles if( str_req <= u.get_str() ) { //calculate exertion factor and movement penalty ///\EFFECT_STR increases speed of dragging vehicles u.moves -= 100 * str_req / std::max( 1, u.get_str() ); const int ex = dice( 1, 3 ) - 1 + str_req; if( ex > u.get_str() ) { add_msg( m_bad, _( "You strain yourself to move the %s!" ), grabbed_vehicle->name ); u.moves -= 200; u.mod_pain( 1 ); } else if( ex == u.get_str() ) { u.moves -= 200; add_msg( _( "It takes some time to move the %s." ), grabbed_vehicle->name ); } } else { u.moves -= 100; add_msg( m_bad, _( "You lack the strength to move the %s" ), grabbed_vehicle->name ); return true; } std::string blocker_name = _( "errors in movement code" ); const auto get_move_dir = [&]( const tripoint & dir, const tripoint & from ) { tileray mdir; mdir.init( dir.x, dir.y ); grabbed_vehicle->turn( mdir.dir() - grabbed_vehicle->face.dir() ); grabbed_vehicle->face = grabbed_vehicle->turn_dir; grabbed_vehicle->precalc_mounts( 1, mdir.dir(), grabbed_vehicle->pivot_point() ); // Grabbed part has to stay at distance 1 to the player // and in roughly the same direction. const tripoint new_part_pos = grabbed_vehicle->global_pos3() + grabbed_vehicle->parts[ grabbed_part ].precalc[ 1 ]; const tripoint expected_pos = u.pos() + dp + from; const tripoint actual_dir = expected_pos - new_part_pos; // Set player location to illegal value so it can't collide with vehicle. const tripoint player_prev = u.pos(); u.setpos( tripoint_zero ); std::vector<veh_collision> colls; const bool failed = grabbed_vehicle->collision( colls, actual_dir, true ); u.setpos( player_prev ); if( !colls.empty() ) { blocker_name = colls.front().target_name; } return failed ? tripoint_zero : actual_dir; }; // First try the move as intended // But if that fails and the move is a zig-zag, try to recover: // Try to place the vehicle in the position player just left rather than "flattening" the zig-zag tripoint final_dp_veh = get_move_dir( dp_veh, next_grab ); if( final_dp_veh == tripoint_zero && zigzag ) { final_dp_veh = get_move_dir( -prev_grab, -dp ); next_grab = -dp; } if( final_dp_veh == tripoint_zero ) { add_msg( _( "The %s collides with %s." ), grabbed_vehicle->name, blocker_name ); u.grab_point = prev_grab; return true; } u.grab_point = next_grab; tripoint gp = grabbed_vehicle->global_pos3(); grabbed_vehicle = m.displace_vehicle( gp, final_dp_veh ); if( grabbed_vehicle == nullptr ) { debugmsg( "Grabbed vehicle disappeared" ); return false; } for( int p : wheel_indices ) { if( one_in( 2 ) ) { tripoint wheel_p = grabbed_vehicle->global_part_pos3( grabbed_part ); grabbed_vehicle->handle_trap( wheel_p, p ); } } return false; }
void inventory::form_from_map( map &m, const tripoint &origin, int range, bool assign_invlet, bool clear_path ) { items.clear(); for( const tripoint &p : m.points_in_radius( origin, range ) ) { // can not reach this -> can not access its contents if( clear_path ) { if( origin != p && !m.clear_path( origin, p, range, 1, 100 ) ) { continue; } } if( m.has_furn( p ) ) { const furn_t &f = m.furn( p ).obj(); const itype *type = f.crafting_pseudo_item_type(); if( type != nullptr ) { const itype *ammo = f.crafting_ammo_item_type(); item furn_item( type, calendar::turn, 0 ); furn_item.item_tags.insert( "PSEUDO" ); furn_item.charges = ammo ? count_charges_in_list( ammo, m.i_at( p ) ) : 0; add_item( furn_item ); } } if( m.accessible_items( p ) ) { for( auto &i : m.i_at( p ) ) { if( !i.made_of( LIQUID ) ) { add_item( i, false, assign_invlet ); } } } // Kludges for now! if( m.has_nearby_fire( p, 0 ) ) { item fire( "fire", 0 ); fire.charges = 1; add_item( fire ); } // Handle any water from infinite map sources. item water = m.water_from( p ); if( !water.is_null() ) { add_item( water ); } // kludge that can probably be done better to check specifically for toilet water to use in // crafting if( m.furn( p ).obj().examine == &iexamine::toilet ) { // get water charges at location auto toilet = m.i_at( p ); auto water = toilet.end(); for( auto candidate = toilet.begin(); candidate != toilet.end(); ++candidate ) { if( candidate->typeId() == "water" ) { water = candidate; break; } } if( water != toilet.end() && water->charges > 0 ) { add_item( *water ); } } // keg-kludge if( m.furn( p ).obj().examine == &iexamine::keg ) { auto liq_contained = m.i_at( p ); for( auto &i : liq_contained ) { if( i.made_of( LIQUID ) ) { add_item( i ); } } } // WARNING: The part below has a bug that's currently quite minor // When a vehicle has multiple faucets in range, available water is // multiplied by the number of faucets. // Same thing happens for all other tools and resources, but not cargo const optional_vpart_position vp = m.veh_at( p ); if( !vp ) { continue; } vehicle *const veh = &vp->vehicle(); //Adds faucet to kitchen stuff; may be horribly wrong to do such.... //ShouldBreak into own variable const cata::optional<vpart_reference> kpart = vp.part_with_feature( "KITCHEN", true ); const cata::optional<vpart_reference> faupart = vp.part_with_feature( "FAUCET", true ); const cata::optional<vpart_reference> weldpart = vp.part_with_feature( "WELDRIG", true ); const cata::optional<vpart_reference> craftpart = vp.part_with_feature( "CRAFTRIG", true ); const cata::optional<vpart_reference> forgepart = vp.part_with_feature( "FORGE", true ); const cata::optional<vpart_reference> kilnpart = vp.part_with_feature( "KILN", true ); const cata::optional<vpart_reference> chempart = vp.part_with_feature( "CHEMLAB", true ); const cata::optional<vpart_reference> cargo = vp.part_with_feature( "CARGO", true ); if( cargo ) { const auto items = veh->get_items( cargo->part_index() ); *this += std::list<item>( items.begin(), items.end() ); } if( faupart ) { for( const auto &it : veh->fuels_left() ) { item fuel( it.first, 0 ); if( fuel.made_of( LIQUID ) ) { fuel.charges = it.second; add_item( fuel ); } } } if( kpart ) { item hotplate( "hotplate", 0 ); hotplate.charges = veh->fuel_left( "battery", true ); hotplate.item_tags.insert( "PSEUDO" ); add_item( hotplate ); item pot( "pot", 0 ); pot.item_tags.insert( "PSEUDO" ); add_item( pot ); item pan( "pan", 0 ); pan.item_tags.insert( "PSEUDO" ); add_item( pan ); } if( weldpart ) { item welder( "welder", 0 ); welder.charges = veh->fuel_left( "battery", true ); welder.item_tags.insert( "PSEUDO" ); add_item( welder ); item soldering_iron( "soldering_iron", 0 ); soldering_iron.charges = veh->fuel_left( "battery", true ); soldering_iron.item_tags.insert( "PSEUDO" ); add_item( soldering_iron ); } if( craftpart ) { item vac_sealer( "vac_sealer", 0 ); vac_sealer.charges = veh->fuel_left( "battery", true ); vac_sealer.item_tags.insert( "PSEUDO" ); add_item( vac_sealer ); item dehydrator( "dehydrator", 0 ); dehydrator.charges = veh->fuel_left( "battery", true ); dehydrator.item_tags.insert( "PSEUDO" ); add_item( dehydrator ); item food_processor( "food_processor", 0 ); food_processor.charges = veh->fuel_left( "battery", true ); food_processor.item_tags.insert( "PSEUDO" ); add_item( food_processor ); item press( "press", 0 ); press.charges = veh->fuel_left( "battery", true ); press.item_tags.insert( "PSEUDO" ); add_item( press ); } if( forgepart ) { item forge( "forge", 0 ); forge.charges = veh->fuel_left( "battery", true ); forge.item_tags.insert( "PSEUDO" ); add_item( forge ); } if( kilnpart ) { item kiln( "kiln", 0 ); kiln.charges = veh->fuel_left( "battery", true ); kiln.item_tags.insert( "PSEUDO" ); add_item( kiln ); } if( chempart ) { item hotplate( "hotplate", 0 ); hotplate.charges = veh->fuel_left( "battery", true ); hotplate.item_tags.insert( "PSEUDO" ); add_item( hotplate ); item chemistry_set( "chemistry_set", 0 ); chemistry_set.charges = veh->fuel_left( "battery", true ); chemistry_set.item_tags.insert( "PSEUDO" ); add_item( chemistry_set ); } } }