int player::dodge() { 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 += 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 if (rng(1, sklevel[sk_dodge] + dex_cur + 15) <= sklevel[sk_dodge] + dex_cur) ret = rng(0, ret); else ret = 0; } can_dodge = false; if (ret > int(dex_cur / 2) + sklevel[sk_dodge] * 2) ret = int(dex_cur / 2) + sklevel[sk_dodge] * 2; return ret; }
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; }
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::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; }
// Why put this in a Big Switch? Why not let bionics have pointers to // functions, much like monsters and items? // // Well, because like diseases, which are also in a Big Switch, bionics don't // share functions.... void player::activate_bionic(int b, game *g) { bionic bio = my_bionics[b]; int power_cost = bionics[bio.id].power_cost; if (weapon.type->id == itm_bio_claws && bio.id == bio_claws) power_cost = 0; if (power_level < power_cost) { if (my_bionics[b].powered) { g->add_msg("Your %s powers down.", bionics[bio.id].name.c_str()); my_bionics[b].powered = false; } else g->add_msg("You cannot power your %s", bionics[bio.id].name.c_str()); return; } if (my_bionics[b].powered && my_bionics[b].charge > 0) { // Already-on units just lose a bit of charge my_bionics[b].charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (bionics[bio.id].charge_time > 0) { my_bionics[b].powered = true; my_bionics[b].charge = bionics[bio.id].charge_time; } power_level -= power_cost; } std::string junk; std::vector<point> traj; std::vector<std::string> good; std::vector<std::string> bad; WINDOW* w; int dirx, diry, t, index; unsigned int l; item tmp_item; switch (bio.id) { case bio_painkiller: pkill += 6; pain -= 2; if (pkill > pain) pkill = pain; break; case bio_nanobots: healall(4); break; case bio_resonator: g->sound(posx, posy, 30, "VRRRRMP!"); for (int i = posx - 1; i <= posx + 1; i++) { for (int j = posy - 1; j <= posy + 1; j++) { g->m.bash(i, j, 40, junk); g->m.bash(i, j, 40, junk); // Multibash effect, so that doors &c will fall g->m.bash(i, j, 40, junk); if (g->m.is_destructable(i, j) && rng(1, 10) >= 4) g->m.ter(i, j) = t_rubble; } } break; case bio_time_freeze: moves += 100 * power_level; power_level = 0; g->add_msg("Your speed suddenly increases!"); if (one_in(3)) { g->add_msg("Your muscles tear with the strain."); hurt(g, bp_arms, 0, rng(5, 10)); hurt(g, bp_arms, 1, rng(5, 10)); hurt(g, bp_legs, 0, rng(7, 12)); hurt(g, bp_legs, 1, rng(7, 12)); hurt(g, bp_torso, 0, rng(5, 15)); } if (one_in(5)) add_disease(DI_TELEGLOW, rng(50, 400), g); break; case bio_teleport: g->teleport(); add_disease(DI_TELEGLOW, 300, g); break; // TODO: More stuff here (and bio_blood_filter) case bio_blood_anal: w = newwin(20, 40, 3, 10); wborder(w, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); if (has_disease(DI_FUNGUS)) bad.push_back("Fungal Parasite"); if (has_disease(DI_DERMATIK)) bad.push_back("Insect Parasite"); if (has_disease(DI_POISON)) bad.push_back("Poison"); if (radiation > 0) bad.push_back("Irradiated"); if (has_disease(DI_PKILL1)) good.push_back("Minor Painkiller"); if (has_disease(DI_PKILL2)) good.push_back("Moderate Painkiller"); if (has_disease(DI_PKILL3)) good.push_back("Heavy Painkiller"); if (has_disease(DI_PKILL_L)) good.push_back("Slow-Release Painkiller"); if (has_disease(DI_DRUNK)) good.push_back("Alcohol"); if (has_disease(DI_CIG)) good.push_back("Nicotine"); if (has_disease(DI_HIGH)) good.push_back("Intoxicant: Other"); if (has_disease(DI_TOOK_PROZAC)) good.push_back("Prozac"); if (has_disease(DI_TOOK_FLUMED)) good.push_back("Antihistamines"); if (has_disease(DI_ADRENALINE)) good.push_back("Adrenaline Spike"); if (good.size() == 0 && bad.size() == 0) mvwprintz(w, 1, 1, c_white, "No effects."); else { for (unsigned int line = 1; line < 39 && line <= good.size() + bad.size(); line++) { if (line <= bad.size()) mvwprintz(w, line, 1, c_red, bad[line - 1].c_str()); else mvwprintz(w, line, 1, c_green, good[line - 1 - bad.size()].c_str()); } } wrefresh(w); refresh(); getch(); delwin(w); break; case bio_blood_filter: rem_disease(DI_FUNGUS); rem_disease(DI_POISON); rem_disease(DI_PKILL1); rem_disease(DI_PKILL2); rem_disease(DI_PKILL3); rem_disease(DI_PKILL_L); rem_disease(DI_DRUNK); rem_disease(DI_CIG); rem_disease(DI_HIGH); rem_disease(DI_TOOK_PROZAC); rem_disease(DI_TOOK_FLUMED); rem_disease(DI_ADRENALINE); break; case bio_evap: if (query_yn("Drink directly? Otherwise you will need a container.")) { tmp_item = item(g->itypes[itm_water], 0); thirst -= 50; if (has_trait(PF_GOURMAND) && thirst < -60) { g->add_msg("You can't finish it all!"); thirst = -60; } else if (!has_trait(PF_GOURMAND) && thirst < -20) { g->add_msg("You can't finish it all!"); thirst = -20; } } else { t = g->inv("Choose a container:"); if (i_at(t).type == 0) { g->add_msg("You don't have that item!"); power_level += bionics[bio_evap].power_cost; } else if (!i_at(t).is_container()) { g->add_msg("That %s isn't a container!", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else { it_container *cont = dynamic_cast<it_container*>(i_at(t).type); if (i_at(t).volume_contained() + 1 > cont->contains) { g->add_msg("There's no space left in your %s.", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else if (!(cont->flags & con_wtight)) { g->add_msg("Your %s isn't watertight!", i_at(t).tname().c_str()); power_level += bionics[bio_evap].power_cost; } else { g->add_msg("You pour water into your %s.", i_at(t).tname().c_str()); i_at(t).put_in(item(g->itypes[itm_water], 0)); } } } break; case bio_lighter: g->draw(); mvprintw(0, 0, "Torch in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_lighter].power_cost; return; } dirx += posx; diry += posy; if (!g->m.add_field(g, dirx, diry, fd_fire, 1)) // Unsuccessful. g->add_msg("You can't light a fire there."); break; case bio_claws: if (weapon.type->id == itm_bio_claws) { g->add_msg("You withdraw your claws."); weapon = ret_null; } else if (weapon.type->id != 0) { g->add_msg("Your claws extend, forcing you to drop your %s.", weapon.tname().c_str()); g->m.add_item(posx, posy, weapon); weapon = item(g->itypes[itm_bio_claws], 0); weapon.invlet = '#'; } else { g->add_msg("Your claws extend!"); weapon = item(g->itypes[itm_bio_claws], 0); weapon.invlet = '#'; } break; case bio_blaster: tmp_item = weapon; weapon = item(g->itypes[itm_bio_blaster], 0); weapon.curammo = dynamic_cast<it_ammo*>(g->itypes[itm_bio_fusion]); weapon.charges = 1; g->refresh_all(); g->plfire(false); weapon = tmp_item; break; case bio_laser: tmp_item = weapon; weapon = item(g->itypes[itm_v29], 0); weapon.curammo = dynamic_cast<it_ammo*>(g->itypes[itm_laser_pack]); weapon.charges = 1; g->refresh_all(); g->plfire(false); weapon = tmp_item; break; case bio_emp: g->draw(); mvprintw(0, 0, "Fire EMP in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_emp].power_cost; return; } dirx += posx; diry += posy; g->emp_blast(dirx, diry); break; case bio_hydraulics: g->add_msg("Your muscles hiss as hydraulic strength fills them!"); break; case bio_water_extractor: for (unsigned int i = 0; i < g->m.i_at(posx, posy).size(); i++) { item tmp = g->m.i_at(posx, posy)[i]; if (tmp.type->id == itm_corpse && query_yn("Extract water from the %s", tmp.tname().c_str())) { i = g->m.i_at(posx, posy).size() + 1; // Loop is finished t = g->inv("Choose a container:"); if (i_at(t).type == 0) { g->add_msg("You don't have that item!"); power_level += bionics[bio_water_extractor].power_cost; } else if (!i_at(t).is_container()) { g->add_msg("That %s isn't a container!", i_at(t).tname().c_str()); power_level += bionics[bio_water_extractor].power_cost; } else { it_container *cont = dynamic_cast<it_container*>(i_at(t).type); if (i_at(t).volume_contained() + 1 > cont->contains) { g->add_msg("There's no space left in your %s.", i_at(t).tname().c_str()); power_level += bionics[bio_water_extractor].power_cost; } else { g->add_msg("You pour water into your %s.", i_at(t).tname().c_str()); i_at(t).put_in(item(g->itypes[itm_water], 0)); } } } if (i == g->m.i_at(posx, posy).size() - 1) // We never chose a corpse power_level += bionics[bio_water_extractor].power_cost; } break; case bio_magnet: for (int i = posx - 10; i <= posx + 10; i++) { for (int j = posy - 10; j <= posy + 10; j++) { if (g->m.i_at(i, j).size() > 0) { if (g->m.sees(i, j, posx, posy, -1, t)) traj = line_to(i, j, posx, posy, t); else traj = line_to(i, j, posx, posy, 0); } traj.insert(traj.begin(), point(i, j)); for (unsigned int k = 0; k < g->m.i_at(i, j).size(); k++) { if (g->m.i_at(i, j)[k].made_of(IRON) || g->m.i_at(i, j)[k].made_of(STEEL)){ tmp_item = g->m.i_at(i, j)[k]; g->m.i_rem(i, j, k); for (l = 0; l < traj.size(); l++) { index = g->mon_at(traj[l].x, traj[l].y); if (index != -1) { if (g->z[index].hurt(tmp_item.weight() * 2)) g->kill_mon(index, true); g->m.add_item(traj[l].x, traj[l].y, tmp_item); l = traj.size() + 1; } else if (l > 0 && g->m.move_cost(traj[l].x, traj[l].y) == 0) { g->m.bash(traj[l].x, traj[l].y, tmp_item.weight() * 2, junk); g->sound(traj[l].x, traj[l].y, 12, junk); if (g->m.move_cost(traj[l].x, traj[l].y) == 0) { g->m.add_item(traj[l - 1].x, traj[l - 1].y, tmp_item); l = traj.size() + 1; } } } if (l == traj.size()) g->m.add_item(posx, posy, tmp_item); } } } } break; case bio_lockpick: g->draw(); mvprintw(0, 0, "Unlock in which direction?"); get_direction(g, dirx, diry, input()); if (dirx == -2) { g->add_msg("Invalid direction."); power_level += bionics[bio_lockpick].power_cost; return; } dirx += posx; diry += posy; if (g->m.ter(dirx, diry) == t_door_locked) { moves -= 40; g->add_msg("You unlock the door."); g->m.ter(dirx, diry) = t_door_c; } else g->add_msg("You can't unlock that %s.", g->m.tername(dirx, diry).c_str()); break; // Unused enums added for completeness. default: break; } }
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; }
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::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 npc::took_painkiller() { return (has_disease(DI_PKILL1) || has_disease(DI_PKILL2) || has_disease(DI_PKILL3) || has_disease(DI_PKILL_L)); }
// Why put this in a Big Switch? Why not let bionics have pointers to // functions, much like monsters and items? // // Well, because like diseases, which are also in a Big Switch, bionics don't // share functions.... void player::activate_bionic(int b) { bionic bio = my_bionics[b]; int power_cost = bionics[bio.id]->power_cost; if ((weapon.type->id == "bio_claws_weapon" && bio.id == "bio_claws_weapon") || (weapon.type->id == "bio_blade_weapon" && bio.id == "bio_blade_weapon")) { power_cost = 0; } if (power_level < power_cost) { if (my_bionics[b].powered) { add_msg(m_neutral, _("Your %s powers down."), bionics[bio.id]->name.c_str()); my_bionics[b].powered = false; } else { add_msg(m_info, _("You cannot power your %s"), bionics[bio.id]->name.c_str()); } return; } if (my_bionics[b].powered && my_bionics[b].charge > 0) { // Already-on units just lose a bit of charge my_bionics[b].charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (bionics[bio.id]->charge_time > 0) { my_bionics[b].powered = true; my_bionics[b].charge = bionics[bio.id]->charge_time - 1; } power_level -= power_cost; } std::vector<point> traj; std::vector<std::string> good; std::vector<std::string> bad; int dirx, diry; item tmp_item; if(bio.id == "bio_painkiller") { pkill += 6; pain -= 2; if (pkill > pain) { pkill = pain; } } else if (bio.id == "bio_nanobots") { rem_disease("bleed"); healall(4); } else if (bio.id == "bio_night") { if (calendar::turn % 5) { add_msg(m_neutral, _("Artificial night generator active!")); } } else if (bio.id == "bio_resonator") { g->sound(posx, posy, 30, _("VRRRRMP!")); for (int i = posx - 1; i <= posx + 1; i++) { for (int j = posy - 1; j <= posy + 1; j++) { g->m.bash( i, j, 40 ); g->m.bash( i, j, 40 ); // Multibash effect, so that doors &c will fall g->m.bash( i, j, 40 ); if (g->m.is_destructable(i, j) && rng(1, 10) >= 4) { g->m.ter_set(i, j, t_rubble); } } } } else if (bio.id == "bio_time_freeze") { moves += power_level; power_level = 0; add_msg(m_good, _("Your speed suddenly increases!")); if (one_in(3)) { add_msg(m_bad, _("Your muscles tear with the strain.")); apply_damage( nullptr, bp_arm_l, rng( 5, 10 ) ); apply_damage( nullptr, bp_arm_r, rng( 5, 10 ) ); apply_damage( nullptr, bp_leg_l, rng( 7, 12 ) ); apply_damage( nullptr, bp_leg_r, rng( 7, 12 ) ); apply_damage( nullptr, bp_torso, rng( 5, 15 ) ); } if (one_in(5)) { add_disease("teleglow", rng(50, 400)); } } else if (bio.id == "bio_teleport") { g->teleport(); add_disease("teleglow", 300); } // TODO: More stuff here (and bio_blood_filter) else if(bio.id == "bio_blood_anal") { WINDOW *w = newwin(20, 40, 3 + ((TERMY > 25) ? (TERMY - 25) / 2 : 0), 10 + ((TERMX > 80) ? (TERMX - 80) / 2 : 0)); draw_border(w); if (has_disease("fungus")) { bad.push_back(_("Fungal Parasite")); } if (has_disease("dermatik")) { bad.push_back(_("Insect Parasite")); } if (has_effect("stung")) { bad.push_back(_("Stung")); } if (has_effect("poison")) { bad.push_back(_("Poison")); } if (radiation > 0) { bad.push_back(_("Irradiated")); } if (has_disease("pkill1")) { good.push_back(_("Minor Painkiller")); } if (has_disease("pkill2")) { good.push_back(_("Moderate Painkiller")); } if (has_disease("pkill3")) { good.push_back(_("Heavy Painkiller")); } if (has_disease("pkill_l")) { good.push_back(_("Slow-Release Painkiller")); } if (has_disease("drunk")) { good.push_back(_("Alcohol")); } if (has_disease("cig")) { good.push_back(_("Nicotine")); } if (has_disease("meth")) { good.push_back(_("Methamphetamines")); } if (has_disease("high")) { good.push_back(_("Intoxicant: Other")); } if (has_disease("weed_high")) { good.push_back(_("THC Intoxication")); } if (has_disease("hallu") || has_disease("visuals")) { bad.push_back(_("Magic Mushroom")); } if (has_disease("iodine")) { good.push_back(_("Iodine")); } if (has_disease("datura")) { good.push_back(_("Anticholinergic Tropane Alkaloids")); } if (has_disease("took_xanax")) { good.push_back(_("Xanax")); } if (has_disease("took_prozac")) { good.push_back(_("Prozac")); } if (has_disease("took_flumed")) { good.push_back(_("Antihistamines")); } if (has_disease("adrenaline")) { good.push_back(_("Adrenaline Spike")); } if (has_disease("tapeworm")) { // This little guy is immune to the blood filter though, as he lives in your bowels. good.push_back(_("Intestinal Parasite")); } if (has_disease("bloodworms")) { good.push_back(_("Hemolytic Parasites")); } if (has_disease("brainworm")) { // This little guy is immune to the blood filter too, as he lives in your brain. good.push_back(_("Intracranial Parasite")); } if (has_disease("paincysts")) { // These little guys are immune to the blood filter too, as they live in your muscles. good.push_back(_("Intramuscular Parasites")); } if (has_disease("tetanus")) { // Tetanus infection. good.push_back(_("Clostridium Tetani Infection")); } if (good.empty() && bad.empty()) { mvwprintz(w, 1, 1, c_white, _("No effects.")); } else { for (unsigned line = 1; line < 39 && line <= good.size() + bad.size(); line++) { if (line <= bad.size()) { mvwprintz(w, line, 1, c_red, "%s", bad[line - 1].c_str()); } else { mvwprintz(w, line, 1, c_green, "%s", good[line - 1 - bad.size()].c_str()); } } } wrefresh(w); refresh(); getch(); delwin(w); } else if(bio.id == "bio_blood_filter") { add_msg(m_neutral, _("You activate your blood filtration system.")); rem_disease("fungus"); rem_disease("dermatik"); rem_disease("bloodworms"); rem_disease("tetanus"); remove_effect("poison"); remove_effect("stung"); rem_disease("pkill1"); rem_disease("pkill2"); rem_disease("pkill3"); rem_disease("pkill_l"); rem_disease("drunk"); rem_disease("cig"); rem_disease("high"); rem_disease("hallu"); rem_disease("visuals"); rem_disease("iodine"); rem_disease("datura"); rem_disease("took_xanax"); rem_disease("took_prozac"); rem_disease("took_flumed"); rem_disease("adrenaline"); rem_disease("meth"); pkill = 0; stim = 0; } else if(bio.id == "bio_evap") { item water = item("water_clean", 0); int humidity = g->weatherGen.get_weather(pos(), calendar::turn).humidity; int water_charges = (humidity * 3.0) / 100.0 + 0.5; // At 50% relative humidity or more, the player will draw 2 units of water // At 16% relative humidity or less, the player will draw 0 units of water water.charges = water_charges; if (water_charges == 0) { add_msg_if_player(m_bad, _("There was not enough moisture in the air from which to draw water!")); } if (g->handle_liquid(water, true, false)) { moves -= 100; } else if (query_yn(_("Drink from your hands?"))) { inv.push_back(water); consume(inv.position_by_type(water.typeId())); moves -= 350; } else if (water.charges == water_charges && water_charges != 0) { power_level += bionics["bio_evap"]->power_cost; } } else if(bio.id == "bio_lighter") { if(!choose_adjacent(_("Start a fire where?"), dirx, diry) || (!g->m.add_field(dirx, diry, fd_fire, 1))) { add_msg_if_player(m_info, _("You can't light a fire there.")); power_level += bionics["bio_lighter"]->power_cost; } } if(bio.id == "bio_leukocyte") { add_msg(m_neutral, _("You activate your leukocyte breeder system.")); g->u.set_healthy(std::min(100, g->u.get_healthy() + 2)); g->u.mod_healthy_mod(20); } if(bio.id == "bio_geiger") { add_msg(m_info, _("Your radiation level: %d"), radiation); } if(bio.id == "bio_radscrubber") { add_msg(m_neutral, _("You activate your radiation scrubber system.")); if (radiation > 4) { radiation -= 5; } else { radiation = 0; } } if(bio.id == "bio_adrenaline") { add_msg(m_neutral, _("You activate your adrenaline pump.")); if (has_disease("adrenaline")) { add_disease("adrenaline", 50); } else { add_disease("adrenaline", 200); } } else if(bio.id == "bio_claws") { if (weapon.type->id == "bio_claws_weapon") { add_msg(m_neutral, _("You withdraw your claws.")); weapon = ret_null; } else if (weapon.has_flag ("NO_UNWIELD")) { add_msg(m_info, _("Deactivate your %s first!"), weapon.tname().c_str()); power_level += bionics[bio.id]->power_cost; return; } else if(weapon.type->id != "null") { add_msg(m_warning, _("Your claws extend, forcing you to drop your %s."), weapon.tname().c_str()); g->m.add_item_or_charges(posx, posy, weapon); weapon = item("bio_claws_weapon", 0); weapon.invlet = '#'; } else { add_msg(m_neutral, _("Your claws extend!")); weapon = item("bio_claws_weapon", 0); weapon.invlet = '#'; } } else if(bio.id == "bio_blade") { if (weapon.type->id == "bio_blade_weapon") { add_msg(m_neutral, _("You retract your blade.")); weapon = ret_null; } else if (weapon.has_flag ("NO_UNWIELD")) { add_msg(m_info, _("Deactivate your %s first!"), weapon.tname().c_str()); power_level += bionics[bio.id]->power_cost; return; } else if(weapon.type->id != "null") { add_msg(m_warning, _("Your blade extends, forcing you to drop your %s."), weapon.tname().c_str()); g->m.add_item_or_charges(posx, posy, weapon); weapon = item("bio_blade_weapon", 0); weapon.invlet = '#'; } else { add_msg(m_neutral, _("You extend your blade!")); weapon = item("bio_blade_weapon", 0); weapon.invlet = '#'; } } else if(bio.id == "bio_blaster") { tmp_item = weapon; weapon = item("bio_blaster_gun", 0); g->refresh_all(); g->plfire(false); if(weapon.charges == 1) { // not fired power_level += bionics[bio.id]->power_cost; } weapon = tmp_item; } else if (bio.id == "bio_laser") { tmp_item = weapon; weapon = item("bio_laser_gun", 0); g->refresh_all(); g->plfire(false); if(weapon.charges == 1) { // not fired power_level += bionics[bio.id]->power_cost; } weapon = tmp_item; } else if(bio.id == "bio_chain_lightning") { tmp_item = weapon; weapon = item("bio_lightning", 0); g->refresh_all(); g->plfire(false); if(weapon.charges == 1) { // not fired power_level += bionics[bio.id]->power_cost; } weapon = tmp_item; } else if (bio.id == "bio_emp") { if(choose_adjacent(_("Create an EMP where?"), dirx, diry)) { g->emp_blast(dirx, diry); } else { power_level += bionics["bio_emp"]->power_cost; } } else if (bio.id == "bio_hydraulics") { add_msg(m_good, _("Your muscles hiss as hydraulic strength fills them!")); // Sound of hissing hydraulic muscle! (not quite as loud as a car horn) g->sound(posx, posy, 19, _("HISISSS!")); } else if (bio.id == "bio_water_extractor") { bool extracted = false; for (std::vector<item>::iterator it = g->m.i_at(posx, posy).begin(); it != g->m.i_at(posx, posy).end(); ++it) { if (it->type->id == "corpse" ) { int avail = 0; if ( it->item_vars.find("remaining_water") != it->item_vars.end() ) { avail = atoi ( it->item_vars["remaining_water"].c_str() ); } else { avail = it->volume() / 2; } if(avail > 0 && query_yn(_("Extract water from the %s"), it->tname().c_str())) { item water = item("water_clean", 0); if (g->handle_liquid(water, true, true)) { moves -= 100; } else if (query_yn(_("Drink directly from the condenser?"))) { inv.push_back(water); consume(inv.position_by_type(water.typeId())); moves -= 350; } extracted = true; avail--; it->item_vars["remaining_water"] = string_format("%d", avail); break; } } } if (!extracted) { power_level += bionics["bio_water_extractor"]->power_cost; } } else if(bio.id == "bio_magnet") { for (int i = posx - 10; i <= posx + 10; i++) { for (int j = posy - 10; j <= posy + 10; j++) { if (g->m.i_at(i, j).size() > 0) { int t; //not sure why map:sees really needs this, but w/e if (g->m.sees(i, j, posx, posy, -1, t)) { traj = line_to(i, j, posx, posy, t); } else { traj = line_to(i, j, posx, posy, 0); } } traj.insert(traj.begin(), point(i, j)); for (unsigned k = 0; k < g->m.i_at(i, j).size(); k++) { if (g->m.i_at(i, j)[k].made_of("iron") || g->m.i_at(i, j)[k].made_of("steel")) { tmp_item = g->m.i_at(i, j)[k]; g->m.i_rem(i, j, k); std::vector<point>::iterator it; for (it = traj.begin(); it != traj.end(); ++it) { int index = g->mon_at(it->x, it->y); if (index != -1) { g->zombie(index).apply_damage( this, bp_torso, tmp_item.weight() / 225 ); g->m.add_item_or_charges(it->x, it->y, tmp_item); break; } else if (it != traj.begin() && g->m.move_cost(it->x, it->y) == 0) { g->m.bash( it->x, it->y, tmp_item.weight() / 225 ); if (g->m.move_cost(it->x, it->y) == 0) { g->m.add_item_or_charges((it - 1)->x, (it - 1)->y, tmp_item); break; } } } if (it == traj.end()) { g->m.add_item_or_charges(posx, posy, tmp_item); } } } } } } else if(bio.id == "bio_lockpick") { if(!choose_adjacent(_("Activate your bio lockpick where?"), dirx, diry)) { power_level += bionics["bio_lockpick"]->power_cost; return; } ter_id type = g->m.ter(dirx, diry); if (type == t_door_locked || type == t_door_locked_alarm || type == t_door_locked_interior ) { moves -= 40; std::string door_name = rm_prefix(_("<door_name>door")); add_msg_if_player(m_neutral, _("With a satisfying click, the lock on the %s opens."), door_name.c_str()); g->m.ter_set(dirx, diry, t_door_c); // Locked metal doors are the Lab and Bunker entries. Those need to stay locked. } else if(type == t_door_bar_locked) { moves -= 40; std::string door_name = rm_prefix(_("<door_name>door")); add_msg_if_player(m_neutral, _("The %s swings open..."), door_name.c_str()); //Could better copy the messages from lockpick.... g->m.ter_set(dirx, diry, t_door_bar_o); } else if(type == t_chaingate_l) { moves -= 40; std::string gate_name = rm_prefix (_("<door_name>gate")); add_msg_if_player(m_neutral, _("With a satisfying click, the lock on the %s opens."), gate_name.c_str()); g->m.ter_set(dirx, diry, t_chaingate_c); } else if(type == t_door_c) { add_msg(m_info, _("That door isn't locked.")); } else { add_msg_if_player(m_neutral, _("You can't unlock that %s."), g->m.tername(dirx, diry).c_str()); } } else if(bio.id == "bio_flashbang") { add_msg_if_player(m_neutral, _("You activate your integrated flashbang generator!")); g->flashbang(posx, posy, true); } else if(bio.id == "bio_shockwave") { g->shockwave(posx, posy, 3, 4, 2, 8, true); add_msg_if_player(m_neutral, _("You unleash a powerful shockwave!")); } }
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; }