1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-14 01:19:59 +00:00

Merge branch 'mechanics_actors_list' into 'master'

Use std::list to store mechanics actors

See merge request OpenMW/openmw!1893
This commit is contained in:
psi29a 2022-05-22 10:41:46 +00:00
commit d2a9334f39
8 changed files with 246 additions and 339 deletions

View File

@ -95,7 +95,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil spelllist
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
spelleffects
)

View File

@ -1,77 +0,0 @@
#include "actor.hpp"
#include "character.hpp"
namespace MWMechanics
{
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
: mPositionAdjusted(false)
{
mCharacterController.reset(new CharacterController(ptr, animation));
}
void Actor::updatePtr(const MWWorld::Ptr &newPtr)
{
mCharacterController->updatePtr(newPtr);
}
CharacterController* Actor::getCharacterController()
{
return mCharacterController.get();
}
int Actor::getGreetingTimer() const
{
return mGreetingTimer;
}
void Actor::setGreetingTimer(int timer)
{
mGreetingTimer = timer;
}
float Actor::getAngleToPlayer() const
{
return mTargetAngleRadians;
}
void Actor::setAngleToPlayer(float angle)
{
mTargetAngleRadians = angle;
}
GreetingState Actor::getGreetingState() const
{
return mGreetingState;
}
void Actor::setGreetingState(GreetingState state)
{
mGreetingState = state;
}
bool Actor::isTurningToPlayer() const
{
return mIsTurningToPlayer;
}
void Actor::setTurningToPlayer(bool turning)
{
mIsTurningToPlayer = turning;
}
Misc::TimerStatus Actor::updateEngageCombatTimer(float duration)
{
return mEngageCombat.update(duration, MWBase::Environment::get().getWorld()->getPrng());
}
void Actor::setPositionAdjusted(bool adjusted)
{
mPositionAdjusted = adjusted;
}
bool Actor::getPositionAdjusted() const
{
return mPositionAdjusted;
}
}

View File

@ -3,7 +3,8 @@
#include <memory>
#include "../mwmechanics/actorutil.hpp"
#include "actorutil.hpp"
#include "character.hpp"
#include <components/misc/timer.hpp>
@ -18,38 +19,44 @@ namespace MWWorld
namespace MWMechanics
{
class CharacterController;
/// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene.
class Actor
{
public:
Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation);
Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation)
: mCharacterController(ptr, animation)
, mPositionAdjusted(false)
{}
const MWWorld::Ptr& getPtr() const { return mCharacterController.getPtr(); }
/// Notify this actor of its new base object Ptr, use when the object changed cells
void updatePtr(const MWWorld::Ptr& newPtr);
void updatePtr(const MWWorld::Ptr& newPtr) { mCharacterController.updatePtr(newPtr); }
CharacterController* getCharacterController();
CharacterController& getCharacterController() { return mCharacterController; }
int getGreetingTimer() const;
void setGreetingTimer(int timer);
int getGreetingTimer() const { return mGreetingTimer; }
void setGreetingTimer(int timer) { mGreetingTimer = timer; }
float getAngleToPlayer() const;
void setAngleToPlayer(float angle);
float getAngleToPlayer() const { return mTargetAngleRadians; }
void setAngleToPlayer(float angle) { mTargetAngleRadians = angle; }
GreetingState getGreetingState() const;
void setGreetingState(GreetingState state);
GreetingState getGreetingState() const { return mGreetingState; }
void setGreetingState(GreetingState state) { mGreetingState = state; }
bool isTurningToPlayer() const;
void setTurningToPlayer(bool turning);
bool isTurningToPlayer() const { return mIsTurningToPlayer; }
void setTurningToPlayer(bool turning) { mIsTurningToPlayer = turning; }
Misc::TimerStatus updateEngageCombatTimer(float duration);
Misc::TimerStatus updateEngageCombatTimer(float duration)
{
return mEngageCombat.update(duration, MWBase::Environment::get().getWorld()->getPrng());
}
void setPositionAdjusted(bool adjusted);
bool getPositionAdjusted() const;
void setPositionAdjusted(bool adjusted) { mPositionAdjusted = adjusted; }
bool getPositionAdjusted() const { return mPositionAdjusted; }
private:
std::unique_ptr<CharacterController> mCharacterController;
CharacterController mCharacterController;
int mGreetingTimer{0};
float mTargetAngleRadians{0.f};
GreetingState mGreetingState{Greet_None};

View File

@ -105,12 +105,12 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
}
template<class T>
void forEachFollowingPackage(MWMechanics::Actors::PtrActorMap& actors, const MWWorld::Ptr& actor, const MWWorld::Ptr& player, T&& func)
void forEachFollowingPackage(std::list<MWMechanics::Actor>& actors, const MWWorld::Ptr& actorPtr, const MWWorld::Ptr& player, T&& func)
{
for(auto& iter : actors)
for (const MWMechanics::Actor& actor : actors)
{
const MWWorld::Ptr &iteratedActor = iter.first;
if (iteratedActor == player || iteratedActor == actor)
const MWWorld::Ptr &iteratedActor = actor.getPtr();
if (iteratedActor == player || iteratedActor == actorPtr)
continue;
const MWMechanics::CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
@ -121,7 +121,7 @@ void forEachFollowingPackage(MWMechanics::Actors::PtrActorMap& actors, const MWW
// or there are only Combat and Wander packages before the AiFollow package
for (const auto& package : stats.getAiSequence())
{
if(!func(iter, package))
if (!func(actor, package))
break;
}
}
@ -771,32 +771,26 @@ namespace MWMechanics
bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isAttackPreparing();
return it->second->getCharacterController().isAttackPreparing();
}
bool Actors::isRunning(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isRunning();
return it->second->getCharacterController().isRunning();
}
bool Actors::isSneaking(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isSneaking();
return it->second->getCharacterController().isSneaking();
}
void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer)
@ -1031,11 +1025,6 @@ namespace MWMechanics
updateProcessingRange();
}
Actors::~Actors()
{
clear();
}
float Actors::getProcessingRange() const
{
return mActorsProcessingRange;
@ -1057,11 +1046,11 @@ namespace MWMechanics
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if (!anim)
return;
mActors.emplace(ptr, new Actor(ptr, anim));
const auto it = mActors.emplace(mActors.end(), ptr, anim);
mIndex.emplace(ptr.mRef, it);
CharacterController* ctrl = mActors[ptr]->getCharacterController();
if (updateImmediately)
ctrl->update(0);
it->getCharacterController().update(0);
// We should initially hide actors outside of processing range.
// Note: since we update player after other actors, distance will be incorrect during teleportation.
@ -1069,10 +1058,10 @@ namespace MWMechanics
if (MWBase::Environment::get().getWorld()->getPlayer().wasTeleported())
return;
updateVisibility(ptr, ctrl);
updateVisibility(ptr, it->getCharacterController());
}
void Actors::updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl)
void Actors::updateVisibility (const MWWorld::Ptr& ptr, CharacterController& ctrl)
{
MWWorld::Ptr player = MWMechanics::getPlayer();
if (ptr == player)
@ -1097,26 +1086,26 @@ namespace MWMechanics
visibilityRatio = std::min(1.f, visibilityRatio);
ctrl->setVisibility(visibilityRatio);
ctrl.setVisibility(visibilityRatio);
}
void Actors::removeActor (const MWWorld::Ptr& ptr, bool keepActive)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
if(!keepActive)
removeTemporaryEffects(iter->first);
delete iter->second;
mActors.erase(iter);
removeTemporaryEffects(iter->second->getPtr());
mActors.erase(iter->second);
mIndex.erase(iter);
}
}
void Actors::castSpell(const MWWorld::Ptr& ptr, const std::string& spellId, bool manualSpell)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->getCharacterController()->castSpell(spellId, manualSpell);
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
iter->second->getCharacterController().castSpell(spellId, manualSpell);
}
bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
@ -1153,27 +1142,20 @@ namespace MWMechanics
void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
PtrActorMap::iterator iter = mActors.find(old);
if(iter != mActors.end())
{
Actor *actor = iter->second;
mActors.erase(iter);
actor->updatePtr(ptr);
mActors.insert(std::make_pair(ptr, actor));
}
const auto iter = mIndex.find(old.mRef);
if (iter != mIndex.end())
iter->second->updatePtr(ptr);
}
void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)
{
PtrActorMap::iterator iter = mActors.begin();
while(iter != mActors.end())
for (auto iter = mActors.begin(); iter != mActors.end();)
{
if((iter->first.isInCell() && iter->first.getCell()==cellStore) && iter->first != ignore)
if ((iter->getPtr().isInCell() && iter->getPtr().getCell() == cellStore) && iter->getPtr() != ignore)
{
removeTemporaryEffects(iter->first);
delete iter->second;
mActors.erase(iter++);
removeTemporaryEffects(iter->getPtr());
mIndex.erase(iter->getPtr().mRef);
iter = mActors.erase(iter);
}
else
++iter;
@ -1189,14 +1171,14 @@ namespace MWMechanics
if (aiActive)
{
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
if (iter->first == player) continue;
if (actor.getPtr() == player) continue;
bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange;
bool inProcessingRange = (playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange;
if (inProcessingRange)
{
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
MWMechanics::CreatureStats& stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
if (!stats.isDead() && stats.getAiSequence().isInCombat())
{
hasHostiles = true;
@ -1235,9 +1217,9 @@ namespace MWMechanics
const MWWorld::Ptr player = getPlayer();
const MWBase::World* world = MWBase::Environment::get().getWorld();
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
const MWWorld::Ptr& ptr = iter->first;
const MWWorld::Ptr& ptr = actor.getPtr();
if (ptr == player)
continue; // Don't interfere with player controls.
@ -1297,9 +1279,9 @@ namespace MWMechanics
float angleToApproachingActor = 0;
// Iterate through all other actors and predict collisions.
for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter)
for (const Actor& otherActor : mActors)
{
const MWWorld::Ptr& otherPtr = otherIter->first;
const MWWorld::Ptr& otherPtr = otherActor.getPtr();
if (otherPtr == ptr || otherPtr == currentTarget)
continue;
@ -1410,49 +1392,49 @@ namespace MWMechanics
bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();
// AI and magic effects update
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (Actor& actor : mActors)
{
bool isPlayer = iter->first == player;
CharacterController* ctrl = iter->second->getCharacterController();
const bool isPlayer = actor.getPtr() == player;
CharacterController& ctrl = actor.getCharacterController();
MWBase::LuaManager::ActorControls* luaControls =
MWBase::Environment::get().getLuaManager()->getActorControls(iter->first);
MWBase::Environment::get().getLuaManager()->getActorControls(actor.getPtr());
float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2();
const float distSqr = (playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length2();
// AI processing is only done within given distance to the player.
bool inProcessingRange = distSqr <= mActorsProcessingRange*mActorsProcessingRange;
// If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player.
if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead()
|| !iter->first.getClass().getCreatureStats(iter->first).getAiSequence().isInCombat()
if (actor.getPtr() != player && (actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead()
|| !actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getAiSequence().isInCombat()
|| !inProcessingRange))
{
iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(-1);
if (player.getClass().getCreatureStats(player).getHitAttemptActorId() == iter->first.getClass().getCreatureStats(iter->first).getActorId())
actor.getPtr().getClass().getCreatureStats(actor.getPtr()).setHitAttemptActorId(-1);
if (player.getClass().getCreatureStats(player).getHitAttemptActorId() == actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActorId())
player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
}
const Misc::TimerStatus engageCombatTimerStatus = iter->second->updateEngageCombatTimer(duration);
const Misc::TimerStatus engageCombatTimerStatus = actor.updateEngageCombatTimer(duration);
// For dead actors we need to update looping spell particles
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
if (actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead())
{
// They can be added during the death animation
if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished())
adjustMagicEffects(iter->first, duration);
ctrl->updateContinuousVfx();
if (!actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDeathAnimationFinished())
adjustMagicEffects(actor.getPtr(), duration);
ctrl.updateContinuousVfx();
}
else
{
bool cellChanged = world->hasCellChanged();
MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports
updateActor(actor, duration);
const MWWorld::Ptr actorPtr = actor.getPtr(); // make a copy of the map key to avoid it being invalidated when the player teleports
updateActor(actorPtr, 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.
ctrl->updateContinuousVfx();
ctrl.updateContinuousVfx();
if (!cellChanged && world->hasCellChanged())
{
@ -1464,13 +1446,13 @@ namespace MWMechanics
if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed)
{
if (!isPlayer)
adjustCommandedActor(iter->first);
adjustCommandedActor(actor.getPtr());
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
for (const Actor& otherActor : mActors)
{
if (it->first == iter->first || isPlayer) // player is not AI-controlled
if (otherActor.getPtr() == actor.getPtr() || isPlayer) // player is not AI-controlled
continue;
engageCombat(iter->first, it->first, cachedAllies, it->first == player);
engageCombat(actor.getPtr(), otherActor.getPtr(), cachedAllies, otherActor.getPtr() == player);
}
}
if (mTimerUpdateHeadTrack == 0)
@ -1478,7 +1460,7 @@ namespace MWMechanics
float sqrHeadTrackDistance = std::numeric_limits<float>::max();
MWWorld::Ptr headTrackTarget;
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
MWMechanics::CreatureStats& stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
// 1. Unconsious actor can not track target
@ -1493,58 +1475,58 @@ namespace MWMechanics
if (!activePackageTarget.isEmpty())
{
// Track the specified target of package.
updateHeadTracking(iter->first, activePackageTarget, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
updateHeadTracking(actor.getPtr(), activePackageTarget, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
}
}
else
{
// Find something nearby.
for (auto& [ptr, _] : mActors)
for (const Actor& otherActor : mActors)
{
if (ptr == iter->first)
if (otherActor.getPtr() == actor.getPtr())
continue;
updateHeadTracking(iter->first, ptr, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
updateHeadTracking(actor.getPtr(), otherActor.getPtr(), headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
}
}
}
ctrl->setHeadTrackTarget(headTrackTarget);
ctrl.setHeadTrackTarget(headTrackTarget);
}
if (iter->first.getClass().isNpc() && iter->first != player)
updateCrimePursuit(iter->first, duration);
if (actor.getPtr().getClass().isNpc() && actor.getPtr() != player)
updateCrimePursuit(actor.getPtr(), duration);
if (iter->first != player)
if (actor.getPtr() != player)
{
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
if (isConscious(iter->first) && !(luaControls && luaControls->mDisableAI))
CreatureStats &stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
if (isConscious(actor.getPtr()) && !(luaControls && luaControls->mDisableAI))
{
stats.getAiSequence().execute(iter->first, *ctrl, duration);
updateGreetingState(iter->first, *iter->second, mTimerUpdateHello > 0);
playIdleDialogue(iter->first);
updateMovementSpeed(iter->first);
stats.getAiSequence().execute(actor.getPtr(), ctrl, duration);
updateGreetingState(actor.getPtr(), actor, mTimerUpdateHello > 0);
playIdleDialogue(actor.getPtr());
updateMovementSpeed(actor.getPtr());
}
}
}
else if (aiActive && iter->first != player && isConscious(iter->first) && !(luaControls && luaControls->mDisableAI))
else if (aiActive && actor.getPtr() != player && isConscious(actor.getPtr()) && !(luaControls && luaControls->mDisableAI))
{
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true);
CreatureStats &stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
stats.getAiSequence().execute(actor.getPtr(), ctrl, duration, /*outOfRange*/true);
}
if(inProcessingRange && iter->first.getClass().isNpc())
if (inProcessingRange && actor.getPtr().getClass().isNpc())
{
// We can not update drowning state for actors outside of AI distance - they can not resurface to breathe
updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer);
updateDrowning(actor.getPtr(), duration, ctrl.isKnockedOut(), isPlayer);
}
if(mTimerUpdateEquippedLight == 0 && iter->first.getClass().hasInventoryStore(iter->first))
updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches);
if (mTimerUpdateEquippedLight == 0 && actor.getPtr().getClass().hasInventoryStore(actor.getPtr()))
updateEquippedLight(actor.getPtr(), updateEquippedLightInterval, showTorches);
if (luaControls && isConscious(iter->first))
if (luaControls && isConscious(actor.getPtr()))
{
Movement& mov = iter->first.getClass().getMovementSettings(iter->first);
CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
Movement& mov = actor.getPtr().getClass().getMovementSettings(actor.getPtr());
CreatureStats& stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
float speedFactor = isPlayer ? 1.f : mov.mSpeedFactor;
osg::Vec2f movement = osg::Vec2f(mov.mPosition[0], mov.mPosition[1]) * speedFactor;
float rotationX = mov.mRotation[0];
@ -1587,14 +1569,14 @@ namespace MWMechanics
// Animation/movement update
CharacterController* playerCharacter = nullptr;
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (Actor& actor : mActors)
{
const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length();
bool isPlayer = iter->first == player;
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
const float dist = (playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length();
const bool isPlayer = actor.getPtr() == player;
CreatureStats &stats = actor.getPtr().getClass().getCreatureStats(actor.getPtr());
// Actors with active AI should be able to move.
bool alwaysActive = false;
if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
if (!isPlayer && isConscious(actor.getPtr()) && !stats.isParalyzed())
{
MWMechanics::AiSequence& seq = stats.getAiSequence();
alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive();
@ -1605,41 +1587,41 @@ namespace MWMechanics
activeFlag = 2;
int active = inRange ? activeFlag : 0;
CharacterController* ctrl = iter->second->getCharacterController();
ctrl->setActive(active);
CharacterController& ctrl = actor.getCharacterController();
ctrl.setActive(active);
if (!inRange)
{
iter->first.getRefData().getBaseNode()->setNodeMask(0);
world->setActorCollisionMode(iter->first, false, false);
actor.getPtr().getRefData().getBaseNode()->setNodeMask(0);
world->setActorCollisionMode(actor.getPtr(), false, false);
continue;
}
else if (!isPlayer)
{
iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);
if (!iter->second->getPositionAdjusted())
actor.getPtr().getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor);
if (!actor.getPositionAdjusted())
{
iter->first.getClass().adjustPosition(iter->first, false);
iter->second->setPositionAdjusted(true);
actor.getPtr().getClass().adjustPosition(actor.getPtr(), false);
actor.setPositionAdjusted(true);
}
}
const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead();
if (!isDead && (!godmode || !isPlayer) && iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
ctrl->skipAnim();
const bool isDead = actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead();
if (!isDead && (!godmode || !isPlayer) && actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isParalyzed())
ctrl.skipAnim();
// Handle player last, in case a cell transition occurs by casting a teleportation spell
// (would invalidate the iterator)
if (iter->first == getPlayer())
if (actor.getPtr() == getPlayer())
{
playerCharacter = ctrl;
playerCharacter = &ctrl;
continue;
}
world->setActorCollisionMode(iter->first, true, !iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished());
ctrl->update(duration);
world->setActorCollisionMode(actor.getPtr(), true, !actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDeathAnimationFinished());
ctrl.update(duration);
updateVisibility(iter->first, ctrl);
updateVisibility(actor.getPtr(), ctrl);
}
if (playerCharacter)
@ -1649,10 +1631,10 @@ namespace MWMechanics
playerCharacter->setVisibility(1.f);
}
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
const MWWorld::Class &cls = actor.getPtr().getClass();
CreatureStats &stats = cls.getCreatureStats(actor.getPtr());
//KnockedOutOneFrameLogic
//Used for "OnKnockedOut" command
@ -1684,53 +1666,53 @@ namespace MWMechanics
void Actors::resurrect(const MWWorld::Ptr &ptr)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
if(iter->second->getCharacterController()->isDead())
if (iter->second->getCharacterController().isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->getCharacterController()->resurrect();
MWBase::Environment::get().getWorld()->enableActorCollision(iter->second->getPtr(), true);
iter->second->getCharacterController().resurrect();
}
}
}
void Actors::killDeadActors()
{
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (Actor& actor : mActors)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
const MWWorld::Class &cls = actor.getPtr().getClass();
CreatureStats &stats = cls.getCreatureStats(actor.getPtr());
if(!stats.isDead())
continue;
MWBase::Environment::get().getWorld()->removeActorPath(iter->first);
CharacterController::KillResult killResult = iter->second->getCharacterController()->kill();
MWBase::Environment::get().getWorld()->removeActorPath(actor.getPtr());
CharacterController::KillResult killResult = actor.getCharacterController().kill();
if (killResult == CharacterController::Result_DeathAnimStarted)
{
// Play dying words
// Note: It's not known whether the soundgen tags scream, roar, and moan are reliable
// for NPCs since some of the npc death animation files are missing them.
MWBase::Environment::get().getDialogueManager()->say(iter->first, "hit");
MWBase::Environment::get().getDialogueManager()->say(actor.getPtr(), "hit");
// Apply soultrap
if (iter->first.getType() == ESM::Creature::sRecordId)
soulTrap(iter->first);
if (actor.getPtr().getType() == ESM::Creature::sRecordId)
soulTrap(actor.getPtr());
if (cls.isEssential(iter->first))
if (cls.isEssential(actor.getPtr()))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
}
else if (killResult == CharacterController::Result_DeathAnimJustFinished)
{
bool isPlayer = iter->first == getPlayer();
notifyDied(iter->first);
const bool isPlayer = actor.getPtr() == getPlayer();
notifyDied(actor.getPtr());
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
float vampirism = stats.getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude();
stats.getActiveSpells().clear(iter->first);
stats.getActiveSpells().clear(actor.getPtr());
// Make sure spell effects are removed
purgeSpellEffects(stats.getActorId());
@ -1746,7 +1728,7 @@ namespace MWMechanics
else
{
// NPC death animation is over, disable actor collision
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
MWBase::Environment::get().getWorld()->enableActorCollision(actor.getPtr(), false);
}
}
}
@ -1785,10 +1767,10 @@ namespace MWMechanics
void Actors::purgeSpellEffects(int casterActorId)
{
for (PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
for (const Actor& actor : mActors)
{
MWMechanics::ActiveSpells& spells = iter->first.getClass().getCreatureStats(iter->first).getActiveSpells();
spells.purge(iter->first, casterActorId);
MWMechanics::ActiveSpells& spells = actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActiveSpells();
spells.purge(actor.getPtr(), casterActorId);
}
}
@ -1802,28 +1784,28 @@ namespace MWMechanics
const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
if (actor.getPtr().getClass().getCreatureStats(actor.getPtr()).isDead())
{
adjustMagicEffects (iter->first, duration);
adjustMagicEffects(actor.getPtr(), duration);
continue;
}
if (!sleep || iter->first == player)
restoreDynamicStats(iter->first, hours, sleep);
if (!sleep || actor.getPtr() == player)
restoreDynamicStats(actor.getPtr(), hours, sleep);
if ((!iter->first.getRefData().getBaseNode()) ||
(playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
if ((!actor.getPtr().getRefData().getBaseNode()) ||
(playerPos - actor.getPtr().getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
continue;
adjustMagicEffects (iter->first, duration);
adjustMagicEffects (actor.getPtr(), duration);
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(actor.getPtr());
if (animation)
{
animation->removeEffects();
MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first);
MWBase::Environment::get().getWorld()->applyLoopingParticles(actor.getPtr());
}
}
@ -1935,17 +1917,17 @@ namespace MWMechanics
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->getCharacterController()->forceStateUpdate();
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
iter->second->getCharacterController().forceStateUpdate();
}
bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
const auto iter = mIndex.find(ptr.mRef);
if(iter != mIndex.end())
{
return iter->second->getCharacterController()->playGroup(groupName, mode, number, persist);
return iter->second->getCharacterController().playGroup(groupName, mode, number, persist);
}
else
{
@ -1955,55 +1937,55 @@ namespace MWMechanics
}
void Actors::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->getCharacterController()->skipAnim();
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
iter->second->getCharacterController().skipAnim();
}
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
return iter->second->getCharacterController()->isAnimPlaying(groupName);
const auto iter = mIndex.find(ptr.mRef);
if(iter != mIndex.end())
return iter->second->getCharacterController().isAnimPlaying(groupName);
return false;
}
void Actors::persistAnimationStates()
{
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
iter->second->getCharacterController()->persistAnimationState();
for (Actor& actor : mActors)
actor.getCharacterController().persistAnimationState();
}
void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
{
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
out.push_back(iter->first);
if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
out.push_back(actor.getPtr());
}
}
bool Actors::isAnyObjectInRange(const osg::Vec3f& position, float radius)
{
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
if ((iter->first.getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
if ((actor.getPtr().getRefData().getPosition().asVec3() - position).length2() <= radius*radius)
return true;
}
return false;
}
std::vector<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actor)
std::vector<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actorPtr)
{
std::vector<MWWorld::Ptr> list;
for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
const MWWorld::Ptr &iteratedActor = iter->first;
const MWWorld::Ptr& iteratedActor = actor.getPtr();
if (iteratedActor == getPlayer())
continue;
const bool sameActor = (iteratedActor == actor);
const bool sameActor = (iteratedActor == actorPtr);
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
if (stats.isDead())
@ -2019,7 +2001,7 @@ namespace MWMechanics
{
list.push_back(package->getTarget());
}
else if (package->getTarget() == actor)
else if (package->getTarget() == actorPtr)
{
list.push_back(iteratedActor);
}
@ -2032,13 +2014,13 @@ namespace MWMechanics
return list;
}
std::vector<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
std::vector<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actorPtr)
{
std::vector<MWWorld::Ptr> list;
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
forEachFollowingPackage(mActors, actorPtr, getPlayer(), [&] (const Actor& actor, const std::shared_ptr<AiPackage>& package)
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iter.first);
if (package->followTargetThroughDoors() && package->getTarget() == actorPtr)
list.push_back(actor.getPtr());
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
return false;
return true;
@ -2086,7 +2068,7 @@ namespace MWMechanics
std::vector<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
{
std::vector<int> list;
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (const Actor&, const std::shared_ptr<AiPackage>& package)
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
{
@ -2103,12 +2085,12 @@ namespace MWMechanics
std::map<int, MWWorld::Ptr> Actors::getActorsFollowingByIndex(const MWWorld::Ptr &actor)
{
std::map<int, MWWorld::Ptr> map;
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::shared_ptr<AiPackage>& package)
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (const Actor& otherActor, const std::shared_ptr<AiPackage>& package)
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
{
int index = static_cast<const AiFollow*>(package.get())->getFollowIndex();
map[index] = iter.first;
map[index] = otherActor.getPtr();
return false;
}
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
@ -2190,12 +2172,7 @@ namespace MWMechanics
void Actors::clear()
{
PtrActorMap::iterator it(mActors.begin());
for (; it != mActors.end(); ++it)
{
delete it->second;
it->second = nullptr;
}
mIndex.clear();
mActors.clear();
mDeathCount.clear();
}
@ -2207,36 +2184,35 @@ namespace MWMechanics
bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
return it->second->getCharacterController()->isReadyToBlock();
return it->second->getCharacterController().isReadyToBlock();
}
bool Actors::isCastingSpell(const MWWorld::Ptr &ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
return it->second->getCharacterController()->isCastingSpell();
return it->second->getCharacterController().isCastingSpell();
}
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isAttackingOrSpell();
return it->second->getCharacterController().isAttackingOrSpell();
}
int Actors::getGreetingTimer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return 0;
return it->second->getGreetingTimer();
@ -2244,8 +2220,8 @@ namespace MWMechanics
float Actors::getAngleToPlayer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return 0.f;
return it->second->getAngleToPlayer();
@ -2253,8 +2229,8 @@ namespace MWMechanics
GreetingState Actors::getGreetingState(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return Greet_None;
return it->second->getGreetingState();
@ -2262,8 +2238,8 @@ namespace MWMechanics
bool Actors::isTurningToPlayer(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
const auto it = mIndex.find(ptr.mRef);
if (it == mIndex.end())
return false;
return it->second->isTurningToPlayer();
@ -2274,11 +2250,9 @@ namespace MWMechanics
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
return;
// making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator
PtrActorMap map = mActors;
for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it)
for (const Actor& actor : mActors)
{
MWWorld::Ptr ptr = it->first;
const MWWorld::Ptr ptr = actor.getPtr();
if (ptr == getPlayer()
|| !isConscious(ptr)
|| ptr.getClass().getCreatureStats(ptr).isParalyzed())

View File

@ -7,7 +7,8 @@
#include <list>
#include <map>
#include "../mwmechanics/actorutil.hpp"
#include "actorutil.hpp"
#include "actor.hpp"
namespace ESM
{
@ -42,12 +43,9 @@ namespace MWMechanics
public:
Actors();
~Actors();
typedef std::map<MWWorld::Ptr,Actor*> PtrActorMap;
PtrActorMap::const_iterator begin() { return mActors.begin(); }
PtrActorMap::const_iterator end() { return mActors.end(); }
std::list<Actor>::const_iterator begin() const { return mActors.begin(); }
std::list<Actor>::const_iterator end() const { return mActors.end(); }
std::size_t size() const { return mActors.size(); }
void notifyDied(const MWWorld::Ptr &actor);
@ -190,7 +188,8 @@ namespace MWMechanics
};
std::map<std::string, int> mDeathCount;
PtrActorMap mActors;
std::list<Actor> mActors;
std::map<const MWWorld::LiveCellRefBase*, std::list<Actor>::iterator> mIndex;
float mTimerDisposeSummonsCorpses;
float mTimerUpdateHeadTrack = 0;
float mTimerUpdateEquippedLight = 0;
@ -201,7 +200,7 @@ namespace MWMechanics
bool mSmoothMovement;
MusicType mCurrentMusic = MusicType::Explore;
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl);
void updateVisibility (const MWWorld::Ptr& ptr, CharacterController& ctrl);
void adjustMagicEffects (const MWWorld::Ptr& creature, float duration);

View File

@ -241,7 +241,10 @@ public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
virtual ~CharacterController();
const MWWorld::Ptr& getPtr() const { return mPtr; };
CharacterController(const CharacterController&) = delete;
CharacterController(CharacterController&&) = delete;
const MWWorld::Ptr& getPtr() const { return mPtr; }
void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) override;

View File

@ -30,6 +30,7 @@
#include "npcstats.hpp"
#include "actorutil.hpp"
#include "combat.hpp"
#include "actor.hpp"
namespace
{
@ -1599,16 +1600,16 @@ namespace MWMechanics
if (ptr.getClass().isClass(ptr, "Guard"))
{
stats.setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable
for (Actors::PtrActorMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
for (const Actor& actor : mActors)
{
if (iter->first.getClass().isClass(iter->first, "Guard"))
if (actor.getPtr().getClass().isClass(actor.getPtr(), "Guard"))
{
MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence();
MWMechanics::AiSequence& aiSeq = actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getAiSequence();
if (aiSeq.getTypeId() == MWMechanics::AiPackageTypeId::Pursue)
{
aiSeq.stopPursuit();
aiSeq.stack(MWMechanics::AiCombat(target), ptr);
iter->first.getClass().getCreatureStats(iter->first).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable
actor.getPtr().getClass().getCreatureStats(actor.getPtr()).setHitAttemptActorId(target.getClass().getCreatureStats(target).getActorId()); // Stops guard from ending combat if player is unreachable
}
}
}

View File

@ -22,12 +22,12 @@ void Objects::addObject(const MWWorld::Ptr& ptr)
return;
const auto it = mObjects.emplace(mObjects.end(), ptr, anim);
mIndex.emplace(ptr.getBase(), it);
mIndex.emplace(ptr.mRef, it);
}
void Objects::removeObject(const MWWorld::Ptr& ptr)
{
const auto iter = mIndex.find(ptr.getBase());
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
mObjects.erase(iter->second);
@ -37,7 +37,7 @@ void Objects::removeObject(const MWWorld::Ptr& ptr)
void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
const auto iter = mIndex.find(old.getBase());
const auto iter = mIndex.find(old.mRef);
if (iter != mIndex.end())
iter->second->updatePtr(ptr);
}
@ -48,7 +48,7 @@ void Objects::dropObjects (const MWWorld::CellStore *cellStore)
{
if (iter->getPtr().getCell() == cellStore)
{
mIndex.erase(iter->getPtr().getBase());
mIndex.erase(iter->getPtr().mRef);
iter = mObjects.erase(iter);
}
else
@ -86,7 +86,7 @@ void Objects::update(float duration, bool paused)
bool Objects::onOpen(const MWWorld::Ptr& ptr)
{
const auto iter = mIndex.find(ptr.getBase());
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
return iter->second->onOpen();
return true;
@ -94,14 +94,14 @@ bool Objects::onOpen(const MWWorld::Ptr& ptr)
void Objects::onClose(const MWWorld::Ptr& ptr)
{
const auto iter = mIndex.find(ptr.getBase());
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
iter->second->onClose();
}
bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
{
const auto iter = mIndex.find(ptr.getBase());
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
{
return iter->second->playGroup(groupName, mode, number, persist);
@ -114,7 +114,7 @@ bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& gro
}
void Objects::skipAnimation(const MWWorld::Ptr& ptr)
{
const auto iter = mIndex.find(ptr.getBase());
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())
iter->second->skipAnim();
}