Beispiel #1
0
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;
}
Beispiel #5
0
    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 );
    }
Beispiel #6
0
    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 = &it;
                    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;
    }
Beispiel #7
0
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;
}
Beispiel #8
0
    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 &it;
                }
            }
        }

        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;
    }
Beispiel #9
0
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
            );
        }
    }
}