void player::mutate() { bool force_bad = one_in(3); bool force_good = false; if (has_trait("ROBUST") && force_bad) { // Robust Genetics gives you a 33% chance for a good mutation, // instead of the 33% chance of a bad one. force_bad = false; force_good = true; } // Determine the highest mutation categorie std::string cat = get_highest_category(); // See if we should ugrade/extend an existing mutation... std::vector<std::string> upgrades; // ... or remove one that is not in our highest category std::vector<std::string> downgrades; // For each mutation... for (std::map<std::string, trait>::iterator iter = traits.begin(); iter != traits.end(); ++iter) { std::string base_mutation = iter->first; // ...that we have... if (has_trait(base_mutation)) { // ...consider the mutations that replace it. for (int i = 0; i < mutation_data[base_mutation].replacements.size(); i++) { std::string mutation = mutation_data[base_mutation].replacements[i]; if (mutation_ok(mutation, force_good, force_bad)) { upgrades.push_back(mutation); } } // ...consider the mutations that add to it. for (int i = 0; i < mutation_data[base_mutation].additions.size(); i++) { std::string mutation = mutation_data[base_mutation].additions[i]; if (mutation_ok(mutation, force_good, force_bad)) { upgrades.push_back(mutation); } } // ...consider whether its in our highest category if( has_trait(base_mutation) && !has_base_trait(base_mutation) ) { // Starting traits don't count toward categories std::vector<std::string> group = mutations_category[cat]; bool in_cat = false; for (int j = 0; j < group.size(); j++) { if (group[j] == base_mutation) { in_cat = true; break; } } // mark for removal if(!in_cat) { downgrades.push_back(base_mutation); } } } } // Preliminary round to either upgrade or remove existing mutations if(one_in(2)) { if (upgrades.size() > 0) { // (upgrade count) chances to pick an upgrade, 4 chances to pick something else. int roll = rng(0, upgrades.size() + 4); if (roll < upgrades.size()) { // We got a valid upgrade index, so use it and return. mutate_towards(upgrades[roll]); return; } } } else { // Remove existing mutations that don't fit into our category if (downgrades.size() > 0 && cat != "") { int roll = rng(0, downgrades.size() + 4); if (roll < downgrades.size()) { remove_mutation(downgrades[roll]); return; } } } std::vector<std::string> valid; // Valid mutations bool first_pass = true; do { // If we tried once with a non-NULL category, and couldn't find anything valid // there, try again with MUTCAT_NULL if (!first_pass) { cat = ""; } if (cat == "") { // Pull the full list for (std::map<std::string, trait>::iterator iter = traits.begin(); iter != traits.end(); ++iter) { if (mutation_data[iter->first].valid) { valid.push_back( iter->first ); } } } else { // Pull the category's list valid = mutations_category[cat]; } // Remove anything we already have, that we have a child of, or that // goes against our intention of a good/bad mutation for (int i = 0; i < valid.size(); i++) { if (!mutation_ok(valid[i], force_good, force_bad)) { valid.erase(valid.begin() + i); i--; } } if (valid.empty()) { // So we won't repeat endlessly first_pass = false; } } while (valid.empty() && cat != ""); if (valid.empty()) { // Couldn't find anything at all! return; } std::string selection = valid[ rng(0, valid.size() - 1) ]; // Pick one! mutate_towards(selection); }
void player::remove_mutation(std::string mut) { // Check for dependant mutations first std::vector<std::string> dependant; for (std::map<std::string, trait>::iterator iter = traits.begin(); iter != traits.end(); ++iter) { for (int i = 0; i < mutation_data[iter->first].prereqs.size(); i++) { if (mutation_data[iter->first].prereqs[i] == iter->first) { dependant.push_back(iter->first); break; } } } if (dependant.size() > 0) { remove_mutation(dependant[rng(0, dependant.size()-1)]); return; } // Check if there's a prereq we should shrink back into std::string replacing = ""; std::vector<std::string> originals = mutation_data[mut].prereqs; for (int i = 0; replacing == "" && i < originals.size(); i++) { std::string pre = originals[i]; for (int j = 0; replacing == "" && j < mutation_data[pre].replacements.size(); j++) { if (mutation_data[pre].replacements[j] == mut) { replacing = pre; } } } std::string replacing2 = ""; std::vector<std::string> originals2 = mutation_data[mut].prereqs2; for (int i = 0; replacing2 == "" && i < originals2.size(); i++) { std::string pre2 = originals2[i]; for (int j = 0; replacing2 == "" && j < mutation_data[pre2].replacements.size(); j++) { if (mutation_data[pre2].replacements[j] == mut) { replacing2 = pre2; } } } // See if this mutation is cancelled by a base trait //Only if there's no prereq to shrink to, thus we're at the bottom of the trait line if (replacing == "") { //Check each mutation until we reach the end or find a trait to revert to for (std::map<std::string, trait>::iterator iter = traits.begin(); replacing == "" && iter != traits.end(); ++iter) { //See if it's in our list of base traits but not active if (has_base_trait(iter->first) && !has_trait(iter->first)) { //See if that base trait cancels the mutation we are using std::vector<std::string> traitcheck = mutation_data[iter->first].cancels; if (!traitcheck.empty()) { for (int j = 0; replacing == "" && j < traitcheck.size(); j++) { if (traitcheck[j] == mut) { replacing = (iter->first); } } } } } } // Duplicated for prereq2 if (replacing2 == "") { //Check each mutation until we reach the end or find a trait to revert to for (std::map<std::string, trait>::iterator iter = traits.begin(); replacing2 == "" && iter != traits.end(); ++iter) { //See if it's in our list of base traits but not active if (has_base_trait(iter->first) && !has_trait(iter->first)) { //See if that base trait cancels the mutation we are using std::vector<std::string> traitcheck = mutation_data[iter->first].cancels; if (!traitcheck.empty()) { for (int j = 0; replacing2 == "" && j < traitcheck.size(); j++) { if (traitcheck[j] == mut) { replacing2 = (iter->first); } } } } } } // This should revert back to a removed base trait rather than simply removing the mutation toggle_mutation(mut); bool mutation_replaced = false; if (replacing != "") { g->add_msg(_("Your %1$s mutation turns into %2$s."), traits[mut].name.c_str(), traits[replacing].name.c_str()); toggle_mutation(replacing); mutation_loss_effect(*this, mut); mutation_effect(*this, replacing); mutation_replaced = true; } if (replacing2 != "") { g->add_msg(_("Your %1$s mutation turns into %2$s."), traits[mut].name.c_str(), traits[replacing2].name.c_str()); toggle_mutation(replacing2); mutation_loss_effect(*this, mut); mutation_effect(*this, replacing2); mutation_replaced = true; } if(!mutation_replaced) { g->add_msg(_("You lose your %s mutation."), traits[mut].name.c_str()); mutation_loss_effect(*this, mut); } set_highest_cat_level(); drench_mut_calc(); }
bool player::create(game *g, character_type type, std::string tempname) { weapon = item(g->itypes["null"], 0); g->u.prof = profession::generic(); WINDOW* w = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, (TERMY > FULL_SCREEN_HEIGHT) ? (TERMY-FULL_SCREEN_HEIGHT)/2 : 0, (TERMX > FULL_SCREEN_WIDTH) ? (TERMX-FULL_SCREEN_WIDTH)/2 : 0); int tab = 0, points = 38, max_trait_points = 12; if (type != PLTYPE_CUSTOM) { switch (type) { case PLTYPE_RANDOM: { str_max = rng(6, 12); dex_max = rng(6, 12); int_max = rng(6, 12); per_max = rng(6, 12); points = points - str_max - dex_max - int_max - per_max; if (str_max > HIGH_STAT) points -= (str_max - HIGH_STAT); if (dex_max > HIGH_STAT) points -= (dex_max - HIGH_STAT); if (int_max > HIGH_STAT) points -= (int_max - HIGH_STAT); if (per_max > HIGH_STAT) points -= (per_max - HIGH_STAT); int num_gtraits = 0, num_btraits = 0, rn, tries; while (points < 0 || rng(-3, 20) > points) { if (num_btraits < max_trait_points && one_in(3)) { tries = 0; do { rn = random_bad_trait(); tries++; } while ((has_trait(rn) || num_btraits - traits[rn].points > max_trait_points) && tries < 5); if (tries < 5) { toggle_trait(rn); points -= traits[rn].points; num_btraits -= traits[rn].points; } } else { switch (rng(1, 4)) { case 1: if (str_max > 5) { str_max--; points++; } break; case 2: if (dex_max > 5) { dex_max--; points++; } break; case 3: if (int_max > 5) { int_max--; points++; } break; case 4: if (per_max > 5) { per_max--; points++; } break; } } } while (points > 0) { switch (rng((num_gtraits < max_trait_points ? 1 : 5), 9)) { case 1: case 2: case 3: case 4: rn = random_good_trait(); if (!has_trait(rn) && points >= traits[rn].points && num_gtraits + traits[rn].points <= max_trait_points) { toggle_trait(rn); points -= traits[rn].points; num_gtraits += traits[rn].points; } break; case 5: switch (rng(1, 4)) { case 1: if (str_max < HIGH_STAT) { str_max++; points--; } break; case 2: if (dex_max < HIGH_STAT) { dex_max++; points--; } break; case 3: if (int_max < HIGH_STAT) { int_max++; points--; } break; case 4: if (per_max < HIGH_STAT) { per_max++; points--; } break; } break; case 6: case 7: case 8: case 9: rn = random_skill(); Skill *aSkill = Skill::skill(rn); int level = skillLevel(aSkill); if (level < points) { points -= level + 1; skillLevel(aSkill).level(level + 2); } break; } } } break; case PLTYPE_TEMPLATE: { std::ifstream fin; std::stringstream filename; filename << "data/" << tempname << ".template"; fin.open(filename.str().c_str()); if (!fin.is_open()) { debugmsg("Couldn't open %s!", filename.str().c_str()); return false; } std::string(data); getline(fin, data); load_info(g, data); points = 0; } break; } tab = NEWCHAR_TAB_MAX; } else points = OPTIONS[OPT_INITIAL_POINTS]; max_trait_points = OPTIONS[OPT_MAX_TRAIT_POINTS]; do { werase(w); wrefresh(w); switch (tab) { case 0: tab += set_stats (w, g, this, points); break; case 1: tab += set_traits (w, g, this, points, max_trait_points); break; case 2: tab += set_profession (w, g, this, points); break; case 3: tab += set_skills (w, g, this, points); break; case 4: tab += set_description(w, g, this, points); break; } } while (tab >= 0 && tab <= NEWCHAR_TAB_MAX); delwin(w); if (tab < 0) return false; // Character is finalized. Now just set up HP, &c for (int i = 0; i < num_hp_parts; i++) { hp_max[i] = calc_HP(str_max, has_trait(PF_TOUGH)); hp_cur[i] = hp_max[i]; } if (has_trait(PF_HARDCORE)) { for (int i = 0; i < num_hp_parts; i++) { hp_max[i] = int(hp_max[i] * .25); hp_cur[i] = hp_max[i]; } } if (has_trait(PF_GLASSJAW)) { hp_max[hp_head] = int(hp_max[hp_head] * .80); hp_cur[hp_head] = hp_max[hp_head]; } if (has_trait(PF_SMELLY)) scent = 800; if (has_trait(PF_ANDROID)) { bionic_id first_bio; do { first_bio = g->random_good_bionic(); } while (bionics[first_bio]->power_cost > 10); add_bionic(first_bio); add_bionic(bionic_id(power_source_bionics[rng(0,power_source_bionics.size()-1)])); // Power Source max_power_level = 10; power_level = 10; } if (has_trait(PF_MARTIAL_ARTS)) { itype_id ma_type; do { int choice = menu(false, _("Pick your style:"), _("Karate"), _("Judo"), _("Aikido"), _("Tai Chi"), _("Taekwondo"), NULL); if (choice == 1) ma_type = "style_karate"; if (choice == 2) ma_type = "style_judo"; if (choice == 3) ma_type = "style_aikido"; if (choice == 4) ma_type = "style_tai_chi"; if (choice == 5) ma_type = "style_taekwondo"; item tmpitem = item(g->itypes[ma_type], 0); full_screen_popup(tmpitem.info(true).c_str()); } while (!query_yn(_("Use this style?"))); styles.push_back(ma_type); style_selected=ma_type; } if (has_trait(PF_MARTIAL_ARTS2)) { itype_id ma_type; do { int choice = menu(false, _("Pick your style:"), _("Capoeira"), _("Krav Maga"), _("Muay Thai"), _("Ninjutsu"), _("Zui Quan"), NULL); if (choice == 1) ma_type = "style_capoeira"; if (choice == 2) ma_type = "style_krav_maga"; if (choice == 3) ma_type = "style_muay_thai"; if (choice == 4) ma_type = "style_ninjutsu"; if (choice == 5) ma_type = "style_zui_quan"; item tmpitem = item(g->itypes[ma_type], 0); full_screen_popup(tmpitem.info(true).c_str()); } while (!query_yn(_("Use this style?"))); styles.push_back(ma_type); style_selected=ma_type; } if (has_trait(PF_MARTIAL_ARTS3)) { itype_id ma_type; do { int choice = menu(false, _("Pick your style:"), _("Tiger"), _("Crane"), _("Leopard"), _("Snake"), _("Dragon"), NULL); if (choice == 1) ma_type = "style_tiger"; if (choice == 2) ma_type = "style_crane"; if (choice == 3) ma_type = "style_leopard"; if (choice == 4) ma_type = "style_snake"; if (choice == 5) ma_type = "style_dragon"; item tmpitem = item(g->itypes[ma_type], 0); full_screen_popup(tmpitem.info(true).c_str()); } while (!query_yn(_("Use this style?"))); styles.push_back(ma_type); style_selected=ma_type; } if (has_trait(PF_MARTIAL_ARTS4)) { itype_id ma_type; do { int choice = menu(false, _("Pick your style:"), _("Centipede"), _("Viper"), _("Scorpion"), _("Lizard"), _("Toad"), NULL); if (choice == 1) ma_type = "style_centipede"; if (choice == 2) ma_type = "style_venom_snake"; if (choice == 3) ma_type = "style_scorpion"; if (choice == 4) ma_type = "style_lizard"; if (choice == 5) ma_type = "style_toad"; item tmpitem = item(g->itypes[ma_type], 0); full_screen_popup(tmpitem.info(true).c_str()); } while (!query_yn(_("Use this style?"))); styles.push_back(ma_type); style_selected=ma_type; } ret_null = item(g->itypes["null"], 0); weapon = get_combat_style(); item tmp; //gets used several times std::vector<std::string> prof_items = g->u.prof->items(); for (std::vector<std::string>::const_iterator iter = prof_items.begin(); iter != prof_items.end(); ++iter) { tmp = item(item_controller->find_template(*iter), 0); inv.push_back(tmp); } std::vector<addiction> prof_addictions = g->u.prof->addictions(); for (std::vector<addiction>::const_iterator iter = prof_addictions.begin(); iter != prof_addictions.end(); ++iter) { g->u.addictions.push_back(*iter); } // Grab the skills from the profession, if there are any profession::StartingSkillList prof_skills = g->u.prof->skills(); for (profession::StartingSkillList::const_iterator iter = prof_skills.begin(); iter != prof_skills.end(); ++iter) { assert(Skill::skill(iter->first)); if (Skill::skill(iter->first)) { g->u.boost_skill_level(iter->first, iter->second); } } // Those who are both near-sighted and far-sighted start with bifocal glasses. if (has_trait(PF_HYPEROPIC) && has_trait(PF_MYOPIC)) { tmp = item(g->itypes["glasses_bifocal"], 0); inv.push_back(tmp); } // The near-sighted start with eyeglasses. else if (has_trait(PF_MYOPIC)) { tmp = item(g->itypes["glasses_eye"], 0); inv.push_back(tmp); } // The far-sighted start with reading glasses. else if (has_trait(PF_HYPEROPIC)) { tmp = item(g->itypes["glasses_reading"], 0); inv.push_back(tmp); } // Likewise, the asthmatic start with their medication. if (has_trait(PF_ASTHMA)) { tmp = item(g->itypes["inhaler"], 0); inv.push_back(tmp); } // Basic starter gear, added independently of profession. tmp = item(g->itypes["pockknife"], 0); inv.push_back(tmp); tmp = item(g->itypes["matches"], 0); inv.push_back(tmp); // make sure we have no mutations for (int i = 0; i < PF_MAX2; i++) if (!has_base_trait(i)) my_mutations[i] = false; // Equip any armor from our inventory. If we are unable to wear some of it due to encumberance, it will silently fail. std::vector<item*> tmp_inv; inv.dump(tmp_inv); for(std::vector<item*>::iterator i = tmp_inv.begin(); i != tmp_inv.end(); ++i) { if( (*i)->is_armor()) { if( (*i)->has_flag("VARSIZE")) { (*i)->item_tags.insert("FIT"); } // It might be more elegant to use player::wear_item, but then we have to implement our own inventory removal. wear(g, (*i)->invlet, false); } } // Ensure that persistent morale effects (e.g. Optimist) are present at the start. apply_persistent_morale(); return true; }
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) // 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; } } } } toggle_mutation(mut); if (replacing != "") { g->add_msg(_("Your %1$s mutation turns into %2$s!"), traits[replacing].name.c_str(), traits[mut].name.c_str()); g->u.add_memorial_log(_("'%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); } else 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()); g->u.add_memorial_log(_("'%s' trait 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); } else { g->add_msg(_("You gain a mutation called %s!"), traits[mut].name.c_str()); g->u.add_memorial_log(_("Gained the mutation '%s'."), traits[mut].name.c_str()); mutation_effect(*this, mut); } set_highest_cat_level(); drench_mut_calc(); }
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::remove_mutation( const trait_id &mut ) { const auto &mdata = mut.obj(); // Check if there's a prerequisite we should shrink back into trait_id replacing = trait_id::NULL_ID(); std::vector<trait_id> originals = mdata.prereqs; for (size_t i = 0; !replacing && i < originals.size(); i++) { trait_id pre = originals[i]; const auto &p = pre.obj(); for (size_t j = 0; !replacing && j < p.replacements.size(); j++) { if (p.replacements[j] == mut) { replacing = pre; } } } trait_id replacing2 = trait_id::NULL_ID(); std::vector<trait_id> originals2 = mdata.prereqs2; for (size_t i = 0; !replacing2 && i < originals2.size(); i++) { trait_id pre2 = originals2[i]; const auto &p = pre2.obj(); for (size_t j = 0; !replacing2 && j < p.replacements.size(); j++) { if (p.replacements[j] == mut) { replacing2 = pre2; } } } // See if this mutation is canceled by a base trait //Only if there's no prerequisite to shrink to, thus we're at the bottom of the trait line if( !replacing ) { //Check each mutation until we reach the end or find a trait to revert to for( auto &iter : mutation_branch::get_all() ) { //See if it's in our list of base traits but not active if (has_base_trait(iter.first) && !has_trait(iter.first)) { //See if that base trait cancels the mutation we are using std::vector<trait_id> traitcheck = iter.second.cancels; if (!traitcheck.empty()) { for (size_t j = 0; !replacing && j < traitcheck.size(); j++) { if (traitcheck[j] == mut) { replacing = (iter.first); } } } } if( replacing ) { break; } } } // Duplicated for prereq2 if( !replacing2 ) { //Check each mutation until we reach the end or find a trait to revert to for( auto &iter : mutation_branch::get_all() ) { //See if it's in our list of base traits but not active if (has_base_trait(iter.first) && !has_trait(iter.first)) { //See if that base trait cancels the mutation we are using std::vector<trait_id> traitcheck = iter.second.cancels; if (!traitcheck.empty()) { for (size_t j = 0; !replacing2 && j < traitcheck.size(); j++) { if (traitcheck[j] == mut && (iter.first) != replacing) { replacing2 = (iter.first); } } } } if( replacing2 ) { break; } } } // make sure we don't toggle a mutation or trait twice, or it will cancel itself out. if(replacing == replacing2) { replacing2 = trait_id::NULL_ID(); } // This should revert back to a removed base trait rather than simply removing the mutation unset_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; } add_msg_player_or_npc( rating, _("Your %1$s mutation turns into %2$s."), _("<npcname>'s %1$s mutation turns into %2$s."), mdata.name.c_str(), replace_mdata.name.c_str() ); set_mutation(replacing); mutation_loss_effect(mut); mutation_effect(replacing); 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."), mdata.name.c_str(), replace_mdata.name.c_str() ); set_mutation(replacing2); mutation_loss_effect(mut); mutation_effect(replacing2); mutation_replaced = true; } if(!mutation_replaced) { if(mdata.mixed_effect) { rating = m_mixed; } else if(mdata.points > 0) { rating = m_bad; } else if(mdata.points < 0) { rating = m_good; } else { rating = m_neutral; } add_msg_player_or_npc( rating, _("You lose your %s mutation."), _("<npcname> loses their %s mutation."), mdata.name.c_str() ); mutation_loss_effect(mut); } set_highest_cat_level(); drench_mut_calc(); }
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() { bool force_bad = one_in(3); bool force_good = false; if (has_trait( trait_ROBUST ) && force_bad) { // Robust Genetics gives you a 33% chance for a good mutation, // instead of the 33% chance of a bad one. force_bad = false; force_good = true; } // Determine the highest mutation category std::string cat = get_highest_category(); // See if we should upgrade/extend an existing mutation... std::vector<trait_id> upgrades; // ... or remove one that is not in our highest category std::vector<trait_id> downgrades; // For each mutation... for( auto &traits_iter : mutation_branch::get_all() ) { const auto &base_mutation = traits_iter.first; const auto &base_mdata = traits_iter.second; bool thresh_save = base_mdata.threshold; bool prof_save = base_mdata.profession; bool purify_save = base_mdata.purifiable; // ...that we have... if (has_trait(base_mutation)) { // ...consider the mutations that replace it. for( auto &mutation : base_mdata.replacements ) { bool valid_ok = mutation->valid; if ( (mutation_ok(mutation, force_good, force_bad)) && (valid_ok) ) { upgrades.push_back(mutation); } } // ...consider the mutations that add to it. for( auto &mutation : base_mdata.additions ) { bool valid_ok = mutation->valid; if ( (mutation_ok(mutation, force_good, force_bad)) && (valid_ok) ) { upgrades.push_back(mutation); } } // ...consider whether its in our highest category if( has_trait(base_mutation) && !has_base_trait(base_mutation) ) { // Starting traits don't count toward categories std::vector<trait_id> group = mutations_category[cat]; bool in_cat = false; for( auto &elem : group ) { if( elem == base_mutation ) { in_cat = true; break; } } // mark for removal // no removing Thresholds/Professions this way! if(!in_cat && !thresh_save && !prof_save) { // non-purifiable stuff should be pretty tenacious // category-enforcement only targets it 25% of the time // (purify_save defaults true, = false for non-purifiable) if( purify_save || ( one_in( 4 ) && !purify_save ) ) { downgrades.push_back(base_mutation); } } } } } // Preliminary round to either upgrade or remove existing mutations if(one_in(2)) { if (!upgrades.empty()) { // (upgrade count) chances to pick an upgrade, 4 chances to pick something else. size_t roll = rng(0, upgrades.size() + 4); if (roll < upgrades.size()) { // We got a valid upgrade index, so use it and return. mutate_towards(upgrades[roll]); return; } } } else { // Remove existing mutations that don't fit into our category if( !downgrades.empty() && !cat.empty() ) { size_t roll = rng(0, downgrades.size() + 4); if (roll < downgrades.size()) { remove_mutation(downgrades[roll]); return; } } } std::vector<trait_id> valid; // Valid mutations bool first_pass = true; do { // If we tried once with a non-NULL category, and couldn't find anything valid // there, try again with MUTCAT_NULL if (!first_pass) { cat.clear(); } if( cat.empty() ) { // Pull the full list for( auto &traits_iter : mutation_branch::get_all() ) { if( traits_iter.second.valid ) { valid.push_back( traits_iter.first ); } } } else { // Pull the category's list valid = mutations_category[cat]; } // Remove anything we already have, that we have a child of, or that // goes against our intention of a good/bad mutation for (size_t i = 0; i < valid.size(); i++) { if ( (!mutation_ok(valid[i], force_good, force_bad)) || (!valid[i]->valid) ) { valid.erase(valid.begin() + i); i--; } } if (valid.empty()) { // So we won't repeat endlessly first_pass = false; } } while ( valid.empty() && !cat.empty() ); if (valid.empty()) { // Couldn't find anything at all! return; } if (mutate_towards(random_entry(valid))) { return; } else { // if mutation failed (errors, post-threshold pick), try again once. mutate_towards(random_entry(valid)); } }
void player::remove_mutation(game *g, pl_flag mut) { // Check for dependant mutations first std::vector<pl_flag> dependant; for (int i = 0; i < PF_MAX2; i++) { for (std::vector<pl_flag>::iterator it = g->mutation_data[i].prereqs.begin(); it != g->mutation_data[i].prereqs.end(); it++) { if (*it == i) { dependant.push_back((pl_flag)i); break; } } } if (dependant.size() != 0) { remove_mutation(g,dependant[rng(0,dependant.size())]); return; } // Check if there's a prereq we should shrink back into pl_flag replacing = PF_NULL; std::vector<pl_flag> originals = g->mutation_data[mut].prereqs; for (int i = 0; replacing == PF_NULL && i < originals.size(); i++) { pl_flag pre = originals[i]; for (int j = 0; replacing == PF_NULL && j < g->mutation_data[pre].replacements.size(); j++) { if (g->mutation_data[pre].replacements[j] == mut) replacing = pre; } } // See if this mutation is cancelled by a base trait //Only if there's no prereq to shrink to, thus we're at the bottom of the trait line if (replacing == PF_NULL) { //Check each mutation until we reach the end or find a trait to revert to for (int i = 1; replacing == PF_NULL && i < PF_MAX2; i++) { //See if it's in our list of base traits but not active if (has_base_trait(i) && !has_trait(i)) { //See if that base trait cancels the mutation we are using std::vector<pl_flag> traitcheck = g->mutation_data[i].cancels; if (!traitcheck.empty()) { for (int j = 0; replacing == PF_NULL && j < traitcheck.size(); j++) { if (g->mutation_data[i].cancels[j] == mut) replacing = ((pl_flag)i); } } } } } // This should revert back to a removed base trait rather than simply removing the mutation toggle_mutation(mut); if (replacing != PF_NULL) { g->add_msg("Your %s mutation turns into %s.", traits[mut].name.c_str(), traits[replacing].name.c_str()); toggle_mutation(replacing); mutation_loss_effect(g, *this, mut); mutation_effect(g, *this, replacing); } else { g->add_msg("You lose your %s mutation.", traits[mut].name.c_str()); mutation_loss_effect(g, *this, mut); } }
void player::mutate(game *g) { bool force_bad = one_in(3); bool force_good = false; if (has_trait(PF_ROBUST) && force_bad) { // Robust Genetics gives you a 33% chance for a good mutation, // instead of the 33% chance of a bad one. force_bad = false; force_good = true; } // Determine the mutation categorie mutation_category cat = MUTCAT_NULL; // Count up the players number of mutations in categories and find // the category with the highest single count. int total = 0, highest = 0; for (int i = 0; i < NUM_MUTATION_CATEGORIES; i++) { total += mutation_category_level[i]; if (mutation_category_level[i] > highest) { cat = mutation_category(i); highest = mutation_category_level[i]; } } // See if we should ugrade/extend an existing mutation... std::vector<pl_flag> upgrades; // ... or remove one that is not in our highest category std::vector<pl_flag> downgrades; // For each mutation... for (int base_mutation_index = 1; base_mutation_index < PF_MAX2; base_mutation_index++) { pl_flag base_mutation = (pl_flag) base_mutation_index; // ...that we have... if (has_trait(base_mutation)) { // ...consider the mutations that replace it. for (int i = 0; i < g->mutation_data[base_mutation].replacements.size(); i++) { pl_flag mutation = g->mutation_data[base_mutation].replacements[i]; if (mutation_ok(g, mutation, force_good, force_bad)) { upgrades.push_back(mutation); } } // ...consider the mutations that add to it. for (int i = 0; i < g->mutation_data[base_mutation].additions.size(); i++) { pl_flag mutation = g->mutation_data[base_mutation].additions[i]; if (mutation_ok(g, mutation, force_good, force_bad)) { upgrades.push_back(mutation); } } // ...consider whether its in our highest category if( has_trait(base_mutation) && !has_base_trait(base_mutation) ){ // Starting traits don't count toward categories std::vector<pl_flag> group = mutations_from_category(cat); bool in_cat = false; for (int j = 0; j < group.size(); j++) { if (group[j] == base_mutation) { in_cat = true; break; } } // mark for removal if(!in_cat) downgrades.push_back(base_mutation); } } } // Preliminary round to either upgrade or remove existing mutations if(one_in(2)){ if (upgrades.size() > 0) { // (upgrade count) chances to pick an upgrade, 4 chances to pick something else. int roll = rng(0, upgrades.size() + 4); if (roll < upgrades.size()) { // We got a valid upgrade index, so use it and return. mutate_towards(g, upgrades[roll]); return; } } } else { // Remove existing mutations that don't fit into our category if (downgrades.size() > 0 && cat != MUTCAT_NULL) { int roll = rng(0, downgrades.size() + 4); if (roll < downgrades.size()) { remove_mutation(g, downgrades[roll]); return; } } } std::vector<pl_flag> valid; // Valid mutations bool first_pass = true; do { // If we tried once with a non-NULL category, and couldn't find anything valid // there, try again with MUTCAT_NULL if (!first_pass) { cat = MUTCAT_NULL; } if (cat == MUTCAT_NULL) { // Pull the full list for (int i = 1; i < PF_MAX2; i++) { if (g->mutation_data[i].valid) { valid.push_back( pl_flag(i) ); } } } else { // Pull the category's list valid = mutations_from_category(cat); } // Remove anything we already have, that we have a child of, or that // goes against our intention of a good/bad mutation for (int i = 0; i < valid.size(); i++) { if (!mutation_ok(g, valid[i], force_good, force_bad)) { valid.erase(valid.begin() + i); i--; } } if (valid.empty()) { // So we won't repeat endlessly first_pass = false; } } while (valid.empty() && cat != MUTCAT_NULL); if (valid.empty()) { // Couldn't find anything at all! return; } pl_flag selection = valid[ rng(0, valid.size() - 1) ]; // Pick one! mutate_towards(g, selection); }
void player::mutate_towards(game *g, pl_flag mut) { if (has_child_flag(g, mut)) { remove_child_flag(g, mut); return; } bool has_prereqs = false; pl_flag canceltrait = PF_NULL; std::vector<pl_flag> prereq = g->mutation_data[mut].prereqs; std::vector<pl_flag> cancel = g->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 we have the trait, but it's a base trait, don't allow it to be removed normally if (has_base_trait( cancel[i] )) { canceltrait = cancel[i]; cancel.erase(cancel.begin() + i); i--; } } } if (!cancel.empty()) { pl_flag removed = cancel[ rng(0, cancel.size() - 1) ]; remove_mutation(g, removed); return; } for (int i = 0; !has_prereqs && i < prereq.size(); i++) { if (has_trait(prereq[i])) has_prereqs = true; } if (!has_prereqs && !prereq.empty()) { pl_flag devel = prereq[ rng(0, prereq.size() - 1) ]; mutate_towards(g, devel); return; } // Check if one of the prereqs that we have TURNS INTO this one pl_flag replacing = PF_NULL; prereq = g->mutation_data[mut].prereqs; // Reset it for (int i = 0; i < prereq.size(); i++) { if (has_trait(prereq[i])) { pl_flag pre = prereq[i]; for (int j = 0; replacing == PF_NULL && j < g->mutation_data[pre].replacements.size(); j++) { if (g->mutation_data[pre].replacements[j] == mut) replacing = pre; } } } toggle_mutation(mut); if (replacing != PF_NULL) { g->add_msg("Your %s mutation turns into %s!", traits[replacing].name.c_str(), traits[mut].name.c_str()); toggle_mutation(replacing); mutation_loss_effect(g, *this, replacing); mutation_effect(g, *this, mut); } else // If this new mutation cancels a base trait, remove it and add the mutation at the same time if (canceltrait != PF_NULL) { g->add_msg("Your innate %s trait turns into %s!", traits[canceltrait].name.c_str(), traits[mut].name.c_str()); toggle_mutation(canceltrait); mutation_loss_effect(g, *this, canceltrait); mutation_effect(g, *this, mut); } else { g->add_msg("You gain a mutation called %s!", traits[mut].name.c_str()); mutation_effect(g, *this, mut); } // Weight us towards any categories that include this mutation for (int i = 0; i < NUM_MUTATION_CATEGORIES; i++) { std::vector<pl_flag> group = mutations_from_category(mutation_category(i)); bool found = false; for (int j = 0; !found && j < group.size(); j++) { if (group[j] == mut) found = true; } if (found) mutation_category_level[i] += 8; else if (mutation_category_level[i] > 0 && !one_in(mutation_category_level[i])) mutation_category_level[i]--; } }
void player::mutate_towards(game *g, std::string mut) { if (has_child_flag(g, mut)) { remove_child_flag(g, mut); return; } bool has_prereqs = false; std::string canceltrait = ""; std::vector<std::string> prereq = mutation_data[mut].prereqs; 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(g, removed); return; } for (int i = 0; !has_prereqs && i < prereq.size(); i++) { if (has_trait(prereq[i])) { has_prereqs = true; } } if (!has_prereqs && !prereq.empty()) { std::string devel = prereq[ rng(0, prereq.size() - 1) ]; mutate_towards(g, devel); 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; } } } } toggle_mutation(mut); if (replacing != "") { g->add_msg(_("Your %1$s mutation turns into %2$s!"), traits[replacing].name.c_str(), traits[mut].name.c_str()); g->u.add_memorial_log(_("'%s' mutation turned into '%s'"), traits[replacing].name.c_str(), traits[mut].name.c_str()); toggle_mutation(replacing); mutation_loss_effect(g, *this, replacing); mutation_effect(g, *this, mut); } else 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()); g->u.add_memorial_log(_("'%s' trait turned into '%s'"), traits[canceltrait].name.c_str(), traits[mut].name.c_str()); toggle_mutation(canceltrait); mutation_loss_effect(g, *this, canceltrait); mutation_effect(g, *this, mut); } else { g->add_msg(_("You gain a mutation called %s!"), traits[mut].name.c_str()); g->u.add_memorial_log(_("Gained the mutation '%s'."), traits[mut].name.c_str()); mutation_effect(g, *this, mut); } set_highest_cat_level(); drench_mut_calc(); }
void player::remove_mutation(game *g, pl_flag mut) { // Check for dependant mutations first std::vector<pl_flag> dependant; for (int i = 0; i < PF_MAX2; i++) { for (std::vector<pl_flag>::iterator it = g->mutation_data[i].prereqs.begin(); it != g->mutation_data[i].prereqs.end(); it++) { if (*it == i) { dependant.push_back((pl_flag)i); break; } } } if (dependant.size() != 0) { remove_mutation(g,dependant[rng(0,dependant.size())]); return; } // Check if there's a prereq we should shrink back into pl_flag replacing = PF_NULL; std::vector<pl_flag> originals = g->mutation_data[mut].prereqs; for (int i = 0; replacing == PF_NULL && i < originals.size(); i++) { pl_flag pre = originals[i]; for (int j = 0; replacing == PF_NULL && j < g->mutation_data[pre].replacements.size(); j++) { if (g->mutation_data[pre].replacements[j] == mut) replacing = pre; } } // See if this mutation is cancelled by a base trait //Only if there's no prereq to shrink to, thus we're at the bottom of the trait line if (replacing == PF_NULL) { //Check each mutation until we reach the end or find a trait to revert to for (int i = 1; replacing == PF_NULL && i < PF_MAX2; i++) { //See if it's in our list of base traits but not active if (has_base_trait(i) && !has_trait(i)) { //See if that base trait cancels the mutation we are using std::vector<pl_flag> traitcheck = g->mutation_data[i].cancels; if (!traitcheck.empty()) { for (int j = 0; replacing == PF_NULL && j < traitcheck.size(); j++) { if (g->mutation_data[i].cancels[j] == mut) replacing = ((pl_flag)i); } } } } } // This should revert back to a removed base trait rather than simply removing the mutation toggle_mutation(mut); if (replacing != PF_NULL) { g->add_msg(_("Your %1$s mutation turns into %2$s."), traits[mut].name.c_str(), traits[replacing].name.c_str()); toggle_mutation(replacing); mutation_loss_effect(g, *this, mut); mutation_effect(g, *this, replacing); } else { g->add_msg(_("You lose your %s mutation."), traits[mut].name.c_str()); mutation_loss_effect(g, *this, mut); } // Reduce the strength of the categories the removed mutation is a part of for (int i = 0; i < NUM_MUTATION_CATEGORIES; i++) { std::vector<pl_flag> group = mutations_from_category(mutation_category(i)); bool found = false; for (int j = 0; !found && j < group.size(); j++) { if (group[j] == mut) found = true; } if (found) { mutation_category_level[i] -= 8; // If the category strength is below 0, set it to 0. We don't want negative category strength. if (mutation_category_level[i] < 0) { mutation_category_level[i] = 0; } } } }