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; }
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; }
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; } } }
/* * 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 ); } } }
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; }
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(); }
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(); }
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; }
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; }