forked from mirror/openmw-tes3mp
Implement actors fighting for the actor they are following (Fixes #1141)
This commit is contained in:
parent
1a04501951
commit
d81e9cfefd
9 changed files with 78 additions and 52 deletions
|
@ -366,10 +366,17 @@ namespace MWClass
|
||||||
getCreatureStats(ptr).setAttacked(true);
|
getCreatureStats(ptr).setAttacked(true);
|
||||||
|
|
||||||
// Self defense
|
// Self defense
|
||||||
if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)
|
if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr))
|
||||||
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
|
|| attacker == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
|
&& !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
|
||||||
|
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
|
||||||
// (they have no movement or attacks anyway)
|
// (they have no movement or attacks anyway)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
|
||||||
|
// Note: accidental or collateral damage attacks are ignored.
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
||||||
|
}
|
||||||
|
|
||||||
if(!successful)
|
if(!successful)
|
||||||
{
|
{
|
||||||
|
|
|
@ -192,6 +192,15 @@ namespace MWMechanics
|
||||||
CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1);
|
CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1);
|
||||||
|
|
||||||
if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player
|
if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player
|
||||||
|
if (actor2.getClass().getCreatureStats(actor2).isDead()
|
||||||
|
|| actor1.getClass().getCreatureStats(actor1).isDead())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ESM::Position& actor1Pos = actor2.getRefData().getPosition();
|
||||||
|
const ESM::Position& actor2Pos = actor2.getRefData().getPosition();
|
||||||
|
float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos));
|
||||||
|
if (sqrDist > 7168*7168)
|
||||||
|
return;
|
||||||
|
|
||||||
// pure water creatures won't try to fight with the target on the ground
|
// pure water creatures won't try to fight with the target on the ground
|
||||||
// except that creature is already hostile
|
// except that creature is already hostile
|
||||||
|
@ -208,9 +217,9 @@ namespace MWMechanics
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
aggressive = false;
|
aggressive = false;
|
||||||
// if one of actors is creature then we should make a decision to start combat or not
|
|
||||||
// NOTE: function doesn't take into account combat between 2 creatures
|
// Make guards fight aggressive creatures
|
||||||
if (!actor1.getClass().isNpc())
|
if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard"))
|
||||||
{
|
{
|
||||||
// if creature is hostile then it is necessarily to start combat
|
// if creature is hostile then it is necessarily to start combat
|
||||||
if (creatureStats.isHostile()) aggressive = true;
|
if (creatureStats.isHostile()) aggressive = true;
|
||||||
|
@ -218,12 +227,23 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(aggressive)
|
// start combat if we are in combat with any followers of this actor
|
||||||
|
const std::list<MWWorld::Ptr>& followers = getActorsFollowing(actor2);
|
||||||
|
for (std::list<MWWorld::Ptr>::const_iterator it = followers.begin(); it != followers.end(); ++it)
|
||||||
{
|
{
|
||||||
const ESM::Position& actor1Pos = actor2.getRefData().getPosition();
|
if (creatureStats.getAiSequence().isInCombat(*it))
|
||||||
const ESM::Position& actor2Pos = actor2.getRefData().getPosition();
|
aggressive = true;
|
||||||
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
|
}
|
||||||
if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d))
|
// start combat if we are in combat with someone this actor is following
|
||||||
|
const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
|
||||||
|
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
|
||||||
|
creatureStats.getAiSequence().isInCombat(dynamic_cast<MWMechanics::AiFollow*>(*it)->getTarget()))
|
||||||
|
aggressive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(aggressive)
|
||||||
{
|
{
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||||
|
|
||||||
|
@ -239,7 +259,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration)
|
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration)
|
||||||
{
|
{
|
||||||
|
@ -964,19 +983,10 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ogre::Vector3 sBasePoint;
|
|
||||||
bool comparePtrDist (const MWWorld::Ptr& ptr1, const MWWorld::Ptr& ptr2)
|
|
||||||
{
|
|
||||||
return (sBasePoint.squaredDistance(Ogre::Vector3(ptr1.getRefData().getPosition().pos))
|
|
||||||
< sBasePoint.squaredDistance(Ogre::Vector3(ptr2.getRefData().getPosition().pos)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Actors::update (float duration, bool paused)
|
void Actors::update (float duration, bool paused)
|
||||||
{
|
{
|
||||||
if(!paused)
|
if(!paused)
|
||||||
{
|
{
|
||||||
std::list<MWWorld::Ptr> listGuards; // at the moment only guards certainly will fight with creatures
|
|
||||||
|
|
||||||
static float timerUpdateAITargets = 0;
|
static float timerUpdateAITargets = 0;
|
||||||
|
|
||||||
// target lists get updated once every 1.0 sec
|
// target lists get updated once every 1.0 sec
|
||||||
|
@ -989,16 +999,10 @@ namespace MWMechanics
|
||||||
// Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation
|
// Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation
|
||||||
// (below)
|
// (below)
|
||||||
iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string());
|
iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string());
|
||||||
|
|
||||||
// add guards to list to later make them fight with creatures
|
|
||||||
if (timerUpdateAITargets == 0 && iter->first.getClass().isClass(iter->first, "Guard"))
|
|
||||||
listGuards.push_back(iter->first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
|
||||||
listGuards.push_back(player);
|
|
||||||
|
|
||||||
// AI and magic effects update
|
// AI and magic effects update
|
||||||
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
@ -1008,20 +1012,16 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||||
{
|
{
|
||||||
// make guards and creatures fight each other
|
if (timerUpdateAITargets == 0)
|
||||||
if (timerUpdateAITargets == 0 && iter->first.getTypeName() == typeid(ESM::Creature).name() && !listGuards.empty())
|
|
||||||
{
|
{
|
||||||
sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos);
|
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||||
listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest guard
|
|
||||||
|
|
||||||
for (std::list<MWWorld::Ptr>::iterator it = listGuards.begin(); it != listGuards.end(); ++it)
|
|
||||||
{
|
{
|
||||||
engageCombat(iter->first, *it, *it == player);
|
if (it->first == iter->first || iter->first == player) // player is not AI-controlled
|
||||||
|
continue;
|
||||||
|
engageCombat(iter->first, it->first, it->first == player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter->first != player) engageCombat(iter->first, player, true);
|
|
||||||
|
|
||||||
if (iter->first.getClass().isNpc() && iter->first != player)
|
if (iter->first.getClass().isNpc() && iter->first != player)
|
||||||
updateCrimePersuit(iter->first, duration);
|
updateCrimePersuit(iter->first, duration);
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,8 @@ namespace MWMechanics
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||||
|
if (target.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
if(target.getClass().getCreatureStats(target).isDead())
|
if(target.getClass().getCreatureStats(target).isDead())
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -121,3 +121,8 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr MWMechanics::AiFollow::getTarget() const
|
||||||
|
{
|
||||||
|
return MWBase::Environment::get().getWorld()->searchPtr(mActorId, false);
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
AiFollow(const ESM::AiSequence::AiFollow* follow);
|
AiFollow(const ESM::AiSequence::AiFollow* follow);
|
||||||
|
|
||||||
|
MWWorld::Ptr getTarget() const;
|
||||||
|
|
||||||
virtual AiFollow *clone() const;
|
virtual AiFollow *clone() const;
|
||||||
|
|
||||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||||
|
|
|
@ -72,6 +72,16 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<AiPackage*>::const_iterator AiSequence::begin() const
|
||||||
|
{
|
||||||
|
return mPackages.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<AiPackage*>::const_iterator AiSequence::end() const
|
||||||
|
{
|
||||||
|
return mPackages.end();
|
||||||
|
}
|
||||||
|
|
||||||
bool AiSequence::isInCombat() const
|
bool AiSequence::isInCombat() const
|
||||||
{
|
{
|
||||||
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||||
|
|
|
@ -50,6 +50,10 @@ namespace MWMechanics
|
||||||
|
|
||||||
virtual ~AiSequence();
|
virtual ~AiSequence();
|
||||||
|
|
||||||
|
/// Iterator may be invalidated by any function calls other than begin() or end().
|
||||||
|
std::list<AiPackage*>::const_iterator begin() const;
|
||||||
|
std::list<AiPackage*>::const_iterator end() const;
|
||||||
|
|
||||||
/// Returns currently executing AiPackage type
|
/// Returns currently executing AiPackage type
|
||||||
/** \see enum AiPackage::TypeId **/
|
/** \see enum AiPackage::TypeId **/
|
||||||
int getTypeId() const;
|
int getTypeId() const;
|
||||||
|
|
|
@ -1171,6 +1171,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
||||||
{
|
{
|
||||||
|
if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target))
|
||||||
|
return;
|
||||||
ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
|
ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
|
||||||
if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
if (target == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,12 +72,6 @@ namespace MWWorld
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// NPC-stances.
|
|
||||||
enum Stance
|
|
||||||
{
|
|
||||||
Run, Sneak
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~Class();
|
virtual ~Class();
|
||||||
|
|
||||||
const std::string& getTypeName() const {
|
const std::string& getTypeName() const {
|
||||||
|
|
Loading…
Reference in a new issue