int npc::average_damage_dealt() { int ret = base_damage(); ret += weapon.damage_cut() + weapon.damage_bash() / 2; ret *= (base_to_hit() + weapon.type->m_to_hit); ret /= 15; 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; }
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); }
RPG_Player_PlayerXML_XMLTree_Type* RPG_Player_Common_Tools::playerToPlayerXML(const RPG_Player& player_in) { RPG_TRACE(ACE_TEXT("RPG_Player_Common_Tools::playerToPlayerXML")); RPG_Character_Alignment_XMLTree_Type alignment(RPG_Character_AlignmentCivicHelper::RPG_Character_AlignmentCivicToString(player_in.getAlignment().civic), RPG_Character_AlignmentEthicHelper::RPG_Character_AlignmentEthicToString(player_in.getAlignment().ethic)); RPG_Character_Attributes_XMLTree_Type attributes(player_in.getAttribute(ATTRIBUTE_STRENGTH), player_in.getAttribute(ATTRIBUTE_DEXTERITY), player_in.getAttribute(ATTRIBUTE_CONSTITUTION), player_in.getAttribute(ATTRIBUTE_INTELLIGENCE), player_in.getAttribute(ATTRIBUTE_WISDOM), player_in.getAttribute(ATTRIBUTE_CHARISMA)); RPG_Player_Conditions_XMLTree_Type conditions; RPG_Character_Conditions_t character_condition = player_in.getCondition(); for (RPG_Character_ConditionsIterator_t iterator = character_condition.begin(); iterator != character_condition.end(); iterator++) conditions.condition().push_back(RPG_Common_ConditionHelper::RPG_Common_ConditionToString(*iterator)); RPG_Item_InventoryXML_XMLTree_Type inventory; RPG_Player_Inventory character_inventory = player_in.getInventory(); RPG_Item_Base* item_base = NULL; for (RPG_Item_ListIterator_t iterator = character_inventory.myItems.begin(); iterator != character_inventory.myItems.end(); iterator++) { // retrieve item details RPG_ITEM_INSTANCE_MANAGER_SINGLETON::instance()->get(*iterator, item_base); ACE_ASSERT(item_base); RPG_Item_BaseXML_XMLTree_Type item(RPG_Item_TypeHelper::RPG_Item_TypeToString(item_base->getType())); switch (item_base->getType()) { case ITEM_ARMOR: { RPG_Item_Armor* armor = dynamic_cast<RPG_Item_Armor*>(item_base); ACE_ASSERT(armor); RPG_Item_ArmorProperties armor_properties = RPG_ITEM_DICTIONARY_SINGLETON::instance()->getArmorProperties(armor->getArmorType()); RPG_Item_StorePrice_XMLTree_Type store_price; if (armor_properties.baseStorePrice.numGoldPieces) store_price.numGoldPieces(armor_properties.baseStorePrice.numGoldPieces); if (armor_properties.baseStorePrice.numSilverPieces) store_price.numSilverPieces(armor_properties.baseStorePrice.numSilverPieces); RPG_Item_ArmorPropertiesXML_XMLTree_Type armor_properties_xml(armor_properties.baseWeight, store_price, RPG_Item_ArmorTypeHelper::RPG_Item_ArmorTypeToString(armor->getArmorType()), RPG_Item_ArmorCategoryHelper::RPG_Item_ArmorCategoryToString(armor_properties.category), armor_properties.baseBonus, armor_properties.maxDexterityBonus, armor_properties.checkPenalty, armor_properties.arcaneSpellFailure, armor_properties.baseSpeed); if (armor_properties.defenseModifier) armor_properties_xml.defenseModifier(armor_properties.defenseModifier); if (armor_properties.aura != RPG_MAGIC_SCHOOL_INVALID) armor_properties_xml.aura(RPG_Magic_SchoolHelper::RPG_Magic_SchoolToString(armor_properties.aura)); if (armor_properties.prerequisites.minCasterLevel) { RPG_Item_MagicalPrerequisites_XMLTree_Type magical_prerequisites; magical_prerequisites.minCasterLevel(armor_properties.prerequisites.minCasterLevel); armor_properties_xml.prerequisites(magical_prerequisites); } // end IF if (armor_properties.costToCreate.numGoldPieces || armor_properties.costToCreate.numExperiencePoints) { RPG_Item_CreationCost_XMLTree_Type costs_to_create; if (armor_properties.costToCreate.numGoldPieces) costs_to_create.numGoldPieces(armor_properties.costToCreate.numGoldPieces); if (armor_properties.costToCreate.numExperiencePoints) costs_to_create.numExperiencePoints(armor_properties.costToCreate.numExperiencePoints); armor_properties_xml.costToCreate(costs_to_create); } // end IF item.armor(armor_properties_xml); inventory.item().push_back(item); break; } case ITEM_COMMODITY: { RPG_Item_Commodity* commodity = dynamic_cast<RPG_Item_Commodity*>(item_base); ACE_ASSERT(commodity); RPG_Item_CommodityProperties commodity_properties = RPG_ITEM_DICTIONARY_SINGLETON::instance()->getCommodityProperties(commodity->getCommoditySubType()); RPG_Item_StorePrice_XMLTree_Type store_price; if (commodity_properties.baseStorePrice.numGoldPieces) store_price.numGoldPieces(commodity_properties.baseStorePrice.numGoldPieces); if (commodity_properties.baseStorePrice.numSilverPieces) store_price.numSilverPieces(commodity_properties.baseStorePrice.numSilverPieces); RPG_Item_CommodityPropertiesBase_XMLTree_Type commodity_properties_xml(commodity_properties.baseWeight, store_price, RPG_Item_CommodityTypeHelper::RPG_Item_CommodityTypeToString(commodity->getCommodityType()), RPG_Item_Common_Tools::commoditySubTypeToXMLString(commodity->getCommoditySubType())); if (commodity_properties.aura != RPG_MAGIC_SCHOOL_INVALID) commodity_properties_xml.aura(RPG_Magic_SchoolHelper::RPG_Magic_SchoolToString(commodity_properties.aura)); if (commodity_properties.prerequisites.minCasterLevel) { RPG_Item_MagicalPrerequisites_XMLTree_Type magical_prerequisites; magical_prerequisites.minCasterLevel(commodity_properties.prerequisites.minCasterLevel); commodity_properties_xml.prerequisites(magical_prerequisites); } // end IF if (commodity_properties.costToCreate.numGoldPieces || commodity_properties.costToCreate.numExperiencePoints) { RPG_Item_CreationCost_XMLTree_Type costs_to_create; if (commodity_properties.costToCreate.numGoldPieces) costs_to_create.numGoldPieces(commodity_properties.costToCreate.numGoldPieces); if (commodity_properties.costToCreate.numExperiencePoints) costs_to_create.numExperiencePoints(commodity_properties.costToCreate.numExperiencePoints); commodity_properties_xml.costToCreate(costs_to_create); } // end IF item.commodity(commodity_properties_xml); inventory.item().push_back(item); break; } case ITEM_OTHER: case ITEM_VALUABLE: { // *TODO* ACE_ASSERT(false); break; } case ITEM_WEAPON: { RPG_Item_Weapon* weapon = dynamic_cast<RPG_Item_Weapon*>(item_base); ACE_ASSERT(weapon); RPG_Item_WeaponProperties weapon_properties = RPG_ITEM_DICTIONARY_SINGLETON::instance()->getWeaponProperties(weapon->getWeaponType()); RPG_Item_BaseXML_XMLTree_Type item(RPG_Item_TypeHelper::RPG_Item_TypeToString(item_base->getType())); RPG_Item_StorePrice_XMLTree_Type store_price; if (weapon_properties.baseStorePrice.numGoldPieces) store_price.numGoldPieces(weapon_properties.baseStorePrice.numGoldPieces); if (weapon_properties.baseStorePrice.numSilverPieces) store_price.numSilverPieces(weapon_properties.baseStorePrice.numSilverPieces); RPG_Dice_Roll_XMLTree_Type base_damage(RPG_Dice_DieTypeHelper::RPG_Dice_DieTypeToString(weapon_properties.baseDamage.typeDice)); if (weapon_properties.baseDamage.numDice) base_damage.numDice(weapon_properties.baseDamage.numDice); if (weapon_properties.baseDamage.modifier) base_damage.modifier(weapon_properties.baseDamage.modifier); RPG_Item_CriticalHitProperties_XMLTree_Type critical_hit_properties(weapon_properties.criticalHit.minToHitRoll, weapon_properties.criticalHit.damageModifier); RPG_Item_WeaponPropertiesXML_XMLTree_Type weapon_properties_xml(weapon_properties.baseWeight, store_price, RPG_Item_WeaponTypeHelper::RPG_Item_WeaponTypeToString(weapon->getWeaponType()), RPG_Item_WeaponCategoryHelper::RPG_Item_WeaponCategoryToString(weapon_properties.category), RPG_Item_WeaponClassHelper::RPG_Item_WeaponClassToString(weapon_properties.weaponClass), base_damage, critical_hit_properties); if (weapon_properties.toHitModifier) weapon_properties_xml.toHitModifier(weapon_properties.toHitModifier); if (weapon_properties.rangeIncrement) weapon_properties_xml.rangeIncrement(weapon_properties.rangeIncrement); RPG_Item_WeaponPropertiesBase_XMLTree_Type::typeOfDamage_sequence type_of_damage; int index = PHYSICALDAMAGE_NONE; index++; for (unsigned int i = 0; i < weapon_properties.typeOfDamage.size(); i++, index++) if (weapon_properties.typeOfDamage.test(i)) type_of_damage.push_back(RPG_Common_PhysicalDamageTypeHelper::RPG_Common_PhysicalDamageTypeToString(static_cast<RPG_Common_PhysicalDamageType>(index))); weapon_properties_xml.typeOfDamage(type_of_damage); weapon_properties_xml.isNonLethal(weapon_properties.isNonLethal); weapon_properties_xml.isReachWeapon(weapon_properties.isReachWeapon); weapon_properties_xml.isDoubleWeapon(weapon_properties.isDoubleWeapon); if (weapon_properties.aura != RPG_MAGIC_SCHOOL_INVALID) weapon_properties_xml.aura(RPG_Magic_SchoolHelper::RPG_Magic_SchoolToString(weapon_properties.aura)); if (weapon_properties.prerequisites.minCasterLevel) { RPG_Item_MagicalPrerequisites_XMLTree_Type magical_prerequisites; magical_prerequisites.minCasterLevel(weapon_properties.prerequisites.minCasterLevel); weapon_properties_xml.prerequisites(magical_prerequisites); } // end IF if (weapon_properties.costToCreate.numGoldPieces || weapon_properties.costToCreate.numExperiencePoints) { RPG_Item_CreationCost_XMLTree_Type costs_to_create; if (weapon_properties.costToCreate.numGoldPieces) costs_to_create.numGoldPieces(weapon_properties.costToCreate.numGoldPieces); if (weapon_properties.costToCreate.numExperiencePoints) costs_to_create.numExperiencePoints(weapon_properties.costToCreate.numExperiencePoints); weapon_properties_xml.costToCreate(costs_to_create); } // end IF item.weapon(weapon_properties_xml); inventory.item().push_back(item); break; } default: { ACE_DEBUG((LM_ERROR, ACE_TEXT("invalid item type (was: \"%s\"), aborting\n"), ACE_TEXT(RPG_Item_TypeHelper::RPG_Item_TypeToString(item_base->getType()).c_str()))); return NULL; } } // end SWITCH } // end FOR RPG_Character_Class character_class = player_in.getClass(); RPG_Character_ClassXML_XMLTree_Type classXML(RPG_Character_MetaClass_XMLTree_Type(static_cast<RPG_Character_MetaClass_XMLTree_Type::value>(character_class.metaClass))); for (RPG_Character_SubClassesIterator_t iterator = character_class.subClasses.begin(); iterator != character_class.subClasses.end(); iterator++) classXML.subClass().push_back(RPG_Common_SubClass_XMLTree_Type(static_cast<RPG_Common_SubClass_XMLTree_Type::value>(*iterator))); RPG_Player_PlayerXML_XMLTree_Type* player_p = NULL; ACE_NEW_NORETURN(player_p, RPG_Player_PlayerXML_XMLTree_Type(player_in.getName(), alignment, attributes, RPG_Common_Size_XMLTree_Type(static_cast<RPG_Common_Size_XMLTree_Type::value>(player_in.getSize())), player_in.getNumTotalHitPoints(), classXML, RPG_Character_Gender_XMLTree_Type(static_cast<RPG_Character_Gender_XMLTree_Type::value>(player_in.getGender())), RPG_Character_OffHand_XMLTree_Type(static_cast<RPG_Character_OffHand_XMLTree_Type::value>(player_in.getOffHand())), player_in.getWealth(), inventory, player_in.getExperience())); if (!player_p) { ACE_DEBUG((LM_CRITICAL, ACE_TEXT("failed to allocate memory(%u), aborting\n"), sizeof(RPG_Player_PlayerXML_XMLTree_Type))); return NULL; } // end IF ACE_ASSERT(player_p); // *NOTE*: add race, skills, feats, abilities, known spells, prepared spells sequences "manually" RPG_Character_Race_t character_race = player_in.getRace(); int index = RACE_NONE; index++; for (unsigned int i = 0; i < character_race.size(); i++, index++) if (character_race.test(i)) player_p->race().push_back(RPG_Character_RaceHelper::RPG_Character_RaceToString(static_cast<RPG_Character_Race>(index))); RPG_Character_Skills_XMLTree_Type skills; RPG_Character_Skills_t character_skills = player_in.getSkills(); for (RPG_Character_SkillsConstIterator_t iterator = character_skills.begin(); iterator != character_skills.end(); iterator++) { RPG_Character_SkillValue_XMLTree_Type skill(RPG_Common_SkillHelper::RPG_Common_SkillToString((*iterator).first), (*iterator).second); skills.skill().push_back(skill); } // end FOR player_p->skills().set(skills); RPG_Character_Feats_XMLTree_Type feats; RPG_Character_Feats_t character_feats = player_in.getFeats(); for (RPG_Character_FeatsConstIterator_t iterator = character_feats.begin(); iterator != character_feats.end(); iterator++) feats.feat().push_back(RPG_Character_FeatHelper::RPG_Character_FeatToString(*iterator)); player_p->feats().set(feats); RPG_Player_Abilities_XMLTree_Type abilities; RPG_Character_Abilities_t character_abilities = player_in.getAbilities(); for (RPG_Character_AbilitiesConstIterator_t iterator = character_abilities.begin(); iterator != character_abilities.end(); iterator++) abilities.ability().push_back(RPG_Character_AbilityHelper::RPG_Character_AbilityToString(*iterator)); player_p->abilities().set(abilities); RPG_Player_Spells_XMLTree_Type spell_list; RPG_Magic_SpellTypes_t character_known_spells = player_in.getKnownSpells(); for (RPG_Magic_SpellTypesIterator_t iterator = character_known_spells.begin(); iterator != character_known_spells.end(); iterator++) spell_list.spell().push_back(RPG_Magic_SpellTypeHelper::RPG_Magic_SpellTypeToString(*iterator)); player_p->knownSpells().set(spell_list); return player_p; }
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; }