From 0bdc1b243ae23e8f51a6cced0a5016ed02f66abf Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 28 Aug 2014 01:54:32 +0200 Subject: [PATCH] Consider weapon ammunition in combat AI (Fixes #1576) --- apps/openmw/mwmechanics/aicombataction.cpp | 74 ++++++++++++++++++++-- apps/openmw/mwmechanics/aicombataction.hpp | 12 +++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 7a8ee75fc..528bd6d6c 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -73,14 +73,16 @@ namespace MWMechanics return rateEffects(potion->mEffects, actor, MWWorld::Ptr()); } - float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) + float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& target, int type, + float arrowRating, float boltRating) { if (item.getTypeName() != typeid(ESM::Weapon).name()) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; - // TODO: Check that we have ammunition if needed + if (type != -1 && weapon->mData.mType != type) + return 0.f; float rating=0.f; @@ -102,7 +104,20 @@ namespace MWMechanics if (item.getClass().hasItemHealth(item)) rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item)); - rating *= actor.getClass().getSkill(actor, item.getClass().getEquipmentSkill(item)) / 100.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()) { @@ -112,6 +127,11 @@ namespace MWMechanics || item.getCellRef().getEnchantmentCharge() >= enchantment->mData.mCost)) rating += rateEffects(enchantment->mEffects, actor, target); } + + int skill = item.getClass().getEquipmentSkill(item); + if (skill != -1) + rating *= actor.getClass().getSkill(actor, skill) / 100.f; + return rating; } @@ -323,7 +343,12 @@ namespace MWMechanics MWWorld::ActionEquip equip(mWeapon); equip.execute(actor); } - // TODO: equip ammunition and shield where needed + + if (!mAmmunition.isEmpty()) + { + MWWorld::ActionEquip equip(mAmmunition); + equip.execute(actor); + } } actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Weapon); } @@ -365,13 +390,50 @@ namespace MWMechanics } } + float bestArrowRating = 0; + MWWorld::Ptr bestArrow; + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + float rating = rateWeapon(*it, actor, target, ESM::Weapon::Arrow); + if (rating > bestArrowRating) + { + bestArrowRating = rating; + bestArrow = *it; + } + } + + float bestBoltRating = 0; + MWWorld::Ptr bestBolt; + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + float rating = rateWeapon(*it, actor, target, ESM::Weapon::Bolt); + if (rating > bestBoltRating) + { + bestBoltRating = rating; + bestBolt = *it; + } + } + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - float rating = rateWeapon(*it, actor, target); + std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; + if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) + == equipmentSlots.end()) + continue; + + float rating = rateWeapon(*it, actor, target, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { + const ESM::Weapon* weapon = it->get()->mBase; + + MWWorld::Ptr ammo; + if (weapon->mData.mType == ESM::Weapon::MarksmanBow) + ammo = bestArrow; + else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow) + ammo = bestBolt; + bestActionRating = rating; - bestAction.reset(new ActionWeapon(*it)); + bestAction.reset(new ActionWeapon(*it, ammo)); } } } diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 8c95e14c6..487405838 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -58,10 +58,14 @@ namespace MWMechanics class ActionWeapon : public Action { + private: + MWWorld::Ptr mAmmunition; + MWWorld::Ptr mWeapon; + public: /// \a weapon may be empty for hand-to-hand combat - ActionWeapon(const MWWorld::Ptr& weapon) : mWeapon(weapon) {} - MWWorld::Ptr mWeapon; + ActionWeapon(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo = MWWorld::Ptr()) + : mWeapon(weapon), mAmmunition(ammo) {} /// Equips the given weapon. virtual void prepare(const MWWorld::Ptr& actor); virtual void getCombatRange (float& rangeAttack, float& rangeFollow); @@ -70,7 +74,9 @@ namespace MWMechanics float rateSpell (const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); float rateMagicItem (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr &target); float ratePotion (const MWWorld::Ptr& item, const MWWorld::Ptr &actor); - float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor); + /// @param type Skip all weapons that are not of this type (i.e. return rating 0) + float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& target, + int type=-1, float arrowRating=0.f, float boltRating=0.f); /// @note target may be empty float rateEffect (const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);