Пример #1
0
SkillLevel const& Character::get_skill_level(const skill_id &ident) const
{
    if( !ident ) {
        static const SkillLevel none{};
        return none;
    }
    return get_skill_level( &ident.obj() );
}
Пример #2
0
int SkillLevelMap::exceeds_recipe_requirements( const recipe &rec ) const
{
    int over = rec.skill_used ? get_skill_level( rec.skill_used ) - rec.difficulty : 0;
    for( const auto &elem : compare_skill_requirements( rec.required_skills ) ) {
        over = std::min( over, elem.second );
    }
    return over;
}
Пример #3
0
bool SkillLevelMap::meets_skill_requirements( const std::map<skill_id, int> &req,
        const item &context ) const
{
    return std::all_of( req.begin(), req.end(),
    [this, &context]( const std::pair<skill_id, int> &pr ) {
        return get_skill_level( pr.first, context ) >= pr.second;
    } );
}
Пример #4
0
std::map<skill_id, int> SkillLevelMap::compare_skill_requirements(
    const std::map<skill_id, int> &req, const item &context ) const
{
    std::map<skill_id, int> res;

    for( const auto &elem : req ) {
        const int diff = get_skill_level( elem.first, context ) - elem.second;
        if( diff != 0 ) {
            res[elem.first] = diff;
        }
    }

    return res;
}
Пример #5
0
int Character::skill_dispersion( const item& gun, bool random ) const
{
    static skill_id skill_gun( "gun" );

    int dispersion = 0; // Measured in Minutes of Arc.

    const int lvl = get_skill_level( gun.gun_skill() );
    if( lvl < 10 ) {
        // Up to 0.75 degrees for each skill point < 10.
        ///\EFFECT_PISTOL <10 randomly increases dispersion for pistols
        ///\EFFECT_SMG <10 randomly increases dispersion for smgs
        ///\EFFECT_RIFLE <10 randomly increases dispersion for rifles
        ///\EFFECT_LAUNCHER <10 randomly increases dispersion for launchers
        dispersion += 45 * ( 10 - lvl );
    }

    if( get_skill_level( skill_gun ) < 10 ) {
        // Up to 0.25 deg per each skill point < 10.
        ///\EFFECT_GUN <10 randomly increased dispersion of all gunfire
        dispersion += 15 * ( 10 - lvl );
    }

    return random ? rng(0, dispersion) : dispersion;
}
Пример #6
0
bool player::can_arm_block() const
{
    const martialart &ma = style_selected.obj();
    ///\EFFECT_UNARMED increases ability to perform arm block
    int unarmed_skill = has_active_bionic( bionic_id( "bio_cqb" ) ) ? 5 : (int)get_skill_level(skill_id("unarmed"));

    // Success conditions.
    if (hp_cur[hp_arm_l] > 0 || hp_cur[hp_arm_r] > 0) {
        if( unarmed_skill >= ma.arm_block ) {
            return true;
        } else if( ma.arm_block_with_bio_armor_arms && has_bionic( bionic_id( "bio_armor_arms" ) ) ) {
            return true;
        }
    }
    // if not above, can't block.
    return false;
}
Пример #7
0
SkillLevel const& Character::get_skill_level(const Skill &_skill) const
{
    return get_skill_level(&_skill);
}
Пример #8
0
void player::complete_craft()
{
    const recipe *making = &recipe_dict[ activity.name ]; // Which recipe is it?
    int batch_size = activity.values.front();
    if( making == nullptr ) {
        debugmsg( "no recipe with id %s found", activity.name.c_str() );
        activity.set_to_null();
        return;
    }

    int secondary_dice = 0;
    int secondary_difficulty = 0;
    for( const auto &pr : making->required_skills ) {
        secondary_dice += get_skill_level( pr.first );
        secondary_difficulty += pr.second;
    }

    // # of dice is 75% primary skill, 25% secondary (unless secondary is null)
    int skill_dice;
    if( secondary_difficulty > 0 ) {
        skill_dice = get_skill_level( making->skill_used ) * 3 + secondary_dice;
    } else {
        skill_dice = get_skill_level( making->skill_used ) * 4;
    }

    auto helpers = g->u.get_crafting_helpers();
    for( const npc *np : helpers ) {
        if( np->get_skill_level( making->skill_used ) >=
            get_skill_level( making->skill_used ) ) {
            // NPC assistance is worth half a skill level
            skill_dice += 2;
            add_msg( m_info, _( "%s helps with crafting..." ), np->name.c_str() );
            break;
        }
    }

    // farsightedness can impose a penalty on electronics and tailoring success
    // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring
    if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) &&
        !is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) {
        int main_rank_penalty = 0;
        if( making->skill_used == skill_id( "electronics" ) ) {
            main_rank_penalty = 2;
        } else if( making->skill_used == skill_id( "tailor" ) ) {
            main_rank_penalty = 1;
        }
        skill_dice -= main_rank_penalty * 4;
    }

    // It's tough to craft with paws.  Fortunately it's just a matter of grip and fine-motor,
    // not inability to see what you're doing
    if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) {
        int paws_rank_penalty = 0;
        if( has_trait( trait_PAWS_LARGE ) ) {
            paws_rank_penalty += 1;
        }
        if( making->skill_used == skill_id( "electronics" )
            || making->skill_used == skill_id( "tailor" )
            || making->skill_used == skill_id( "mechanics" ) ) {
            paws_rank_penalty += 1;
        }
        skill_dice -= paws_rank_penalty * 4;
    }

    // Sides on dice is 16 plus your current intelligence
    ///\EFFECT_INT increases crafting success chance
    int skill_sides = 16 + int_cur;

    int diff_dice;
    if( secondary_difficulty > 0 ) {
        diff_dice = making->difficulty * 3 + secondary_difficulty;
    } else {
        // Since skill level is * 4 also
        diff_dice = making->difficulty * 4;
    }

    int diff_sides = 24; // 16 + 8 (default intelligence)

    int skill_roll = dice( skill_dice, skill_sides );
    int diff_roll  = dice( diff_dice,  diff_sides );

    if( making->skill_used ) {
        const double batch_mult = 1 + time_to_craft( *making, batch_size ) / 30000.0;
        //normalize experience gain to crafting time, giving a bonus for longer crafting
        practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult ),
                  ( int )making->difficulty * 1.25 );

        //NPCs assisting or watching should gain experience...
        for( auto &elem : helpers ) {
            //If the NPC can understand what you are doing, they gain more exp
            if( elem->get_skill_level( making->skill_used ) >= making->difficulty ) {
                elem->practice( making->skill_used,
                                ( int )( ( making->difficulty * 15 + 10 ) * batch_mult *
                                         .50 ), ( int )making->difficulty * 1.25 );
                if( batch_size > 1 ) {
                    add_msg( m_info, _( "%s assists with crafting..." ), elem->name.c_str() );
                }
                if( batch_size == 1 ) {
                    add_msg( m_info, _( "%s could assist you with a batch..." ), elem->name.c_str() );
                }
                //NPCs around you understand the skill used better
            } else {
                elem->practice( making->skill_used,
                                ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .15 ),
                                ( int )making->difficulty * 1.25 );
                add_msg( m_info, _( "%s watches you craft..." ), elem->name.c_str() );
            }
        }

    }

    // Messed up badly; waste some components.
    if( making->difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) {
        add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ),
                 item::nname( making->result ).c_str() );
        if( last_craft->has_cached_selections() ) {
            last_craft->consume_components();
        } else {
            // @todo Guarantee that selections are cached
            const auto &req = making->requirements();
            for( const auto &it : req.get_components() ) {
                consume_items( it, batch_size );
            }
            for( const auto &it : req.get_tools() ) {
                consume_tools( it, batch_size );
            }
        }
        activity.set_to_null();
        return;
        // Messed up slightly; no components wasted.
    } else if( diff_roll > skill_roll ) {
        add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ),
                 item::nname( making->result ).c_str() );
        //this method would only have been called from a place that nulls activity.type,
        //so it appears that it's safe to NOT null that variable here.
        //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures
        return;
    }

    // If we're here, the craft was a success!
    // Use up the components and tools
    std::list<item> used;
    if( !last_craft->has_cached_selections() ) {
        // This should fail and return, but currently crafting_command isn't saved
        // Meaning there are still cases where has_cached_selections will be false
        // @todo Allow saving last_craft and debugmsg+fail craft if selection isn't cached
        if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
            const auto &req = making->requirements();
            for( const auto &it : req.get_components() ) {
                std::list<item> tmp = consume_items( it, batch_size );
                used.splice( used.end(), tmp );
            }
            for( const auto &it : req.get_tools() ) {
                consume_tools( it, batch_size );
            }
        }
    } else if( !has_trait( trait_id( "DEBUG_HS" ) ) ) {
        used = last_craft->consume_components();
        if( used.empty() ) {
            return;
        }
    }

    // Set up the new item, and assign an inventory letter if available
    std::vector<item> newits = making->create_results( batch_size );
    bool first = true;
    float used_age_tally = 0;
    int used_age_count = 0;
    size_t newit_counter = 0;
    for( item &newit : newits ) {
        // messages, learning of recipe, food spoilage calc only once
        if( first ) {
            first = false;
            if( knows_recipe( making ) ) {
                add_msg( _( "You craft %s from memory." ), newit.type_name( 1 ).c_str() );
            } else {
                add_msg( _( "You craft %s using a book as a reference." ), newit.type_name( 1 ).c_str() );
                // If we made it, but we don't know it,
                // we're making it from a book and have a chance to learn it.
                // Base expected time to learn is 1000*(difficulty^4)/skill/int moves.
                // This means time to learn is greatly decreased with higher skill level,
                // but also keeps going up as difficulty goes up.
                // Worst case is lvl 10, which will typically take
                // 10^4/10 (1,000) minutes, or about 16 hours of crafting it to learn.
                int difficulty = has_recipe( making, crafting_inventory(), helpers );
                ///\EFFECT_INT increases chance to learn recipe when crafting from a book
                if( x_in_y( making->time, ( 1000 * 8 *
                                            ( difficulty * difficulty * difficulty * difficulty ) ) /
                            ( std::max( get_skill_level( making->skill_used ).level(), 1 ) * std::max( get_int(), 1 ) ) ) ) {
                    learn_recipe( ( recipe * )making );
                    add_msg( m_good, _( "You memorized the recipe for %s!" ),
                             newit.type_name( 1 ).c_str() );
                }
            }

            for( auto &elem : used ) {
                if( elem.goes_bad() ) {
                    used_age_tally += elem.get_relative_rot();
                    ++used_age_count;
                }
            }
        }

        // Don't store components for things made by charges,
        // don't store components for things that can't be uncrafted.
        if( recipe_dictionary::get_uncraft( making->result ) && !newit.count_by_charges() ) {
            // Setting this for items counted by charges gives only problems:
            // those items are automatically merged everywhere (map/vehicle/inventory),
            // which would either loose this information or merge it somehow.
            set_components( newit.components, used, batch_size, newit_counter );
            newit_counter++;
        }
        finalize_crafted_item( newit, used_age_tally, used_age_count );
        set_item_inventory( newit );
    }

    if( making->has_byproducts() ) {
        std::vector<item> bps = making->create_byproducts( batch_size );
        for( auto &bp : bps ) {
            finalize_crafted_item( bp, used_age_tally, used_age_count );
            set_item_inventory( bp );
        }
    }

    inv.restack( this );
}
Пример #9
0
void player::complete_disassemble( int item_pos, const tripoint &loc,
                                   bool from_ground, const recipe &dis )
{
    // Get the proper recipe - the one for disassembly, not assembly
    const auto dis_requirements = dis.disassembly_requirements();
    item &org_item = get_item_for_uncraft( *this, item_pos, loc, from_ground );
    bool filthy = org_item.is_filthy();
    if( org_item.is_null() ) {
        add_msg( _( "The item has vanished." ) );
        activity.set_to_null();
        return;
    }

    if( org_item.typeId() != dis.result ) {
        add_msg( _( "The item might be gone, at least it is not at the expected position anymore." ) );
        activity.set_to_null();
        return;
    }
    // Make a copy to keep its data (damage/components) even after it
    // has been removed.
    item dis_item = org_item;

    float component_success_chance = std::min( std::pow( 0.8, dis_item.damage() ), 1.0 );

    add_msg( _( "You disassemble the %s into its components." ), dis_item.tname().c_str() );
    // Remove any batteries, ammo and mods first
    remove_ammo( &dis_item, *this );
    remove_radio_mod( dis_item, *this );

    if( dis_item.count_by_charges() ) {
        // remove the charges that one would get from crafting it
        org_item.charges -= dis.create_result().charges;
    }
    // remove the item, except when it's counted by charges and still has some
    if( !org_item.count_by_charges() || org_item.charges <= 0 ) {
        if( from_ground ) {
            g->m.i_rem( loc, item_pos );
        } else {
            i_rem( item_pos );
        }
    }

    // Consume tool charges
    for( const auto &it : dis_requirements.get_tools() ) {
        consume_tools( it );
    }

    // add the components to the map
    // Player skills should determine how many components are returned

    int skill_dice = 2 + get_skill_level( dis.skill_used ) * 3;
    skill_dice += get_skill_level( dis.skill_used );

    // Sides on dice is 16 plus your current intelligence
    ///\EFFECT_INT increases success rate for disassembling items
    int skill_sides = 16 + int_cur;

    int diff_dice = dis.difficulty;
    int diff_sides = 24; // 16 + 8 (default intelligence)

    // disassembly only nets a bit of practice
    if( dis.skill_used ) {
        practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty );
    }

    for( const auto &altercomps : dis_requirements.get_components() ) {
        const item_comp comp = find_component( altercomps, dis_item );
        int compcount = comp.count;
        item newit( comp.type, calendar::turn );
        // Counted-by-charge items that can be disassembled individually
        // have their component count multiplied by the number of charges.
        if( dis_item.count_by_charges() && dis.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) {
            compcount *= std::min( dis_item.charges, dis.create_result().charges );
        }
        // Compress liquids and counted-by-charges items into one item,
        // they are added together on the map anyway and handle_liquid
        // should only be called once to put it all into a container at once.
        if( newit.count_by_charges() || newit.made_of( LIQUID ) ) {
            newit.charges = compcount;
            compcount = 1;
        } else if( !newit.craft_has_charges() && newit.charges > 0 ) {
            // tools that can be unloaded should be created unloaded,
            // tools that can't be unloaded will keep their default charges.
            newit.charges = 0;
        }

        for( ; compcount > 0; compcount-- ) {
            const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice,  diff_sides ) );
            if( dis.difficulty != 0 && !comp_success ) {
                add_msg( m_bad, _( "You fail to recover %s." ), newit.tname().c_str() );
                continue;
            }
            const bool dmg_success = component_success_chance > rng_float( 0, 1 );
            if( !dmg_success ) {
                // Show reason for failure (damaged item, tname contains the damage adjective)
                //~ %1s - material, %2$s - disassembled item
                add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit.tname().c_str(),
                         dis_item.tname().c_str() );
                continue;
            }
            // Use item from components list, or (if not contained)
            // use newit, the default constructed.
            item act_item = newit;

            if( filthy ) {
                act_item.item_tags.insert( "FILTHY" );
            }

            for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end();
                 ++a ) {
                if( a->type == newit.type ) {
                    act_item = *a;
                    dis_item.components.erase( a );
                    break;
                }
            }

            int veh_part = -1;
            vehicle *veh = g->m.veh_at( pos(), veh_part );
            if( veh != nullptr ) {
                veh_part = veh->part_with_feature( veh_part, "CARGO" );
            }

            if( act_item.made_of( LIQUID ) ) {
                g->handle_all_liquid( act_item, PICKUP_RANGE );
            } else if( veh_part != -1 && veh->add_item( veh_part, act_item ) ) {
                // add_item did put the items in the vehicle, nothing further to be done
            } else {
                // TODO: For items counted by charges, add as much as we can to the vehicle, and
                // the rest on the ground (see dropping code and @vehicle::add_charges)
                g->m.add_item_or_charges( pos(), act_item );
            }
        }
    }

    if( !dis.learn_by_disassembly.empty() && !knows_recipe( &dis ) ) {
        if( can_decomp_learn( dis ) ) {
            // @todo: make this depend on intelligence
            if( one_in( 4 ) ) {
                learn_recipe( &recipe_dict[ dis.ident() ] );
                add_msg( m_good, _( "You learned a recipe from disassembling it!" ) );
            } else {
                add_msg( m_info, _( "You might be able to learn a recipe if you disassemble another." ) );
            }
        } else {
            add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) );
        }
    }
}
Пример #10
0
SkillLevel const& Character::get_skill_level(const std::string &ident) const
{
    const Skill* sk = Skill::skill(ident);
    return get_skill_level(sk);
}
Пример #11
0
/*
 * Variables common to player and npc
 */
void player::json_save_common_variables(JsonOut &json) const
{
    // assumes already in player object
    // positional data
    json.member( "posx", posx );            json.member( "posy", posy );

    // attributes, current / max levels
    json.member( "str_cur", str_cur );      json.member( "str_max", str_max );
    json.member( "dex_cur", dex_cur );      json.member( "dex_max", dex_max );
    json.member( "int_cur", int_cur );      json.member( "int_max", int_max );
    json.member( "per_cur", per_cur );      json.member( "per_max", per_max );

    // om-noms or lack thereof
    json.member( "hunger", hunger );        json.member( "thirst", thirst );
    // energy
    json.member( "fatigue", fatigue );      json.member( "stim", stim );
    // pain
    json.member( "pain", pain );            json.member( "pkill", pkill );
    // misc levels
    json.member( "radiation", radiation );
    json.member( "scent", int(scent) );

    // initiative type stuff
    json.member( "moves", moves );
    json.member( "dodges_left", num_dodges);

    // breathing
    json.member( "underwater", underwater );
    json.member( "oxygen", oxygen );

    // gender
    json.member( "male", male );

    json.member( "cash", cash );
    json.member( "recoil", int(recoil) );

    // potential incompatibility with future expansion
    // todo: consider ["parts"]["head"]["hp_cur"] instead of ["hp_cur"][head_enum_value]
    json.member( "hp_cur", std::vector<int>( hp_cur, hp_cur + num_hp_parts ) );
    json.member( "hp_max", std::vector<int>( hp_max, hp_max + num_hp_parts ) );

    // npc; unimplemented
    json.member( "power_level", power_level );
    json.member( "max_power_level", max_power_level );

    // traits: permanent 'mutations' more or less
    json.member( "traits", my_traits );

    // skills
    json.member( "skills" );
    json.start_object();
    for (std::vector<Skill*>::iterator aSkill = Skill::skills.begin(); aSkill != Skill::skills.end(); ++aSkill) {
        SkillLevel sk = get_skill_level(*aSkill);
        json.member((*aSkill)->ident(), sk);
    }
    json.end_object();

    // martial arts
    /*for (int i = 0; i < ma_styles.size(); i++) {
        ptmpvect.push_back( pv( ma_styles[i] ) );
    }*/
    json.member( "ma_styles", ma_styles );

    // disease
    json.member( "illness", illness );
    json.member( "effects", effects );

    // "Looks like I picked the wrong week to quit smoking." - Steve McCroskey
    json.member( "addictions", addictions );

    // "Fracking Toasters" - Saul Tigh, toaster
    json.member( "my_bionics", my_bionics );
}
Пример #12
0
int SkillLevelMap::get_skill_level( const skill_id &ident, const item &context ) const
{
    const auto id = context.is_null() ? ident : context.contextualize_skill( ident );
    return get_skill_level( id );
}