int player::dodge(game *g) { if (has_disease(DI_SLEEP) || has_disease(DI_LYING_DOWN)) return 0; if (activity.type != ACT_NULL) return 0; int ret = 4 + (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); 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 hyuuge else if (str_max <= 5) ret++; // Bonus if we're small if (dodges_left <= 0) { // We already dodged this turn if (rng(1, skillLevel("dodge") + dex_cur + 15) <= skillLevel("dodge") + dex_cur) ret = rng(0, ret); else ret = 0; } dodges_left--; // If we're over our cap, average it with our cap if (ret > int(dex_cur / 2) + skillLevel("dodge") * 2) ret = ( ret + int(dex_cur / 2) + skillLevel("dodge") * 2 ) / 2; return ret; }
bool player::install_bionics(it_bionic *type) { if (type == NULL) { debugmsg("Tried to install NULL bionic"); return false; } if (bionics.count(type->id) == 0) { popup("invalid / unknown bionic id %s", type->id.c_str()); return false; } if (has_bionic(type->id)) { if (!(type->id == "bio_power_storage" || type->id == "bio_power_storage_mkII")) { popup(_("You have already installed this bionic.")); return false; } } int chance_of_success = bionic_manip_cos(int_cur, skillLevel("electronics"), skillLevel("firstaid"), skillLevel("mechanics"), type->difficulty); if (!query_yn( _("WARNING: %i percent chance of genetic damage, blood loss, or damage to existing bionics! Install anyway?"), 100 - chance_of_success)) { return false; } int pow_up = 0; if (type->id == "bio_power_storage" || type->id == "bio_power_storage_mkII") { pow_up = BATTERY_AMOUNT; if (type->id == "bio_power_storage_mkII") { pow_up = 250; } } practice( "electronics", int((100 - chance_of_success) * 1.5) ); practice( "firstaid", int((100 - chance_of_success) * 1.0) ); practice( "mechanics", int((100 - chance_of_success) * 0.5) ); int success = chance_of_success - rng(1, 100); if (success > 0) { add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."), pgettext("memorial_female", "Installed bionic: %s."), bionics[type->id]->name.c_str()); if (pow_up) { max_power_level += pow_up; add_msg_if_player(m_good, _("Increased storage capacity by %i"), pow_up); } else { add_msg(m_good, _("Successfully installed %s."), bionics[type->id]->name.c_str()); add_bionic(type->id); } } else { add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."), pgettext("memorial_female", "Installed bionic: %s."), bionics[type->id]->name.c_str()); bionics_install_failure(this, type, success); } g->refresh_all(); return true; }
// utility functions for projectile_attack double player::get_weapon_dispersion(item *weapon) { int weapon_skill_level = 0; if(weapon->is_gunmod()) { it_gunmod* firing = dynamic_cast<it_gunmod *>(weapon->type); weapon_skill_level = skillLevel(firing->skill_used); } else { it_gun* firing = dynamic_cast<it_gun *>(weapon->type); weapon_skill_level = skillLevel(firing->skill_used); } double dispersion = 0.; // Measured in quarter-degrees. // Up to 0.75 degrees for each skill point < 8. if (weapon_skill_level < 8) { dispersion += rng(0, 3 * (8 - weapon_skill_level)); } // Up to 0.25 deg per each skill point < 9. if (skillLevel("gun") < 9) { dispersion += rng(0, 9 - skillLevel("gun")); } dispersion += rng(0, ranged_dex_mod()); dispersion += rng(0, ranged_per_mod()); dispersion += rng(0, 2 * encumb(bp_arms)) + rng(0, 4 * encumb(bp_eyes)); dispersion += rng(0, weapon->curammo->dispersion); // item::dispersion() doesn't support gunmods. dispersion += rng(0, weapon->dispersion()); int adj_recoil = recoil + driving_recoil; dispersion += rng(int(adj_recoil / 4), adj_recoil); // this is what the total bonus USED to look like // rng(0,x) on each term in the sum // 3 * skill + skill + 2 * dex + 2 * per // - 2*p.encumb(bp_arms) - 4*p.encumb(bp_eyes) - 5/8 * recoil // old targeting bionic suddenly went from 0.8 to 0.65 when LONG_RANGE was // crossed, so increasing range by 1 would actually increase accuracy by a // lot. This is kind of a compromise if (has_bionic("bio_targeting")) dispersion *= 0.75; if ((is_underwater() && !weapon->has_flag("UNDERWATER_GUN")) || // Range is effectively four times longer when shooting unflagged guns underwater. (!is_underwater() && weapon->has_flag("UNDERWATER_GUN"))) { // Range is effectively four times longer when shooting flagged guns out of water. dispersion *= 4; } if (dispersion < 0) { return 0; } return dispersion; }
int player::roll_stuck_penalty(monster *z, bool stabbing) { int ret = 0; int basharm = (z == NULL ? 6 : z->armor_bash()), cutarm = (z == NULL ? 6 : z->armor_cut()); if (stabbing) ret = weapon.damage_cut() * 3 + basharm * 3 + cutarm * 3 - dice(skillLevel("stabbing"), 10); else ret = weapon.damage_cut() * 4 + basharm * 5 + cutarm * 4 - dice(skillLevel("cutting"), 10); if (ret >= weapon.damage_cut() * 10) return weapon.damage_cut() * 10; return (ret < 0 ? 0 : ret); }
bool player::can_arm_block() { if (martialarts[style_selected].arm_block < 0) return false; if (skillLevel("unarmed") >= martialarts[style_selected].arm_block && (hp_cur[hp_arm_l] > 0 || hp_cur[hp_arm_r] > 0)) return true; else return false; }
SkillLevel& Character::skillLevel(const skill_id &ident) { if( !ident ) { static SkillLevel none; none.level( 0 ); return none; } return skillLevel( &ident.obj() ); }
int player::roll_stab_damage(monster *z, bool crit) { int ret = 0; int z_armor = (z == NULL ? 0 : z->armor_cut() - 3 * skillLevel("stabbing")); if (crit) z_armor /= 3; if (z_armor < 0) z_armor = 0; if (unarmed_attack() && !wearing_something_on(bp_hands)) { ret = 0 - z_armor; if (has_trait(PF_CLAWS)) ret += 6; if (has_trait(PF_NAILS) && z_armor == 0) ret++; if (has_trait(PF_THORNS)) ret += 4; } else if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) ret = int((weapon.damage_cut() - z_armor) / 4); else return 0; // Can't stab at all! if (z != NULL && z->speed > 100) { // Bonus against fast monsters int speed_min = (z->speed - 100) / 10, speed_max = (z->speed - 100) / 5; int speed_dam = rng(speed_min, speed_max); if (speed_dam > ret * 2) speed_dam = ret * 2; if (speed_dam > 0) ret += speed_dam; } if (ret <= 0) return 0; // No negative stabbing! if (crit) { int multiplier = double( 1.0 + double(skillLevel("stabbing") / 5) ); if (multiplier > 2.5) multiplier = 2.5; ret *= multiplier; } return ret; }
bool player::can_arm_block() { martialart ma = martialarts[style_selected]; if (ma.arm_block < 0 || !(ma.arm_block_with_bio_armor_arms && has_bionic("bio_armor_arms"))) return false; int unarmed_skill = has_active_bionic("bio_cqb") ? 5 : (int)skillLevel("unarmed"); if (unarmed_skill < ma.arm_block && !(ma.arm_block_with_bio_armor_arms && has_bionic("bio_armor_arms"))) return false; if (hp_cur[hp_arm_l] > 0 || hp_cur[hp_arm_r] > 0) return true; else return false; }
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; }
bool player::can_arm_block() { martialart ma = martialarts[style_selected]; int unarmed_skill = has_active_bionic("bio_cqb") ? 5 : (int)skillLevel("unarmed"); // Success conditions. if (hp_cur[hp_arm_l] > 0 || hp_cur[hp_arm_r] > 0) { if( unarmed_skill >= ma.arm_block ) { return true; } else if( ma.arm_block_with_bio_armor_arms && has_bionic("bio_armor_arms") ) { return true; } } // if not above, can't block. return false; }
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; }
std::vector<special_attack> player::mutation_attacks(monster *z, player *p) { std::vector<special_attack> ret; if (z == NULL && p == NULL) return ret; bool mon = (z != NULL); bool is_u = (!is_npc());// Affects how we'll display messages std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); std::string target = (mon ? "the " + z->name() : p->name); std::stringstream text; if (has_trait(PF_FANGS) && !wearing_something_on(bp_mouth) && one_in(20 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " sink" << (is_u ? " " : "s ") << your << " fangs into " << target << "!"; tmp.text = text.str(); tmp.stab = 20; ret.push_back(tmp); } if (has_trait(PF_MANDIBLES) && one_in(22 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " slice" << (is_u ? " " : "s ") << target << " with " << your << " mandibles!"; tmp.text = text.str(); tmp.cut = 12; ret.push_back(tmp); } if (has_trait(PF_BEAK) && one_in(15 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " peck" << (is_u ? " " : "s ") << target << "!"; tmp.text = text.str(); tmp.stab = 15; ret.push_back(tmp); } if (has_trait(PF_HOOVES) && one_in(25 - dex_cur - 2 * skillLevel("unarmed"))) { special_attack tmp; text << You << " kick" << (is_u ? " " : "s ") << target << " with " << your << " hooves!"; tmp.text = text.str(); tmp.bash = str_cur * 3; if (tmp.bash > 40) tmp.bash = 40; ret.push_back(tmp); } if (has_trait(PF_HORNS) && one_in(20 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " headbutt" << (is_u ? " " : "s ") << target << " with " << your << " horns!"; tmp.text = text.str(); tmp.bash = 3; tmp.stab = 3; ret.push_back(tmp); } if (has_trait(PF_HORNS_CURLED) && one_in(20 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " headbutt" << (is_u ? " " : "s ") << target << " with " << your << " curled horns!"; tmp.text = text.str(); tmp.bash = 14; ret.push_back(tmp); } if (has_trait(PF_HORNS_POINTED) && one_in(22 - dex_cur - skillLevel("unarmed"))){ special_attack tmp; text << You << " stab" << (is_u ? " " : "s ") << target << " with " << your << " pointed horns!"; tmp.text = text.str(); tmp.stab = 24; ret.push_back(tmp); } if (has_trait(PF_ANTLERS) && one_in(20 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text << You << " butt" << (is_u ? " " : "s ") << target << " with " << your << " antlers!"; tmp.text = text.str(); tmp.bash = 4; ret.push_back(tmp); } if (has_trait(PF_TAIL_STING) && one_in(3) && one_in(10 - dex_cur)) { special_attack tmp; text << You << " sting" << (is_u ? " " : "s ") << target << " with " << your << " tail!"; tmp.text = text.str(); tmp.stab = 20; ret.push_back(tmp); } if (has_trait(PF_TAIL_CLUB) && one_in(3) && one_in(10 - dex_cur)) { special_attack tmp; text << You << " hit" << (is_u ? " " : "s ") << target << " with " << your << " tail!"; tmp.text = text.str(); tmp.bash = 18; ret.push_back(tmp); } if (has_trait(PF_ARM_TENTACLES) || has_trait(PF_ARM_TENTACLES_4) || has_trait(PF_ARM_TENTACLES_8)) { int num_attacks = 1; if (has_trait(PF_ARM_TENTACLES_4)) num_attacks = 3; if (has_trait(PF_ARM_TENTACLES_8)) num_attacks = 7; if (weapon.is_two_handed(this)) num_attacks--; for (int i = 0; i < num_attacks; i++) { if (one_in(18 - dex_cur - skillLevel("unarmed"))) { special_attack tmp; text.str(""); text << You << " slap" << (is_u ? " " : "s ") << target << " with " << your << " tentacle!"; tmp.text = text.str(); tmp.bash = str_cur / 2; ret.push_back(tmp); } } } return ret; }
int player::hit_roll() { int stat = dex_cur; // Some martial arts use something else to determine hits! if(weapon.typeId() == "style_tiger"){ stat = (str_cur * 2 + dex_cur) / 3; } else if(weapon.typeId() == "style_leopard"){ stat = (per_cur + int_cur + dex_cur * 2) / 4; } else if(weapon.typeId() == "style_snake"){ stat = (per_cur + dex_cur) / 2; } int numdice = base_to_hit(stat) + weapon.type->m_to_hit + disease_intensity(DI_ATTACK_BOOST); int sides = 10 - encumb(bp_torso); int best_bonus = 0; if (sides < 2) sides = 2; // Are we unarmed? if (unarmed_attack()) { best_bonus = skillLevel("unarmed"); if (skillLevel("unarmed") > 4) best_bonus += skillLevel("unarmed") - 4; // Extra bonus for high levels } // Using a bashing weapon? if (weapon.is_bashing_weapon()) { int bash_bonus = int(skillLevel("bashing") / 3); if (bash_bonus > best_bonus) best_bonus = bash_bonus; } // Using a cutting weapon? if (weapon.is_cutting_weapon()) { int cut_bonus = int(skillLevel("cutting") / 2); if (cut_bonus > best_bonus) best_bonus = cut_bonus; } // Using a spear? if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) { int stab_bonus = int(skillLevel("stabbing") / 2); if (stab_bonus > best_bonus) best_bonus = stab_bonus; } numdice += best_bonus; // Use whichever bonus is best. // Drunken master makes us hit better if (has_trait(PF_DRUNKEN)) { if (unarmed_attack()) numdice += int(disease_level(DI_DRUNK) / 300); else numdice += int(disease_level(DI_DRUNK) / 400); } if (numdice < 1) { numdice = 1; sides = 8 - encumb(bp_torso); } return dice(numdice, sides); }
int player::roll_bash_damage(monster *z, bool crit) { int ret = 0; int stat = str_cur; // Which stat determines damage? int skill = skillLevel("bashing"); // Which skill determines damage? if (unarmed_attack()) skill = skillLevel("unarmed"); if(weapon.typeId() =="style_crane"){ stat = (dex_cur * 2 + str_cur) / 3; } else if(weapon.typeId() == "style_snake"){ stat = int(str_cur + per_cur) / 2; } else if(weapon.typeId() == "style_dragon"){ stat = int(str_cur + int_cur) / 2; } ret = base_damage(true, stat); // Drunken Master damage bonuses if (has_trait(PF_DRUNKEN) && has_disease(DI_DRUNK)) { // Remember, a single drink gives 600 levels of DI_DRUNK int mindrunk, maxdrunk; if (unarmed_attack()) { mindrunk = disease_level(DI_DRUNK) / 600; maxdrunk = disease_level(DI_DRUNK) / 250; } else { mindrunk = disease_level(DI_DRUNK) / 900; maxdrunk = disease_level(DI_DRUNK) / 400; } ret += rng(mindrunk, maxdrunk); } int bash_dam = int(stat / 2) + weapon.damage_bash(), bash_cap = 5 + stat + skill; if (unarmed_attack()) bash_dam = rng(0, int(stat / 2) + skillLevel("unarmed")); if (crit) { bash_dam *= 1.5; bash_cap *= 2; } if (bash_dam > bash_cap)// Cap for weak characters bash_dam = (bash_cap * 3 + bash_dam) / 4; if (z != NULL && z->has_flag(MF_PLASTIC)) bash_dam /= rng(2, 4); int bash_min = bash_dam / 4; bash_dam = rng(bash_min, bash_dam); if (bash_dam < skill + int(stat / 2)) bash_dam = rng(bash_dam, skill + int(stat / 2)); ret += bash_dam; ret += disease_intensity(DI_DAMAGE_BOOST); // Finally, extra crit effects if (crit) { ret += int(stat / 2); ret += skill; if (z != NULL) ret -= z->armor_bash() / 2; } else if (z != NULL) ret -= z->armor_bash(); return (ret < 0 ? 0 : ret); }
void player::json_load_common_variables(JsonObject & data) { JsonArray parray; // todo/maybe: // std::map<std::string, int*> strmap_common_variables; // void player::init_strmap_common_variables() { // strmap_common_variables["posx"]=&posx; // + all this below and in save_common_variables // } // load: // for(std::map<std::string, int*>::iterator it... // data.read(it->first,it->second); // save: // for(... // json.member( it->first, it->second ); if(!data.read("posx",posx) ) { // uh-oh. debugmsg("BAD PLAYER/NPC JSON: no 'posx'?"); } data.read("posy",posy); data.read("str_cur",str_cur); data.read("str_max",str_max); data.read("dex_cur",dex_cur); data.read("dex_max",dex_max); data.read("int_cur",int_cur); data.read("int_max",int_max); data.read("per_cur",per_cur); data.read("per_max",per_max); data.read("hunger",hunger); data.read("thirst",thirst); data.read("fatigue",fatigue); data.read("stim",stim); data.read("pain",pain); data.read("pkill",pkill); data.read("radiation",radiation); data.read("scent",scent); data.read("moves",moves); data.read("dodges_left",num_dodges); data.read("underwater",underwater); data.read("oxygen",oxygen); data.read("male",male); data.read("cash",cash); data.read("recoil",recoil); parray = data.get_array("hp_cur"); if ( parray.size() == num_hp_parts ) { for(int i=0; i < num_hp_parts; i++) { hp_cur[i] = parray.get_int(i); } } else { debugmsg("Error, incompatible hp_cur in save file '%s'",parray.str().c_str()); } parray = data.get_array("hp_max"); if ( parray.size() == num_hp_parts ) { for(int i=0; i < num_hp_parts; i++) { hp_max[i] = parray.get_int(i); } } else { debugmsg("Error, incompatible hp_max in save file '%s'",parray.str().c_str()); } data.read("power_level",power_level); data.read("max_power_level",max_power_level); data.read("traits",my_traits); if (data.has_object("skills")) { JsonObject pmap = data.get_object("skills"); for (std::vector<Skill*>::iterator aSkill = Skill::skills.begin(); aSkill != Skill::skills.end(); ++aSkill) { if ( pmap.has_object( (*aSkill)->ident() ) ) { pmap.read( (*aSkill)->ident(), skillLevel(*aSkill) ); } else { debugmsg("Load (%s) Missing skill %s","",(*aSkill)->ident().c_str() ); } } } else { debugmsg("Skills[] no bueno"); } data.read("ma_styles",ma_styles); data.read("illness",illness); data.read("effects",effects); data.read("addictions",addictions); data.read("my_bionics",my_bionics); }
void player::activate_mutation( const std::string &mut ) { const auto &mdata = mutation_branch::get( mut ); auto &tdata = my_mutations[mut]; int cost = mdata.cost; // You can take yourself halfway to Near Death levels of hunger/thirst. // Fatigue can go to Exhausted. if ((mdata.hunger && get_hunger() >= 700) || (mdata.thirst && get_thirst() >= 260) || (mdata.fatigue && get_fatigue() >= EXHAUSTED)) { // Insufficient Foo to *maintain* operation is handled in player::suffer add_msg_if_player(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str()); return; } if (tdata.powered && tdata.charge > 0) { // Already-on units just lose a bit of charge tdata.charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (mdata.cooldown > 0) { tdata.charge = mdata.cooldown - 1; } if (mdata.hunger){ mod_hunger(cost); } if (mdata.thirst){ mod_thirst(cost); } if (mdata.fatigue){ mod_fatigue(cost); } tdata.powered = true; // Handle stat changes from activation apply_mods(mut, true); recalc_sight_limits(); } if( mut == "WEB_WEAVER" ) { g->m.add_field(pos(), fd_web, 1, 0); add_msg_if_player(_("You start spinning web with your spinnerets!")); } else if (mut == "BURROW"){ if( is_underwater() ) { add_msg_if_player(m_info, _("You can't do that while underwater.")); tdata.powered = false; return; } tripoint dirp; if (!choose_adjacent(_("Burrow where?"), dirp)) { tdata.powered = false; return; } if( dirp == pos() ) { add_msg_if_player(_("You've got places to go and critters to beat.")); add_msg_if_player(_("Let the lesser folks eat their hearts out.")); tdata.powered = false; return; } int turns; if (g->m.is_bashable(dirp) && g->m.has_flag("SUPPORTS_ROOF", dirp) && g->m.ter(dirp) != t_tree) { // Takes about 100 minutes (not quite two hours) base time. // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast. ///\EFFECT_CARPENTRY speeds up burrowing turns = (100000 - 5000 * skillLevel( skill_id( "carpentry" ) )); } else if (g->m.move_cost(dirp) == 2 && g->get_levz() == 0 && g->m.ter(dirp) != t_dirt && g->m.ter(dirp) != t_grass) { turns = 18000; } else { add_msg_if_player(m_info, _("You can't burrow there.")); tdata.powered = false; return; } assign_activity(ACT_BURROW, turns, -1, 0); activity.placement = dirp; add_msg_if_player(_("You tear into the %s with your teeth and claws."), g->m.tername(dirp).c_str()); tdata.powered = false; return; // handled when the activity finishes } else if (mut == "SLIMESPAWNER") { std::vector<tripoint> valid; for (int x = posx() - 1; x <= posx() + 1; x++) { for (int y = posy() - 1; y <= posy() + 1; y++) { tripoint dest(x, y, posz()); if (g->is_empty(dest)) { valid.push_back( dest ); } } } // Oops, no room to divide! if (valid.size() == 0) { add_msg_if_player(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!")); tdata.powered = false; return; } add_msg_if_player(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!")); int numslime = 1; for (int i = 0; i < numslime && !valid.empty(); i++) { const tripoint target = random_entry_removed( valid ); if (g->summon_mon(mtype_id( "mon_player_blob" ), target)) { monster *slime = g->monster_at( target ); slime->friendly = -1; } } if (one_in(3)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("wow! you look just like me! we should look out for each other!")); } else if (one_in(2)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("come on, big me, let's go!")); } else { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("we're a team, we've got this!")); } tdata.powered = false; return; } else if ((mut == "NAUSEA") || (mut == "VOMITOUS") ){ vomit(); tdata.powered = false; return; } else if (mut == "M_FERTILE"){ spores(); tdata.powered = false; return; } else if (mut == "M_BLOOM"){ blossoms(); tdata.powered = false; return; } else if (mut == "VINES3"){ item newit( "vine_30", calendar::turn ); if (!can_pickVolume(newit.volume())) { //Accounts for result_mult add_msg_if_player(_("You detach a vine but don't have room to carry it, so you drop it.")); g->m.add_item_or_charges(pos(), newit); } else if (!can_pickWeight(newit.weight(), !OPTIONS["DANGEROUS_PICKUPS"])) { add_msg_if_player(_("Your freshly-detached vine is too heavy to carry, so you drop it.")); g->m.add_item_or_charges(pos(), newit); } else { inv.assign_empty_invlet(newit); newit = i_add(newit); add_msg_if_player(m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str()); } tdata.powered = false; return; } else if( mut == "SELFAWARE" ) { print_health(); tdata.powered = false; return; } }
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; }
technique_id player::pick_defensive_technique(game *g, monster *z, player *p) { if (blocks_left == 0) return TEC_NULL; int foe_melee_skill = 0; if (z != NULL) foe_melee_skill = z->type->melee_skill; else if (p != NULL) foe_melee_skill = p->dex_cur + p->skillLevel("melee"); int foe_dodge = 0; if (z != NULL) foe_dodge = z->dodge_roll(); else if (p != NULL) foe_dodge = p->dodge_roll(g); int foe_size = 0; if (z) foe_size = 4 + z->type->size * 4; else if (p) { foe_size = 12; if (p->str_max <= 5) foe_size -= 3; if (p->str_max >= 12) foe_size += 3; } blocks_left--; if (weapon.has_technique(TEC_WBLOCK_3) && dice(dex_cur + skillLevel("melee"), 12) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_3; if (weapon.has_technique(TEC_WBLOCK_2) && dice(dex_cur + skillLevel("melee"), 6) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_2; if (weapon.has_technique(TEC_WBLOCK_1) && dice(dex_cur + skillLevel("melee"), 3) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_1; if (weapon.has_technique(TEC_DEF_DISARM, this) && z == NULL && p->weapon.typeId() != "null" && !p->weapon.has_flag(IF_UNARMED_WEAPON) && dice( dex_cur + skillLevel("unarmed"), 8) > dice(p->dex_cur + p->skillLevel("melee"), 10)) return TEC_DEF_DISARM; if (weapon.has_technique(TEC_DEF_THROW, this) && str_cur + skillLevel("melee") >= foe_size + rng(-4, 4) && hit_roll() > rng(1, 5) + foe_dodge && !one_in(3)) return TEC_DEF_THROW; if (weapon.has_technique(TEC_COUNTER, this) && hit_roll() > rng(1, 10) + foe_dodge && !one_in(3)) return TEC_COUNTER; if (weapon.has_technique(TEC_BLOCK_LEGS, this) && (hp_cur[hp_leg_l] >= 20 || hp_cur[hp_leg_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 13) > dice(8 + foe_melee_skill, 10)) return TEC_BLOCK_LEGS; if (weapon.has_technique(TEC_BLOCK, this) && (hp_cur[hp_arm_l] >= 20 || hp_cur[hp_arm_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 16) > dice(6 + foe_melee_skill, 10)) return TEC_BLOCK; blocks_left++; // We didn't use any blocks, so give it back! return TEC_NULL; }
void player::melee_special_effects(game *g, monster *z, player *p, bool crit, int &bash_dam, int &cut_dam, int &stab_dam) { if (z == NULL && p == NULL) return; bool mon = (z != NULL); int junk; bool is_u = (!is_npc()); bool can_see = (is_u || g->u_see(posx, posy, junk)); std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : name + "'s"); std::string target = (mon ? "the " + z->name() : (p->is_npc() ? p->name : "you")); std::string target_possessive = (mon ? "the " + z->name() + "'s" : (p->is_npc() ? p->name + "'s" : your)); int tarposx = (mon ? z->posx : p->posx), tarposy = (mon ? z->posy : p->posy); // Bashing effecs if (mon) z->moves -= rng(0, bash_dam * 2); else p->moves -= rng(0, bash_dam * 2); // Bashing crit if (crit && !unarmed_attack()) { int turns_stunned = int(bash_dam / 20) + rng(0, int(skillLevel("bashing") / 2)); if (turns_stunned > 6) turns_stunned = 6; if (turns_stunned > 0) { if (mon) z->add_effect(ME_STUNNED, turns_stunned); else p->add_disease(DI_STUNNED, 1 + turns_stunned / 2, g); } } // Stabbing effects int stab_moves = rng(stab_dam / 2, stab_dam * 1.5); if (crit) stab_moves *= 1.5; if (stab_moves >= 150) { if (can_see) g->add_msg("%s force%s the %s to the ground!", You.c_str(), (is_u ? "" : "s"), target.c_str()); if (mon) { z->add_effect(ME_DOWNED, 1); z->moves -= stab_moves / 2; } else { p->add_disease(DI_DOWNED, 1, g); p->moves -= stab_moves / 2; } } else if (mon) z->moves -= stab_moves; else p->moves -= stab_moves; // Bonus attacks! bool shock_them = (has_bionic("bio_shock") && power_level >= 2 && unarmed_attack() && (!mon || !z->has_flag(MF_ELECTRIC)) && one_in(3)); bool drain_them = (has_bionic("bio_heat_absorb") && power_level >= 1 && !is_armed() && (!mon || z->has_flag(MF_WARM))); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time if (shock_them) { power_level -= 2; int shock = rng(2, 5); if (mon) { z->hurt( shock * rng(1, 3) ); z->moves -= shock * 180; if (can_see) g->add_msg("%s shock%s %s!", You.c_str(), (is_u ? "" : "s"), target.c_str()); } else { p->hurt(g, bp_torso, 0, shock * rng(1, 3)); p->moves -= shock * 80; } } if (drain_them) { charge_power(rng(0, 4)); if (can_see) g->add_msg("%s drain%s %s body heat!", You.c_str(), (is_u ? "" : "s"), target_possessive.c_str()); if (mon) { z->moves -= rng(80, 120); z->speed -= rng(4, 6); } else p->moves -= rng(80, 120); } bool conductive = !wearing_something_on(bp_hands) && weapon.conductive(); if (mon && z->has_flag(MF_ELECTRIC) && conductive) { hurtall(rng(0, 1)); moves -= rng(0, 50); if (is_u) g->add_msg("Contact with the %s shocks you!", z->name().c_str()); } // Glass weapons shatter sometimes if (weapon.made_of(GLASS) && rng(0, weapon.volume() + 8) < weapon.volume() + str_cur) { if (can_see) g->add_msg("%s %s shatters!", Your.c_str(), weapon.tname(g).c_str()); g->sound(posx, posy, 16, ""); // Dump its contents on the ground for (int i = 0; i < weapon.contents.size(); i++) g->m.add_item(posx, posy, weapon.contents[i]); hit(g, bp_arms, 1, 0, rng(0, weapon.volume() * 2));// Take damage if (weapon.is_two_handed(this))// Hurt left arm too, if it was big hit(g, bp_arms, 0, 0, rng(0, weapon.volume())); cut_dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra remove_weapon(); } // Getting your weapon stuck int cutting_penalty = roll_stuck_penalty(z, stab_dam > cut_dam); if (weapon.has_flag(IF_MESSY)) { // e.g. chainsaws cutting_penalty /= 6; // Harder to get stuck for (int x = tarposx - 1; x <= tarposx + 1; x++) { for (int y = tarposy - 1; y <= tarposy + 1; y++) { if (!one_in(3)) { if (g->m.field_at(x, y).type == fd_blood && g->m.field_at(x, y).density < 3) g->m.field_at(x, y).density++; else g->m.add_field(g, x, y, fd_blood, 1); } } } } if (!unarmed_attack() && cutting_penalty > dice(str_cur * 2, 20)) { if (is_u) g->add_msg("Your %s gets stuck in %s, pulling it out of your hands!", weapon.tname().c_str(), target.c_str()); if (mon) { if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) z->speed *= .7; else z->speed *= .85; z->add_item(remove_weapon()); } else g->m.add_item(posx, posy, remove_weapon()); } else { if (mon && (cut_dam >= z->hp || stab_dam >= z->hp)) { cutting_penalty /= 2; cutting_penalty -= rng(skillLevel("cutting"), skillLevel("cutting") * 2 + 2); } if (cutting_penalty > 0) moves -= cutting_penalty; if (cutting_penalty >= 50 && is_u) g->add_msg("Your %s gets stuck in %s, but you yank it free.", weapon.tname().c_str(), target.c_str()); if (mon && (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB))) z->speed *= .9; } // Finally, some special effects for martial arts if(weapon.typeId() == "style_karate"){ dodges_left++; blocks_left += 2; } else if(weapon.typeId() == "style_aikido"){ bash_dam /= 2; } else if(weapon.typeId() == "style_capoeira"){ add_disease(DI_DODGE_BOOST, 2, g, 2); } else if(weapon.typeId() == "style_muay_thai"){ if ((mon && z->type->size >= MS_LARGE) || (!mon && p->str_max >= 12)) bash_dam += rng((mon ? z->type->size : (p->str_max - 8) / 4), 3 * (mon ? z->type->size : (p->str_max - 8) / 4)); } else if(weapon.typeId() == "style_tiger"){ add_disease(DI_DAMAGE_BOOST, 2, g, 2, 10); } else if(weapon.typeId() == "style_centipede"){ add_disease(DI_SPEED_BOOST, 2, g, 4, 40); } else if(weapon.typeId() == "style_venom_snake"){ if (has_disease(DI_VIPER_COMBO)) { if (disease_intensity(DI_VIPER_COMBO) == 1) { if (is_u) g->add_msg("Snakebite!"); int dambuf = bash_dam; bash_dam = stab_dam; stab_dam = dambuf; add_disease(DI_VIPER_COMBO, 2, g, 1, 2); // Upgrade to Viper Strike } else if (disease_intensity(DI_VIPER_COMBO) == 2) { if (hp_cur[hp_arm_l] >= hp_max[hp_arm_l] * .75 && hp_cur[hp_arm_r] >= hp_max[hp_arm_r] * .75 ) { if (is_u) g->add_msg("Viper STRIKE!"); bash_dam *= 3; } else if (is_u) g->add_msg("Your injured arms prevent a viper strike!"); rem_disease(DI_VIPER_COMBO); } } else if (crit) { if (is_u) g->add_msg("Tail whip! Viper Combo Intiated!"); bash_dam += 5; add_disease(DI_VIPER_COMBO, 2, g, 1, 2); } } else if(weapon.typeId() == "style_scorpion"){ if (crit) { if (!is_npc()) g->add_msg("Stinger Strike!"); if (mon) { z->add_effect(ME_STUNNED, 3); int zposx = z->posx, zposy = z->posy; z->knock_back_from(g, posx, posy); if (z->posx != zposx || z->posy != zposy) z->knock_back_from(g, posx, posy); // Knock a 2nd time if the first worked } else { p->add_disease(DI_STUNNED, 2, g); int pposx = p->posx, pposy = p->posy; p->knock_back_from(g, posx, posy); if (p->posx != pposx || p->posy != pposy) p->knock_back_from(g, posx, posy); // Knock a 2nd time if the first worked } } } else if(weapon.typeId() == "style_zui_quan"){ dodges_left = 50; // Basically, unlimited. } }
bool player::install_bionics(game *g, it_bionic* type) { if (type == NULL) { debugmsg("Tried to install NULL bionic"); return false; } std::string bio_name = type->name.substr(5); // Strip off "CBM: " 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); WINDOW* w_description = newwin(3, FULL_SCREEN_WIDTH-2, 21 + getbegy(w), 1 + getbegx(w)); werase(w); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); int pl_skill = int_cur + skillLevel("electronics") * 4 + skillLevel("firstaid") * 3 + skillLevel("mechanics") * 2; int skint = int(pl_skill / 4); int skdec = int((pl_skill * 10) / 4) % 10; // Header text mvwprintz(w, 1, 1, c_white, "Installing bionics:"); mvwprintz(w, 1, 21, type->color, bio_name.c_str()); // Dividing bars for (int i = 1; i < 79; i++) { mvwputch(w, 2, i, c_ltgray, LINE_OXOX); mvwputch(w, 20, i, c_ltgray, LINE_OXOX); } mvwputch(w, 2, 0, c_ltgray, LINE_XXXO); // |- mvwputch(w, 2, 79, c_ltgray, LINE_XOXX); // -| mvwputch(w, 20, 0, c_ltgray, LINE_XXXO); // |- mvwputch(w, 20, 79, c_ltgray, LINE_XOXX); // -| // Init the list of bionics for (int i = 1; i < type->options.size(); i++) { bionic_id bio_id = type->options[i]; mvwprintz(w, i + 3, 1, (has_bionic(bio_id) ? c_ltred : c_ltblue), bionics[bio_id]->name.c_str()); } // Helper text mvwprintz(w, 3, 39, c_white, "Difficulty of this module: %d", type->difficulty); mvwprintz(w, 4, 39, c_white, "Your installation skill: %d.%d", skint, skdec); mvwprintz(w, 5, 39, c_white, "Installation requires high intelligence,"); mvwprintz(w, 6, 39, c_white, "and skill in electronics, first aid, and"); mvwprintz(w, 7, 39, c_white, "mechanics (in that order of importance)."); int chance_of_success = int((100 * pl_skill) / (pl_skill + 4 * type->difficulty)); mvwprintz(w, 9, 39, c_white, "Chance of success:"); nc_color col_suc; if (chance_of_success >= 95) col_suc = c_green; else if (chance_of_success >= 80) col_suc = c_ltgreen; else if (chance_of_success >= 60) col_suc = c_yellow; else if (chance_of_success >= 35) col_suc = c_ltred; else col_suc = c_red; mvwprintz(w, 9, 59, col_suc, "%d%%%%", chance_of_success); mvwprintz(w, 11, 39, c_white, "Failure may result in crippling damage,"); mvwprintz(w, 12, 39, c_white, "loss of existing bionics, genetic damage"); mvwprintz(w, 13, 39, c_white, "or faulty installation."); wrefresh(w); if (type->id == "bio_power_storage" || type->id == "bio_power_storage_mkII") { // No selection list; just confirm int pow_up = BATTERY_AMOUNT; if (type->id == "bio_power_storage_mkII") { pow_up = 10; } mvwprintz(w, 3, 1, h_ltblue, "Power Storage +%d", pow_up); mvwprintz(w_description, 0, 0, c_ltblue, "\ Installing this bionic will increase your total power storage by %d.\n\ Power is necessary for most bionics to function. You also require a\n\ charge mechanism, which must be installed from another CBM.", pow_up); InputEvent input; wrefresh(w_description); wrefresh(w); do input = get_input(); while (input != Confirm && input != Cancel); if (input == Confirm) { practice(g->turn, "electronics", (100 - chance_of_success) * 1.5); practice(g->turn, "firstaid", (100 - chance_of_success) * 1.0); practice(g->turn, "mechanics", (100 - chance_of_success) * 0.5); int success = chance_of_success - rng(1, 100); if (success > 0) { g->add_msg("Successfully installed batteries."); max_power_level += pow_up; } else bionics_install_failure(g, this, success); werase(w); delwin(w); g->refresh_all(); return true; } werase(w); delwin(w); g->refresh_all(); return false; }
technique_id player::pick_technique(game *g, monster *z, player *p, bool crit, bool allowgrab) { if (z == NULL && p == NULL) return TEC_NULL; std::vector<technique_id> possible; bool downed = ((z && !z->has_effect(ME_DOWNED)) || (p && !p->has_disease(DI_DOWNED)) ); int base_str_req = 0; if (z) base_str_req = z->type->size; else if (p) base_str_req = 1 + (2 + p->str_cur) / 4; if (allowgrab) { // Check if grabs AREN'T REALLY ALLOWED if (z && z->has_flag(MF_PLASTIC)) allowgrab = false; } if (crit) { // Some are crit-only if (weapon.has_technique(TEC_SWEEP, this) && (!z || !z->has_flag(MF_FLIES)) && !downed) possible.push_back(TEC_SWEEP); if (weapon.has_technique(TEC_PRECISE, this)) possible.push_back(TEC_PRECISE); if (weapon.has_technique(TEC_BRUTAL, this) && !downed && str_cur + skillLevel("melee") >= 4 + base_str_req) possible.push_back(TEC_BRUTAL); } if (possible.empty()) { // Use non-crits only if any crit-onlies aren't used if (weapon.has_technique(TEC_DISARM, this) && !z && p->weapon.typeId() != "null" && !p->weapon.has_flag(IF_UNARMED_WEAPON) && dice( dex_cur + skillLevel("unarmed"), 8) > dice(p->dex_cur + p->skillLevel("melee"), 10)) possible.push_back(TEC_DISARM); if (weapon.has_technique(TEC_GRAB, this) && allowgrab) possible.push_back(TEC_GRAB); if (weapon.has_technique(TEC_RAPID, this)) possible.push_back(TEC_RAPID); if (weapon.has_technique(TEC_THROW, this) && !downed && str_cur + skillLevel("melee") >= 4 + base_str_req * 4 + rng(-4, 4)) possible.push_back(TEC_THROW); if (weapon.has_technique(TEC_WIDE, this)) { // Count monsters int enemy_count = 0; for (int x = posx - 1; x <= posx + 1; x++) { for (int y = posy - 1; y <= posy + 1; y++) { int mondex = g->mon_at(x, y); if (mondex != -1) { if (g->z[mondex].friendly == 0) enemy_count++; else enemy_count -= 2; } int npcdex = g->npc_at(x, y); if (npcdex != -1) { if (g->active_npc[npcdex].attitude == NPCATT_KILL) enemy_count++; else enemy_count -= 2; } } } if (enemy_count >= (possible.empty() ? 2 : 3)) { possible.push_back(TEC_WIDE); } } } // if (possible.empty()) if (possible.empty()) return TEC_NULL; possible.push_back(TEC_NULL); // Always a chance to not use any technique return possible[ rng(0, possible.size() - 1) ]; }
void player::hit_player(game *g, player &p, bool allow_grab) { bool is_u = (this == &(g->u)); // Affects how we'll display messages if (is_u && p.is_npc()) { npc* npcPtr = dynamic_cast<npc*>(&p); npcPtr->make_angry(); } std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); std::string verb = "hit"; // Divide their dodge roll by 2 if this is a grab int target_dodge = (allow_grab ? p.dodge_roll(g) : p.dodge_roll(g) / 2); int hit_value = hit_roll() - target_dodge; bool missed = (hit_roll() <= 0); int move_cost = attack_speed(*this, missed); if (missed) { int stumble_pen = stumble(*this); if (is_u) { // Only display messages if this is the player if (weapon.has_technique(TEC_FEINT, this)) g->add_msg("You feint."); else 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."); } melee_practice(*this, false, unarmed_attack(), weapon.is_bashing_weapon(), weapon.is_cutting_weapon(), (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB))); move_cost += stumble_pen; if (weapon.has_technique(TEC_FEINT, this)) move_cost = rng(move_cost / 3, move_cost); moves -= move_cost; return; } moves -= move_cost; body_part bp_hit; int side = rng(0, 1); hit_value += rng(-10, 10); if (hit_value >= 30) bp_hit = bp_eyes; else if (hit_value >= 20) bp_hit = bp_head; else if (hit_value >= 10) bp_hit = bp_torso; else if (one_in(4)) bp_hit = bp_legs; else bp_hit = bp_arms; std::string target = (p.is_npc() ? p.name + "'s " : "your "); target += body_part_name(bp_hit, side); bool critical_hit = scored_crit(target_dodge); int bash_dam = roll_bash_damage(NULL, critical_hit); int cut_dam = roll_cut_damage(NULL, critical_hit); int stab_dam = roll_stab_damage(NULL, critical_hit); technique_id tech_def = p.pick_defensive_technique(g, NULL, this); p.perform_defensive_technique(tech_def, g, NULL, this, bp_hit, side, bash_dam, cut_dam, stab_dam); if (bash_dam + cut_dam + stab_dam <= 0) return; // Defensive technique canceled our attack! if (critical_hit) // Crits cancel out Toad Style's armor boost p.rem_disease(DI_ARMOR_BOOST); int pain = 0; // Boost to pain; required for perform_technique // Moves lost to getting your weapon stuck int stuck_penalty = roll_stuck_penalty(NULL, (stab_dam >= cut_dam)); if (weapon.is_style()) stuck_penalty = 0; // Pick one or more special attacks technique_id technique = pick_technique(g, NULL, &p, critical_hit, allow_grab); // Handles effects as well; not done in melee_affect_* perform_technique(technique, g, NULL, &p, bash_dam, cut_dam, stab_dam, pain); p.pain += pain; // Mutation-based attacks perform_special_attacks(g, NULL, &p, bash_dam, cut_dam, stab_dam); // Handles speed penalties to monster & us, etc melee_special_effects(g, NULL, &p, critical_hit, bash_dam, cut_dam, stab_dam); // Make a rather quiet sound, to alert any nearby monsters if (weapon.typeId() != "style_ninjutsu") // Ninjutsu is silent! g->sound(posx, posy, 8, ""); p.hit(g, bp_hit, side, bash_dam, (cut_dam > stab_dam ? cut_dam : stab_dam)); verb = melee_verb(technique, your, *this, bash_dam, cut_dam, stab_dam); int dam = bash_dam + (cut_dam > stab_dam ? cut_dam : stab_dam); hit_message(g, You.c_str(), verb.c_str(), target.c_str(), dam, critical_hit); bool bashing = (bash_dam >= 10 && !unarmed_attack()); bool cutting = (cut_dam >= 10 && cut_dam >= stab_dam); bool stabbing = (stab_dam >= 10 && stab_dam >= cut_dam); melee_practice(*this, true, unarmed_attack(), bashing, cutting, stabbing); if (dam >= 5 && has_artifact_with(AEP_SAP_LIFE)) healall( rng(dam / 10, dam / 5) ); if (allow_grab && technique == TEC_GRAB) { // Move our weapon to a temp slot, if it's not unarmed if (p.weapon.has_technique(TEC_BREAK, &p) && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(dex_cur + skillLevel("melee"), 10)) { if (is_u) g->add_msg("%s break%s the grab!", target.c_str(), (p.is_npc() ? "s" : "")); } else if (!unarmed_attack()) { item tmpweap = remove_weapon(); hit_player(g, p, false); // False means a second grab isn't allowed weapon = tmpweap; } else hit_player(g, p, false); // False means a second grab isn't allowed } if (tech_def == TEC_COUNTER) { if (!p.is_npc()) g->add_msg("Counter-attack!"); p.hit_player(g, *this); } }
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; }
bool player::scored_crit(int target_dodge) { int num_crits = 0; // Weapon to-hit roll int chance = 25; if (unarmed_attack()) { // Unarmed attack: 1/2 of unarmed skill is to-hit for (int i = 1; i <= int(skillLevel("unarmed") * .5); i++) chance += (50 / (2 + i)); } if (weapon.type->m_to_hit > 0) { for (int i = 1; i <= weapon.type->m_to_hit; i++) chance += (50 / (2 + i)); } else if (chance < 0) { for (int i = 0; i > weapon.type->m_to_hit; i--) chance /= 2; } if (rng(0, 99) < chance + 4 * disease_intensity(DI_ATTACK_BOOST)) num_crits++; // Dexterity to-hit roll // ... except sometimes we don't use dexteiry! int stat = dex_cur; // Some martial arts use something else to determine hits! if(weapon.typeId() == "style_tiger"){ stat = (str_cur * 2 + dex_cur) / 3; } else if(weapon.typeId() == "style_leopard"){ stat = (per_cur + int_cur + dex_cur * 2) / 4; } else if(weapon.typeId() == "style_snake"){ stat = (per_cur + dex_cur) / 2; } chance = 25; if (stat > 8) { for (int i = 9; i <= stat; i++) chance += (21 - i); // 12, 11, 10... } else { int decrease = 5; for (int i = 7; i >= stat; i--) { chance -= decrease; if (i % 2 == 0) decrease--; } } if (rng(0, 99) < chance) num_crits++; // Skill level roll int best_skill = 0; if (weapon.is_bashing_weapon() && skillLevel("bashing") > best_skill) best_skill = skillLevel("bashing"); if (weapon.is_cutting_weapon() && skillLevel("cutting") > best_skill) best_skill = skillLevel("cutting"); if ((weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) && skillLevel("stabbing") > best_skill) best_skill = skillLevel("stabbing"); if (unarmed_attack() && skillLevel("unarmed") > best_skill) best_skill = skillLevel("unarmed"); best_skill += int(skillLevel("melee") / 2.5); chance = 25; if (best_skill > 3) { for (int i = 3; i < best_skill; i++) chance += (50 / (2 + i)); } else if (chance < 3) { for (int i = 3; i > best_skill; i--) chance /= 2; } if (rng(0, 99) < chance + 4 * disease_intensity(DI_ATTACK_BOOST)) num_crits++; if (num_crits == 3) return true; else if (num_crits == 2) return (hit_roll() >= target_dodge * 1.5 && !one_in(4)); return false; }
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; }
void player::fire_gun(int tarx, int tary, bool burst) { item ammotmp; item* gunmod = weapon.active_gunmod(); it_ammo *curammo = NULL; item *used_weapon = NULL; if (weapon.has_flag("CHARGE")) { // It's a charger gun, so make up a type // Charges maxes out at 8. int charges = weapon.num_charges(); it_ammo *tmpammo = dynamic_cast<it_ammo*>(itypes["charge_shot"]); tmpammo->damage = charges * charges; tmpammo->pierce = (charges >= 4 ? (charges - 3) * 2.5 : 0); if (charges <= 4) tmpammo->dispersion = 14 - charges * 2; else // 5, 12, 21, 32 tmpammo->dispersion = charges * (charges - 4); tmpammo->recoil = tmpammo->dispersion * .8; tmpammo->ammo_effects.clear(); // Reset effects. if (charges == 8) { tmpammo->ammo_effects.insert("EXPLOSIVE_BIG"); } else if (charges >= 6) { tmpammo->ammo_effects.insert("EXPLOSIVE"); } if (charges >= 5){ tmpammo->ammo_effects.insert("FLAME"); } else if (charges >= 4) { tmpammo->ammo_effects.insert("INCENDIARY"); } if (gunmod != NULL) { // TODO: range calculation in case of active gunmod. used_weapon = gunmod; } else { used_weapon = &weapon; } curammo = tmpammo; used_weapon->curammo = tmpammo; } else if (gunmod != NULL) { used_weapon = gunmod; curammo = used_weapon->curammo; } else {// Just a normal gun. If we're here, we know curammo is valid. curammo = weapon.curammo; used_weapon = &weapon; } ammotmp = item(curammo, 0); ammotmp.charges = 1; if (!used_weapon->is_gun() && !used_weapon->is_gunmod()) { debugmsg("%s tried to fire a non-gun (%s).", name.c_str(), used_weapon->tname().c_str()); return; } projectile proj; // damage will be set later proj.aoe_size = 0; proj.ammo = curammo; proj.speed = 1000; std::set<std::string> *curammo_effects = &curammo->ammo_effects; if(gunmod == NULL){ std::set<std::string> *gun_effects = &dynamic_cast<it_gun*>(used_weapon->type)->ammo_effects; proj.proj_effects.insert(gun_effects->begin(),gun_effects->end()); } proj.proj_effects.insert(curammo_effects->begin(),curammo_effects->end()); proj.wide = (curammo->phase == LIQUID || proj.proj_effects.count("SHOT") || proj.proj_effects.count("BOUNCE")); proj.drops = (curammo->type == "bolt" || curammo->type == "arrow"); //int x = xpos(), y = ypos(); // Have to use the gun, gunmods don't have a type it_gun* firing = dynamic_cast<it_gun*>(weapon.type); if (has_trait("TRIGGERHAPPY") && one_in(30)) burst = true; if (burst && used_weapon->burst_size() < 2) burst = false; // Can't burst fire a semi-auto // Use different amounts of time depending on the type of gun and our skill if (!proj.proj_effects.count("BOUNCE")) { moves -= time_to_fire(*this, firing); } // Decide how many shots to fire int num_shots = 1; if (burst) num_shots = used_weapon->burst_size(); if (num_shots > used_weapon->num_charges() && !used_weapon->has_flag("CHARGE") && !used_weapon->has_flag("NO_AMMO")) num_shots = used_weapon->num_charges(); if (num_shots == 0) debugmsg("game::fire() - num_shots = 0!"); int ups_drain = 0; int adv_ups_drain = 0; if (weapon.has_flag("USE_UPS")) { ups_drain = 5; adv_ups_drain = 3; } else if (weapon.has_flag("USE_UPS_20")) { ups_drain = 20; adv_ups_drain = 12; } else if (weapon.has_flag("USE_UPS_40")) { ups_drain = 40; adv_ups_drain = 24; } // cap our maximum burst size by the amount of UPS power left if (ups_drain > 0 || adv_ups_drain > 0) while (!(has_charges("UPS_off", ups_drain*num_shots) || has_charges("UPS_on", ups_drain*num_shots) || has_charges("adv_UPS_off", adv_ups_drain*num_shots) || has_charges("adv_UPS_on", adv_ups_drain*num_shots))) { num_shots--; } const bool debug_retarget = false; // this will inevitably be needed //const bool wildly_spraying = false; // stub for now. later, rng based on stress/skill/etc at the start, int weaponrange = weapon.range(); // this is expensive, let's cache. todo: figure out if we need weapon.range(&p); for (int curshot = 0; curshot < num_shots; curshot++) { // Burst-fire weapons allow us to pick a new target after killing the first int zid = g->mon_at(tarx, tary); if ( curshot > 0 && (zid == -1 || g->zombie(zid).hp <= 0) ) { std::vector<point> new_targets; new_targets.clear(); if ( debug_retarget == true ) { mvprintz(curshot,5,c_red,"[%d] %s: retarget: mon_at(%d,%d)",curshot,name.c_str(),tarx,tary); if(zid == -1) { printz(c_red, " = -1"); } else { printz(c_red, ".hp=%d", g->zombie(zid).hp); } } for (unsigned long int i = 0; i < g->num_zombies(); i++) { monster &z = g->zombie(i); int dummy; // search for monsters in radius if (rl_dist(z.posx(), z.posy(), tarx, tary) <= std::min(2 + skillLevel("gun"), weaponrange) && rl_dist(xpos(),ypos(),z.xpos(),z.ypos()) <= weaponrange && sees(&z, dummy) ) { if (!z.is_dead_state()) new_targets.push_back(point(z.xpos(), z.ypos())); // oh you're not dead and I don't like you. Hello! } } if ( new_targets.empty() == false ) { /* new victim! or last victim moved */ int target_picked = rng(0, new_targets.size() - 1); /* 1 victim list unless wildly spraying */ tarx = new_targets[target_picked].x; tary = new_targets[target_picked].y; zid = g->mon_at(tarx, tary); /* debug */ if (debug_retarget) printz(c_ltgreen, " NEW:(%d:%d,%d) %d,%d (%s)[%d] hp: %d", target_picked, new_targets[target_picked].x, new_targets[target_picked].y, tarx, tary, g->zombie(zid).name().c_str(), zid, g->zombie(zid).hp); } else if ( ( !has_trait("TRIGGERHAPPY") || /* double ta TRIPLE TAP! wait, no... */ one_in(3) /* on second though...everyone double-taps at times. */ ) && ( skillLevel("gun") >= 7 || /* unless trained */ one_in(7 - skillLevel("gun")) /* ...sometimes */ ) ) { return; // No targets, so return } else if (debug_retarget) { printz(c_red, " new targets.empty()!"); } } else if (debug_retarget) { const int zid = g->mon_at(tarx, tary); mvprintz(curshot,5,c_red,"[%d] %s: target == mon_at(%d,%d)[%d] %s hp %d",curshot, name.c_str(), tarx ,tary, zid, g->zombie(zid).name().c_str(), g->zombie(zid).hp); } // Drop a shell casing if appropriate. itype_id casing_type = curammo->casing; if (casing_type != "NULL" && !casing_type.empty()) { item casing; casing.make(itypes[casing_type]); // Casing needs a charges of 1 to stack properly with other casings. casing.charges = 1; if( used_weapon->has_gunmod("brass_catcher") != -1 ) { i_add( casing ); } else { int x = 0; int y = 0; int count = 0; do { x = xpos() - 1 + rng(0, 2); y = ypos() - 1 + rng(0, 2); count++; // Try not to drop the casing on a wall if at all possible. } while( g->m.move_cost( x, y ) == 0 && count < 10 ); g->m.add_item_or_charges(x, y, casing); } } // Use up a round (or 100) if (used_weapon->has_flag("FIRE_100")) { used_weapon->charges -= 100; } else if (used_weapon->has_flag("FIRE_50")) { used_weapon->charges -= 50; } else if (used_weapon->has_flag("CHARGE")) { used_weapon->active = false; used_weapon->charges = 0; } else if (!used_weapon->has_flag("NO_AMMO")) { used_weapon->charges--; } // Drain UPS power if (has_charges("adv_UPS_off", adv_ups_drain)) { use_charges("adv_UPS_off", adv_ups_drain); } else if (has_charges("adv_UPS_on", adv_ups_drain)) { use_charges("adv_UPS_on", adv_ups_drain); } else if (has_charges("UPS_off", ups_drain)) { use_charges("UPS_off", ups_drain); } else if (has_charges("UPS_on", ups_drain)) { use_charges("UPS_on", ups_drain); } if (firing->skill_used != Skill::skill("archery") && firing->skill_used != Skill::skill("throw")) { // Current guns have a durability between 5 and 9. // Misfire chance is between 1/64 and 1/1024. if (is_underwater() && !weapon.has_flag("WATERPROOF_GUN") && one_in(firing->durability)) { g->add_msg_player_or_npc(this, _("Your weapon misfires with a wet click!"), _("<npcname>'s weapon misfires with a wet click!") ); return; } else if (one_in(2 << firing->durability)) { g->add_msg_player_or_npc(this, _("Your weapon misfires!"), _("<npcname>'s weapon misfires!") ); return; } } make_gun_sound_effect(*this, burst, used_weapon); double total_dispersion = get_weapon_dispersion(used_weapon); //debugmsg("%f",total_dispersion); int range = rl_dist(xpos(), ypos(), tarx, tary); // penalties for point-blank if (range < (firing->volume/3) && firing->ammo != "shot") total_dispersion *= double(firing->volume/3) / double(range); // rifle has less range penalty past LONG_RANGE if (firing->skill_used == Skill::skill("rifle") && range > LONG_RANGE) total_dispersion *= 1 - 0.4*double(range - LONG_RANGE) / double(range); if (curshot > 0) { if (recoil_add(*this) % 2 == 1) { recoil++; } recoil += recoil_add(*this) / 2; } else { recoil += recoil_add(*this); } int mtarx = tarx; int mtary = tary; int adjusted_damage = used_weapon->gun_damage(); proj.impact = damage_instance::physical(0,adjusted_damage,0); double missed_by = projectile_attack(proj, mtarx, mtary, total_dispersion); if (missed_by <= .1) { // TODO: check head existence for headshot practice(g->turn, firing->skill_used, 5); lifetime_stats()->headshots++; } else if (missed_by <= .2) { practice(g->turn, firing->skill_used, 3); } else if (missed_by <= .4) { practice(g->turn, firing->skill_used, 2); } else if (missed_by <= .6) { practice(g->turn, firing->skill_used, 1); } } if (used_weapon->num_charges() == 0) { used_weapon->curammo = NULL; } }
SkillLevel& Character::skillLevel(Skill const &_skill) { return skillLevel(&_skill); }
bool player::uninstall_bionic(bionic_id b_id) { // malfunctioning bionics don't have associated items and get a difficulty of 12 int difficulty = 12; if( item_controller->has_template(b_id) > 0) { const it_bionic *type = dynamic_cast<it_bionic *> (item_controller->find_template(b_id)); difficulty = type->difficulty; } if (!has_bionic(b_id)) { popup(_("You don't have this bionic installed.")); return false; } if (!(inv.has_items_with_quality("CUT", 1, 1) && has_amount("1st_aid", 1))) { popup(_("Removing bionics requires a cutting tool and a first aid kit.")); return false; } if ( b_id == "bio_blaster" ) { popup(_("Removing your Fusion Blaster Arm would leave you with a useless stump.")); return false; } // removal of bionics adds +2 difficulty over installation int chance_of_success = bionic_manip_cos(int_cur, skillLevel("electronics"), skillLevel("firstaid"), skillLevel("mechanics"), difficulty + 2); if (!query_yn(_("WARNING: %i percent chance of SEVERE bodily damage! Remove anyway?"), 100 - chance_of_success)) { return false; } use_charges("1st_aid", 1); practice( "electronics", int((100 - chance_of_success) * 1.5) ); practice( "firstaid", int((100 - chance_of_success) * 1.0) ); practice( "mechanics", int((100 - chance_of_success) * 0.5) ); int success = chance_of_success - rng(1, 100); if (success > 0) { add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), pgettext("memorial_female", "Removed bionic: %s."), bionics[b_id]->name.c_str()); // until bionics can be flagged as non-removable add_msg(m_neutral, _("You jiggle your parts back into their familiar places.")); add_msg(m_good, _("Successfully removed %s."), bionics[b_id]->name.c_str()); remove_bionic(b_id); g->m.spawn_item(posx, posy, "burnt_out_bionic", 1); } else { add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), pgettext("memorial_female", "Removed bionic: %s."), bionics[b_id]->name.c_str()); bionics_uninstall_failure(this); } g->refresh_all(); return true; }
bool player::install_bionics(game *g, it_bionic* type) { if (type == NULL) { debugmsg("Tried to install NULL bionic"); return false; } std::string bio_name = type->name.substr(5); // Strip off "CBM: " WINDOW* w = newwin(25, 80, 0, 0); int pl_skill = int_cur + skillLevel("electronics").level() * 4 + skillLevel("firstaid").level() * 3 + skillLevel("mechanics").level() * 2; int skint = int(pl_skill / 4); int skdec = int((pl_skill * 10) / 4) % 10; // Header text mvwprintz(w, 0, 0, c_white, "Installing bionics:"); mvwprintz(w, 0, 20, type->color, bio_name.c_str()); // Dividing bars for (int i = 0; i < 80; i++) { mvwputch(w, 1, i, c_ltgray, LINE_OXOX); mvwputch(w, 21, i, c_ltgray, LINE_OXOX); } // Init the list of bionics for (unsigned int i = 1; i < type->options.size(); i++) { bionic_id id = type->options[i]; mvwprintz(w, i + 2, 0, (has_bionic(id) ? c_ltred : c_ltblue), bionics[id].name.c_str()); } // Helper text mvwprintz(w, 2, 40, c_white, "Difficulty of this module: %d", type->difficulty); mvwprintz(w, 3, 40, c_white, "Your installation skill: %d.%d", skint, skdec); mvwprintz(w, 4, 40, c_white, "Installation requires high intelligence,"); mvwprintz(w, 5, 40, c_white, "and skill in electronics, first aid, and"); mvwprintz(w, 6, 40, c_white, "mechanics (in that order of importance)."); int chance_of_success = int((100 * pl_skill) / (pl_skill + 4 * type->difficulty)); mvwprintz(w, 8, 40, c_white, "Chance of success:"); nc_color col_suc; if (chance_of_success >= 95) col_suc = c_green; else if (chance_of_success >= 80) col_suc = c_ltgreen; else if (chance_of_success >= 60) col_suc = c_yellow; else if (chance_of_success >= 35) col_suc = c_ltred; else col_suc = c_red; mvwprintz(w, 8, 59, col_suc, "%d%%%%", chance_of_success); mvwprintz(w, 10, 40, c_white, "Failure may result in crippling damage,"); mvwprintz(w, 11, 40, c_white, "loss of existing bionics, genetic damage"); mvwprintz(w, 12, 40, c_white, "or faulty installation."); wrefresh(w); if (type->id == itm_bionics_battery) { // No selection list; just confirm mvwprintz(w, 2, 0, h_ltblue, "Battery Level +%d", BATTERY_AMOUNT); mvwprintz(w, 22, 0, c_ltblue, "\ Installing this bionic will increase your total battery capacity by %d.\n\ Batteries are necessary for most bionics to function. They also require a\n\ charge mechanism, which must be installed from another CBM.", BATTERY_AMOUNT); char ch; wrefresh(w); do ch = getch(); while (ch != 'q' && ch != '\n' && ch != KEY_ESCAPE); if (ch == '\n') { practice("electronics", (100 - chance_of_success) * 1.5); practice("firstaid", (100 - chance_of_success) * 1.0); practice("mechanics", (100 - chance_of_success) * 0.5); int success = chance_of_success - rng(1, 100); if (success > 0) { g->add_msg("Successfully installed batteries."); max_power_level += BATTERY_AMOUNT; } else bionics_install_failure(g, this, success); werase(w); delwin(w); g->refresh_all(); return true; } werase(w); delwin(w); g->refresh_all(); return false; }
int player::base_to_hit(bool real_life, int stat) { if (stat == -999) stat = (real_life ? dex_cur : dex_max); return 1 + int(stat / 2) + skillLevel("melee"); }