#include "weaponpriority.hpp" #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "npcstats.hpp" #include "combat.hpp" #include "aicombataction.hpp" #include "spellpriority.hpp" #include "spellcasting.hpp" namespace MWMechanics { float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type, float arrowRating, float boltRating) { if (item.getTypeName() != typeid(ESM::Weapon).name()) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; if (type != -1 && weapon->mData.mType != type) return 0.f; float rating=0.f; float rangedMult=1.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { // Range weapon is useless under water if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)) return 0.f; if (enemy.isEmpty()) return 0.f; if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) rangedMult = 1.5f; } if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) { float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1]; MWMechanics::adjustWeaponDamage(rangedDamage, item, actor); rating = rangedDamage / 2.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } else { float meleeDamage = 0.f; for (int i=0; i<2; ++i) { meleeDamage += weapon->mData.mSlash[i]; meleeDamage += weapon->mData.mThrust[i]; meleeDamage += weapon->mData.mChop[i]; } MWMechanics::adjustWeaponDamage(meleeDamage, item, actor); rating = meleeDamage / 6.f; MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } if (item.getClass().hasItemHealth(item)) { if (item.getClass().getItemHealth(item) == 0) return 0.f; } if (weapon->mData.mType == ESM::Weapon::MarksmanBow) { if (arrowRating <= 0.f) rating = 0.f; else rating += arrowRating; } else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow) { if (boltRating <= 0.f) rating = 0.f; else rating += boltRating; } if (!weapon->mEnchant.empty()) { const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(weapon->mEnchant); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { int castCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), actor); if (item.getCellRef().getEnchantmentCharge() == -1 || item.getCellRef().getEnchantmentCharge() >= castCost) rating += rateEffects(enchantment->mEffects, actor, enemy); } } int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) { int value = actor.getClass().getSkill(actor, skill); rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; } return rating * rangedMult; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType) { float bestAmmoRating = 0.f; if (!actor.getClass().hasInventoryStore(actor)) return bestAmmoRating; MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { float rating = rateWeapon(*it, actor, enemy, ammoType); if (rating > bestAmmoRating) { bestAmmoRating = rating; bestAmmo = *it; } } return bestAmmoRating; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, ESM::Weapon::Type ammoType) { MWWorld::Ptr emptyPtr; return rateAmmo(actor, enemy, emptyPtr, ammoType); } float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat(); static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat(); static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat(); if (weapon.isEmpty()) return 0.f; float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f; float chopMult = fAIMeleeWeaponMult; float bonusDamage = 0.f; const ESM::Weapon* esmWeap = weapon.get()->mBase; if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) { if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy)) { bonusDamage = ammo.get()->mBase->mData.mChop[1]; chopMult = fAIRangeMeleeWeaponMult; } else chopMult = 0.f; } float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult; float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult; float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult; return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult + std::max(std::max(chopRating, slashRating), thrustRating); } }