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; }
void SkillLevel::readBook( int minimumGain, int maximumGain, int maximumLevel ) { if( _level < maximumLevel || maximumLevel < 0 ) { train( ( _level + 1 ) * rng( minimumGain, maximumGain ) ); } practice(); }
void SkillLevel::readBook(int minimumGain, int maximumGain, int maximumLevel) { int gain = rng(minimumGain, maximumGain); if (_level < maximumLevel) { train(gain); } practice(); }
void SkillLevel::readBook(int minimumGain, int maximumGain, const calendar &turn, int maximumLevel) { int gain = rng(minimumGain, maximumGain); if (_level < maximumLevel) { train(gain); } practice(turn); }
int SkillLevel::readBook(int minimumGain, int maximumGain, const calendar& turn, int maximumLevel) { int gain = rng(minimumGain, maximumGain); int level; for (int i = 0; i < gain; ++i) { train(level); if (level >= maximumLevel) break; } practice(turn); return _exercise; }
void main(){ int choice; practice(); do{ printf("\nEnter Choice: "); scanf("%d",&choice); switch (choice){ case 1: firstClassProb(); break; case 2: break; default: printf("\nError in Input"); break; } }while(choice!=2); }
int player::dodge() { // If this function is called, it should be assumed that we're exercising // the dodge skill. if (has_disease(DI_SLEEP) || has_disease(DI_LYING_DOWN)) return 0; practice(sk_dodge, 5); int ret = 4 + (dex_cur / 2); ret += sklevel[sk_dodge]; ret -= (encumb(bp_legs) / 2) + encumb(bp_torso); ret += int(current_speed() / 150); if (str_max >= 16) ret--; // Penalty if we're hyuuge else if (str_max <= 5) ret++; // Bonus if we're small if (!can_dodge) // We already dodged this turn ret = rng(0, ret); can_dodge = false; return ret; }
int player::hit_mon(game *g, monster *z) { bool is_u = (this == &(g->u)); // Affects how we'll display messages int j; bool can_see = (is_u || g->u_see(posx, posy, j)); std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); // Types of combat (may overlap!) bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(), stabbing = (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)); // Recoil penalty if (recoil <= 30) recoil += 6; // Movement cost moves -= weapon.attack_time() + 20 * encumb(bp_torso); // Different sizes affect your chance to hit if (hit_roll() < z->dodge_roll()) {// A miss! stumble(g); return 0; } if (z->has_flag(MF_SHOCK) && !wearing_something_on(bp_hands) && (unarmed || weapon.conductive())) { if (is_u) g->add_msg("The %s's electric body shocks you!", z->name().c_str()); hurtall(rng(1, 3)); } // For very high hit rolls, we crit! bool critical_hit = (hit_roll() >= 50 + 10 * z->dodge_roll()); int dam = base_damage(true); int cutting_penalty = 0; // Moves lost from getting a cutting weapon stuck // Drunken Master damage bonuses if (has_trait(PF_DRUNKEN) && has_disease(DI_DRUNK)) { // Remember, a single drink gives 600 levels of DI_DRUNK if (unarmed) dam += disease_level(DI_DRUNK) / 250; else dam += disease_level(DI_DRUNK) / 400; } if (unarmed) { // Unarmed bonuses dam += rng(0, sklevel[sk_unarmed]); if (has_trait(PF_TALONS) && z->type->armor - sklevel[sk_unarmed] < 10) { int z_armor = (z->type->armor - sklevel[sk_unarmed]); if (z_armor < 0) z_armor = 0; dam += 10 - z_armor; } } else if (rng(1, 45 - dex_cur) < 2 * sklevel[sk_unarmed] && rng(1, 65 - dex_cur) < 2 * sklevel[sk_unarmed] ) { // If we're not unarmed, there's still a possibility of getting in a bonus // unarmed attack. if (is_u || can_see) { switch (rng(1, 4)) { case 1: g->add_msg("%s kick%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 2: g->add_msg("%s headbutt%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 3: g->add_msg("%s elbow%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 4: g->add_msg("%s knee%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; } } dam += rng(1, sklevel[sk_unarmed]); practice(sk_unarmed, 2); } // Melee skill bonus dam += rng(0, sklevel[sk_melee]); // Bashing damage bonus int bash_dam = weapon.type->melee_dam, bash_cap = 5 + str_cur + sklevel[sk_bashing]; if (bash_dam > bash_cap)// Cap for weak characters bash_dam = (bash_cap * 3 + bash_dam) / 4; if (bashing) bash_dam += rng(0, sklevel[sk_bashing]) * sqrt(str_cur); int bash_min = bash_dam / 4; if (bash_min < sklevel[sk_bashing] * 2) bash_min = sklevel[sk_bashing] * 2; dam += rng(bash_dam / 4, bash_dam); // Take some moves away from the target; at this point it's skill & bash damage z->moves -= rng(0, dam * 2); // Spears treat cutting damage specially. if (weapon.has_weapon_flag(WF_SPEAR) && weapon.type->melee_cut > z->type->armor - int(sklevel[sk_stabbing])) { int z_armor = z->type->armor - int(sklevel[sk_stabbing]); dam += int(weapon.type->melee_cut / 5); int minstab = sklevel[sk_stabbing] * 8 + weapon.type->melee_cut * 2, maxstab = sklevel[sk_stabbing] * 20 + weapon.type->melee_cut * 4; int monster_penalty = rng(minstab, maxstab); if (monster_penalty >= 150) g->add_msg("You force the %s to the ground!", z->name().c_str()); else if (monster_penalty >= 80) g->add_msg("The %s is skewered and flinches!", z->name().c_str()); z->moves -= monster_penalty; cutting_penalty = weapon.type->melee_cut * 4 + z_armor * 8 - dice(sklevel[sk_stabbing], 10); practice(sk_stabbing, 2); // Cutting damage bonus } else if (weapon.type->melee_cut > z->type->armor - int(sklevel[sk_cutting] / 2)) { int z_armor = z->type->armor - int(sklevel[sk_cutting] / 2); if (z_armor < 0) z_armor = 0; dam += weapon.type->melee_cut - z_armor; cutting_penalty = weapon.type->melee_cut * 3 + z_armor * 8 - dice(sklevel[sk_cutting], 10); } if (weapon.has_weapon_flag(WF_MESSY)) { // e.g. chainsaws cutting_penalty /= 6; // Harder to get stuck for (int x = z->posx - 1; x <= z->posx + 1; x++) { for (int y = z->posy - 1; y <= z->posy + 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); } } } } // Bonus attacks! bool shock_them = (!z->has_flag(MF_SHOCK) && has_bionic(bio_shock) && power_level >= 2 && unarmed && one_in(3)); bool drain_them = (has_bionic(bio_heat_absorb) && power_level >= 1 && !is_armed() && z->has_flag(MF_WARM)); bool bite_them = (has_trait(PF_FANGS) && z->armor() < 18 && one_in(20 - dex_cur - sklevel[sk_unarmed])); bool peck_them = (has_trait(PF_BEAK) && z->armor() < 16 && one_in(15 - dex_cur - sklevel[sk_unarmed])); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time // Critical hit effects if (critical_hit) { bool headshot = (!z->has_flag(MF_NOHEAD) && !one_in(3)); // Second chance for shock_them, drain_them, bite_them and peck_them shock_them = (shock_them || (!z->has_flag(MF_SHOCK) && has_bionic(bio_shock)&& power_level >= 2 && unarmed && !one_in(3))); drain_them = (drain_them || (has_bionic(bio_heat_absorb) && !is_armed() && power_level >= 1 && z->has_flag(MF_WARM) && !one_in(3))); bite_them = ( bite_them || (has_trait(PF_FANGS) && z->armor() < 18 && one_in(5))); peck_them = ( peck_them || (has_trait(PF_BEAK) && z->armor() < 16 && one_in(4))); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) { dam += weapon.type->melee_cut; dam += weapon.type->melee_cut * double(sklevel[sk_stabbing] / 10); practice(sk_stabbing, 5); } if (unarmed) { dam += rng(2, 6) * sklevel[sk_unarmed]; if (sklevel[sk_unarmed] > 5) dam += 4 * (sklevel[sk_unarmed - 3]); z->moves -= dam; // Stunning blow if (weapon.type->id == itm_bio_claws) { if (sklevel[sk_cutting] >= 3) dam += 5; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s claws pierce the %s's skull!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s claws stab straight through the %s!", Your.c_str(), z->name().c_str()); } else if (has_trait(PF_TALONS)) { dam += 2; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s talons tear the %s's head open!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s bur%s %s talons into the %s!", You.c_str(),(is_u?"y":"ies"), your.c_str(), z->name().c_str()); } else { headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s crush%s the %s's skull in a single blow!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s deliver%s a crushing punch!",You.c_str(),(is_u ? "" : "s")); } if (z->hp > 0 && rng(1, 5) < sklevel[sk_unarmed]) z->add_effect(ME_STUNNED, 1 + sklevel[sk_unarmed]); } else { // Not unarmed if (bashing) { dam += 8 + (str_cur / 2); int turns_stunned = int(dam / 20) + int(sklevel[sk_bashing] / 2); if (turns_stunned > 6) turns_stunned = 6; z->add_effect(ME_STUNNED, turns_stunned); } if (cutting) { double cut_multiplier = double(sklevel[sk_cutting] / 12); if (cut_multiplier > 1.5) cut_multiplier = 1.5; dam += cut_multiplier * weapon.type->melee_cut; headshot &= z->hp < dam; if (stabbing) { if (headshot && can_see) g->add_msg("%s %s stabs through the %s's skull!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s stab %s %s through the %s!", You.c_str(), your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } else { if (headshot && can_see) g->add_msg("%s %s slices the %s's head off!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else g->add_msg("%s %s cuts the %s deeply!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } } else { // Not cutting, probably bashing headshot &= z->hp < dam; if (headshot && can_see) g->add_msg("%s crush%s the %s's skull!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s crush%s the %s's body!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); } } // End of not-unarmed } // End of critical hit if (shock_them) { power_level -= 2; if (can_see) g->add_msg("%s shock%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); int shock = rng(2, 5); dam += shock * rng(1, 3); z->moves -= shock * 180; } if (drain_them) { charge_power(rng(0, 4)); if (can_see) g->add_msg("%s drain%s the %s's body heat!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += rng(4, 10); z->moves -= rng(80, 120); } if (bite_them) { if (can_see) g->add_msg("%s sink %s fangs into the %s!", You.c_str(), your.c_str(), z->name().c_str()); dam += 18 - z->armor(); } if (peck_them) { if (can_see) g->add_msg("%s peck%s the %s viciously!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += 16 - z->armor(); } // Make a rather quiet sound, to alert any nearby monsters g->sound(posx, posy, 8, ""); // 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())); dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra remove_weapon(); } if (dam <= 0) { if (is_u) g->add_msg("You hit the %s, but do no damage.", z->name().c_str()); else if (can_see) g->add_msg("%s's %s hits the %s, but does no damage.", You.c_str(), weapon.tname(g).c_str(), z->name().c_str()); practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 2); if (stabbing) practice(sk_stabbing, 2); return 0; } if (is_u) g->add_msg("You hit the %s for %d damage.", z->name().c_str(), dam); else if (can_see) g->add_msg("%s hits the %s with %s %s.", You.c_str(), z->name().c_str(), (male ? "his" : "her"), (weapon.type->id == 0 ? "fists" : weapon.tname(g).c_str())); practice(sk_melee, rng(5, 10)); if (unarmed) practice(sk_unarmed, rng(5, 10)); if (bashing) practice(sk_bashing, rng(5, 10)); if (cutting) practice(sk_cutting, rng(5, 10)); if (stabbing) practice(sk_stabbing, rng(5, 10)); // Penalize the player if their cutting weapon got stuck if (!unarmed && dam < z->hp && cutting_penalty > dice(str_cur * 2, 20)) { if (is_u) g->add_msg("Your %s gets stuck in the %s, pulling it out of your hands!", weapon.tname().c_str(), z->type->name.c_str()); z->add_item(remove_weapon()); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) z->speed *= .7; else z->speed *= .85; } else { if (dam >= z->hp) { cutting_penalty /= 2; cutting_penalty -= rng(sklevel[sk_cutting], sklevel[sk_cutting] * 2 + 2); } if (cutting_penalty > 0) moves -= cutting_penalty; if (cutting_penalty >= 50 && is_u) g->add_msg("Your %s gets stuck in the %s, but you yank it free.", weapon.tname().c_str(), z->type->name.c_str()); if (weapon.has_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) z->speed *= .9; } return dam; }
bool player::hit_player(player &p, body_part &bp, int &hitdam, int &hitcut) { // TODO: Add bionics and other bonus (e.g. heat drain, shock, etc) if (!is_npc() && p.is_npc()) { npc *foe = dynamic_cast<npc*>(&p); foe->make_angry(); } bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(); int hitit = hit_roll() - p.dodge_roll(); if (hitit < 0) { // They dodged practice(sk_melee, rng(2, 4)); if (unarmed) practice(sk_unarmed, 3); if (bashing) practice(sk_bashing, 1); if (cutting) practice(sk_cutting, 2); return false; } if (hitit >= 15) bp = bp_eyes; else if (hitit >= 12) bp = bp_mouth; else if (hitit >= 10) bp = bp_head; else if (hitit >= 6) bp = bp_torso; else if (hitit >= 2) bp = bp_arms; else bp = bp_legs; hitdam = base_damage(); if (unarmed) {// Unarmed bonuses hitdam += rng(0, sklevel[sk_unarmed]); if (sklevel[sk_unarmed] >= 5) hitdam += rng(sklevel[sk_unarmed], 3 * sklevel[sk_unarmed]); if (has_trait(PF_TALONS)) hitcut += 10; if (sklevel[sk_unarmed] >= 8 && (one_in(3) || rng(5, 20) < sklevel[sk_unarmed])) hitdam *= rng(2, 3); } // Weapon adds (melee_dam / 4) to (melee_dam) hitdam += rng(weapon.type->melee_dam / 4, weapon.type->melee_dam); if (bashing) hitdam += rng(0, sklevel[sk_bashing]) * sqrt(str_cur); hitdam += int(pow(1.5, sklevel[sk_melee])); hitcut = weapon.type->melee_cut; if (hitcut > 0) hitcut += int(sklevel[sk_cutting] / 3); if (hitdam < 0) hitdam = 0; if (hitdam > 0 || hitcut > 0) { // Practicing practice(sk_melee, rng(5, 10)); if (unarmed) practice(sk_unarmed, rng(5, 10)); if (bashing) practice(sk_bashing, rng(5, 10)); if (cutting) practice(sk_cutting, rng(5, 10)); } else { // Less practice if we missed practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 3); } 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(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; }
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; } }
void player::complete_disassemble( int item_pos, const tripoint &loc, bool from_ground, const recipe &dis ) { // Get the proper recipe - the one for disassembly, not assembly const auto dis_requirements = dis.disassembly_requirements(); item &org_item = get_item_for_uncraft( *this, item_pos, loc, from_ground ); bool filthy = org_item.is_filthy(); if( org_item.is_null() ) { add_msg( _( "The item has vanished." ) ); activity.set_to_null(); return; } if( org_item.typeId() != dis.result ) { add_msg( _( "The item might be gone, at least it is not at the expected position anymore." ) ); activity.set_to_null(); return; } // Make a copy to keep its data (damage/components) even after it // has been removed. item dis_item = org_item; float component_success_chance = std::min( std::pow( 0.8, dis_item.damage() ), 1.0 ); add_msg( _( "You disassemble the %s into its components." ), dis_item.tname().c_str() ); // Remove any batteries, ammo and mods first remove_ammo( &dis_item, *this ); remove_radio_mod( dis_item, *this ); if( dis_item.count_by_charges() ) { // remove the charges that one would get from crafting it org_item.charges -= dis.create_result().charges; } // remove the item, except when it's counted by charges and still has some if( !org_item.count_by_charges() || org_item.charges <= 0 ) { if( from_ground ) { g->m.i_rem( loc, item_pos ); } else { i_rem( item_pos ); } } // Consume tool charges for( const auto &it : dis_requirements.get_tools() ) { consume_tools( it ); } // add the components to the map // Player skills should determine how many components are returned int skill_dice = 2 + get_skill_level( dis.skill_used ) * 3; skill_dice += get_skill_level( dis.skill_used ); // Sides on dice is 16 plus your current intelligence ///\EFFECT_INT increases success rate for disassembling items int skill_sides = 16 + int_cur; int diff_dice = dis.difficulty; int diff_sides = 24; // 16 + 8 (default intelligence) // disassembly only nets a bit of practice if( dis.skill_used ) { practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty ); } for( const auto &altercomps : dis_requirements.get_components() ) { const item_comp comp = find_component( altercomps, dis_item ); int compcount = comp.count; item newit( comp.type, calendar::turn ); // Counted-by-charge items that can be disassembled individually // have their component count multiplied by the number of charges. if( dis_item.count_by_charges() && dis.has_flag( "UNCRAFT_SINGLE_CHARGE" ) ) { compcount *= std::min( dis_item.charges, dis.create_result().charges ); } // Compress liquids and counted-by-charges items into one item, // they are added together on the map anyway and handle_liquid // should only be called once to put it all into a container at once. if( newit.count_by_charges() || newit.made_of( LIQUID ) ) { newit.charges = compcount; compcount = 1; } else if( !newit.craft_has_charges() && newit.charges > 0 ) { // tools that can be unloaded should be created unloaded, // tools that can't be unloaded will keep their default charges. newit.charges = 0; } for( ; compcount > 0; compcount-- ) { const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides ) ); if( dis.difficulty != 0 && !comp_success ) { add_msg( m_bad, _( "You fail to recover %s." ), newit.tname().c_str() ); continue; } const bool dmg_success = component_success_chance > rng_float( 0, 1 ); if( !dmg_success ) { // Show reason for failure (damaged item, tname contains the damage adjective) //~ %1s - material, %2$s - disassembled item add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit.tname().c_str(), dis_item.tname().c_str() ); continue; } // Use item from components list, or (if not contained) // use newit, the default constructed. item act_item = newit; if( filthy ) { act_item.item_tags.insert( "FILTHY" ); } for( item::t_item_vector::iterator a = dis_item.components.begin(); a != dis_item.components.end(); ++a ) { if( a->type == newit.type ) { act_item = *a; dis_item.components.erase( a ); break; } } int veh_part = -1; vehicle *veh = g->m.veh_at( pos(), veh_part ); if( veh != nullptr ) { veh_part = veh->part_with_feature( veh_part, "CARGO" ); } if( act_item.made_of( LIQUID ) ) { g->handle_all_liquid( act_item, PICKUP_RANGE ); } else if( veh_part != -1 && veh->add_item( veh_part, act_item ) ) { // add_item did put the items in the vehicle, nothing further to be done } else { // TODO: For items counted by charges, add as much as we can to the vehicle, and // the rest on the ground (see dropping code and @vehicle::add_charges) g->m.add_item_or_charges( pos(), act_item ); } } } if( !dis.learn_by_disassembly.empty() && !knows_recipe( &dis ) ) { if( can_decomp_learn( dis ) ) { // @todo: make this depend on intelligence if( one_in( 4 ) ) { learn_recipe( &recipe_dict[ dis.ident() ] ); add_msg( m_good, _( "You learned a recipe from disassembling it!" ) ); } else { add_msg( m_info, _( "You might be able to learn a recipe if you disassemble another." ) ); } } else { add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) ); } } }
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; }
int player::hit_mon(game *g, monster *z) { bool is_u = (this == &(g->u)); // Affects how we'll display messages if (is_u) z->add_effect(ME_HIT_BY_PLAYER, 100); // Flag as attacked by us int j; bool can_see = (is_u || g->u_see(posx, posy, j)); std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); // Types of combat (may overlap!) bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(), stabbing = (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB) ); bool can_poison = false; // Recoil penalty if (recoil <= 30) recoil += 6; // Movement cost int move_cost = weapon.attack_time() + 20 * encumb(bp_torso); if (has_trait(PF_LIGHT_BONES)) move_cost *= .9; if (has_trait(PF_HOLLOW_BONES)) move_cost *= .8; moves -= move_cost; // Different sizes affect your chance to hit if (hit_roll() < z->dodge_roll() || one_in(4 + dex_cur + weapon.type->m_to_hit)) {// A miss! stumble(g); return 0; } // For very high hit rolls, we crit! bool critical_hit = scored_crit(z->dodge_roll()); int dam = base_damage(true); int cutting_penalty = 0; // Moves lost from getting a cutting weapon stuck // 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) { 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; } dam += rng(mindrunk, maxdrunk); } if (unarmed) { // Unarmed bonuses dam += rng(0, sklevel[sk_unarmed]); if (has_trait(PF_NAILS) && z->armor_cut() == 0 && !wearing_something_on(bp_hands)) { dam++; if (one_in(2)) can_poison = true; } if (has_trait(PF_CLAWS) && z->armor_cut() < 6 && !wearing_something_on(bp_hands)) { dam += 6; if (one_in(2)) can_poison = true; } if (has_trait(PF_TALONS) && z->armor_cut() - sklevel[sk_unarmed] < 10) { int z_armor = (z->armor_cut() - sklevel[sk_unarmed]); if (z_armor < 0) z_armor = 0; dam += 10 - z_armor; if (one_in(2)) can_poison = true; } if (has_trait(PF_THORNS) && z->armor_cut() < 4 && !wearing_something_on(bp_hands)) { dam += 4 - z->armor_cut(); if (one_in(2)) can_poison = true; } if (has_trait(PF_SLIME_HANDS) && !z->has_flag(MF_ACIDPROOF) && !wearing_something_on(bp_hands)) { dam += rng(4, 6); can_poison = true; } } if (rng(1, 45 - dex_cur) < 2 * sklevel[sk_unarmed] && rng(1, 65 - dex_cur) < 2 * sklevel[sk_unarmed] ) { // Bonus unarmed attack! if (is_u || can_see) { switch (rng(1, 2)) { case 1: g->add_msg("%s elbow%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 2: g->add_msg("%s knee%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; } } if (sklevel[sk_unarmed] >= 4) dam += rng(1, sklevel[sk_unarmed] / 2); else dam++; practice(sk_unarmed, 2); } // Melee skill bonus dam += rng(0, sklevel[sk_melee]); // Bashing damage bonus int bash_dam = weapon.damage_bash() - z->armor_bash(), bash_cap = 5 + str_cur + sklevel[sk_bashing]; if (bash_dam > bash_cap)// Cap for weak characters bash_dam = (bash_cap * 3 + bash_dam) / 4; if (bashing) bash_dam += rng(0, sklevel[sk_bashing] + sqrt(double(str_cur))); if (z->has_flag(MF_PLASTIC)) bash_dam /= rng(2, 4); int bash_min = bash_dam / 4; if (bash_min < sklevel[sk_bashing] ) bash_min = sklevel[sk_bashing]; dam += rng(bash_min, bash_dam); // Take some moves away from the target; at this point it's skill & bash damage z->moves -= rng(0, dam * 2); // Spears treat cutting damage specially. if (weapon.has_flag(IF_SPEAR) && weapon.damage_cut() > z->armor_cut() - int(sklevel[sk_stabbing])) { int z_armor = z->armor_cut() - int(sklevel[sk_stabbing]); dam += int(weapon.damage_cut() / 5); int minstab = sklevel[sk_stabbing] * 5 + weapon.volume() * 2, maxstab = sklevel[sk_stabbing] * 15 + weapon.volume() * 4; int monster_penalty = rng(minstab, maxstab); if (monster_penalty >= 150) g->add_msg("You force the %s to the ground!", z->name().c_str()); else if (monster_penalty >= 50) g->add_msg("The %s is skewered and flinches!", z->name().c_str()); z->moves -= monster_penalty; cutting_penalty = weapon.damage_cut() * 4 + z_armor * 8 - dice(sklevel[sk_stabbing], 10); practice(sk_stabbing, 2); // Cutting damage bonus } else if (weapon.damage_cut() > z->armor_cut() - int(sklevel[sk_cutting] / 2)) { int z_armor = z->armor_cut() - int(sklevel[sk_cutting] / 2); if (z_armor < 0) z_armor = 0; dam += weapon.damage_cut() - z_armor; cutting_penalty = weapon.damage_cut() * 3 + z_armor * 8 - dice(sklevel[sk_cutting], 10); } if (weapon.has_flag(IF_MESSY)) { // e.g. chainsaws cutting_penalty /= 6; // Harder to get stuck for (int x = z->posx - 1; x <= z->posx + 1; x++) { for (int y = z->posy - 1; y <= z->posy + 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); } } } } // Critical hit effects if (critical_hit) { bool headshot = (!z->has_flag(MF_NOHEAD) && !one_in(3)); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) { dam += weapon.damage_cut(); dam += weapon.damage_cut() * double(sklevel[sk_stabbing] / 10); practice(sk_stabbing, 5); } if (unarmed) { dam += rng(1, 4) * sklevel[sk_unarmed]; z->moves -= dam; // Stunning blow if (weapon.type->id == itm_bio_claws) { if (sklevel[sk_cutting] >= 3) dam += 5; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s claws pierce the %s's skull!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s claws stab straight through the %s!", Your.c_str(), z->name().c_str()); } else if (has_trait(PF_TALONS)) { dam += 2; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s talons tear the %s's head open!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s bur%s %s talons into the %s!", You.c_str(),(is_u?"y":"ies"), your.c_str(), z->name().c_str()); } else { headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s crush%s the %s's skull in a single blow!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s deliver%s a crushing punch!",You.c_str(),(is_u ? "" : "s")); } if (z->hp > 0 && rng(1, 5) < sklevel[sk_unarmed]) z->add_effect(ME_STUNNED, 1 + sklevel[sk_unarmed]); } else { // Not unarmed if (bashing) { dam += (str_cur / 2); int turns_stunned = int(dam / 20) + rng(0, int(sklevel[sk_bashing] / 2)); if (turns_stunned > 6) turns_stunned = 6; z->add_effect(ME_STUNNED, turns_stunned); } if (cutting || stabbing) { double cut_multiplier; if (cutting) cut_multiplier = double(sklevel[sk_cutting] / 12); else cut_multiplier = double(sklevel[sk_stabbing] / 5); if (cut_multiplier > 1.5) cut_multiplier = 1.5; dam += cut_multiplier * weapon.damage_cut(); headshot &= z->hp < dam; if (stabbing) { if (headshot && can_see) g->add_msg("%s %s stabs through the %s's skull!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s stab %s %s through the %s!", You.c_str(), your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } else { if (headshot && can_see) g->add_msg("%s %s slices the %s's head off!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else g->add_msg("%s %s cuts the %s deeply!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } } else if (bashing) { headshot &= z->hp < dam; if (headshot && can_see) g->add_msg("%s crush%s the %s's skull!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s crush%s the %s's body!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); } } // End of not-unarmed } // End of critical hit // Bonus attacks! bool shock_them = (has_bionic(bio_shock) && power_level >= 2 && unarmed && one_in(3)); bool drain_them = (has_bionic(bio_heat_absorb) && power_level >= 1 && !is_armed() && z->has_flag(MF_WARM)); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time std::vector<special_attack> special_attacks = mutation_attacks(z); if (shock_them) { power_level -= 2; if (can_see) g->add_msg("%s shock%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); int shock = rng(2, 5); dam += shock * rng(1, 3); z->moves -= shock * 180; } if (drain_them) { charge_power(rng(0, 4)); if (can_see) g->add_msg("%s drain%s the %s's body heat!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += rng(4, 10); z->moves -= rng(80, 120); } for (int i = 0; i < special_attacks.size(); i++) { int spec_dam = 0; spec_dam += special_attacks[i].bash; if (special_attacks[i].cut > z->armor_cut()) spec_dam += special_attacks[i].cut - z->armor_cut(); if (special_attacks[i].stab > z->armor_cut() * .8) spec_dam += special_attacks[i].stab - z->armor_cut() * .8; if (!can_poison && one_in(2) && (special_attacks[i].cut > z->armor_cut() || special_attacks[i].stab > z->armor_cut() * .8)) can_poison = true; if (spec_dam > 0) { g->add_msg( special_attacks[i].text.c_str() ); dam += spec_dam; } } if (can_poison && has_trait(PF_POISONOUS)) { if (is_u) g->add_msg("You poison the %s!", z->name().c_str()); z->add_effect(ME_POISONED, 6); } // Make a rather quiet sound, to alert any nearby monsters g->sound(posx, posy, 8, ""); // 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())); dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra remove_weapon(); } if (dam <= 0) { if (is_u) g->add_msg("You hit the %s, but do no damage.", z->name().c_str()); else if (can_see) g->add_msg("%s's %s hits the %s, but does no damage.", You.c_str(), weapon.tname(g).c_str(), z->name().c_str()); practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 2); if (stabbing) practice(sk_stabbing, 2); return 0; } if (is_u) g->add_msg("You hit the %s for %d damage.", z->name().c_str(), dam); else if (can_see) g->add_msg("%s hits the %s with %s %s.", You.c_str(), z->name().c_str(), (male ? "his" : "her"), (weapon.type->id == 0 ? "fists" : weapon.tname(g).c_str())); practice(sk_melee, rng(5, 10)); if (unarmed) practice(sk_unarmed, rng(5, 10)); if (bashing) practice(sk_bashing, rng(5, 10)); if (cutting) practice(sk_cutting, rng(5, 10)); if (stabbing) practice(sk_stabbing, rng(5, 10)); // Penalize the player if their cutting weapon got stuck if (!unarmed && dam < z->hp && cutting_penalty > dice(str_cur * 2, 20)) { if (is_u) g->add_msg("Your %s gets stuck in the %s, pulling it out of your hands!", weapon.tname().c_str(), z->type->name.c_str()); z->add_item(remove_weapon()); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) z->speed *= .7; else z->speed *= .85; } else { if (dam >= z->hp) { cutting_penalty /= 2; cutting_penalty -= rng(sklevel[sk_cutting], sklevel[sk_cutting] * 2 + 2); } if (cutting_penalty > 0) moves -= cutting_penalty; if (cutting_penalty >= 50 && is_u) g->add_msg("Your %s gets stuck in the %s, but you yank it free.", weapon.tname().c_str(), z->type->name.c_str()); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) z->speed *= .9; } return dam; }
void player::complete_craft() { const recipe *making = &recipe_dict[ activity.name ]; // Which recipe is it? int batch_size = activity.values.front(); if( making == nullptr ) { debugmsg( "no recipe with id %s found", activity.name.c_str() ); activity.set_to_null(); return; } int secondary_dice = 0; int secondary_difficulty = 0; for( const auto &pr : making->required_skills ) { secondary_dice += get_skill_level( pr.first ); secondary_difficulty += pr.second; } // # of dice is 75% primary skill, 25% secondary (unless secondary is null) int skill_dice; if( secondary_difficulty > 0 ) { skill_dice = get_skill_level( making->skill_used ) * 3 + secondary_dice; } else { skill_dice = get_skill_level( making->skill_used ) * 4; } auto helpers = g->u.get_crafting_helpers(); for( const npc *np : helpers ) { if( np->get_skill_level( making->skill_used ) >= get_skill_level( making->skill_used ) ) { // NPC assistance is worth half a skill level skill_dice += 2; add_msg( m_info, _( "%s helps with crafting..." ), np->name.c_str() ); break; } } // farsightedness can impose a penalty on electronics and tailoring success // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring if( has_trait( trait_id( "HYPEROPIC" ) ) && !is_wearing( "glasses_reading" ) && !is_wearing( "glasses_bifocal" ) && !has_effect( effect_contacts ) ) { int main_rank_penalty = 0; if( making->skill_used == skill_id( "electronics" ) ) { main_rank_penalty = 2; } else if( making->skill_used == skill_id( "tailor" ) ) { main_rank_penalty = 1; } skill_dice -= main_rank_penalty * 4; } // It's tough to craft with paws. Fortunately it's just a matter of grip and fine-motor, // not inability to see what you're doing if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) { int paws_rank_penalty = 0; if( has_trait( trait_PAWS_LARGE ) ) { paws_rank_penalty += 1; } if( making->skill_used == skill_id( "electronics" ) || making->skill_used == skill_id( "tailor" ) || making->skill_used == skill_id( "mechanics" ) ) { paws_rank_penalty += 1; } skill_dice -= paws_rank_penalty * 4; } // Sides on dice is 16 plus your current intelligence ///\EFFECT_INT increases crafting success chance int skill_sides = 16 + int_cur; int diff_dice; if( secondary_difficulty > 0 ) { diff_dice = making->difficulty * 3 + secondary_difficulty; } else { // Since skill level is * 4 also diff_dice = making->difficulty * 4; } int diff_sides = 24; // 16 + 8 (default intelligence) int skill_roll = dice( skill_dice, skill_sides ); int diff_roll = dice( diff_dice, diff_sides ); if( making->skill_used ) { const double batch_mult = 1 + time_to_craft( *making, batch_size ) / 30000.0; //normalize experience gain to crafting time, giving a bonus for longer crafting practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult ), ( int )making->difficulty * 1.25 ); //NPCs assisting or watching should gain experience... for( auto &elem : helpers ) { //If the NPC can understand what you are doing, they gain more exp if( elem->get_skill_level( making->skill_used ) >= making->difficulty ) { elem->practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .50 ), ( int )making->difficulty * 1.25 ); if( batch_size > 1 ) { add_msg( m_info, _( "%s assists with crafting..." ), elem->name.c_str() ); } if( batch_size == 1 ) { add_msg( m_info, _( "%s could assist you with a batch..." ), elem->name.c_str() ); } //NPCs around you understand the skill used better } else { elem->practice( making->skill_used, ( int )( ( making->difficulty * 15 + 10 ) * batch_mult * .15 ), ( int )making->difficulty * 1.25 ); add_msg( m_info, _( "%s watches you craft..." ), elem->name.c_str() ); } } } // Messed up badly; waste some components. if( making->difficulty != 0 && diff_roll > skill_roll * ( 1 + 0.1 * rng( 1, 5 ) ) ) { add_msg( m_bad, _( "You fail to make the %s, and waste some materials." ), item::nname( making->result ).c_str() ); if( last_craft->has_cached_selections() ) { last_craft->consume_components(); } else { // @todo Guarantee that selections are cached const auto &req = making->requirements(); for( const auto &it : req.get_components() ) { consume_items( it, batch_size ); } for( const auto &it : req.get_tools() ) { consume_tools( it, batch_size ); } } activity.set_to_null(); return; // Messed up slightly; no components wasted. } else if( diff_roll > skill_roll ) { add_msg( m_neutral, _( "You fail to make the %s, but don't waste any materials." ), item::nname( making->result ).c_str() ); //this method would only have been called from a place that nulls activity.type, //so it appears that it's safe to NOT null that variable here. //rationale: this allows certain contexts (e.g. ACT_LONGCRAFT) to distinguish major and minor failures return; } // If we're here, the craft was a success! // Use up the components and tools std::list<item> used; if( !last_craft->has_cached_selections() ) { // This should fail and return, but currently crafting_command isn't saved // Meaning there are still cases where has_cached_selections will be false // @todo Allow saving last_craft and debugmsg+fail craft if selection isn't cached if( !has_trait( trait_id( "DEBUG_HS" ) ) ) { const auto &req = making->requirements(); for( const auto &it : req.get_components() ) { std::list<item> tmp = consume_items( it, batch_size ); used.splice( used.end(), tmp ); } for( const auto &it : req.get_tools() ) { consume_tools( it, batch_size ); } } } else if( !has_trait( trait_id( "DEBUG_HS" ) ) ) { used = last_craft->consume_components(); if( used.empty() ) { return; } } // Set up the new item, and assign an inventory letter if available std::vector<item> newits = making->create_results( batch_size ); bool first = true; float used_age_tally = 0; int used_age_count = 0; size_t newit_counter = 0; for( item &newit : newits ) { // messages, learning of recipe, food spoilage calc only once if( first ) { first = false; if( knows_recipe( making ) ) { add_msg( _( "You craft %s from memory." ), newit.type_name( 1 ).c_str() ); } else { add_msg( _( "You craft %s using a book as a reference." ), newit.type_name( 1 ).c_str() ); // If we made it, but we don't know it, // we're making it from a book and have a chance to learn it. // Base expected time to learn is 1000*(difficulty^4)/skill/int moves. // This means time to learn is greatly decreased with higher skill level, // but also keeps going up as difficulty goes up. // Worst case is lvl 10, which will typically take // 10^4/10 (1,000) minutes, or about 16 hours of crafting it to learn. int difficulty = has_recipe( making, crafting_inventory(), helpers ); ///\EFFECT_INT increases chance to learn recipe when crafting from a book if( x_in_y( making->time, ( 1000 * 8 * ( difficulty * difficulty * difficulty * difficulty ) ) / ( std::max( get_skill_level( making->skill_used ).level(), 1 ) * std::max( get_int(), 1 ) ) ) ) { learn_recipe( ( recipe * )making ); add_msg( m_good, _( "You memorized the recipe for %s!" ), newit.type_name( 1 ).c_str() ); } } for( auto &elem : used ) { if( elem.goes_bad() ) { used_age_tally += elem.get_relative_rot(); ++used_age_count; } } } // Don't store components for things made by charges, // don't store components for things that can't be uncrafted. if( recipe_dictionary::get_uncraft( making->result ) && !newit.count_by_charges() ) { // Setting this for items counted by charges gives only problems: // those items are automatically merged everywhere (map/vehicle/inventory), // which would either loose this information or merge it somehow. set_components( newit.components, used, batch_size, newit_counter ); newit_counter++; } finalize_crafted_item( newit, used_age_tally, used_age_count ); set_item_inventory( newit ); } if( making->has_byproducts() ) { std::vector<item> bps = making->create_byproducts( batch_size ); for( auto &bp : bps ) { finalize_crafted_item( bp, used_age_tally, used_age_count ); set_item_inventory( bp ); } } inv.restack( this ); }
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; }
void beforegame() /******游戏主界面*******/ { int i,j; fire(); ShowBmp256(70,65,".\\source\\login.bmp",1,0); InitGraph(); star=malloc(sizeof(struct STAR)*STARNUM); initstar(star); InitMouse(0,0,maxx,maxy); getdat(); handler=getvect(0x1c); mousex=getmaxx()/2; mousey=getmaxy()/2; logo=bmp_to_dat(".\\source\\snooker.bmp"); vs1=bmp_to_dat(".\\source\\head0.bmp"); vs2=bmp_to_dat(".\\source\\head1.bmp"); play(1); for (i=0;i<3;i++) { putimage(0,0,logo,NOT_PUT); for (j=20;j<=1000;j+=10) { delay(1000); } delay(3000); putimage(0,0,logo,COPY_PUT); for (j=1000;j>=20;j-=10) { delay(2000); } delay(3000); } putimage(0,0,logo,COPY_PUT); for (i=0;i<STARNUM;i++) drawstar(star); outchinese(400,80,s_cn,EOF,4,2); putimage(300,280,vs1,COPY_PUT); putimage(520,280,vs2,COPY_PUT); settextstyle(1,0,12); setcolor(GREEN); outtextxy(380,260,"Vs"); drawmouse(mousex,mousey); play(2); drawmenu(mm); while(1) { while(bioskey(1)!=0) getch(); position(1); mm=fill(); drawmouse(mousex,mousey); if (button==1) { switch (mm) { case 0: { free(logo); free(star); cleardevice(); return; } case 1: { practice(); restore(); break; } case 2: { noplay(); option(); break; } case 3: { noplay(); toprank(); restore(); break; } case 4: { if (Music_on==1) play(0); showhelp(); break; } case 5: { position(0); End(1); drawmouse(mousex,mousey); break; } } } button=0; } }