mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 22:15:32 +00:00
Merge pull request #1326 from akortunov/spellpriority
Combat AI improvements [feedback needed]
This commit is contained in:
commit
fde6e29628
4 changed files with 107 additions and 1 deletions
|
@ -535,6 +535,10 @@ namespace MWMechanics
|
||||||
// opponent's weapon range, or not backing up if opponent is also using a ranged weapon
|
// opponent's weapon range, or not backing up if opponent is also using a ranged weapon
|
||||||
if (isDistantCombat && distToTarget < rangeAttack / 4)
|
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;
|
mMovement.mPosition[1] = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,11 +116,26 @@ namespace MWMechanics
|
||||||
float bonus=0.f;
|
float bonus=0.f;
|
||||||
|
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
|
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;
|
bonus+=1.5f;
|
||||||
|
}
|
||||||
|
|
||||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||||
{
|
{
|
||||||
rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -131,6 +146,8 @@ namespace MWMechanics
|
||||||
rating += weapon->mData.mChop[i];
|
rating += weapon->mData.mChop[i];
|
||||||
}
|
}
|
||||||
rating /= 6.f;
|
rating /= 6.f;
|
||||||
|
|
||||||
|
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getClass().hasItemHealth(item))
|
if (item.getClass().hasItemHealth(item))
|
||||||
|
@ -168,6 +185,10 @@ namespace MWMechanics
|
||||||
if (skill != -1)
|
if (skill != -1)
|
||||||
rating *= actor.getClass().getSkill(actor, skill) / 100.f;
|
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;
|
return rating + bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,6 +853,79 @@ namespace MWMechanics
|
||||||
return bestAction;
|
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<int> 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)
|
float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist)
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,6 +101,7 @@ namespace MWMechanics
|
||||||
float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||||
|
|
||||||
std::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
std::shared_ptr<Action> 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 getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false);
|
||||||
float getMaxAttackDistance(const MWWorld::Ptr& actor);
|
float getMaxAttackDistance(const MWWorld::Ptr& actor);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "aifollow.hpp"
|
#include "aifollow.hpp"
|
||||||
#include "aiactivate.hpp"
|
#include "aiactivate.hpp"
|
||||||
#include "aicombat.hpp"
|
#include "aicombat.hpp"
|
||||||
|
#include "aicombataction.hpp"
|
||||||
#include "aipursue.hpp"
|
#include "aipursue.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
|
|
||||||
|
@ -209,6 +210,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
||||||
float nearestDist = std::numeric_limits<float>::max();
|
float nearestDist = std::numeric_limits<float>::max();
|
||||||
osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3();
|
osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
|
float bestRating = 0.f;
|
||||||
|
|
||||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();)
|
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();)
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
||||||
|
@ -223,6 +226,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
float rating = MWMechanics::getBestActionRating(actor, target);
|
||||||
|
|
||||||
const ESM::Position &targetPos = target.getRefData().getPosition();
|
const ESM::Position &targetPos = target.getRefData().getPosition();
|
||||||
|
|
||||||
float distTo = (targetPos.asVec3() - vActorPos).length();
|
float distTo = (targetPos.asVec3() - vActorPos).length();
|
||||||
|
@ -231,10 +236,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
||||||
if (it == mPackages.begin())
|
if (it == mPackages.begin())
|
||||||
distTo = std::max(0.f, distTo - 50.f);
|
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;
|
nearestDist = distTo;
|
||||||
itActualCombat = it;
|
itActualCombat = it;
|
||||||
|
bestRating = rating;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue