Avoid directly iterating the actor map (Fixes #3173)

This commit is contained in:
scrawl 2016-02-02 00:50:21 +01:00
parent 50ed061154
commit 59d2de118f

View file

@ -982,8 +982,11 @@ namespace MWMechanics
/// \todo move update logic to Actor class where appropriate /// \todo move update logic to Actor class where appropriate
// make a copy of the map to avoid invalidated iterators when an actor moves during the update
PtrActorMap actors = mActors;
// AI and magic effects update // AI and magic effects update
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for(PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
{ {
bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
<= sqrProcessingDistance; <= sqrProcessingDistance;
@ -996,6 +999,11 @@ namespace MWMechanics
if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
{ {
updateActor(iter->first, duration); updateActor(iter->first, duration);
if (MWBase::Environment::get().getWorld()->hasCellChanged())
{
return; // for now abort update of the old cell when cell changes by teleportation magic effect
// a better solution might be to apply cell changes at the end of the frame
}
if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange)
{ {
if (timerUpdateAITargets == 0) if (timerUpdateAITargets == 0)
@ -1003,7 +1011,7 @@ namespace MWMechanics
if (iter->first != player) if (iter->first != player)
adjustCommandedActor(iter->first); adjustCommandedActor(iter->first);
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrActorMap::iterator it(actors.begin()); it != actors.end(); ++it)
{ {
if (it->first == iter->first || iter->first == player) // player is not AI-controlled if (it->first == iter->first || iter->first == player) // player is not AI-controlled
continue; continue;
@ -1015,7 +1023,7 @@ namespace MWMechanics
float sqrHeadTrackDistance = std::numeric_limits<float>::max(); float sqrHeadTrackDistance = std::numeric_limits<float>::max();
MWWorld::Ptr headTrackTarget; MWWorld::Ptr headTrackTarget;
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrActorMap::iterator it(actors.begin()); it != actors.end(); ++it)
{ {
if (it->first == iter->first) if (it->first == iter->first)
continue; continue;
@ -1050,12 +1058,12 @@ namespace MWMechanics
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles), // 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. // so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between. // There needs to be a magic effect update in between.
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for(PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
iter->second->getCharacterController()->updateContinuousVfx(); iter->second->getCharacterController()->updateContinuousVfx();
// Animation/movement update // Animation/movement update
CharacterController* playerCharacter = NULL; CharacterController* playerCharacter = NULL;
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for(PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
{ {
if (iter->first != player && if (iter->first != player &&
(player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2()
@ -1065,20 +1073,10 @@ namespace MWMechanics
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
iter->second->getCharacterController()->skipAnim(); iter->second->getCharacterController()->skipAnim();
// Handle player last, in case a cell transition occurs by casting a teleportation spell
// (would invalidate the iterator)
if (iter->first == getPlayer())
{
playerCharacter = iter->second->getCharacterController();
continue;
}
iter->second->getCharacterController()->update(duration); iter->second->getCharacterController()->update(duration);
} }
if (playerCharacter) for(PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
playerCharacter->update(duration);
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{ {
const MWWorld::Class &cls = iter->first.getClass(); const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first);
@ -1136,7 +1134,7 @@ namespace MWMechanics
bool detected = false; bool detected = false;
for (PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for (PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
{ {
if (iter->first == player) // not the player if (iter->first == player) // not the player
continue; continue;
@ -1179,7 +1177,8 @@ namespace MWMechanics
void Actors::killDeadActors() void Actors::killDeadActors()
{ {
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) PtrActorMap actors = mActors;
for(PtrActorMap::iterator iter(actors.begin()); iter != actors.end(); ++iter)
{ {
const MWWorld::Class &cls = iter->first.getClass(); const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first);
@ -1210,7 +1209,7 @@ namespace MWMechanics
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
// Make sure spell effects are removed // Make sure spell effects are removed
for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) for (PtrActorMap::iterator iter2(actors.begin());iter2 != actors.end();++iter2)
{ {
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(stats.getActorId()); spells.purge(stats.getActorId());