mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-12 04:14:05 +00:00
Merge remote-tracking branch 'mrcheko/master'
This commit is contained in:
commit
56c4367c1a
@ -181,54 +181,65 @@ namespace MWMechanics
|
||||
calculateDynamicStats (ptr);
|
||||
|
||||
calculateCreatureStatModifiers (ptr, duration);
|
||||
|
||||
// AI
|
||||
if(MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
{
|
||||
CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
//engage combat or not?
|
||||
if(ptr != player && !creatureStats.isHostile())
|
||||
{
|
||||
ESM::Position playerpos = player.getRefData().getPosition();
|
||||
ESM::Position actorpos = ptr.getRefData().getPosition();
|
||||
float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0])
|
||||
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
|
||||
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
|
||||
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
|
||||
if( (fight == 100 )
|
||||
|| (fight >= 95 && d <= 3000)
|
||||
|| (fight >= 90 && d <= 2000)
|
||||
|| (fight >= 80 && d <= 1000)
|
||||
)
|
||||
{
|
||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player)
|
||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr);
|
||||
|
||||
if (LOS)
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateCrimePersuit(ptr, duration);
|
||||
creatureStats.getAiSequence().execute (ptr,duration);
|
||||
}
|
||||
|
||||
// fatigue restoration
|
||||
calculateRestoration(ptr, duration, false);
|
||||
}
|
||||
|
||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
|
||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
|
||||
{
|
||||
if(!paused)
|
||||
CreatureStats& creatureStats = MWWorld::Class::get(actor1).getCreatureStats(actor1);
|
||||
|
||||
if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player
|
||||
|
||||
float fight;
|
||||
|
||||
if (againstPlayer)
|
||||
fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
else
|
||||
{
|
||||
updateDrowning(ptr, duration);
|
||||
calculateNpcStatModifiers(ptr);
|
||||
updateEquippedLight(ptr, duration);
|
||||
fight = 0;
|
||||
// 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
|
||||
if (!actor1.getClass().isNpc())
|
||||
{
|
||||
// if creature is hostile then it is necessarily to start combat
|
||||
if (creatureStats.isHostile()) fight = 100;
|
||||
else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
}
|
||||
}
|
||||
|
||||
ESM::Position actor1Pos = actor1.getRefData().getPosition();
|
||||
ESM::Position actor2Pos = actor2.getRefData().getPosition();
|
||||
float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos));
|
||||
|
||||
if( (fight == 100 && d <= 5000)
|
||||
|| (fight >= 95 && d <= 3000)
|
||||
|| (fight >= 90 && d <= 2000)
|
||||
|| (fight >= 80 && d <= 1000))
|
||||
{
|
||||
if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d))
|
||||
{
|
||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||
|
||||
if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
||||
|
||||
if (LOS)
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||
if (!againstPlayer) // start combat between each other
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration)
|
||||
{
|
||||
updateDrowning(ptr, duration);
|
||||
calculateNpcStatModifiers(ptr);
|
||||
updateEquippedLight(ptr, duration);
|
||||
}
|
||||
|
||||
void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)
|
||||
@ -861,10 +872,24 @@ 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)
|
||||
{
|
||||
if(!paused)
|
||||
{
|
||||
std::list<MWWorld::Ptr> listGuards; // at the moment only guards certainly will fight with creatures
|
||||
|
||||
static float timerUpdateAITargets = 0;
|
||||
|
||||
// target lists get updated once every 1.0 sec
|
||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||
|
||||
// Reset data from previous frame
|
||||
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
@ -872,29 +897,63 @@ namespace MWMechanics
|
||||
// Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation
|
||||
// (below)
|
||||
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);
|
||||
}
|
||||
|
||||
// AI and magic effects update
|
||||
for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
listGuards.push_back(player);
|
||||
|
||||
// AI and magic effects update
|
||||
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
|
||||
{
|
||||
updateActor(iter->first, duration);
|
||||
if (iter->first.getTypeName() == typeid(ESM::NPC).name())
|
||||
updateNpc(iter->first, duration, paused);
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
{
|
||||
// make guards and creatures fight each other
|
||||
if (timerUpdateAITargets == 0 && iter->first.getTypeName() == typeid(ESM::Creature).name() && !listGuards.empty())
|
||||
{
|
||||
sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos);
|
||||
listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest creature
|
||||
|
||||
for (std::list<MWWorld::Ptr>::iterator it = listGuards.begin(); it != listGuards.end(); ++it)
|
||||
{
|
||||
engageCombat(iter->first, *it, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (iter->first != player) engageCombat(iter->first, player, true);
|
||||
|
||||
if (iter->first.getClass().isNpc() && iter->first != player)
|
||||
updateCrimePersuit(iter->first, duration);
|
||||
|
||||
if (iter->first != player)
|
||||
iter->first.getClass().getCreatureStats(iter->first).getAiSequence().execute(iter->first, duration);
|
||||
}
|
||||
|
||||
if(iter->first.getTypeName() == typeid(ESM::NPC).name())
|
||||
updateNpc(iter->first, duration);
|
||||
}
|
||||
}
|
||||
|
||||
timerUpdateAITargets += duration;
|
||||
|
||||
// Looping magic VFX update
|
||||
// Note: we need to do this before any of the animations are updated.
|
||||
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
|
||||
// so updating VFX immediately after that would just remove the particle effects instantly.
|
||||
// There needs to be a magic effect update in between.
|
||||
for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
|
||||
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
iter->second->updateContinuousVfx();
|
||||
|
||||
// Animation/movement update
|
||||
for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
|
||||
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get(
|
||||
ESM::MagicEffect::Paralyze).mMagnitude > 0)
|
||||
@ -903,7 +962,7 @@ namespace MWMechanics
|
||||
}
|
||||
|
||||
// Kill dead actors, update some variables
|
||||
for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
|
||||
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
|
||||
CreatureStats &stats = cls.getCreatureStats(iter->first);
|
||||
@ -975,7 +1034,6 @@ namespace MWMechanics
|
||||
}
|
||||
|
||||
// if player is in sneak state see if anyone detects him
|
||||
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak))
|
||||
{
|
||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
@ -1105,9 +1163,7 @@ namespace MWMechanics
|
||||
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
|
||||
{
|
||||
MWMechanics::AiCombat* package = static_cast<MWMechanics::AiCombat*>(stats.getAiSequence().getActivePackage());
|
||||
// TODO: This is wrong! It's comparing Ref IDs with Ogre handles. The only case where this (coincidentally) works is the player.
|
||||
// possibly applies to other code using getTargetId.
|
||||
if(package->getTargetId() == actor.getCellRef().mRefID)
|
||||
if(package->getTarget() == actor)
|
||||
list.push_front(*iter);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace MWMechanics
|
||||
{
|
||||
std::map<std::string, int> mDeathCount;
|
||||
|
||||
void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused);
|
||||
void updateNpc(const MWWorld::Ptr &ptr, float duration);
|
||||
|
||||
void adjustMagicEffects (const MWWorld::Ptr& creature);
|
||||
|
||||
@ -81,6 +81,12 @@ namespace MWMechanics
|
||||
///< This function is normally called automatically during the update process, but it can
|
||||
/// also be called explicitly at any time to force an update.
|
||||
|
||||
/** Start combat between two actors
|
||||
@Notes: If againstPlayer = true then actor2 should be the Player.
|
||||
If one of the combatants is creature it should be actor1.
|
||||
*/
|
||||
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer);
|
||||
|
||||
void restoreDynamicStats(bool sleep);
|
||||
///< If the player is sleeping, this should be called every hour.
|
||||
|
||||
|
@ -149,9 +149,7 @@ namespace MWMechanics
|
||||
bool AiCombat::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
//General description
|
||||
if(!actor.getClass().getCreatureStats(actor).isHostile()
|
||||
|| actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0)
|
||||
return true;
|
||||
if(actor.getClass().getCreatureStats(actor).isDead()) return true;
|
||||
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
|
||||
@ -629,12 +627,9 @@ namespace MWMechanics
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string AiCombat::getTargetId() const
|
||||
MWWorld::Ptr AiCombat::getTarget() const
|
||||
{
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
if (target.isEmpty())
|
||||
return "";
|
||||
return target.getRefData().getHandle();
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace MWMechanics
|
||||
virtual unsigned int getPriority() const;
|
||||
|
||||
///Returns target ID
|
||||
std::string getTargetId() const;
|
||||
MWWorld::Ptr getTarget() const;
|
||||
|
||||
private:
|
||||
PathFinder mPathFinder;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "aipursue.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
@ -9,26 +8,27 @@
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
||||
MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor)
|
||||
: mActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
AiPursue::AiPursue(const MWWorld::Ptr& actor)
|
||||
: mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
{
|
||||
}
|
||||
MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
|
||||
AiPursue *MWMechanics::AiPursue::clone() const
|
||||
{
|
||||
return new AiPursue(*this);
|
||||
}
|
||||
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
bool AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
{
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow
|
||||
|
||||
if(target == MWWorld::Ptr())
|
||||
return true; //Target doesn't exist
|
||||
return true; //Target doesn't exist
|
||||
|
||||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
@ -47,7 +47,14 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
return false;
|
||||
}
|
||||
|
||||
int MWMechanics::AiPursue::getTypeId() const
|
||||
int AiPursue::getTypeId() const
|
||||
{
|
||||
return TypeIdPursue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr AiPursue::getTarget() const
|
||||
{
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
}
|
||||
|
||||
} // namespace MWMechanics
|
||||
|
@ -2,7 +2,8 @@
|
||||
#define GAME_MWMECHANICS_AIPURSUE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include <string>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "pathfinding.hpp"
|
||||
|
||||
@ -18,12 +19,16 @@ namespace MWMechanics
|
||||
///Constructor
|
||||
/** \param actor Actor to pursue **/
|
||||
AiPursue(const MWWorld::Ptr& actor);
|
||||
|
||||
virtual AiPursue *clone() const;
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
virtual int getTypeId() const;
|
||||
|
||||
MWWorld::Ptr getTarget() const;
|
||||
|
||||
private:
|
||||
int mActorId; // The actor to pursue
|
||||
|
||||
int mTargetActorId; // The actor to pursue
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
};
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "aifollow.hpp"
|
||||
#include "aiactivate.hpp"
|
||||
#include "aicombat.hpp"
|
||||
#include "aipursue.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
@ -16,21 +17,24 @@
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
void MWMechanics::AiSequence::copy (const AiSequence& sequence)
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
void AiSequence::copy (const AiSequence& sequence)
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
|
||||
iter!=sequence.mPackages.end(); ++iter)
|
||||
mPackages.push_back ((*iter)->clone());
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {}
|
||||
AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {}
|
||||
|
||||
MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false)
|
||||
AiSequence::AiSequence (const AiSequence& sequence) : mDone (false)
|
||||
{
|
||||
copy (sequence);
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& sequence)
|
||||
AiSequence& AiSequence::operator= (const AiSequence& sequence)
|
||||
{
|
||||
if (this!=&sequence)
|
||||
{
|
||||
@ -42,12 +46,12 @@ MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& s
|
||||
return *this;
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence::~AiSequence()
|
||||
AiSequence::~AiSequence()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
int MWMechanics::AiSequence::getTypeId() const
|
||||
int AiSequence::getTypeId() const
|
||||
{
|
||||
if (mPackages.empty())
|
||||
return -1;
|
||||
@ -55,16 +59,45 @@ int MWMechanics::AiSequence::getTypeId() const
|
||||
return mPackages.front()->getTypeId();
|
||||
}
|
||||
|
||||
bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const
|
||||
bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
||||
{
|
||||
if (getTypeId() != AiPackage::TypeIdCombat)
|
||||
return false;
|
||||
const AiCombat *combat = static_cast<const AiCombat *>(mPackages.front());
|
||||
targetActorId = combat->getTargetId();
|
||||
|
||||
targetActor = combat->getTarget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stopCombat()
|
||||
bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const
|
||||
{
|
||||
bool firstCombatFound = false;
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||
{
|
||||
firstCombatFound = true;
|
||||
|
||||
const AiCombat *combat = static_cast<const AiCombat *>(*it);
|
||||
if (combat->getTarget() != player ) return false; // only 1 non-player target allowed
|
||||
else
|
||||
{
|
||||
// add new target only if current target (player) is farther
|
||||
ESM::Position &targetPos = combat->getTarget().getRefData().getPosition();
|
||||
|
||||
float distToCurrTarget = (Ogre::Vector3(targetPos.pos) - Ogre::Vector3(actorPos.pos)).length();
|
||||
return (distToCurrTarget > distToTarget);
|
||||
}
|
||||
}
|
||||
else if (firstCombatFound) break; // assumes combat packages go one-by-one in packages list
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AiSequence::stopCombat()
|
||||
{
|
||||
while (getTypeId() == AiPackage::TypeIdCombat)
|
||||
{
|
||||
@ -73,7 +106,7 @@ void MWMechanics::AiSequence::stopCombat()
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stopPursuit()
|
||||
void AiSequence::stopPursuit()
|
||||
{
|
||||
while (getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
@ -82,12 +115,12 @@ void MWMechanics::AiSequence::stopPursuit()
|
||||
}
|
||||
}
|
||||
|
||||
bool MWMechanics::AiSequence::isPackageDone() const
|
||||
bool AiSequence::isPackageDone() const
|
||||
{
|
||||
return mDone;
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
@ -95,6 +128,36 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
MWMechanics::AiPackage* package = mPackages.front();
|
||||
mLastAiPackage = package->getTypeId();
|
||||
|
||||
// if active package is combat one, choose nearest target
|
||||
if (mLastAiPackage == AiPackage::TypeIdCombat)
|
||||
{
|
||||
std::list<AiPackage *>::iterator itActualCombat;
|
||||
|
||||
float nearestDist = std::numeric_limits<float>::max();
|
||||
Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos);
|
||||
|
||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
||||
|
||||
ESM::Position &targetPos = static_cast<const AiCombat *>(*it)->getTarget().getRefData().getPosition();
|
||||
|
||||
float distTo = (Ogre::Vector3(targetPos.pos) - vActorPos).length();
|
||||
if (distTo < nearestDist)
|
||||
{
|
||||
nearestDist = distTo;
|
||||
itActualCombat = it;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPackages.begin() != itActualCombat)
|
||||
{
|
||||
// move combat package with nearest target to the front
|
||||
mPackages.splice(mPackages.begin(), mPackages, itActualCombat);
|
||||
}
|
||||
}
|
||||
|
||||
if (package->execute (actor,duration))
|
||||
{
|
||||
// To account for the rare case where AiPackage::execute() queued another AI package
|
||||
@ -113,7 +176,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::clear()
|
||||
void AiSequence::clear()
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
delete *iter;
|
||||
@ -121,14 +184,19 @@ void MWMechanics::AiSequence::clear()
|
||||
mPackages.clear();
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
// Notify AiWander of our current position so we can return to it after combat finished
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||
if((*iter)->getTypeId() == AiPackage::TypeIdPursue && package.getTypeId() == AiPackage::TypeIdPursue
|
||||
&& static_cast<const AiPursue*>(*iter)->getTarget() == static_cast<const AiPursue*>(&package)->getTarget())
|
||||
{
|
||||
return; // target is already pursued
|
||||
}
|
||||
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
||||
}
|
||||
}
|
||||
@ -146,12 +214,12 @@ void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Pt
|
||||
mPackages.push_front (package.clone());
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::queue (const AiPackage& package)
|
||||
void AiSequence::queue (const AiPackage& package)
|
||||
{
|
||||
mPackages.push_back (package.clone());
|
||||
}
|
||||
|
||||
MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
{
|
||||
if(mPackages.empty())
|
||||
throw std::runtime_error(std::string("No AI Package!"));
|
||||
@ -159,7 +227,7 @@ MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
return mPackages.front();
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
||||
void AiSequence::fill(const ESM::AIPackageList &list)
|
||||
{
|
||||
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
||||
{
|
||||
@ -195,3 +263,5 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
||||
mPackages.push_back(package);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MWMechanics
|
||||
|
@ -53,7 +53,10 @@ namespace MWMechanics
|
||||
int getLastRunTypeId() const { return mLastAiPackage; }
|
||||
|
||||
/// Return true and assign target if combat package is currently active, return false otherwise
|
||||
bool getCombatTarget (std::string &targetActorId) const;
|
||||
bool getCombatTarget (MWWorld::Ptr &targetActor) const;
|
||||
|
||||
bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;
|
||||
///< Function assumes that actor can have only 1 target apart player
|
||||
|
||||
/// Removes all combat packages until first non-combat or stack empty.
|
||||
void stopCombat();
|
||||
|
@ -1012,7 +1012,16 @@ void CharacterController::update(float duration)
|
||||
bool flying = world->isFlying(mPtr);
|
||||
//Ogre::Vector3 vec = cls.getMovementVector(mPtr);
|
||||
Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition);
|
||||
vec.normalise();
|
||||
if(vec.z > 0.0f) // to avoid slow-down when jumping
|
||||
{
|
||||
Ogre::Vector2 vecXY = Ogre::Vector2(vec.x, vec.y);
|
||||
vecXY.normalise();
|
||||
vec.x = vecXY.x;
|
||||
vec.y = vecXY.y;
|
||||
}
|
||||
else
|
||||
vec.normalise();
|
||||
|
||||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
vec = Ogre::Vector3(0.0f);
|
||||
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
|
||||
@ -1113,9 +1122,12 @@ void CharacterController::update(float duration)
|
||||
if(cls.isNpc())
|
||||
{
|
||||
const NpcStats &stats = cls.getNpcStats(mPtr);
|
||||
mult = gmst.find("fJumpMoveBase")->getFloat() +
|
||||
static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat();
|
||||
static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat();
|
||||
|
||||
mult = fJumpMoveBase +
|
||||
(stats.getSkill(ESM::Skill::Acrobatics).getModified()/100.0f *
|
||||
gmst.find("fJumpMoveMult")->getFloat());
|
||||
fJumpMoveMult);
|
||||
}
|
||||
|
||||
vec.x *= mult;
|
||||
@ -1125,14 +1137,7 @@ void CharacterController::update(float duration)
|
||||
else if(vec.z > 0.0f && mJumpState == JumpState_None)
|
||||
{
|
||||
// Started a jump.
|
||||
float z = cls.getJump(mPtr);
|
||||
if(vec.x == 0 && vec.y == 0)
|
||||
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
||||
else
|
||||
{
|
||||
Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
|
||||
vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f;
|
||||
}
|
||||
vec.z = cls.getJump(mPtr);
|
||||
|
||||
// advance acrobatics
|
||||
if (mPtr.getRefData().getHandle() == "player")
|
||||
@ -1444,14 +1449,14 @@ void CharacterController::updateVisibility()
|
||||
|
||||
void CharacterController::determineAttackType()
|
||||
{
|
||||
float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||
|
||||
if(mPtr.getClass().hasInventoryStore(mPtr))
|
||||
{
|
||||
if (move[0] && !move[1]) //sideway
|
||||
mAttackType = "slash";
|
||||
else if (move[1]) //forward
|
||||
if (move[1]) // forward-backward
|
||||
mAttackType = "thrust";
|
||||
else if (move[0]) //sideway
|
||||
mAttackType = "slash";
|
||||
else
|
||||
mAttackType = "chop";
|
||||
}
|
||||
|
@ -322,11 +322,9 @@ namespace MWMechanics
|
||||
|
||||
bool CreatureStats::getCreatureTargetted() const
|
||||
{
|
||||
std::string target;
|
||||
if (mAiSequence.getCombatTarget(target))
|
||||
MWWorld::Ptr targetPtr;
|
||||
if (mAiSequence.getCombatTarget(targetPtr))
|
||||
{
|
||||
MWWorld::Ptr targetPtr;
|
||||
targetPtr = MWBase::Environment::get().getWorld()->getPtr(target, true);
|
||||
return targetPtr.getTypeName() == typeid(ESM::Creature).name();
|
||||
}
|
||||
return false;
|
||||
|
@ -328,6 +328,12 @@ namespace MWMechanics
|
||||
path.push_front(pt);
|
||||
current = graphParent[current];
|
||||
}
|
||||
|
||||
// add first node to path explicitly
|
||||
ESM::Pathgrid::Point pt = mPathgrid->mPoints[start];
|
||||
pt.mX += xCell;
|
||||
pt.mY += yCell;
|
||||
path.push_front(pt);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
@ -415,9 +415,10 @@ namespace MWScript
|
||||
std::string currentTargetId;
|
||||
|
||||
bool targetsAreEqual = false;
|
||||
if (creatureStats.getAiSequence().getCombatTarget (currentTargetId))
|
||||
MWWorld::Ptr targetPtr;
|
||||
if (creatureStats.getAiSequence().getCombatTarget (targetPtr))
|
||||
{
|
||||
if (currentTargetId == testedTargetId)
|
||||
if (targetPtr.getRefData().getHandle() == testedTargetId)
|
||||
targetsAreEqual = true;
|
||||
}
|
||||
runtime.push(int(targetsAreEqual));
|
||||
|
@ -242,6 +242,15 @@ namespace MWWorld
|
||||
// If falling, add part of the incoming velocity with the current inertia
|
||||
// TODO: but we could be jumping up?
|
||||
velocity = velocity * time + physicActor->getInertialForce();
|
||||
|
||||
// avoid getting infinite inertia in air
|
||||
float actorSpeed = ptr.getClass().getSpeed(ptr);
|
||||
float speedXY = Ogre::Vector2(velocity.x, velocity.y).length();
|
||||
if (speedXY > actorSpeed)
|
||||
{
|
||||
velocity.x *= actorSpeed / speedXY;
|
||||
velocity.y *= actorSpeed / speedXY;
|
||||
}
|
||||
}
|
||||
inertia = velocity; // NOTE: velocity is for z axis only in this code block
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user