diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index a00890cea..68d282d0c 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -535,6 +535,10 @@ namespace MWMechanics // opponent's weapon range, or not backing up if opponent is also using a ranged weapon if (isDistantCombat && distToTarget < rangeAttack / 4) { + // actor should not back up into water + if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) + return; + mMovement.mPosition[1] = -1; } } diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index aad0baae8..3c85af3fd 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -116,11 +116,26 @@ namespace MWMechanics float bonus=0.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; + bonus+=1.5f; + } if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) { rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; + + if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) + MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } else { @@ -131,6 +146,8 @@ namespace MWMechanics rating += weapon->mData.mChop[i]; } rating /= 6.f; + + MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } if (item.getClass().hasItemHealth(item)) @@ -168,6 +185,10 @@ namespace MWMechanics if (skill != -1) rating *= actor.getClass().getSkill(actor, skill) / 100.f; + // There is no need to apply bonus if weapon rating == 0 + if (rating == 0.f) + return 0.f; + return rating + bonus; } @@ -832,6 +853,79 @@ namespace MWMechanics return bestAction; } + float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy) + { + Spells& spells = actor.getClass().getCreatureStats(actor).getSpells(); + + float bestActionRating = 0.f; + // Default to hand-to-hand combat + if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) + { + return bestActionRating; + } + + if (actor.getClass().hasInventoryStore(actor)) + { + MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + float rating = rateMagicItem(*it, actor, enemy); + if (rating > bestActionRating) + { + bestActionRating = rating; + } + } + + float bestArrowRating = 0; + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow); + if (rating > bestArrowRating) + { + bestArrowRating = rating; + } + } + + float bestBoltRating = 0; + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt); + if (rating > bestBoltRating) + { + bestBoltRating = rating; + } + } + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + 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, enemy, -1, bestArrowRating, bestBoltRating); + if (rating > bestActionRating) + { + bestActionRating = rating; + } + } + } + + for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ESM::Spell* spell = it->first; + + float rating = rateSpell(spell, actor, enemy); + if (rating > bestActionRating) + { + bestActionRating = rating; + } + } + + return bestActionRating; + } + float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist) { diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 3db88269b..393bd9f5d 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -101,6 +101,7 @@ namespace MWMechanics float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); std::shared_ptr prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy); + float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy); float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false); float getMaxAttackDistance(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index d9652ef54..2a2d598f5 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -16,6 +16,7 @@ #include "aifollow.hpp" #include "aiactivate.hpp" #include "aicombat.hpp" +#include "aicombataction.hpp" #include "aipursue.hpp" #include "actorutil.hpp" @@ -209,6 +210,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac float nearestDist = std::numeric_limits::max(); osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3(); + float bestRating = 0.f; + for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; @@ -223,6 +226,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } else { + float rating = MWMechanics::getBestActionRating(actor, target); + const ESM::Position &targetPos = target.getRefData().getPosition(); float distTo = (targetPos.asVec3() - vActorPos).length(); @@ -231,10 +236,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (it == mPackages.begin()) distTo = std::max(0.f, distTo - 50.f); - if (distTo < nearestDist) + // if a target has higher priority than current target or has same priority but closer + if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating)) { nearestDist = distTo; itActualCombat = it; + bestRating = rating; } ++it; }