Beispiel #1
0
bool player::install_bionics(it_bionic *type)
{
    if (type == NULL) {
        debugmsg("Tried to install NULL bionic");
        return false;
    }
    if (bionics.count(type->id) == 0) {
        popup("invalid / unknown bionic id %s", type->id.c_str());
        return false;
    }
    if (has_bionic(type->id)) {
        if (!(type->id == "bio_power_storage" || type->id == "bio_power_storage_mkII")) {
            popup(_("You have already installed this bionic."));
            return false;
        }
    }
    int chance_of_success = bionic_manip_cos(int_cur,
                            skillLevel("electronics"),
                            skillLevel("firstaid"),
                            skillLevel("mechanics"),
                            type->difficulty);

    if (!query_yn(
            _("WARNING: %i percent chance of genetic damage, blood loss, or damage to existing bionics! Install anyway?"),
            100 - chance_of_success)) {
        return false;
    }
    int pow_up = 0;
    if (type->id == "bio_power_storage" || type->id == "bio_power_storage_mkII") {
        pow_up = BATTERY_AMOUNT;
        if (type->id == "bio_power_storage_mkII") {
            pow_up = 250;
        }
    }

    practice( "electronics", int((100 - chance_of_success) * 1.5) );
    practice( "firstaid", int((100 - chance_of_success) * 1.0) );
    practice( "mechanics", int((100 - chance_of_success) * 0.5) );
    int success = chance_of_success - rng(1, 100);
    if (success > 0) {
        add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."),
                         pgettext("memorial_female", "Installed bionic: %s."),
                         bionics[type->id]->name.c_str());
        if (pow_up) {
            max_power_level += pow_up;
            add_msg_if_player(m_good, _("Increased storage capacity by %i"), pow_up);
        } else {
            add_msg(m_good, _("Successfully installed %s."), bionics[type->id]->name.c_str());
            add_bionic(type->id);
        }
    } else {
        add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."),
                         pgettext("memorial_female", "Installed bionic: %s."),
                         bionics[type->id]->name.c_str());
        bionics_install_failure(this, type, success);
    }
    g->refresh_all();
    return true;
}
Beispiel #2
0
bool Creature::remove_effect(efftype_id eff_id, body_part bp)
{
    if (!has_effect(eff_id, bp)) {
        //Effect doesn't exist, so do nothing
        return false;
    }

    if (is_player()) {
        // Print the removal message and add the memorial log if needed
        if(effect_types[eff_id].get_remove_message() != "") {
            add_msg(effect_types[eff_id].lose_game_message_type(),
                         _(effect_types[eff_id].get_remove_message().c_str()));
        }
        add_memorial_log(pgettext("memorial_male",
                                       effect_types[eff_id].get_remove_memorial_log().c_str()),
                              pgettext("memorial_female",
                                       effect_types[eff_id].get_remove_memorial_log().c_str()));
    }

    // num_bp means remove all of a given effect id
    if (bp == num_bp) {
        effects.erase(eff_id);
    } else {
        effects[eff_id].erase(bp);
        // If there are no more effects of a given type remove the type map
        if (effects[eff_id].empty()) {
            effects.erase(eff_id);
        }
    }
    return true;
}
Beispiel #3
0
void Creature::process_effects()
{
    for( auto it = effects.begin(); it != effects.end(); ++it ) {
        if( !it->second.is_permanent() ) {
            it->second.mod_duration( -1 );
            add_msg( m_debug, "Duration %d", it->second.get_duration() );
        }
    }
    for( auto it = effects.begin(); it != effects.end(); ) {
        if( !it->second.is_permanent() && it->second.get_duration() <= 0 ) {
            const effect_type *type = it->second.get_effect_type();
            if(type->get_remove_message() != "") {
                add_msg( type->lose_game_message_type(), _(type->get_remove_message().c_str()) );
            }
            add_memorial_log(
                pgettext("memorial_male", type->get_remove_memorial_log().c_str() ),
                pgettext("memorial_female", type->get_remove_memorial_log().c_str()) );
            const auto id = it->second.get_id();
            ++it;
            remove_effect( id );
        } else {
            ++it;
        }
    }
}
Beispiel #4
0
/*
 * Effect-related methods
 */
void Creature::add_effect(efftype_id eff_id, int dur, int intensity, bool permanent)
{
    // check if we already have it
    auto found_effect = effects.find( eff_id );

    if (found_effect != effects.end()) {
        effect &e = found_effect->second;
        // if we do, mod the duration
        e.mod_duration(dur);
        // Adding a permanent effect makes it permanent
        if( e.is_permanent() ) {
            e.pause_effect();
        }
        if( e.get_intensity() + intensity <= e.get_max_intensity() ) {
            e.mod_intensity( intensity );
        }
    } else {
        // if we don't already have it then add a new one
        if (effect_types.find(eff_id) == effect_types.end()) {
            return;
        }
        effect new_eff(&effect_types[eff_id], dur, intensity, permanent);
        effects[eff_id] = new_eff;
        if (is_player()) { // only print the message if we didn't already have it
            if(effect_types[eff_id].get_apply_message() != "") {
                     add_msg(effect_types[eff_id].gain_game_message_type(),
                             _(effect_types[eff_id].get_apply_message().c_str()));
            }
            add_memorial_log(pgettext("memorial_male",
                                           effect_types[eff_id].get_apply_memorial_log().c_str()),
                                  pgettext("memorial_female",
                                           effect_types[eff_id].get_apply_memorial_log().c_str()));
        }
    }
}
bool Creature::remove_effect( const efftype_id &eff_id, body_part bp )
{
    if (!has_effect(eff_id, bp)) {
        //Effect doesn't exist, so do nothing
        return false;
    }
    const effect_type &type = eff_id.obj();

    if (is_player()) {
        // Print the removal message and add the memorial log if needed
        if( !type.get_remove_message().empty() ) {
            add_msg(type.lose_game_message_type(),
                         _(type.get_remove_message().c_str()));
        }
        add_memorial_log(pgettext("memorial_male",
                                       type.get_remove_memorial_log().c_str()),
                              pgettext("memorial_female",
                                       type.get_remove_memorial_log().c_str()));
    }

    // num_bp means remove all of a given effect id
    if (bp == num_bp) {
        for( auto &it : ( *effects )[eff_id] ) {
            on_effect_int_change( eff_id, 0, it.first );
        }
        effects->erase(eff_id);
    } else {
        ( *effects )[eff_id].erase(bp);
        on_effect_int_change( eff_id, 0, bp );
        // If there are no more effects of a given type remove the type map
        if (( *effects )[eff_id].empty()) {
            effects->erase(eff_id);
        }
    }
    return true;
}
void Creature::add_effect( const efftype_id &eff_id, const time_duration dur, body_part bp,
                           bool permanent, int intensity, bool force, bool deferred )
{
    // Check our innate immunity
    if( !force && is_immune_effect( eff_id ) ) {
        return;
    }

    if( !eff_id.is_valid() ) {
        debugmsg( "Invalid effect, ID: %s", eff_id.c_str() );
        return;
    }
    const effect_type &type = eff_id.obj();

    // Mutate to a main (HP'd) body_part if necessary.
    if (type.get_main_parts()) {
        bp = mutate_to_main_part(bp);
    }

    bool found = false;
    // Check if we already have it
    auto matching_map = effects->find(eff_id);
    if (matching_map != effects->end()) {
        auto &bodyparts = matching_map->second;
        auto found_effect = bodyparts.find(bp);
        if (found_effect != bodyparts.end()) {
            found = true;
            effect &e = found_effect->second;
            const int prev_int = e.get_intensity();
            // If we do, mod the duration, factoring in the mod value
            e.mod_duration( dur * e.get_dur_add_perc() / 100);
            // Limit to max duration
            if( e.get_max_duration() > 0_turns && e.get_duration() > e.get_max_duration() ) {
                e.set_duration( e.get_max_duration() );
            }
            // Adding a permanent effect makes it permanent
            if( e.is_permanent() ) {
                e.pause_effect();
            }
            // int_dur_factor overrides all other intensity settings
            // ...but it's handled in set_duration, so explicitly do nothing here
            if( e.get_int_dur_factor() > 0_turns ) {
                // Set intensity if value is given
            } else if (intensity > 0) {
                e.set_intensity(intensity);
                // Else intensity uses the type'd step size if it already exists
            } else if (e.get_int_add_val() != 0) {
                e.mod_intensity(e.get_int_add_val());
            }

            // Bound intensity by [1, max intensity]
            if (e.get_intensity() < 1) {
                add_msg( m_debug, "Bad intensity, ID: %s", e.get_id().c_str() );
                e.set_intensity(1);
            } else if (e.get_intensity() > e.get_max_intensity()) {
                e.set_intensity(e.get_max_intensity());
            }
            if( e.get_intensity() != prev_int ) {
                on_effect_int_change( eff_id, e.get_intensity(), bp );
            }
        }
    }

    if( !found ) {
        // If we don't already have it then add a new one

        // Then check if the effect is blocked by another
        for( auto &elem : *effects ) {
            for( auto &_effect_it : elem.second ) {
                for( const auto& blocked_effect : _effect_it.second.get_blocks_effects() ) {
                    if (blocked_effect == eff_id) {
                        // The effect is blocked by another, return
                        return;
                    }
                }
            }
        }

        // Now we can make the new effect for application
        effect e( &type, dur, bp, permanent, intensity, calendar::turn );
        // Bound to max duration
        if( e.get_max_duration() > 0_turns && e.get_duration() > e.get_max_duration() ) {
            e.set_duration( e.get_max_duration() );
        }

        // Force intensity if it is duration based
        if( e.get_int_dur_factor() != 0_turns ) {
            // + 1 here so that the lowest is intensity 1, not 0
             e.set_intensity( e.get_duration() / e.get_int_dur_factor() + 1 );
        }
        // Bound new effect intensity by [1, max intensity]
        if (e.get_intensity() < 1) {
            add_msg( m_debug, "Bad intensity, ID: %s", e.get_id().c_str() );
            e.set_intensity(1);
        } else if (e.get_intensity() > e.get_max_intensity()) {
            e.set_intensity(e.get_max_intensity());
        }
        ( *effects )[eff_id][bp] = e;
        if (is_player()) {
            // Only print the message if we didn't already have it
            if( !type.get_apply_message().empty() ) {
                     add_msg(type.gain_game_message_type(),
                             _(type.get_apply_message().c_str()));
            }
            add_memorial_log(pgettext("memorial_male",
                                           type.get_apply_memorial_log().c_str()),
                                  pgettext("memorial_female",
                                           type.get_apply_memorial_log().c_str()));
        }
        on_effect_int_change( eff_id, e.get_intensity(), bp );
        // Perform any effect addition effects.
        // only when not deferred
        if( !deferred ) {
            process_one_effect( e, true );
        }
    }
}
Beispiel #7
0
void Creature::add_effect( efftype_id eff_id, int dur, body_part bp,
                           bool permanent, int intensity, bool force )
{
    // Check our innate immunity
    if( !force && is_immune_effect( eff_id ) ) {
        return;
    }

    // Mutate to a main (HP'd) body_part if necessary.
    if (effect_types[eff_id].get_main_parts()) {
        bp = mutate_to_main_part(bp);
    }

    bool found = false;
    // Check if we already have it
    auto matching_map = effects.find(eff_id);
    if (matching_map != effects.end()) {
        auto found_effect = effects[eff_id].find(bp);
        if (found_effect != effects[eff_id].end()) {
            found = true;
            effect &e = found_effect->second;
            // If we do, mod the duration, factoring in the mod value
            e.mod_duration(dur * e.get_dur_add_perc() / 100);
            // Limit to max duration
            if (e.get_max_duration() > 0 && e.get_duration() > e.get_max_duration()) {
                e.set_duration(e.get_max_duration());
            }
            // Adding a permanent effect makes it permanent
            if( e.is_permanent() ) {
                e.pause_effect();
            }
            // Set intensity if value is given
            if (intensity > 0) {
                e.set_intensity(intensity);
            // Else intensity uses the type'd step size if it already exists
            } else if (e.get_int_add_val() != 0) {
                e.mod_intensity(e.get_int_add_val());
            }

            // Bound intensity by [1, max intensity]
            if (e.get_intensity() < 1) {
                add_msg( m_debug, "Bad intensity, ID: %s", e.get_id().c_str() );
                e.set_intensity(1);
            } else if (e.get_intensity() > e.get_max_intensity()) {
                e.set_intensity(e.get_max_intensity());
            }
        }
    }

    if( found == false ) {
        // If we don't already have it then add a new one

        // First make sure it's a valid effect
        if (effect_types.find(eff_id) == effect_types.end()) {
            return;
        }
        // Then check if the effect is blocked by another
        for( auto &elem : effects ) {
            for( auto &_effect_it : elem.second ) {
                for( const auto blocked_effect : _effect_it.second.get_blocks_effects() ) {
                    if (blocked_effect == eff_id) {
                        // The effect is blocked by another, return
                        return;
                    }
                }
            }
        }

        // Now we can make the new effect for application
        effect new_eff(&effect_types[eff_id], dur, bp, permanent, intensity);
        effect &e = new_eff;
        // Bound to max duration
        if (e.get_max_duration() > 0 && e.get_duration() > e.get_max_duration()) {
            e.set_duration(e.get_max_duration());
        }
        // Bound new effect intensity by [1, max intensity]
        if (new_eff.get_intensity() < 1) {
            add_msg( m_debug, "Bad intensity, ID: %s", new_eff.get_id().c_str() );
            new_eff.set_intensity(1);
        } else if (new_eff.get_intensity() > new_eff.get_max_intensity()) {
            new_eff.set_intensity(new_eff.get_max_intensity());
        }
        effects[eff_id][bp] = new_eff;
        if (is_player()) {
            // Only print the message if we didn't already have it
            if(effect_types[eff_id].get_apply_message() != "") {
                     add_msg(effect_types[eff_id].gain_game_message_type(),
                             _(effect_types[eff_id].get_apply_message().c_str()));
            }
            add_memorial_log(pgettext("memorial_male",
                                           effect_types[eff_id].get_apply_memorial_log().c_str()),
                                  pgettext("memorial_female",
                                           effect_types[eff_id].get_apply_memorial_log().c_str()));
        }
        // Perform any effect addition effects.
        bool reduced = has_effect(e.get_resist_effect()) || has_trait(e.get_resist_trait());
        add_eff_effects(e, reduced);
    }
}
bool player::mutate_towards( const trait_id &mut )
{
    if (has_child_flag(mut)) {
        remove_child_flag(mut);
        return true;
    }
    const mutation_branch &mdata = mut.obj();

    bool has_prereqs = false;
    bool prereq1 = false;
    bool prereq2 = false;
    std::vector<trait_id> canceltrait;
    std::vector<trait_id> prereq = mdata.prereqs;
    std::vector<trait_id> prereqs2 = mdata.prereqs2;
    std::vector<trait_id> cancel = mdata.cancels;

    for (size_t i = 0; i < cancel.size(); i++) {
        if (!has_trait( cancel[i] )) {
            cancel.erase(cancel.begin() + i);
            i--;
        } else if (has_base_trait( cancel[i] )) {
            //If we have the trait, but it's a base trait, don't allow it to be removed normally
            canceltrait.push_back( cancel[i]);
            cancel.erase(cancel.begin() + i);
            i--;
        }
    }

    for (size_t i = 0; i < cancel.size(); i++) {
        if (!cancel.empty()) {
            trait_id removed = cancel[i];
            remove_mutation(removed);
            cancel.erase(cancel.begin() + i);
            i--;
            // This checks for cases where one trait knocks out several others
            // Probably a better way, but gets it Fixed Now--KA101
            return mutate_towards(mut);
        }
    }

    for (size_t i = 0; (!prereq1) && i < prereq.size(); i++) {
        if (has_trait(prereq[i])) {
            prereq1 = true;
        }
    }

    for (size_t i = 0; (!prereq2) && i < prereqs2.size(); i++) {
        if (has_trait(prereqs2[i])) {
            prereq2 = true;
        }
    }

    if (prereq1 && prereq2) {
        has_prereqs = true;
    }

    if (!has_prereqs && (!prereq.empty() || !prereqs2.empty())) {
        if (!prereq1 && !prereq.empty()) {
            return mutate_towards( random_entry( prereq ) );
        } else if (!prereq2 && !prereqs2.empty()) {
            return mutate_towards( random_entry( prereqs2 ) );
        }
    }

    // Check for threshold mutation, if needed
    bool threshold = mdata.threshold;
    bool profession = mdata.profession;
    bool has_threshreq = false;
    std::vector<trait_id> threshreq = mdata.threshreq;

    // It shouldn't pick a Threshold anyway--they're supposed to be non-Valid
    // and aren't categorized. This can happen if someone makes a threshold mutation into a prerequisite.
    if (threshold) {
        add_msg_if_player(_("You feel something straining deep inside you, yearning to be free..."));
        return false;
    }
    if (profession) {
        // Profession picks fail silently
        return false;
    }

    for (size_t i = 0; !has_threshreq && i < threshreq.size(); i++) {
        if (has_trait(threshreq[i])) {
            has_threshreq = true;
        }
    }

    // No crossing The Threshold by simply not having it
    if (!has_threshreq && !threshreq.empty()) {
        add_msg_if_player(_("You feel something straining deep inside you, yearning to be free..."));
        return false;
    }

    // Check if one of the prerequisites that we have TURNS INTO this one
    trait_id replacing = trait_id::NULL_ID();
    prereq = mdata.prereqs; // Reset it
    for( auto &elem : prereq ) {
        if( has_trait( elem ) ) {
            trait_id pre = elem;
            const auto &p = pre.obj();
            for (size_t j = 0; !replacing && j < p.replacements.size(); j++) {
                if (p.replacements[j] == mut) {
                    replacing = pre;
                }
            }
        }
    }

    // Loop through again for prereqs2
    trait_id replacing2 = trait_id::NULL_ID();
    prereq = mdata.prereqs2; // Reset it
    for( auto &elem : prereq ) {
        if( has_trait( elem ) ) {
            trait_id pre2 = elem;
            const auto &p = pre2.obj();
            for (size_t j = 0; !replacing2 && j < p.replacements.size(); j++) {
                if (p.replacements[j] == mut) {
                    replacing2 = pre2;
                }
            }
        }
    }

    set_mutation(mut);

    bool mutation_replaced = false;

    game_message_type rating;

    if( replacing ) {
        const auto &replace_mdata = replacing.obj();
        if(mdata.mixed_effect || replace_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(replace_mdata.points - mdata.points < 0) {
            rating = m_good;
        } else if(mdata.points - replace_mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        // TODO: Limit this to visible mutations
        // TODO: In case invisible mutation turns into visible or vice versa
        //  print only the visible mutation appearing/disappearing
        add_msg_player_or_npc(rating,
            _("Your %1$s mutation turns into %2$s!"),
            _("<npcname>'s %1$s mutation turns into %2$s!"),
            replace_mdata.name.c_str(), mdata.name.c_str() );
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                         pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                         replace_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(replacing);
        mutation_loss_effect(replacing);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    if( replacing2 ) {
        const auto &replace_mdata = replacing2.obj();
        if(mdata.mixed_effect || replace_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(replace_mdata.points - mdata.points < 0) {
            rating = m_good;
        } else if(mdata.points - replace_mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        add_msg_player_or_npc(rating,
            _("Your %1$s mutation turns into %2$s!"),
            _("<npcname>'s %1$s mutation turns into %2$s!"),
            replace_mdata.name.c_str(), mdata.name.c_str() );
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                         pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                         replace_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(replacing2);
        mutation_loss_effect(replacing2);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    for (size_t i = 0; i < canceltrait.size(); i++) {
        const auto &cancel_mdata = canceltrait[i].obj();
        if(mdata.mixed_effect || cancel_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(mdata.points < cancel_mdata.points) {
            rating = m_bad;
        } else if(mdata.points > cancel_mdata.points) {
            rating = m_good;
        } else if(mdata.points == cancel_mdata.points) {
            rating = m_neutral;
        } else {
            rating = m_mixed;
        }
        // If this new mutation cancels a base trait, remove it and add the mutation at the same time
        add_msg_player_or_npc( rating,
            _("Your innate %1$s trait turns into %2$s!"),
            _("<npcname>'s innate %1$s trait turns into %2$s!"),
            cancel_mdata.name.c_str(), mdata.name.c_str() );
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                        pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                        cancel_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(canceltrait[i]);
        mutation_loss_effect(canceltrait[i]);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    if (!mutation_replaced) {
        if(mdata.mixed_effect) {
            rating = m_mixed;
        } else if(mdata.points > 0) {
            rating = m_good;
        } else if(mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        // TODO: Limit to visible mutations
        add_msg_player_or_npc( rating,
            _("You gain a mutation called %s!"),
            _("<npcname> gains a mutation called %s!"),
            mdata.name.c_str() );
        add_memorial_log(pgettext("memorial_male", "Gained the mutation '%s'."),
                         pgettext("memorial_female", "Gained the mutation '%s'."),
                         mdata.name.c_str());
        mutation_effect(mut);
    }

    set_highest_cat_level();
    drench_mut_calc();
    return true;
}
Beispiel #9
0
void player::mutate_towards( const std::string &mut )
{
    if (has_child_flag(mut)) {
        remove_child_flag(mut);
        return;
    }
    const auto &mdata = mutation_branch::get( mut );

    bool has_prereqs = false;
    bool prereq1 = false;
    bool prereq2 = false;
    std::vector<std::string> canceltrait;
    std::vector<std::string> prereq = mdata.prereqs;
    std::vector<std::string> prereqs2 = mdata.prereqs2;
    std::vector<std::string> cancel = mdata.cancels;

    for (size_t i = 0; i < cancel.size(); i++) {
        if (!has_trait( cancel[i] )) {
            cancel.erase(cancel.begin() + i);
            i--;
        } else if (has_base_trait( cancel[i] )) {
            //If we have the trait, but it's a base trait, don't allow it to be removed normally
            canceltrait.push_back( cancel[i]);
            cancel.erase(cancel.begin() + i);
            i--;
        }
    }

    for (size_t i = 0; i < cancel.size(); i++) {
        if (!cancel.empty()) {
            std::string removed = cancel[i];
            remove_mutation(removed);
            cancel.erase(cancel.begin() + i);
            i--;
            // This checks for cases where one trait knocks out several others
            // Probably a better way, but gets it Fixed Now--KA101
            mutate_towards(mut);
            return;
        }
    }

    for (size_t i = 0; (!prereq1) && i < prereq.size(); i++) {
        if (has_trait(prereq[i])) {
            prereq1 = true;
        }
    }

    for (size_t i = 0; (!prereq2) && i < prereqs2.size(); i++) {
        if (has_trait(prereqs2[i])) {
            prereq2 = true;
        }
    }

    if (prereq1 && prereq2) {
        has_prereqs = true;
    }

    if (!has_prereqs && (!prereq.empty() || !prereqs2.empty())) {
        if (!prereq1 && !prereq.empty()) {
            mutate_towards( random_entry( prereq ) );
            return;
        } else if (!prereq2 && !prereqs2.empty()) {
            mutate_towards( random_entry( prereqs2 ) );
            return;
        }
    }

    // Check for threshhold mutation, if needed
    bool threshold = mdata.threshold;
    bool profession = mdata.profession;
    bool has_threshreq = false;
    std::vector<std::string> threshreq = mdata.threshreq;

    // It shouldn't pick a Threshold anyway--they're supposed to be non-Valid
    // and aren't categorized--but if it does, just reroll
    if (threshold) {
        add_msg(_("You feel something straining deep inside you, yearning to be free..."));
        mutate();
        return;
    }
    if (profession) {
        // Profession picks fail silently
        mutate();
        return;
    }

    for (size_t i = 0; !has_threshreq && i < threshreq.size(); i++) {
        if (has_trait(threshreq[i])) {
            has_threshreq = true;
        }
    }

    // No crossing The Threshold by simply not having it
    // Rerolling proved more trouble than it was worth, so deleted
    if (!has_threshreq && !threshreq.empty()) {
        add_msg(_("You feel something straining deep inside you, yearning to be free..."));
        return;
    }

    // Check if one of the prereqs that we have TURNS INTO this one
    std::string replacing = "";
    prereq = mdata.prereqs; // Reset it
    for( auto &elem : prereq ) {
        if( has_trait( elem ) ) {
            std::string pre = elem;
            const auto &p = mutation_branch::get( pre );
            for (size_t j = 0; replacing == "" && j < p.replacements.size(); j++) {
                if (p.replacements[j] == mut) {
                    replacing = pre;
                }
            }
        }
    }

    // Loop through again for prereqs2
    std::string replacing2 = "";
    prereq = mdata.prereqs2; // Reset it
    for( auto &elem : prereq ) {
        if( has_trait( elem ) ) {
            std::string pre2 = elem;
            const auto &p = mutation_branch::get( pre2 );
            for (size_t j = 0; replacing2 == "" && j < p.replacements.size(); j++) {
                if (p.replacements[j] == mut) {
                    replacing2 = pre2;
                }
            }
        }
    }

    set_mutation(mut);

    bool mutation_replaced = false;

    game_message_type rating;

    if (replacing != "") {
        const auto &replace_mdata = mutation_branch::get( replacing );
        if(mdata.mixed_effect || replace_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(replace_mdata.points - mdata.points < 0) {
            rating = m_good;
        } else if(mdata.points - replace_mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        add_msg(rating, _("Your %1$s mutation turns into %2$s!"),
                replace_mdata.name.c_str(), mdata.name.c_str());
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                         pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                         replace_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(replacing);
        mutation_loss_effect(replacing);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    if (replacing2 != "") {
        const auto &replace_mdata = mutation_branch::get( replacing2 );
        if(mdata.mixed_effect || replace_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(replace_mdata.points - mdata.points < 0) {
            rating = m_good;
        } else if(mdata.points - replace_mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        add_msg(rating, _("Your %1$s mutation turns into %2$s!"),
                replace_mdata.name.c_str(), mdata.name.c_str());
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                         pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                         replace_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(replacing2);
        mutation_loss_effect(replacing2);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    for (size_t i = 0; i < canceltrait.size(); i++) {
        const auto &cancel_mdata = mutation_branch::get( canceltrait[i] );
        if(mdata.mixed_effect || cancel_mdata.mixed_effect) {
            rating = m_mixed;
        } else if(mdata.points < cancel_mdata.points) {
            rating = m_bad;
        } else if(mdata.points > cancel_mdata.points) {
            rating = m_good;
        } else if(mdata.points == cancel_mdata.points) {
            rating = m_neutral;
        } else {
            rating = m_mixed;
        }
        // If this new mutation cancels a base trait, remove it and add the mutation at the same time
        add_msg(rating, _("Your innate %1$s trait turns into %2$s!"),
                cancel_mdata.name.c_str(), mdata.name.c_str());
        add_memorial_log(pgettext("memorial_male", "'%s' mutation turned into '%s'"),
                        pgettext("memorial_female", "'%s' mutation turned into '%s'"),
                        cancel_mdata.name.c_str(), mdata.name.c_str());
        unset_mutation(canceltrait[i]);
        mutation_loss_effect(canceltrait[i]);
        mutation_effect(mut);
        mutation_replaced = true;
    }
    if (!mutation_replaced) {
        if(mdata.mixed_effect) {
            rating = m_mixed;
        } else if(mdata.points > 0) {
            rating = m_good;
        } else if(mdata.points < 0) {
            rating = m_bad;
        } else {
            rating = m_neutral;
        }
        add_msg(rating, _("You gain a mutation called %s!"), mdata.name.c_str());
        add_memorial_log(pgettext("memorial_male", "Gained the mutation '%s'."),
                         pgettext("memorial_female", "Gained the mutation '%s'."),
                         mdata.name.c_str());
        mutation_effect(mut);
    }

    set_highest_cat_level();
    drench_mut_calc();
}
Beispiel #10
0
void player::mutate_towards(std::string mut)
{
    if (has_child_flag(mut)) {
        remove_child_flag(mut);
        return;
    }

    bool has_prereqs = false;
    bool prereq1 = false;
    bool prereq2 = false;
    std::string canceltrait = "";
    std::vector<std::string> prereq = mutation_data[mut].prereqs;
    std::vector<std::string> prereqs2 = mutation_data[mut].prereqs2;
    std::vector<std::string> cancel = mutation_data[mut].cancels;

    for (int i = 0; i < cancel.size(); i++) {
        if (!has_trait( cancel[i] )) {
            cancel.erase(cancel.begin() + i);
            i--;
        } else if (has_base_trait( cancel[i] )) {
            //If we have the trait, but it's a base trait, don't allow it to be removed normally
            canceltrait = cancel[i];
            cancel.erase(cancel.begin() + i);
            i--;
        }
    }

    if (!cancel.empty()) {
        std::string removed = cancel[ rng(0, cancel.size() - 1) ];
        remove_mutation(removed);
        return;
    }

    for (int i = 0; (!prereq1) && i < prereq.size(); i++) {
        if (has_trait(prereq[i])) {
            prereq1 = true;
        }
    }

    for (int i = 0; (!prereq2) && i < prereqs2.size(); i++) {
        if (has_trait(prereqs2[i])) {
            prereq2 = true;
        }
    }

    if (prereq1 && prereq2) {
        has_prereqs = true;
    }

    if (!has_prereqs && (!prereq.empty() || !prereqs2.empty())) {
        if (!prereq1 && !prereq.empty()) {
            std::string devel = prereq[ rng(0, prereq.size() - 1) ];
            mutate_towards(devel);
            return;
            }
        else if (!prereq2 && !prereqs2.empty()) {
            std::string devel = prereqs2[ rng(0, prereqs2.size() - 1) ];
            mutate_towards(devel);
            return;
            }
    }

    // Check for threshhold mutation, if needed
    bool threshold = mutation_data[mut].threshold;
    bool has_threshreq = false;
    std::vector<std::string> threshreq = mutation_data[mut].threshreq;
    std::vector<std::string> mutcat;
    mutcat = mutation_data[mut].category;

    // It shouldn't pick a Threshold anyway--they're supposed to be non-Valid
    // and aren't categorized--but if it does, just reroll
    if (threshold) {
        g->add_msg(_("You feel something straining deep inside you, yearning to be free..."));
        mutate();
        return;
    }

    for (int i = 0; !has_threshreq && i < threshreq.size(); i++) {
        if (has_trait(threshreq[i])) {
            has_threshreq = true;
        }
    }

    // No crossing The Threshold by simply not having it
    // Reroll mutation, uncategorized (prevents looping)
    if (!has_threshreq && !threshreq.empty()) {
        g->add_msg(_("You feel something straining deep inside you, yearning to be free..."));
        mutate();
        return;
    }

    // Check if one of the prereqs that we have TURNS INTO this one
    std::string replacing = "";
    prereq = mutation_data[mut].prereqs; // Reset it
    for (int i = 0; i < prereq.size(); i++) {
        if (has_trait(prereq[i])) {
            std::string pre = prereq[i];
            for (int j = 0; replacing == "" && j < mutation_data[pre].replacements.size(); j++) {
                if (mutation_data[pre].replacements[j] == mut) {
                    replacing = pre;
                }
            }
        }
    }

    // Loop through again for prereqs2
    std::string replacing2 = "";
    prereq = mutation_data[mut].prereqs2; // Reset it
    for (int i = 0; i < prereq.size(); i++) {
        if (has_trait(prereq[i])) {
            std::string pre2 = prereq[i];
            for (int j = 0; replacing2 == "" && j < mutation_data[pre2].replacements.size(); j++) {
                if (mutation_data[pre2].replacements[j] == mut) {
                    replacing2 = pre2;
                }
            }
        }
    }

    toggle_mutation(mut);

    bool mutation_replaced = false;

    if (replacing != "") {
        g->add_msg(_("Your %1$s mutation turns into %2$s!"), traits[replacing].name.c_str(), traits[mut].name.c_str());
        add_memorial_log(pgettext("memorial_male","'%s' mutation turned into '%s'"),
            pgettext("memorial_female", "'%s' mutation turned into '%s'"),
            traits[replacing].name.c_str(), traits[mut].name.c_str());
        toggle_mutation(replacing);
        mutation_loss_effect(*this, replacing);
        mutation_effect(*this, mut);
        mutation_replaced = true;
    }
    if (replacing2 != "") {
        g->add_msg(_("Your %1$s mutation turns into %2$s!"), traits[replacing2].name.c_str(), traits[mut].name.c_str());
        add_memorial_log(pgettext("memorial_male","'%s' mutation turned into '%s'"),
            pgettext("memorial_female", "'%s' mutation turned into '%s'"),
            traits[replacing2].name.c_str(), traits[mut].name.c_str());
        toggle_mutation(replacing2);
        mutation_loss_effect(*this, replacing2);
        mutation_effect(*this, mut);
        mutation_replaced = true;
    }
    if (canceltrait != "") {
        // If this new mutation cancels a base trait, remove it and add the mutation at the same time
        g->add_msg(_("Your innate %1$s trait turns into %2$s!"), traits[canceltrait].name.c_str(), traits[mut].name.c_str());
        add_memorial_log(pgettext("memorial_male","'%s' mutation turned into '%s'"),
            pgettext("memorial_female", "'%s' mutation turned into '%s'"),
            traits[canceltrait].name.c_str(), traits[mut].name.c_str());
        toggle_mutation(canceltrait);
        mutation_loss_effect(*this, canceltrait);
        mutation_effect(*this, mut);
        mutation_replaced = true;
    }
    if (!mutation_replaced) {
        g->add_msg(_("You gain a mutation called %s!"), traits[mut].name.c_str());
        add_memorial_log(pgettext("memorial_male","Gained the mutation '%s'."),
            pgettext("memorial_female", "Gained the mutation '%s'."),
            traits[mut].name.c_str());
        mutation_effect(*this, mut);
    }

    set_highest_cat_level();
    drench_mut_calc();
}
Beispiel #11
0
bool player::uninstall_bionic(bionic_id b_id)
{
    // malfunctioning bionics don't have associated items and get a difficulty of 12
    int difficulty = 12;
    if( item_controller->has_template(b_id) > 0) {
        const it_bionic *type = dynamic_cast<it_bionic *> (item_controller->find_template(b_id));
        difficulty = type->difficulty;
    }

    if (!has_bionic(b_id)) {
        popup(_("You don't have this bionic installed."));
        return false;
    }
    if (!(inv.has_items_with_quality("CUT", 1, 1) && has_amount("1st_aid", 1))) {
        popup(_("Removing bionics requires a cutting tool and a first aid kit."));
        return false;
    }

    if ( b_id == "bio_blaster" ) {
        popup(_("Removing your Fusion Blaster Arm would leave you with a useless stump."));
        return false;
    }

    // removal of bionics adds +2 difficulty over installation
    int chance_of_success = bionic_manip_cos(int_cur,
                            skillLevel("electronics"),
                            skillLevel("firstaid"),
                            skillLevel("mechanics"),
                            difficulty + 2);

    if (!query_yn(_("WARNING: %i percent chance of SEVERE bodily damage! Remove anyway?"),
                  100 - chance_of_success)) {
        return false;
    }

    use_charges("1st_aid", 1);

    practice( "electronics", int((100 - chance_of_success) * 1.5) );
    practice( "firstaid", int((100 - chance_of_success) * 1.0) );
    practice( "mechanics", int((100 - chance_of_success) * 0.5) );

    int success = chance_of_success - rng(1, 100);

    if (success > 0) {
        add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."),
                         pgettext("memorial_female", "Removed bionic: %s."),
                         bionics[b_id]->name.c_str());
        // until bionics can be flagged as non-removable
        add_msg(m_neutral, _("You jiggle your parts back into their familiar places."));
        add_msg(m_good, _("Successfully removed %s."), bionics[b_id]->name.c_str());
        remove_bionic(b_id);
        g->m.spawn_item(posx, posy, "burnt_out_bionic", 1);
    } else {
        add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."),
                         pgettext("memorial_female", "Removed bionic: %s."),
                         bionics[b_id]->name.c_str());
        bionics_uninstall_failure(this);
    }
    g->refresh_all();
    return true;
}
Beispiel #12
0
bool player::eat( item &food, bool force )
{
    if( !food.is_food() ) {
        return false;
    }
    // Check if it's rotten before eating!
    food.calc_rot( global_square_location() );
    const auto ret = force ? can_eat( food ) : will_eat( food, is_player() );
    if( !ret.success() ) {
        return false;
    }

    if( food.type->has_use() ) {
        if( food.type->invoke( *this, food, pos() ) <= 0 ) {
            return false;
        }
    }

    // Note: the block below assumes we decided to eat it
    // No coming back from here

    const bool hibernate = has_active_mutation( trait_id( "HIBERNATE" ) );
    const int nutr = nutrition_for( food );
    const int quench = food.type->comestible->quench;
    const bool spoiled = food.rotten();

    // The item is solid food
    const bool chew = food.type->comestible->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" );
    // This item is a drink and not a solid food (and not a thick soup)
    const bool drinkable = !chew && food.type->comestible->comesttype == "DRINK";
    // If neither of the above is true then it's a drug and shouldn't get mealtime penalty/bonus

    if( hibernate &&
        ( get_hunger() > -60 && get_thirst() > -60 ) &&
        ( get_hunger() - nutr < -60 || get_thirst() - quench < -60 ) ) {
        add_memorial_log( pgettext( "memorial_male", "Began preparing for hibernation." ),
                          pgettext( "memorial_female", "Began preparing for hibernation." ) );
        add_msg_if_player(
            _( "You've begun stockpiling calories and liquid for hibernation.  You get the feeling that you should prepare for bed, just in case, but...you're hungry again, and you could eat a whole week's worth of food RIGHT NOW." ) );
    }

    const bool will_vomit = get_hunger() < 0 && nutr >= 5 && !has_trait( trait_id( "GOURMAND" ) ) &&
                            !hibernate &&
                            !has_trait( trait_id( "SLIMESPAWNER" ) ) && !has_trait( trait_id( "EATHEALTH" ) ) &&
                            rng( -200, 0 ) > get_hunger() - nutr;
    const bool saprophage = has_trait( trait_id( "SAPROPHAGE" ) );
    if( spoiled && !saprophage ) {
        add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good..." ), food.tname().c_str() );
        if( !has_trait( trait_id( "SAPROVORE" ) ) && !has_trait( trait_id( "EATDEAD" ) ) &&
            ( !has_bionic( bio_digestion ) || one_in( 3 ) ) ) {
            add_effect( effect_foodpoison, rng( 60, ( nutr + 1 ) * 60 ) );
        }
        consume_effects( food );
    } else if( spoiled && saprophage ) {
        add_msg_if_player( m_good, _( "Mmm, this %s tastes delicious..." ), food.tname().c_str() );
        consume_effects( food );
    } else {
        consume_effects( food );
    }

    const bool amorphous = has_trait( trait_id( "AMORPHOUS" ) );
    int mealtime = 250;
    if( drinkable || chew ) {
        // Those bonuses/penalties only apply to food
        // Not to smoking weed or applying bandages!
        if( has_trait( trait_id( "MOUTH_TENTACLES" ) )  || has_trait( trait_id( "MANDIBLES" ) ) ) {
            mealtime /= 2;
        } else if( has_trait( trait_id( "GOURMAND" ) ) ) {
            // Don't stack those two - that would be 25 moves per item
            mealtime -= 100;
        }

        if( has_trait( trait_id( "BEAK_HUM" ) ) && !drinkable ) {
            mealtime += 200; // Much better than PROBOSCIS but still optimized for fluids
        } else if( has_trait( trait_id( "SABER_TEETH" ) ) ) {
            mealtime += 250; // They get In The Way
        }

        if( amorphous ) {
            mealtime *= 1.1;
            // Minor speed penalty for having to flow around it
            // rather than just grab & munch
        }
    }

    moves -= mealtime;

    // If it's poisonous... poison us.
    // TODO: Move this to a flag
    if( food.poison > 0 && !has_trait( trait_id( "EATPOISON" ) ) &&
        !has_trait( trait_id( "EATDEAD" ) ) ) {
        if( food.poison >= rng( 2, 4 ) ) {
            add_effect( effect_poison, food.poison * 100 );
        }

        add_effect( effect_foodpoison, food.poison * 300 );
    }

    if( amorphous ) {
        add_msg_player_or_npc( _( "You assimilate your %s." ), _( "<npcname> assimilates a %s." ),
                               food.tname().c_str() );
    } else if( drinkable ) {
        add_msg_player_or_npc( _( "You drink your %s." ), _( "<npcname> drinks a %s." ),
                               food.tname().c_str() );
    } else if( chew ) {
        add_msg_player_or_npc( _( "You eat your %s." ), _( "<npcname> eats a %s." ),
                               food.tname().c_str() );
    }

    if( item::find_type( food.type->comestible->tool )->tool ) {
        // Tools like lighters get used
        use_charges( food.type->comestible->tool, 1 );
    }

    if( has_bionic( bio_ethanol ) && food.type->can_use( "ALCOHOL" ) ) {
        charge_power( rng( 50, 200 ) );
    }
    if( has_bionic( bio_ethanol ) && food.type->can_use( "ALCOHOL_WEAK" ) ) {
        charge_power( rng( 25, 100 ) );
    }
    if( has_bionic( bio_ethanol ) && food.type->can_use( "ALCOHOL_STRONG" ) ) {
        charge_power( rng( 75, 300 ) );
    }

    if( food.has_flag( "CANNIBALISM" ) ) {
        // Sapiovores don't recognize humans as the same species.
        // But let them possibly feel cool about eating sapient stuff - treat like psycho
        const bool cannibal = has_trait( trait_id( "CANNIBAL" ) );
        const bool psycho = has_trait( trait_id( "PSYCHOPATH" ) ) || has_trait( trait_id( "SAPIOVORE" ) );
        const bool spiritual = has_trait( trait_id( "SPIRITUAL" ) );
        if( cannibal && psycho && spiritual ) {
            add_msg_if_player( m_good,
                               _( "You feast upon the human flesh, and in doing so, devour their spirit." ) );
            // You're not really consuming anything special; you just think you are.
            add_morale( MORALE_CANNIBAL, 25, 300 );
        } else if( cannibal && psycho ) {
            add_msg_if_player( m_good, _( "You feast upon the human flesh." ) );
            add_morale( MORALE_CANNIBAL, 15, 200 );
        } else if( cannibal && spiritual ) {
            add_msg_if_player( m_good, _( "You consume the sacred human flesh." ) );
            // Boosted because you understand the philosophical implications of your actions, and YOU LIKE THEM.
            add_morale( MORALE_CANNIBAL, 15, 200 );
        } else if( cannibal ) {
            add_msg_if_player( m_good, _( "You indulge your shameful hunger." ) );
            add_morale( MORALE_CANNIBAL, 10, 50 );
        } else if( psycho && spiritual ) {
            add_msg_if_player( _( "You greedily devour the taboo meat." ) );
            // Small bonus for violating a taboo.
            add_morale( MORALE_CANNIBAL, 5, 50 );
        } else if( psycho ) {
            add_msg_if_player( _( "Meh. You've eaten worse." ) );
        } else if( spiritual ) {
            add_msg_if_player( m_bad,
                               _( "This is probably going to count against you if there's still an afterlife." ) );
            add_morale( MORALE_CANNIBAL, -60, -400, 600, 300 );
        } else {
            add_msg_if_player( m_bad, _( "You feel horrible for eating a person." ) );
            add_morale( MORALE_CANNIBAL, -60, -400, 600, 300 );
        }
    }

    // Allergy check
    const auto allergy = allergy_type( food );
    if( allergy != MORALE_NULL ) {
        add_msg_if_player( m_bad, _( "Yuck! How can anybody eat this stuff?" ) );
        add_morale( allergy, -75, -400, 300, 240 );
    }
    // Carnivores CAN eat junk food, but they won't like it much.
    // Pizza-scraping happens in consume_effects.
    if( has_trait( trait_id( "CARNIVORE" ) ) && food.has_flag( "ALLERGEN_JUNK" ) &&
        !food.has_flag( "CARNIVORE_OK" ) ) {
        add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) );
        add_morale( MORALE_NO_DIGEST, -25, -125, 300, 240 );
    }
    if( !spoiled && chew && has_trait( trait_id( "SAPROPHAGE" ) ) ) {
        // It's OK to *drink* things that haven't rotted.  Alternative is to ban water.  D:
        add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) );
        add_morale( MORALE_NO_DIGEST, -75, -400, 300, 240 );
    }
    if( food.has_flag( "URSINE_HONEY" ) && ( !crossed_threshold() ||
            has_trait( trait_id( "THRESH_URSINE" ) ) ) &&
        mutation_category_level["MUTCAT_URSINE"] > 40 ) {
        //Need at least 5 bear mutations for effect to show, to filter out mutations in common with other mutcats
        int honey_fun = has_trait( trait_id( "THRESH_URSINE" ) ) ?
                        std::min( mutation_category_level["MUTCAT_URSINE"] / 8, 20 ) :
                        mutation_category_level["MUTCAT_URSINE"] / 12;
        if( honey_fun < 10 ) {
            add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) );
        } else {
            add_msg_if_player( m_good, _( "You feast upon the sweet honey." ) );
        }
        add_morale( MORALE_HONEY, honey_fun, 100 );
    }

    if( will_vomit ) {
        vomit();
    }

    // chance to become parasitised
    if( !( has_bionic( bio_digestion ) || has_trait( trait_id( "PARAIMMUNE" ) ) ) ) {
        if( food.type->comestible->parasites > 0 && one_in( food.type->comestible->parasites ) ) {
            switch( rng( 0, 3 ) ) {
                case 0:
                    if( !has_trait( trait_id( "EATHEALTH" ) ) ) {
                        add_effect( effect_tapeworm, 1, num_bp, true );
                    }
                    break;
                case 1:
                    if( !has_trait( trait_id( "ACIDBLOOD" ) ) ) {
                        add_effect( effect_bloodworms, 1, num_bp, true );
                    }
                    break;
                case 2:
                    add_effect( effect_brainworms, 1, num_bp, true );
                    break;
                case 3:
                    add_effect( effect_paincysts, 1, num_bp, true );
            }
        }
    }

    for( const auto &v : this->vitamins_from( food ) ) {
        auto qty = has_effect( effect_tapeworm ) ? v.second / 2 : v.second;

        // can never develop hypervitaminosis from consuming food
        vitamin_mod( v.first, qty );
    }

    food.mod_charges( -1 );
    return true;
}