bool repair_part( vehicle &veh, vehicle_part &pt, Character &who_c ) { // @todo: Get rid of this cast after moving relevant functions down to Character player &who = ( player & )who_c; int part_index = veh.index_of_part( &pt ); auto &vp = pt.info(); // @todo: Expose base part damage somewhere, don't recalculate it here const auto reqs = pt.is_broken() ? vp.install_requirements() : vp.repair_requirements() * pt.damage_level( 4 ); inventory map_inv; map_inv.form_from_map( who.pos(), PICKUP_RANGE ); if( !reqs.can_make_with_inventory( who.crafting_inventory() ) ) { who.add_msg_if_player( m_info, _( "You don't meet the requirements to repair the %s." ), pt.name().c_str() ); return false; } // consume items extracting any base item (which we will need if replacing broken part) item base( vp.item ); for( const auto &e : reqs.get_components() ) { for( auto &obj : who.consume_items( who.select_item_component( e, 1, map_inv ), 1 ) ) { if( obj.typeId() == vp.item ) { base = obj; } } } for( const auto &e : reqs.get_tools() ) { who.consume_tools( who.select_tool_component( e, 1, map_inv ), 1 ); } who.invalidate_crafting_inventory(); for( const auto &sk : pt.is_broken() ? vp.install_skills : vp.repair_skills ) { who.practice( sk.first, calc_xp_gain( vp, sk.first ) ); } // If part is broken, it will be destroyed and references invalidated std::string partname = pt.name(); if( pt.is_broken() ) { const int dir = pt.direction; point loc = pt.mount; auto replacement_id = pt.info().get_id(); g->m.spawn_items( who.pos(), pt.pieces_for_broken_part() ); veh.remove_part( part_index ); const int partnum = veh.install_part( loc, replacement_id, std::move( base ) ); veh.parts[partnum].direction = dir; veh.part_removal_cleanup(); } else { veh.set_hp( pt, pt.info().durability ); } // @todo: NPC doing that who.add_msg_if_player( m_good, _( "You repair the %1$s's %2$s." ), veh.name.c_str(), partname.c_str() ); return true; }
// Returns how much fuel did it provide // But contains only fuels actually used by engines std::map<itype_id, long> set_vehicle_fuel( vehicle &v, const float veh_fuel_mult ) { // First we need to find the fuels to set // That is, fuels actually used by some engine std::set<itype_id> actually_used; for( const vpart_reference vp : v.get_all_parts() ) { vehicle_part &pt = vp.part(); if( pt.is_engine() ) { actually_used.insert( pt.info().fuel_type ); pt.enabled = true; } else { // Disable all parts that use up power or electric cars become non-deterministic pt.enabled = false; } } // We ignore battery when setting fuel because it uses designated "tanks" actually_used.erase( "battery" ); // Currently only one liquid fuel supported REQUIRE( actually_used.size() <= 1 ); itype_id liquid_fuel = "null"; for( const auto &ft : actually_used ) { if( item::find_type( ft )->phase == LIQUID ) { liquid_fuel = ft; break; } } // Set fuel to a given percentage // Batteries are special cased because they aren't liquid fuel std::map<itype_id, long> ret; for( const vpart_reference vp : v.get_all_parts() ) { vehicle_part &pt = vp.part(); if( pt.is_battery() ) { pt.ammo_set( "battery", pt.ammo_capacity() * veh_fuel_mult ); ret[ "battery" ] += pt.ammo_capacity() * veh_fuel_mult; } else if( pt.is_tank() && liquid_fuel != "null" ) { float qty = pt.ammo_capacity() * veh_fuel_mult; qty *= std::max( item::find_type( liquid_fuel )->stack_size, 1 ); qty /= to_milliliter( units::legacy_volume_factor ); pt.ammo_set( liquid_fuel, qty ); ret[ liquid_fuel ] += qty; } else { pt.ammo_unset(); } } // We re-add battery because we want it accounted for, just not in the section above actually_used.insert( "battery" ); for( auto iter = ret.begin(); iter != ret.end(); ) { if( iter->second <= 0 || actually_used.count( iter->first ) == 0 ) { iter = ret.erase( iter ); } else { ++iter; } } return ret; }
void put_into_vehicle( player &p, const std::list<item> &items, vehicle &veh, int part ) { if( items.empty() ) { return; } const tripoint where = veh.global_part_pos3( part ); const std::string ter_name = g->m.name( where ); int fallen_count = 0; for( auto it : items ) { // cant use constant reference here because of the spill_contents() if( it.is_bucket_nonempty() && !it.spill_contents( p ) ) { p.add_msg_player_or_npc( _( "To avoid spilling its contents, you set your %1$s on the %2$s." ), _( "To avoid spilling its contents, <npcname> sets their %1$s on the %2$s." ), it.display_name().c_str(), ter_name.c_str() ); g->m.add_item_or_charges( where, it ); continue; } if( !veh.add_item( part, it ) ) { if( it.count_by_charges() ) { // Maybe we can add a few charges in the trunk and the rest on the ground. it.mod_charges( -veh.add_charges( part, it ) ); } g->m.add_item_or_charges( where, it ); ++fallen_count; } } const std::string part_name = veh.part_info( part ).name(); if( same_type( items ) ) { const item &it = items.front(); const int dropcount = items.size() * ( it.count_by_charges() ? it.charges : 1 ); p.add_msg_player_or_npc( ngettext( "You put your %1$s in the %2$s's %3$s.", "You put your %1$s in the %2$s's %3$s.", dropcount ), ngettext( "<npcname> puts their %1$s in the %2$s's %3$s.", "<npcname> puts their %1$s in the %2$s's %3$s.", dropcount ), it.tname( dropcount ).c_str(), veh.name.c_str(), part_name.c_str() ); } else { p.add_msg_player_or_npc( _( "You put several items in the %1$s's %2$s." ), _( "<npcname> puts several items in the %1$s's %2$s." ), veh.name.c_str(), part_name.c_str() ); } if( fallen_count > 0 ) { add_msg( m_warning, _( "The trunk is full, so some items fell to the %s." ), ter_name.c_str() ); } }
// Returns the lowest percentage of fuel left // ie. 1 means no fuel was used, 0 means at least one dry tank float fuel_percentage_left( vehicle &v, const std::map<itype_id, long> &started_with ) { std::map<itype_id, long> fuel_amount; std::set<itype_id> consumed_fuels; for( const vpart_reference vp : v.get_all_parts() ) { vehicle_part &pt = vp.part(); if( ( pt.is_battery() || pt.is_reactor() || pt.is_tank() ) && pt.ammo_current() != "null" ) { fuel_amount[ pt.ammo_current() ] += pt.ammo_remaining(); } if( pt.is_engine() && pt.info().fuel_type != "null" ) { consumed_fuels.insert( pt.info().fuel_type ); } } float left = 1.0f; for( const auto &type : consumed_fuels ) { const auto iter = started_with.find( type ); // Weird - we started without this fuel float fuel_amt_at_start = iter != started_with.end() ? iter->second : 0.0f; REQUIRE( fuel_amt_at_start != 0.0f ); left = std::min( left, static_cast<float>( fuel_amount[type] ) / fuel_amt_at_start ); } return left; }
void remove_item() override { if( what == nullptr ) { return; } const auto parts = veh->parts_at_relative( local_coords.x, local_coords.y ); for( const int i : parts ) { if( veh->remove_item( i, what ) ) { what = nullptr; return; } } debugmsg( "Tried to remove an item from vehicle %s, tile %d:%d, but it wasn't there", veh->name.c_str(), local_coords.x, local_coords.y ); }
item_on_vehicle( vehicle &v, const point &where, const item *which ) { veh = &v; local_coords = where; for( const int i : v.parts_at_relative( where.x, where.y ) ) { for( item &it : v.get_items( i ) ) { if( &it == which ) { partnum = i; what = ⁢ return; } } } debugmsg( "Tried to find an item on vehicle %s, tile %d:%d, but it wasn't there", veh->name.c_str(), local_coords.x, local_coords.y ); what = nullptr; }
static int max_quality_from_vpart( const vehicle& veh, int part, const quality_id& qual ) { int res = INT_MIN; auto pos = veh.parts[ part ].mount; for( const auto &n : veh.parts_at_relative( pos.x, pos.y ) ) { // only unbroken parts can provide tool qualities if( !veh.parts[ n ].is_broken() ) { auto tq = veh.part_info( n ).qualities; auto iter = tq.find( qual ); // does the part provide this quality? if( iter != tq.end() ) { res = std::max( res, iter->second ); } } } return res; }
item *get_item() override { if( what == nullptr ) { return nullptr; } const auto parts = veh->parts_at_relative( local_coords.x, local_coords.y ); for( const int i : parts ) { for( item &it : veh->get_items( i ) ) { if( &it == what ) { return ⁢ } } } debugmsg( "Tried to find an item on vehicle %s, tile %d:%d, but it wasn't there", veh->name.c_str(), local_coords.x, local_coords.y ); what = nullptr; return nullptr; }
static int has_quality_from_vpart( const vehicle& veh, int part, const quality_id& qual, int level, int limit ) { int qty = 0; auto pos = veh.parts[ part ].mount; for( const auto &n : veh.parts_at_relative( pos.x, pos.y ) ) { // only unbroken parts can provide tool qualities if( !veh.parts[ n ].is_broken() ) { auto tq = veh.part_info( n ).qualities; auto iter = tq.find( qual ); // does the part provide this quality? if( iter != tq.end() && iter->second >= level ) { if( ++qty >= limit ) { break; } } } } return std::min( qty, limit ); }
void put_into_vehicle( Character &c, item_drop_reason reason, const std::list<item> &items, vehicle &veh, int part ) { if( items.empty() ) { return; } const tripoint where = veh.global_part_pos3( part ); const std::string ter_name = g->m.name( where ); int fallen_count = 0; int into_vehicle_count = 0; for( auto it : items ) { // cant use constant reference here because of the spill_contents() if( Pickup::handle_spillable_contents( c, it, g->m ) ) { continue; } if( veh.add_item( part, it ) ) { into_vehicle_count += it.count(); } else { if( it.count_by_charges() ) { // Maybe we can add a few charges in the trunk and the rest on the ground. auto charges_added = veh.add_charges( part, it ); it.mod_charges( -charges_added ); into_vehicle_count += charges_added; } g->m.add_item_or_charges( where, it ); fallen_count += it.count(); } } const std::string part_name = veh.part_info( part ).name(); if( same_type( items ) ) { const item &it = items.front(); const int dropcount = items.size() * it.count(); const std::string it_name = it.tname( dropcount ); switch( reason ) { case item_drop_reason::deliberate: c.add_msg_player_or_npc( ngettext( "You put your %1$s in the %2$s's %3$s.", "You put your %1$s in the %2$s's %3$s.", dropcount ), ngettext( "<npcname> puts their %1$s in the %2$s's %3$s.", "<npcname> puts their %1$s in the %2$s's %3$s.", dropcount ), it_name, veh.name, part_name ); break; case item_drop_reason::too_large: c.add_msg_if_player( ngettext( "There's no room in your inventory for the %s, so you drop it into the %s's %s.", "There's no room in your inventory for the %s, so you drop them into the %s's %s.", dropcount ), it_name, veh.name, part_name ); break; case item_drop_reason::too_heavy: c.add_msg_if_player( ngettext( "The %s is too heavy to carry, so you drop it into the %s's %s.", "The %s are too heavy to carry, so you drop them into the %s's %s.", dropcount ), it_name, veh.name, part_name ); break; case item_drop_reason::tumbling: c.add_msg_if_player( m_bad, ngettext( "Your %s tumbles into the %s's %s.", "Your %s tumble into the %s's %s.", dropcount ), it_name, veh.name, part_name ); break; } } else { switch( reason ) { case item_drop_reason::deliberate: c.add_msg_player_or_npc( _( "You put several items in the %1$s's %2$s." ), _( "<npcname> puts several items in the %1$s's %2$s." ), veh.name, part_name ); break; case item_drop_reason::too_large: case item_drop_reason::too_heavy: case item_drop_reason::tumbling: c.add_msg_if_player( m_bad, _( "Some items tumble into the %1$s's %2$s." ), veh.name, part_name ); break; } } if( fallen_count > 0 ) { if( into_vehicle_count > 0 ) { c.add_msg_if_player( m_warning, ngettext( "The %s is full, so something fell to the %s.", "The %s is full, so some items fell to the %s.", fallen_count ), part_name, ter_name ); } else { c.add_msg_if_player( m_warning, ngettext( "The %s is full, so it fell to the %s.", "The %s is full, so they fell to the %s.", fallen_count ), part_name, ter_name ); } } }