mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-10 12:39:53 +00:00
Merge pull request #1174 from Allofich/combat
Adjustments to AI combat engaging and disengaging
This commit is contained in:
commit
212e85e810
@ -29,6 +29,7 @@
|
|||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
#include "character.hpp"
|
#include "character.hpp"
|
||||||
#include "aicombat.hpp"
|
#include "aicombat.hpp"
|
||||||
|
#include "aicombataction.hpp"
|
||||||
#include "aifollow.hpp"
|
#include "aifollow.hpp"
|
||||||
#include "aipursue.hpp"
|
#include "aipursue.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
@ -286,10 +287,12 @@ namespace MWMechanics
|
|||||||
|
|
||||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
|
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
|
||||||
{
|
{
|
||||||
CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1);
|
const CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1);
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat(actor2))
|
||||||
|
return;
|
||||||
|
|
||||||
if (actor2.getClass().getCreatureStats(actor2).isDead()
|
const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
|
||||||
|| actor1.getClass().getCreatureStats(actor1).isDead())
|
if (creatureStats1.isDead() || creatureStats2.isDead())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ESM::Position& actor1Pos = actor1.getRefData().getPosition();
|
const ESM::Position& actor1Pos = actor1.getRefData().getPosition();
|
||||||
@ -298,55 +301,26 @@ namespace MWMechanics
|
|||||||
if (sqrDist > sqrAiProcessingDistance)
|
if (sqrDist > sqrAiProcessingDistance)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// pure water creatures won't try to fight with the target on the ground
|
// No combat for totally static creatures
|
||||||
// except that creature is already hostile
|
|
||||||
if ((againstPlayer || !creatureStats.getAiSequence().isInCombat())
|
|
||||||
&& !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no combat for totally static creatures (they have no movement or attack animations anyway)
|
|
||||||
if (!actor1.getClass().isMobile(actor1))
|
if (!actor1.getClass().isMobile(actor1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool aggressive;
|
// Start combat if target actor is in combat with one of our followers or escorters
|
||||||
|
const std::list<MWWorld::Ptr>& followersAndEscorters = getActorsSidingWith(actor1);
|
||||||
if (againstPlayer)
|
for (std::list<MWWorld::Ptr>::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it)
|
||||||
{
|
{
|
||||||
// followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon)
|
// Need to check both ways since player doesn't use AI packages
|
||||||
const std::list<MWWorld::Ptr>& followers = getActorsSidingWith(actor2);
|
if ((creatureStats2.getAiSequence().isInCombat(*it)
|
||||||
if (std::find(followers.begin(), followers.end(), actor1) != followers.end())
|
|| it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2))
|
||||||
return;
|
&& !creatureStats1.getAiSequence().isInCombat(*it))
|
||||||
|
|
||||||
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aggressive = false;
|
|
||||||
|
|
||||||
// Make guards fight aggressive creatures
|
|
||||||
if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard"))
|
|
||||||
{
|
{
|
||||||
if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2))
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
aggressive = true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start combat if target actor is in combat with one of our followers
|
// Start combat if target actor is in combat with someone we are following through a follow package
|
||||||
const std::list<MWWorld::Ptr>& followers = getActorsSidingWith(actor1);
|
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats1.getAiSequence().begin(); it != creatureStats1.getAiSequence().end(); ++it)
|
||||||
const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
|
|
||||||
for (std::list<MWWorld::Ptr>::const_iterator it = followers.begin(); it != followers.end(); ++it)
|
|
||||||
{
|
|
||||||
// need to check both ways since player doesn't use AI packages
|
|
||||||
if ((creatureStats2.getAiSequence().isInCombat(*it)
|
|
||||||
|| it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2))
|
|
||||||
&& !creatureStats.getAiSequence().isInCombat(*it))
|
|
||||||
aggressive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start combat if target actor is in combat with someone we are following
|
|
||||||
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (!(*it)->sideWithTarget())
|
if (!(*it)->sideWithTarget())
|
||||||
continue;
|
continue;
|
||||||
@ -356,20 +330,63 @@ namespace MWMechanics
|
|||||||
if (followTarget.isEmpty())
|
if (followTarget.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (creatureStats.getAiSequence().isInCombat(followTarget))
|
if (creatureStats1.getAiSequence().isInCombat(followTarget))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// need to check both ways since player doesn't use AI packages
|
// Need to check both ways since player doesn't use AI packages
|
||||||
if (creatureStats2.getAiSequence().isInCombat(followTarget)
|
if (creatureStats2.getAiSequence().isInCombat(followTarget)
|
||||||
|| followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2))
|
|| followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2))
|
||||||
aggressive = true;
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start combat with the player if we are already in combat with a player follower or escorter
|
||||||
|
const std::list<MWWorld::Ptr>& playerFollowersAndEscorters = getActorsSidingWith(getPlayer());
|
||||||
|
if (againstPlayer)
|
||||||
|
{
|
||||||
|
for (std::list<MWWorld::Ptr>::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it)
|
||||||
|
{
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat(*it))
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(aggressive)
|
// Otherwise, don't initiate combat with an unreachable target
|
||||||
|
if (!MWMechanics::canFight(actor1,actor2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool aggressive = false;
|
||||||
|
|
||||||
|
if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())
|
||||||
|
{
|
||||||
|
// Player followers and escorters with high fight should not initiate combat here with the player or with
|
||||||
|
// other player followers or escorters
|
||||||
|
if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Make guards fight aggressive creatures
|
||||||
|
if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard"))
|
||||||
|
{
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2))
|
||||||
|
aggressive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aggressive)
|
||||||
{
|
{
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||||
|
|
||||||
if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())
|
||||||
|
LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
||||||
|
|
||||||
if (LOS)
|
if (LOS)
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "aicombataction.hpp"
|
#include "aicombataction.hpp"
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
#include "coordinateconverter.hpp"
|
#include "coordinateconverter.hpp"
|
||||||
|
#include "actorutil.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -210,13 +211,14 @@ namespace MWMechanics
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
timerReact = 0;
|
timerReact = 0;
|
||||||
attack(actor, target, storage, characterController);
|
if (attack(actor, target, storage, characterController))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
||||||
{
|
{
|
||||||
const MWWorld::CellStore*& currentCell = storage.mCell;
|
const MWWorld::CellStore*& currentCell = storage.mCell;
|
||||||
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
||||||
@ -231,7 +233,10 @@ namespace MWMechanics
|
|||||||
storage.stopAttack();
|
storage.stopAttack();
|
||||||
characterController.setAttackingOrSpell(false);
|
characterController.setAttackingOrSpell(false);
|
||||||
storage.mActionCooldown = 0.f;
|
storage.mActionCooldown = 0.f;
|
||||||
forceFlee = true;
|
if (target == MWMechanics::getPlayer())
|
||||||
|
forceFlee = true;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWWorld::Class& actorClass = actor.getClass();
|
const MWWorld::Class& actorClass = actor.getClass();
|
||||||
@ -243,7 +248,7 @@ namespace MWMechanics
|
|||||||
if (!forceFlee)
|
if (!forceFlee)
|
||||||
{
|
{
|
||||||
if (actionCooldown > 0)
|
if (actionCooldown > 0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (characterController.readyToPrepareAttack())
|
if (characterController.readyToPrepareAttack())
|
||||||
{
|
{
|
||||||
@ -258,7 +263,7 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!currentAction)
|
if (!currentAction)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (storage.isFleeing() != currentAction->isFleeing())
|
if (storage.isFleeing() != currentAction->isFleeing())
|
||||||
{
|
{
|
||||||
@ -266,7 +271,7 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
storage.startFleeing();
|
storage.startFleeing();
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
storage.stopFleeing();
|
storage.stopFleeing();
|
||||||
@ -311,6 +316,7 @@ namespace MWMechanics
|
|||||||
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
|
void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
|
||||||
|
@ -59,7 +59,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
int mTargetActorId;
|
int mTargetActorId;
|
||||||
|
|
||||||
void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
|
/// Returns true if combat should end
|
||||||
|
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
|
||||||
|
|
||||||
void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
|
void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user