void player::remove_child_flag(std::string flag) { for (int i = 0; i < mutation_data[flag].replacements.size(); i++) { std::string tmp = mutation_data[flag].replacements[i]; if (has_trait(tmp)) { remove_mutation(tmp); return; } else if (has_child_flag(tmp)) { remove_child_flag(tmp); return; } } }
void player::remove_child_flag(game *g, pl_flag flag) { for (int i = 0; i < g->mutation_data[flag].replacements.size(); i++) { pl_flag tmp = g->mutation_data[flag].replacements[i]; if (has_trait(tmp)) { remove_mutation(g, tmp); return; } else if (has_child_flag(g, tmp)) { remove_child_flag(g, tmp); return; } } }
void player::remove_child_flag( const std::string &flag ) { for( auto &elem : mutation_branch::get( flag ).replacements ) { std::string tmp = elem; if (has_trait(tmp)) { remove_mutation(tmp); return; } else if (has_child_flag(tmp)) { remove_child_flag(tmp); return; } } }
void player::remove_child_flag( const trait_id &flag ) { for( auto &elem : flag->replacements ) { trait_id tmp = elem; if (has_trait(tmp)) { remove_mutation(tmp); return; } else if (has_child_flag(tmp)) { remove_child_flag(tmp); return; } } }
int player::hit_roll() { int numdice = base_to_hit() + weapon.type->m_to_hit; int sides = 10 - encumb(bp_torso); int best_bonus = 0; if (sides < 2) sides = 2; // Are we unarmed? if (unarmed_attack()) { best_bonus = sklevel[sk_unarmed]; if (sklevel[sk_unarmed] > 4) best_bonus += sklevel[sk_unarmed] - 2; // Extra bonus for high levels } // Using a bashing weapon? if (weapon.is_bashing_weapon()) { int bash_bonus = int(sklevel[sk_bashing] / 3); if (bash_bonus > best_bonus) best_bonus = bash_bonus; } // Using a cutting weapon? if (weapon.is_cutting_weapon()) { int cut_bonus = int(sklevel[sk_cutting] / 2); if (cut_bonus > best_bonus) best_bonus = cut_bonus; } // Using a spear? if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) { int stab_bonus = int(sklevel[sk_stabbing] / 2); if (stab_bonus > best_bonus) best_bonus = stab_bonus; } numdice += best_bonus; if (has_trait(PF_DRUNKEN)) { if (unarmed_attack()) numdice += rng(0, 1) + int(disease_level(DI_DRUNK) / 300); else numdice += int(disease_level(DI_DRUNK) / 400); } if (numdice < 1) { numdice = 1; sides = 8; } return dice(numdice, sides); }
bool Creature::resists_effect(effect e) { for (auto &i : e.get_resist_effects()) { if (has_effect(i)) { return true; } } for (auto &i : e.get_resist_traits()) { if (has_trait(i)) { return true; } } return false; }
std::list<item> player::consume_items( const comp_selection<item_comp> &is, int batch ) { std::list<item> ret; if( has_trait( trait_DEBUG_HS ) ) { return ret; } item_comp selected_comp = is.comp; const tripoint &loc = pos(); const bool by_charges = ( item::count_by_charges( selected_comp.type ) && selected_comp.count > 0 ); // Count given to use_amount/use_charges, changed by those functions! long real_count = ( selected_comp.count > 0 ) ? selected_comp.count * batch : abs( selected_comp.count ); // First try to get everything from the map, than (remaining amount) from player if( is.use_from & use_from_map ) { if( by_charges ) { std::list<item> tmp = g->m.use_charges( loc, PICKUP_RANGE, selected_comp.type, real_count ); ret.splice( ret.end(), tmp ); } else { std::list<item> tmp = g->m.use_amount( loc, PICKUP_RANGE, selected_comp.type, real_count ); remove_ammo( tmp, *this ); ret.splice( ret.end(), tmp ); } } if( is.use_from & use_from_player ) { if( by_charges ) { std::list<item> tmp = use_charges( selected_comp.type, real_count ); ret.splice( ret.end(), tmp ); } else { std::list<item> tmp = use_amount( selected_comp.type, real_count ); remove_ammo( tmp, *this ); ret.splice( ret.end(), tmp ); } } // condense those items into one if( by_charges && ret.size() > 1 ) { std::list<item>::iterator b = ret.begin(); b++; while( ret.size() > 1 ) { ret.front().charges += b->charges; b = ret.erase( b ); } } lastconsumed = selected_comp.type; empty_buckets( *this ); return ret; }
int player::dodge(game *g) //Returns 1/2*DEX + dodge skill level + static bonuses from mutations //Return numbers range from around 4 (starting player, no boosts) to 29 (20 DEX, 10 dodge, +9 mutations) { //If we're asleep or busy we can't dodge if (has_disease(DI_SLEEP) || has_disease(DI_LYING_DOWN)) {return 0;} if (activity.type != ACT_NULL) {return 0;} int ret = (dex_cur / 2); ret += skillLevel("dodge"); ret += disease_intensity(DI_DODGE_BOOST); ret -= (encumb(bp_legs) / 2) + encumb(bp_torso); ret += int(current_speed(g) / 150); //Faster = small dodge advantage //Mutations if (has_trait(PF_TAIL_LONG)) {ret += 4;} if (has_trait(PF_TAIL_FLUFFY)) {ret += 8;} if (has_trait(PF_WHISKERS)) {ret += 1;} if (has_trait(PF_WINGS_BAT)) {ret -= 3;} if (str_max >= 16) {ret--;} // Penalty if we're huge else if (str_max <= 5) {ret++;} // Bonus if we're small if (dodges_left <= 0) // We already dodged this turn { if (rng(0, skillLevel("dodge") + dex_cur + 15) <= skillLevel("dodge") + dex_cur) { ret = rng(ret/2, ret); //Penalize multiple dodges per turn } else { ret = 0; } } dodges_left--; return ret; }
int Character::volume_capacity() const { int ret = 0; for (auto &i : worn) { ret += i.get_storage(); } if (has_bionic("bio_storage")) { ret += 8; } if (has_trait("SHELL")) { ret += 16; } if (has_trait("SHELL2") && !has_active_mutation("SHELL2")) { ret += 24; } if (has_trait("PACKMULE")) { ret = int(ret * 1.4); } if (has_trait("DISORGANIZED")) { ret = int(ret * 0.6); } ret = std::max(ret, 0); return ret; }
int player::roll_cut_damage(monster *z, bool crit) { if (weapon.has_flag(IF_SPEAR)) return 0; // Stabs, doesn't cut! int z_armor_cut = (z == NULL ? 0 : z->armor_cut() - skillLevel("cutting") / 2); if (crit) z_armor_cut /= 2; if (z_armor_cut < 0) z_armor_cut = 0; int ret = weapon.damage_cut() - z_armor_cut; if (unarmed_attack() && !wearing_something_on(bp_hands)) { if (has_trait(PF_CLAWS)) ret += 6; if (has_trait(PF_TALONS)) ret += 6 + ((int)skillLevel("unarmed") > 8 ? 8 : (int)skillLevel("unarmed")); if (has_trait(PF_SLIME_HANDS) && (z == NULL || !z->has_flag(MF_ACIDPROOF))) ret += rng(4, 6); } if (ret <= 0) return 0; // No negative damage! // 80%, 88%, 96%, 104%, 112%, 116%, 120%, 124%, 128%, 132% if (skillLevel("cutting") <= 5) ret *= double( 0.8 + 0.08 * skillLevel("cutting") ); else ret *= double( 0.92 + 0.04 * skillLevel("cutting") ); if (crit) ret *= double( 1.0 + double(skillLevel("cutting") / 12) ); return ret; }
/* we use this if we selected the tool earlier */ void player::consume_tools( const comp_selection<tool_comp> &tool, int batch ) { if( has_trait( trait_DEBUG_HS ) ) { return; } if( tool.use_from & use_from_player ) { use_charges( tool.comp.type, tool.comp.count * batch ); } if( tool.use_from & use_from_map ) { long quantity = tool.comp.count * batch; g->m.use_charges( pos(), PICKUP_RANGE, tool.comp.type, quantity ); } // else, use_from_none (or cancel), so we don't use up any tools; }
void player::perform_special_attacks(game *g, monster *z, player *p, int &bash_dam, int &cut_dam, int &stab_dam) { bool can_poison = false; int bash_armor = (z == NULL ? 0 : z->armor_bash()); int cut_armor = (z == NULL ? 0 : z->armor_cut()); std::vector<special_attack> special_attacks = mutation_attacks(z, p); for (int i = 0; i < special_attacks.size(); i++) { bool did_damage = false; if (special_attacks[i].bash > bash_armor) { bash_dam += special_attacks[i].bash; did_damage = true; } if (special_attacks[i].cut > cut_armor) { cut_dam += special_attacks[i].cut - cut_armor; did_damage = true; } if (special_attacks[i].stab > cut_armor * .8) { stab_dam += special_attacks[i].stab - cut_armor * .8; did_damage = true; } if (!can_poison && one_in(2) && (special_attacks[i].cut > cut_armor || special_attacks[i].stab > cut_armor * .8)) can_poison = true; if (did_damage) g->add_msg( special_attacks[i].text.c_str() ); } if (can_poison && has_trait(PF_POISONOUS)) { if (z != NULL) { if (!is_npc() && !z->has_effect(ME_POISONED)) g->add_msg("You poison the %s!", z->name().c_str()); z->add_effect(ME_POISONED, 6); } else if (p != NULL) { if (!is_npc() && !p->has_disease(DI_POISON)) g->add_msg("You poison %s!", p->name.c_str()); p->add_disease(DI_POISON, 6, g); } } }
bool player::mutation_ok( const std::string &mutation, bool force_good, bool force_bad ) const { if (has_trait(mutation) || has_child_flag(mutation)) { // We already have this mutation or something that replaces it. return false; } const auto &mdata = mutation_branch::get( mutation ); if (force_bad && mdata.points > 0) { // This is a good mutation, and we're due for a bad one. return false; } if (force_good && mdata.points < 0) { // This is a bad mutation, and we're due for a good one. return false; } return true; }
bool player::mutation_ok(std::string mutation, bool force_good, bool force_bad) { if (has_trait(mutation) || has_child_flag(mutation)) { // We already have this mutation or something that replaces it. return false; } if (force_bad && traits[mutation].points > 0) { // This is a good mutation, and we're due for a bad one. return false; } if (force_good && traits[mutation].points < 0) { // This is a bad mutation, and we're due for a good one. return false; } return true; }
void player::mutate_category( const std::string &cat ) { // Hacky ID comparison is better than separate hardcoded branch used before // @todo: Turn it into the null id if( cat == "MUTCAT_ANY" ) { mutate(); return; } 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; } // Pull the category's list for valid mutations std::vector<trait_id> valid; 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.erase(valid.begin() + i); i--; } } // if we can't mutate in the category do nothing if (valid.empty()) { 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::stumble(game *g) { int stumble_pen = 2 * weapon.volume() + weapon.weight(); if (has_trait(PF_DEFT)) stumble_pen = int(stumble_pen * .3) - 10; if (stumble_pen < 0) stumble_pen = 0; if (stumble_pen > 0 && (str_cur >= 15 || dex_cur >= 21 || one_in(16 - str_cur) || one_in(22 - dex_cur))) stumble_pen = rng(0, stumble_pen); if (!is_npc()) { // Only display messages if this is the player if (stumble_pen >= 60) g->add_msg("You miss and stumble with the momentum."); else if (stumble_pen >= 10) g->add_msg("You swing wildly and miss."); else g->add_msg("You miss."); } moves -= stumble_pen; }
bool player::mutation_ok( const trait_id &mutation, bool force_good, bool force_bad ) const { if (mutation_branch::trait_is_blacklisted(mutation)) { return false; } if (has_trait(mutation) || has_child_flag(mutation)) { // We already have this mutation or something that replaces it. return false; } const mutation_branch &mdata = mutation.obj(); if (force_bad && mdata.points > 0) { // This is a good mutation, and we're due for a bad one. return false; } if (force_good && mdata.points < 0) { // This is a bad mutation, and we're due for a good one. return false; } return true; }
morale_type player::allergy_type( const item &food ) const { using allergy_tuple = std::tuple<trait_id, std::string, morale_type>; static const std::array<allergy_tuple, 8> allergy_tuples = {{ std::make_tuple( trait_id( "VEGETARIAN" ), "ALLERGEN_MEAT", MORALE_VEGETARIAN ), std::make_tuple( trait_id( "MEATARIAN" ), "ALLERGEN_VEGGY", MORALE_MEATARIAN ), std::make_tuple( trait_id( "LACTOSE" ), "ALLERGEN_MILK", MORALE_LACTOSE ), std::make_tuple( trait_id( "ANTIFRUIT" ), "ALLERGEN_FRUIT", MORALE_ANTIFRUIT ), std::make_tuple( trait_id( "ANTIJUNK" ), "ALLERGEN_JUNK", MORALE_ANTIJUNK ), std::make_tuple( trait_id( "ANTIWHEAT" ), "ALLERGEN_WHEAT", MORALE_ANTIWHEAT ) } }; for( const auto &tp : allergy_tuples ) { if( has_trait( std::get<0>( tp ) ) && food.has_flag( std::get<1>( tp ) ) ) { return std::get<2>( tp ); } } return MORALE_NULL; }
void player::mutate_category( const std::string &cat ) { 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; } // Pull the category's list for valid mutations std::vector<std::string> valid; 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.erase(valid.begin() + i); i--; } } // if we can't mutate in the category do nothing if (valid.empty()) { return; } if (mutate_towards(random_entry(valid))) { return; } else { // if mutation failed (errors, post-threshold pick), try again once. mutate_towards(random_entry(valid)); } }
int visitable<Character>::max_quality( const quality_id &qual ) const { int res = INT_MIN; auto self = static_cast<const Character *>( this ); for( const auto &bio : self->my_bionics ) { res = std::max( res, bio.get_quality( qual ) ); } static const quality_id BUTCHER( "BUTCHER" ); if( qual == BUTCHER ) { if( self->has_trait( "CLAWS_ST" ) ) { res = std::max( res, 8 ); } else if( self->has_trait( "TALONS" ) || self->has_trait( "MANDIBLES" ) || self->has_trait( "CLAWS" ) || self->has_trait( "CLAWS_RETRACT" ) || self->has_trait( "CLAWS_RAT" ) ) { res = std::max( res, 4 ); } } return std::max( res, max_quality_internal( *this, qual ) ); }
// Why put this in a Big Switch? Why not let bionics have pointers to // functions, much like monsters and items? // // Well, because like diseases, which are also in a Big Switch, bionics don't // share functions.... void player::activate_bionic(int b, game *g) { bionic bio = my_bionics[b]; int power_cost = bionics[bio.id].power_cost; if (weapon.type->id == itm_bio_claws && bio.id == bio_claws) power_cost = 0; if (power_level < power_cost) { if (my_bionics[b].powered) { g->add_msg("Your %s powers down.", bionics[bio.id].name.c_str()); my_bionics[b].powered = false; } else g->add_msg("You cannot power your %s", bionics[bio.id].name.c_str()); return; } if (my_bionics[b].powered && my_bionics[b].charge > 0) { // Already-on units just lose a bit of charge my_bionics[b].charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (bionics[bio.id].charge_time > 0) { my_bionics[b].powered = true; my_bionics[b].charge = bionics[bio.id].charge_time; } power_level -= power_cost; } std::string junk; std::vector<point> traj; std::vector<std::string> good; std::vector<std::string> bad; WINDOW* w; int dirx, diry, t, index; unsigned int l; item tmp_item; switch (bio.id) { case bio_painkiller: pkill += 6; pain -= 2; if (pkill > pain) pkill = pain; break; case bio_nanobots: healall(4); break; case bio_resonator: g->sound(posx, posy, 30, "VRRRRMP!"); for (int i = posx - 1; i <= posx + 1; i++) { for (int j = posy - 1; j <= posy + 1; j++) { g->m.bash(i, j, 40, junk); g->m.bash(i, j, 40, junk); // Multibash effect, so that doors &c will fall g->m.bash(i, j, 40, junk); if (g->m.is_destructable(i, j) && rng(1, 10) >= 4) g->m.ter(i, j) = t_rubble; } } break; case bio_time_freeze: moves += 100 * power_level; power_level = 0; g->add_msg("Your speed suddenly increases!"); if (one_in(3)) { g->add_msg("Your muscles tear with the strain."); hurt(g, bp_arms, 0, rng(5, 10)); hurt(g, bp_arms, 1, rng(5, 10)); hurt(g, bp_legs, 0, rng(7, 12)); hurt(g, bp_legs, 1, rng(7, 12)); hurt(g, bp_torso, 0, rng(5, 15)); } if (one_in(5)) add_disease(DI_TELEGLOW, rng(50, 400), g); break; case bio_teleport: g->teleport(); add_disease(DI_TELEGLOW, 300, g); break; // TODO: More stuff here (and bio_blood_filter) case bio_blood_anal: w = newwin(20, 40, 3, 10); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); if (has_disease(DI_FUNGUS)) bad.push_back("Fungal Parasite"); if (has_disease(DI_DERMATIK)) bad.push_back("Insect Parasite"); if (has_disease(DI_POISON)) bad.push_back("Poison"); if (radiation > 0) bad.push_back("Irradiated"); if (has_disease(DI_PKILL1)) good.push_back("Minor Painkiller"); if (has_disease(DI_PKILL2)) good.push_back("Moderate Painkiller"); if (has_disease(DI_PKILL3)) good.push_back("Heavy Painkiller"); if (has_disease(DI_PKILL_L)) good.push_back("Slow-Release Painkiller"); if (has_disease(DI_DRUNK)) good.push_back("Alcohol"); if (has_disease(DI_CIG)) good.push_back("Nicotine"); if (has_disease(DI_HIGH)) good.push_back("Intoxicant: Other"); if (has_disease(DI_TOOK_PROZAC)) good.push_back("Prozac"); if (has_disease(DI_TOOK_FLUMED)) good.push_back("Antihistamines"); if (has_disease(DI_ADRENALINE)) good.push_back("Adrenaline Spike"); if (good.size() == 0 && bad.size() == 0) mvwprintz(w, 1, 1, c_white, "No effects."); else { for (unsigned int line = 1; line < 39 && line <= good.size() + bad.size(); line++) { if (line <= bad.size()) mvwprintz(w, line, 1, c_red, bad[line - 1].c_str()); else mvwprintz(w, line, 1, c_green, good[line - 1 - bad.size()].c_str()); } } wrefresh(w); refresh(); getch(); delwin(w); break; case bio_blood_filter: rem_disease(DI_FUNGUS); rem_disease(DI_POISON); rem_disease(DI_PKILL1); rem_disease(DI_PKILL2); rem_disease(DI_PKILL3); rem_disease(DI_PKILL_L); rem_disease(DI_DRUNK); rem_disease(DI_CIG); rem_disease(DI_HIGH); rem_disease(DI_TOOK_PROZAC); rem_disease(DI_TOOK_FLUMED); rem_disease(DI_ADRENALINE); break; case bio_evap: if (query_yn("Drink directly? Otherwise you will need a container.")) { tmp_item = item(g->itypes[itm_water], 0); thirst -= 50; if (has_trait(PF_GOURMAND) && thirst < -60) { g->add_msg("You can't finish it all!"); thirst = -60; } else if (!has_trait(PF_GOURMAND) && thirst < -20) { g->add_msg("You can't finish it all!"); thirst = -20; } } else { t = g->inv("Choose a container:"); if (i_at(t).type == 0) { g->add_msg("You don't have that item!"); power_level += bionics[bio_evap].power_cost; } else if (!i_at(t).is_container()) { g->add_msg("That %s isn't a container!", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else { it_container *cont = dynamic_cast<it_container*>(i_at(t).type); if (i_at(t).volume_contained() + 1 > cont->contains) { g->add_msg("There's no space left in your %s.", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else if (!(cont->flags & con_wtight)) { g->add_msg("Your %s isn't watertight!", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else { g->add_msg("You pour water into your %s.", i_at(t).tname().c_str()); i_at(t).put_in(item(g->itypes[itm_water], 0)); } } } break; case bio_lighter: g->draw(); mvprintw(0, 0, "Torch in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_lighter].power_cost; return; } dirx += posx; diry += posy; if (!g->m.add_field(g, dirx, diry, fd_fire, 1)) // Unsuccessful. g->add_msg("You can't light a fire there."); break; case bio_claws: if (weapon.type->id == itm_bio_claws) { g->add_msg("You withdraw your claws."); weapon = ret_null; } else if (weapon.type->id != 0) { g->add_msg("Your claws extend, forcing you to drop your %s.", weapon.tname().c_str()); g->m.add_item(posx, posy, weapon); weapon = item(g->itypes[itm_bio_claws], 0); weapon.invlet = '#'; } else { g->add_msg("Your claws extend!"); weapon = item(g->itypes[itm_bio_claws], 0); weapon.invlet = '#'; } break; case bio_blaster: tmp_item = weapon; weapon = item(g->itypes[itm_bio_blaster], 0); weapon.curammo = dynamic_cast<it_ammo*>(g->itypes[itm_bio_fusion]); weapon.charges = 1; g->refresh_all(); g->plfire(false); weapon = tmp_item; break; case bio_laser: tmp_item = weapon; weapon = item(g->itypes[itm_v29], 0); weapon.curammo = dynamic_cast<it_ammo*>(g->itypes[itm_laser_pack]); weapon.charges = 1; g->refresh_all(); g->plfire(false); weapon = tmp_item; break; case bio_emp: g->draw(); mvprintw(0, 0, "Fire EMP in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_emp].power_cost; return; } dirx += posx; diry += posy; g->emp_blast(dirx, diry); break; case bio_hydraulics: g->add_msg("Your muscles hiss as hydraulic strength fills them!"); break; case bio_water_extractor: for (unsigned int i = 0; i < g->m.i_at(posx, posy).size(); i++) { item tmp = g->m.i_at(posx, posy)[i]; if (tmp.type->id == itm_corpse && query_yn("Extract water from the %s", tmp.tname().c_str())) { i = g->m.i_at(posx, posy).size() + 1; // Loop is finished t = g->inv("Choose a container:"); if (i_at(t).type == 0) { g->add_msg("You don't have that item!"); power_level += bionics[bio_water_extractor].power_cost; } else if (!i_at(t).is_container()) { g->add_msg("That %s isn't a container!", i_at(t).tname().c_str()); power_level += bionics[bio_water_extractor].power_cost; } else { it_container *cont = dynamic_cast<it_container*>(i_at(t).type); if (i_at(t).volume_contained() + 1 > cont->contains) { g->add_msg("There's no space left in your %s.", i_at(t).tname().c_str()); power_level += bionics[bio_water_extractor].power_cost; } else { g->add_msg("You pour water into your %s.", i_at(t).tname().c_str()); i_at(t).put_in(item(g->itypes[itm_water], 0)); } } } if (i == g->m.i_at(posx, posy).size() - 1) // We never chose a corpse power_level += bionics[bio_water_extractor].power_cost; } break; case bio_magnet: for (int i = posx - 10; i <= posx + 10; i++) { for (int j = posy - 10; j <= posy + 10; j++) { if (g->m.i_at(i, j).size() > 0) { if (g->m.sees(i, j, posx, posy, -1, t)) traj = line_to(i, j, posx, posy, t); else traj = line_to(i, j, posx, posy, 0); } traj.insert(traj.begin(), point(i, j)); for (unsigned int k = 0; k < g->m.i_at(i, j).size(); k++) { if (g->m.i_at(i, j)[k].made_of(IRON) || g->m.i_at(i, j)[k].made_of(STEEL)){ tmp_item = g->m.i_at(i, j)[k]; g->m.i_rem(i, j, k); for (l = 0; l < traj.size(); l++) { index = g->mon_at(traj[l].x, traj[l].y); if (index != -1) { if (g->z[index].hurt(tmp_item.weight() * 2)) g->kill_mon(index, true); g->m.add_item(traj[l].x, traj[l].y, tmp_item); l = traj.size() + 1; } else if (l > 0 && g->m.move_cost(traj[l].x, traj[l].y) == 0) { g->m.bash(traj[l].x, traj[l].y, tmp_item.weight() * 2, junk); g->sound(traj[l].x, traj[l].y, 12, junk); if (g->m.move_cost(traj[l].x, traj[l].y) == 0) { g->m.add_item(traj[l - 1].x, traj[l - 1].y, tmp_item); l = traj.size() + 1; } } } if (l == traj.size()) g->m.add_item(posx, posy, tmp_item); } } } } break; case bio_lockpick: g->draw(); mvprintw(0, 0, "Unlock in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_lockpick].power_cost; return; } dirx += posx; diry += posy; if (g->m.ter(dirx, diry) == t_door_locked) { moves -= 40; g->add_msg("You unlock the door."); g->m.ter(dirx, diry) = t_door_c; } else g->add_msg("You can't unlock that %s.", g->m.tername(dirx, diry).c_str()); break; // Unused enums added for completeness. default: break; } }
bool Player::create_new_character() { Window w_newch(0, 0, 80, 24); cuss::interface i_newch; if (!i_newch.load_from_file(CUSS_DIR + "/i_newchar_stats.cuss")) { return false; } New_char_screen cur_screen = NCS_STATS; Stat_selected cur_stat = STATSEL_STR; int* stat_value = &(stats.strength); /* We need to set up a list of traits which does NOT include the placeholder / * marker "traits" like TRAIT_MAX_GOOD and TRAIT_MAX_NEUTRAL etc. */ std::vector<Trait_id> selectable_traits; for (int i = 1; i < TRAIT_MAX_BAD; i++) { if (i != TRAIT_MAX_GOOD && i != TRAIT_MAX_NEUTRAL) { selectable_traits.push_back( Trait_id(i) ); } } std::vector<std::string> traits_list = get_trait_list (this); std::vector<std::string> profession_list = get_profession_list(this); name = ""; int points = 4; int num_traits = 0; i_newch.ref_data("num_points", &points); i_newch.ref_data("num_strength", &stats.strength); i_newch.ref_data("num_dexterity", &stats.dexterity); i_newch.ref_data("num_perception", &stats.perception); i_newch.ref_data("num_intelligence", &stats.intelligence); i_newch.set_data("text_description", get_stat_description(cur_stat)); i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence", "<c=ltgray>Intelligence<c=/>"); bool done = false; while (!done) { // We'll exit this function via keypresses, always // Always set num_points! i_newch.draw(&w_newch); w_newch.refresh(); long ch = getch(); bool changed_screen = false; if (ch == '<') { cur_screen = New_char_screen( cur_screen - 1 ); if (cur_screen == NCS_CANCEL) { if (query_yn("Cancel character creation?")) { return false; } cur_screen = NCS_STATS; } else { changed_screen = true; } } else if (ch == '>') { cur_screen = New_char_screen( cur_screen + 1 ); if (cur_screen == NCS_DONE) { std::string reason_for_fail; if (points > 0) { reason_for_fail += "\nYou have unspent points!"; } if (profession == NULL) { reason_for_fail += "\nYou didn't choose a profession!"; } if (name.empty()) { reason_for_fail += "\nYour name is blank!"; } if (!reason_for_fail.empty()) { popup("Wait, you can't start the game yet!%s", reason_for_fail.c_str()); } else if (query_yn("Complete character and start the game?")) { done = true; } cur_screen = NCS_DESCRIPTION; } else { changed_screen = true; } } else { // We should be doing this with cuss keybindings, but... that gets complex. // Maybe one day I'll update cuss to be more friendly. switch (cur_screen) { case NCS_STATS: { bool changed_stat = false; switch (ch) { case '2': case 'j': case KEY_DOWN: if (cur_stat == STATSEL_INT) { cur_stat = STATSEL_STR; } else { cur_stat = Stat_selected( cur_stat + 1 ); } changed_stat = true; break; case '8': case 'k': case KEY_UP: if (cur_stat == STATSEL_STR) { cur_stat = STATSEL_INT; } else { cur_stat = Stat_selected( cur_stat - 1 ); } changed_stat = true; break; case '4': case 'h': case KEY_LEFT: if (*stat_value > 4) { if (*stat_value > 16) { points++; // Stats above 16 cost 2 points, so get extra back } points++; (*stat_value)--; } break; case '6': case 'l': case KEY_RIGHT: { int point_req = (*stat_value >= 16 ? 2 : 1); if (*stat_value < 20 && points >= point_req) { points -= point_req; (*stat_value)++; } } break; } // switch (ch) if (changed_stat) { // Update stat_value i_newch.set_data("text_strength", "<c=ltgray>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence","<c=ltgray>Intelligence<c=/>"); i_newch.set_data("text_description", get_stat_description(cur_stat)); switch (cur_stat) { case STATSEL_STR: stat_value = &(stats.strength); i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); break; case STATSEL_DEX: stat_value = &(stats.dexterity); i_newch.set_data("text_dexterity", "<c=ltblue>Dexterity<c=/>"); break; case STATSEL_PER: stat_value = &(stats.perception); i_newch.set_data("text_perception", "<c=ltblue>Perception<c=/>"); break; case STATSEL_INT: stat_value = &(stats.intelligence); i_newch.set_data("text_intelligence", "<c=ltblue>Intelligence<c=/>"); break; } } } break; case NCS_TRAITS: { switch (ch) { case '2': case 'j': case KEY_DOWN: { i_newch.add_data("list_traits", 1); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("text_description", trait_description(cur_trait)); } break; case '8': case 'k': case KEY_UP: { i_newch.add_data("list_traits", -1); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("text_description", trait_description(cur_trait)); } break; case '\n': case ' ': { int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; if (has_trait(cur_trait)) { traits[cur_trait] = false; points += trait_cost(cur_trait); num_traits--; traits_list = get_trait_list(this); } else if (points >= trait_cost(cur_trait) && num_traits < 5){ traits[cur_trait] = true; points -= trait_cost(cur_trait); num_traits++; traits_list = get_trait_list(this); } i_newch.set_data("num_traits_left", 5 - num_traits); if (num_traits >= 5) { i_newch.set_data("num_traits_left", c_red); } } break; } // switch (ch) } break; case NCS_PROFESSION: { switch (ch) { case '2': case 'j': case KEY_DOWN: { i_newch.add_data("list_professions", 1); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case '8': case 'k': case KEY_UP: { i_newch.add_data("list_professions", -1); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case '\n': case ' ': { std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } set_profession(cur_prof); profession_list = get_profession_list(this); } break; } // switch (ch) } break; case NCS_DESCRIPTION: { if (ch == '/') { male = !male; if (male) { i_newch.set_data("text_male", "<c=yellow>Male<c=/>"); i_newch.set_data("text_female", "<c=dkgray>Female<c=/>"); } else { i_newch.set_data("text_male", "<c=dkgray>Male<c=/>"); i_newch.set_data("text_female", "<c=yellow>Female<c=/>"); } } else { /* Let the interface handle name entry; this includes cursor movement, * backspace, etc. The only downside is that this allows entry of "invalid" * name characters like "'&^%$#@ etc. Bad? */ cuss::element* entry = i_newch.find_by_name("entry_name"); entry->handle_keypress(ch); } } break; } // switch (cur_screen) } // key pressed isn't '<' or '>' if (changed_screen) { std::string filename = CUSS_DIR + "/i_newchar_"; switch (cur_screen) { case NCS_STATS: filename += "stats.cuss"; break; case NCS_TRAITS: filename += "traits.cuss"; break; case NCS_PROFESSION: filename += "profession.cuss"; break; case NCS_DESCRIPTION: filename += "description.cuss"; break; } if (!i_newch.load_from_file(filename)) { return false; } i_newch.ref_data("num_points", &points); switch (cur_screen) { case NCS_STATS: cur_stat = STATSEL_STR; i_newch.set_data("text_strength", "<c=ltblue>Strength<c=/>"); i_newch.set_data("text_dexterity", "<c=ltgray>Dexterity<c=/>"); i_newch.set_data("text_perception", "<c=ltgray>Perception<c=/>"); i_newch.set_data("text_intelligence", "<c=ltgray>Intelligence<c=/>"); i_newch.ref_data("num_strength", &stats.strength); i_newch.ref_data("num_dexterity", &stats.dexterity); i_newch.ref_data("num_perception", &stats.perception); i_newch.ref_data("num_intelligence", &stats.intelligence); i_newch.set_data("text_description", get_stat_description(cur_stat)); break; case NCS_TRAITS: { i_newch.select("list_traits"); i_newch.ref_data("list_traits", &traits_list); int sel = i_newch.get_int("list_traits"); Trait_id cur_trait = selectable_traits[sel]; i_newch.set_data("text_description", trait_description(cur_trait)); i_newch.set_data("num_cost", abs(trait_cost(cur_trait))); if (trait_cost(cur_trait) >= 0) { i_newch.set_data("text_cost", "<c=yellow>Cost:<c=/>"); } else { i_newch.set_data("text_cost", "<c=yellow>Earns:<c=/>"); } if (trait_cost(cur_trait) > points) { i_newch.set_data("num_cost", c_red); } else { i_newch.set_data("num_cost", c_white); } i_newch.set_data("num_traits_left", 5 - num_traits); if (num_traits >= 5) { i_newch.set_data("num_traits_left", c_red); } } break; case NCS_PROFESSION: { i_newch.select("list_professions"); i_newch.ref_data("list_professions", &profession_list); std::string prof_name = i_newch.get_str("list_professions"); prof_name = remove_color_tags(prof_name); Profession* cur_prof = PROFESSIONS.lookup_name(prof_name); if (!cur_prof) { debugmsg("No such profession as '%s'!", prof_name.c_str()); return false; } i_newch.set_data("text_description", cur_prof->description); } break; case NCS_DESCRIPTION: i_newch.ref_data("entry_name", &name); if (male) { i_newch.set_data("text_male", "<c=yellow>Male<c=/>"); i_newch.set_data("text_female", "<c=dkgray>Female<c=/>"); } else { i_newch.set_data("text_male", "<c=dkgray>Male<c=/>"); i_newch.set_data("text_female", "<c=yellow>Female<c=/>"); } break; } // switch (cur_screen) } // if (changed_screen) } // Now set up our skills and equipment based on our profession if (!profession) { debugmsg("Character creation finished without a profession!"); return false; } std::vector<Item_type_chance> prof_items = profession->items.item_types; for (int i = 0; i < prof_items.size(); i++) { int count = prof_items[i].number; Item tmp_it(prof_items[i].item); for (int i = 0; i < count; i++) { if (tmp_it.get_item_class() == ITEM_CLASS_CLOTHING) { items_worn.push_back(tmp_it); } else { inventory.push_back(tmp_it); } } } skills = profession->skills; // The "Durable" trait needs to be set up here. if (has_trait(TRAIT_DURABLE)) { for (int i = 0; i < HP_PART_MAX; i++) { current_hp[i] = 115; max_hp[i] = 115; } } // Myopic characters get free glasses if (has_trait(TRAIT_MYOPIC)) { Item_type* glasses = ITEM_TYPES.lookup_name("glasses"); if (!glasses) { debugmsg("No item 'glasses' exists - required for the Myopic trait!"); return false; } Item tmp_it(glasses); items_worn.push_back(tmp_it); } return true; }
void Character::reset_stats() { // Bionic buffs if (has_active_bionic("bio_hydraulics")) mod_str_bonus(20); if (has_bionic("bio_eye_enhancer")) mod_per_bonus(2); if (has_bionic("bio_str_enhancer")) mod_str_bonus(2); if (has_bionic("bio_int_enhancer")) mod_int_bonus(2); if (has_bionic("bio_dex_enhancer")) mod_dex_bonus(2); // Trait / mutation buffs if (has_trait("THICK_SCALES")) { mod_dex_bonus(-2); } if (has_trait("CHITIN2") || has_trait("CHITIN3") || has_trait("CHITIN_FUR3")) { mod_dex_bonus(-1); } if (has_trait("BIRD_EYE")) { mod_per_bonus(4); } if (has_trait("INSECT_ARMS")) { mod_dex_bonus(-2); } if (has_trait("WEBBED")) { mod_dex_bonus(-1); } if (has_trait("ARACHNID_ARMS")) { mod_dex_bonus(-4); } if (has_trait("ARM_TENTACLES") || has_trait("ARM_TENTACLES_4") || has_trait("ARM_TENTACLES_8")) { mod_dex_bonus(1); } // Dodge-related effects if (has_trait("TAIL_LONG")) { mod_dodge_bonus(2); } if (has_trait("TAIL_CATTLE")) { mod_dodge_bonus(1); } if (has_trait("TAIL_RAT")) { mod_dodge_bonus(2); } if (has_trait("TAIL_THICK") && !(has_active_mutation("TAIL_THICK")) ) { mod_dodge_bonus(1); } if (has_trait("TAIL_RAPTOR")) { mod_dodge_bonus(3); } if (has_trait("TAIL_FLUFFY")) { mod_dodge_bonus(4); } if (has_trait("WINGS_BAT")) { mod_dodge_bonus(-3); } if (has_trait("WINGS_BUTTERFLY")) { mod_dodge_bonus(-4); } ///\EFFECT_STR_MAX above 15 decreases Dodge bonus by 1 (NEGATIVE) if (str_max >= 16) {mod_dodge_bonus(-1);} // Penalty if we're huge ///\EFFECT_STR_MAX below 6 increases Dodge bonus by 1 else if (str_max <= 5) {mod_dodge_bonus(1);} // Bonus if we're small nv_cached = false; // Reset our stats to normal levels // Any persistent buffs/debuffs will take place in effects, // player::suffer(), etc. // repopulate the stat fields str_cur = str_max + get_str_bonus(); dex_cur = dex_max + get_dex_bonus(); per_cur = per_max + get_per_bonus(); int_cur = int_max + get_int_bonus(); // Floor for our stats. No stat changes should occur after this! if( dex_cur < 0 ) { dex_cur = 0; } if( str_cur < 0 ) { str_cur = 0; } if( per_cur < 0 ) { per_cur = 0; } if( int_cur < 0 ) { int_cur = 0; } // Does nothing! TODO: Remove Creature::reset_stats(); }
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(25, 80, (TERMY > 25) ? (TERMY-25)/2 : 0, (TERMX > 80) ? (TERMX-80)/2 : 0); int tab = 0, points = 38; 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 = 3; } else points = OPTIONS[OPT_INITIAL_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); 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 < 5); 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_GLASSJAW)) { hp_max[hp_head] = int(hp_max[hp_head] * .85); hp_cur[hp_head] = hp_max[hp_head]; } if (has_trait(PF_SMELLY)) scent = 800; if (has_trait(PF_ANDROID)) { std::map<std::string,bionic_data*>::iterator random_bionic = bionics.begin(); std::advance(random_bionic,rng(0,bionics.size()-1)); add_bionic(random_bionic->first);// Other if (bionics[my_bionics[0].id]->power_cost > 0) { add_bionic(bionic_id(power_source_bionics[rng(0,power_source_bionics.size())])); // Power Source max_power_level = 10; power_level = 10; } else { bionic_id tmpbio; do tmpbio = bionic_id(unpowered_bionics[rng(0, unpowered_bionics.size())]); while (bionics[tmpbio]->power_cost > 0); add_bionic(tmpbio); max_power_level = 0; power_level = 0; } /* CHEATER'S STUFF add_bionic(bionic_id(rng(0, "bio_ethanol"))); // Power Source for (int i = 0; i < 5; i++) add_bionic(bionic_id(rng("bio_memory", max_"bio_start" - 1)));// Other max_power_level = 80; power_level = 80; End of cheatery */ } if (has_trait(PF_MARTIAL_ARTS)) { itype_id ma_type; do { int choice = menu("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); } ret_null = item(g->itypes["null"], 0); if (!styles.empty()) weapon = item(g->itypes[ styles[0] ], 0, ':'); else weapon = item(g->itypes["null"], 0); 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) { item tmp = item(g->itypes.at(*iter), 0, 'a' + worn.size()); if (tmp.is_armor()) { if (tmp.has_flag(IF_VARSIZE)) tmp.item_flags |= mfb(IF_FIT); worn.push_back(tmp); } else { inv.push_back(tmp); } // if we start with drugs, need to start strongly addicted, too if (tmp.is_food()) { it_comest *comest = dynamic_cast<it_comest*>(tmp.type); if (comest->add != ADD_NULL) { addiction add(comest->add, 10); g->u.addictions.push_back(add); } } } // The near-sighted get to start with glasses. if (has_trait(PF_MYOPIC)) { tmp = item(g->itypes["glasses_eye"], 0, 'a' + worn.size()); worn.push_back(tmp); } // And the far-sighted get to start with reading glasses. if (has_trait(PF_HYPEROPIC)) { tmp = item(g->itypes["glasses_reading"], 0, 'a' + worn.size()); worn.push_back(tmp); } // Likewise, the asthmatic start with their medication. if (has_trait(PF_ASTHMA)) { tmp = item(g->itypes["inhaler"], 0, 'a' + worn.size()); inv.push_back(tmp); } // Basic starter gear, added independently of profession. tmp = item(g->itypes["pockknife"], 0,'a' + worn.size()); inv.push_back(tmp); tmp = item(g->itypes["matches"], 0,'a' + worn.size()); inv.push_back(tmp); // make sure we have no mutations for (int i = 0; i < PF_MAX2; i++) my_mutations[i] = false; return true; }
// This must be called when any of the following change: // - effects // - bionics // - traits // - underwater // - clothes // With the exception of clothes, all changes to these character attributes must // occur through a function in this class which calls this function. Clothes are // typically added/removed with wear() and takeoff(), but direct access to the // 'wears' vector is still allowed due to refactor exhaustion. void Character::recalc_sight_limits() { sight_max = 9999; vision_mode_cache.reset(); // Set sight_max. if (has_effect("blind") || worn_with_flag("BLIND") || has_active_bionic("bio_blindfold")) { sight_max = 0; } else if( has_effect("boomered") && (!(has_trait("PER_SLIME_OK"))) ) { sight_max = 1; vision_mode_cache.set( BOOMERED ); } else if (has_effect("in_pit") || (underwater && !has_bionic("bio_membrane") && !has_trait("MEMBRANE") && !worn_with_flag("SWIM_GOGGLES") && !has_trait("CEPH_EYES") && !has_trait("PER_SLIME_OK") ) ) { sight_max = 1; } else if (has_active_mutation("SHELL2")) { // You can kinda see out a bit. sight_max = 2; } else if ( (has_trait("MYOPIC") || has_trait("URSINE_EYE")) && !is_wearing("glasses_eye") && !is_wearing("glasses_monocle") && !is_wearing("glasses_bifocal") && !has_effect("contacts")) { sight_max = 4; } else if (has_trait("PER_SLIME")) { sight_max = 6; } else if( has_effect( "darkness" ) ) { vision_mode_cache.set( DARKNESS ); sight_max = 10; } // Debug-only NV, by vache's request if( has_trait("DEBUG_NIGHTVISION") ) { vision_mode_cache.set( DEBUG_NIGHTVISION ); } if( has_nv() ) { vision_mode_cache.set( NV_GOGGLES ); } if( has_active_mutation("NIGHTVISION3") || is_wearing("rm13_armor_on") ) { vision_mode_cache.set( NIGHTVISION_3 ); } if( has_active_mutation("ELFA_FNV") ) { vision_mode_cache.set( FULL_ELFA_VISION ); } if( has_active_mutation("CEPH_VISION") ) { vision_mode_cache.set( CEPH_VISION ); } if (has_active_mutation("ELFA_NV")) { vision_mode_cache.set( ELFA_VISION ); } if( has_active_mutation("NIGHTVISION2") ) { vision_mode_cache.set( NIGHTVISION_2 ); } if( has_active_mutation("FEL_NV") ) { vision_mode_cache.set( FELINE_VISION ); } if( has_active_mutation("URSINE_EYE") ) { vision_mode_cache.set( URSINE_VISION ); } if (has_active_mutation("NIGHTVISION")) { vision_mode_cache.set(NIGHTVISION_1); } if( has_trait("BIRD_EYE") ) { vision_mode_cache.set( BIRD_EYE); } }
void Character::recalc_hp() { int new_max_hp[num_hp_parts]; for( auto &elem : new_max_hp ) { ///\EFFECT_STR_MAX increases base hp elem = 60 + str_max * 3; if (has_trait("HUGE")) { // Bad-Huge doesn't quite have the cardio/skeletal/etc to support the mass, // so no HP bonus from the ST above/beyond that from Large elem -= 6; } // You lose half the HP you'd expect from BENDY mutations. Your gelatinous // structure can help with that, a bit. if (has_trait("BENDY2")) { elem += 3; } if (has_trait("BENDY3")) { elem += 6; } // Only the most extreme applies. if (has_trait("TOUGH")) { elem *= 1.2; } else if (has_trait("TOUGH2")) { elem *= 1.3; } else if (has_trait("TOUGH3")) { elem *= 1.4; } else if (has_trait("FLIMSY")) { elem *= .75; } else if (has_trait("FLIMSY2")) { elem *= .5; } else if (has_trait("FLIMSY3")) { elem *= .25; } // Mutated toughness stacks with starting, by design. if (has_trait("MUT_TOUGH")) { elem *= 1.2; } else if (has_trait("MUT_TOUGH2")) { elem *= 1.3; } else if (has_trait("MUT_TOUGH3")) { elem *= 1.4; } } if( has_trait( "GLASSJAW" ) ) { new_max_hp[hp_head] *= 0.8; } for( int i = 0; i < num_hp_parts; i++ ) { hp_cur[i] *= (float)new_max_hp[i] / (float)hp_max[i]; hp_max[i] = new_max_hp[i]; } }
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(); }
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::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::create(game *g, character_type type, std::string tempname) { weapon = item(g->itypes[0], 0); int iMaxX = (g->VIEWX < 12) ? 80 : (g->VIEWX*2)+56; int iMaxY = (g->VIEWY < 12) ? 25 : (g->VIEWY*2)+1; WINDOW* w = newwin(25, 80, (iMaxY > 25) ? (iMaxY-25)/2 : 0, (iMaxX > 80) ? (iMaxX-80)/2 : 0); int tab = 0, points = 38; 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 = 3; } else points = OPTIONS[OPT_INITIAL_POINTS]; do { werase(w); wrefresh(w); switch (tab) { case 0: tab += set_stats (w, this, points); break; case 1: tab += set_traits (w, this, points); break; case 2: tab += set_skills (w, this, points); break; case 3: tab += set_description(w, this, points); break; } } while (tab >= 0 && tab < 4); 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_GLASSJAW)) { hp_max[hp_head] = int(hp_max[hp_head] * .85); hp_cur[hp_head] = hp_max[hp_head]; } if (has_trait(PF_SMELLY)) scent = 800; if (has_trait(PF_ANDROID)) { add_bionic(bionic_id(rng(bio_memory, max_bio_start - 1)));// Other if (bionics[my_bionics[0].id].power_cost > 0) { add_bionic(bionic_id(rng(1, bio_ethanol))); // Power Source max_power_level = 10; power_level = 10; } else { bionic_id tmpbio; do tmpbio = bionic_id(rng(bio_ethanol + 1, bio_armor_legs)); while (bionics[tmpbio].power_cost > 0); add_bionic(tmpbio); max_power_level = 0; power_level = 0; } /* CHEATER'S STUFF add_bionic(bionic_id(rng(0, bio_ethanol))); // Power Source for (int i = 0; i < 5; i++) add_bionic(bionic_id(rng(bio_memory, max_bio_start - 1)));// Other max_power_level = 80; power_level = 80; End of cheatery */ } if (has_trait(PF_MARTIAL_ARTS)) { itype_id ma_type; do { int choice = menu("Pick your style:", "Karate", "Judo", "Aikido", "Tai Chi", "Taekwando", NULL); if (choice == 1) ma_type = itm_style_karate; if (choice == 2) ma_type = itm_style_judo; if (choice == 3) ma_type = itm_style_aikido; if (choice == 4) ma_type = itm_style_tai_chi; if (choice == 5) ma_type = itm_style_taekwando; 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); } ret_null = item(g->itypes[0], 0); if (!styles.empty()) weapon = item(g->itypes[ styles[0] ], 0, ':'); else weapon = item(g->itypes[0], 0); // Nice to start out less than naked. item tmp(g->itypes[itm_jeans_fit], 0, 'a'); worn.push_back(tmp); tmp = item(g->itypes[itm_tshirt_fit], 0, 'b'); worn.push_back(tmp); tmp = item(g->itypes[itm_sneakers_fit], 0, 'c'); worn.push_back(tmp); // The near-sighted get to start with glasses. if (has_trait(PF_MYOPIC)) { tmp = item(g->itypes[itm_glasses_eye], 0, 'd'); worn.push_back(tmp); } // Likewise, the asthmatic start with their medication. if (has_trait(PF_ASTHMA)) { tmp = item(g->itypes[itm_inhaler], 0, 'a' + worn.size()); inv.push_back(tmp); } // Basic starter gear, added independently of profession. tmp = item(g->itypes[itm_pockknife], 0,'a' + worn.size()); inv.push_back(tmp); tmp = item(g->itypes[itm_lighter], 0,'a' + worn.size()); inv.push_back(tmp); // make sure we have no mutations for (int i = 0; i < PF_MAX2; i++) my_mutations[i] = false; return true; }