diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0ee49ac45..92f2ba34d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -501,12 +501,6 @@ namespace MWMechanics } } - void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) - { - updateDrowning(ptr, duration); - calculateNpcStatModifiers(ptr, duration); - } - void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); @@ -926,13 +920,8 @@ namespace MWMechanics return ctrl->isSneaking(); } - void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer) { - PtrActorMap::iterator it = mActors.find(ptr); - if (it == mActors.end()) - return; - CharacterController* ctrl = it->second->getCharacterController(); - NpcStats &stats = ptr.getClass().getNpcStats(ptr); // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST @@ -940,9 +929,10 @@ namespace MWMechanics if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); - if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + if (stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { - if(ptr != MWMechanics::getPlayer() ) { + if(!isPlayer) + { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once seq.stack(MWMechanics::AiBreathe(), ptr); @@ -950,7 +940,7 @@ namespace MWMechanics } MWBase::World *world = MWBase::Environment::get().getWorld(); - bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); + bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { @@ -965,7 +955,7 @@ namespace MWMechanics stats.setTimeToStartDrowning(timeLeft); } - bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(timeLeft == 0.0f && !godmode) { @@ -980,7 +970,7 @@ namespace MWMechanics if(!sndmgr->getSoundPlaying(ptr, "drown")) sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f); - if(ptr == world->getPlayerPtr()) + if(isPlayer) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } @@ -1242,21 +1232,24 @@ namespace MWMechanics void Actors::updateCombatMusic () { MWWorld::Ptr player = getPlayer(); - int hostilesCount = 0; // need to know this to play Battle music + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + bool hasHostiles = false; // need to know this to play Battle music + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); - for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + if (aiActive) { - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - <= sqrAiProcessingDistance; + if (iter->first == player) continue; - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance; + if (inProcessingRange) { - if (iter->first != player) + MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + if (!stats.isDead() && stats.getAiSequence().isInCombat()) { - MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; + hasHostiles = true; + break; } } } @@ -1265,13 +1258,13 @@ namespace MWMechanics // check if we still have any player enemies to switch music static int currentMusic = 0; - if (currentMusic != 1 && hostilesCount == 0 && !(player.getClass().getCreatureStats(player).isDead() && + if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getSoundManager()->isMusicPlaying())) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); currentMusic = 1; } - else if (currentMusic != 2 && hostilesCount > 0) + else if (currentMusic != 2 && hasHostiles) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); currentMusic = 2; @@ -1298,17 +1291,29 @@ namespace MWMechanics bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); MWWorld::Ptr player = getPlayer(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); /// \todo move update logic to Actor class where appropriate std::map > cachedAllies; // will be filled as engageCombat iterates + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); + int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId(); + if (attackedByPlayerId != -1) + { + const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId); + + if (!playerHitAttemptActor.isInCell()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); + } + // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { bool isPlayer = iter->first == player; + CharacterController* ctrl = iter->second->getCharacterController(); - float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // This distance could be made configurable later, but the setting must be marked with a big warning: @@ -1316,7 +1321,7 @@ namespace MWMechanics bool inProcessingRange = distSqr <= sqrAiProcessingDistance; if (isPlayer) - iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + ctrl->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); // 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() @@ -1328,11 +1333,6 @@ namespace MWMechanics player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } - const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); - - if (!playerHitAttemptActor.isInCell()) - player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); @@ -1343,7 +1343,7 @@ namespace MWMechanics 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 (aiActive && inProcessingRange) { if (timerUpdateAITargets == 0) { @@ -1381,7 +1381,7 @@ namespace MWMechanics } } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); + ctrl->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) @@ -1391,13 +1391,14 @@ namespace MWMechanics { CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); if (isConscious(iter->first)) - stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration); + stats.getAiSequence().execute(iter->first, *ctrl, duration); } } if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateNpc(iter->first, duration); + updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer); + calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches); @@ -1423,7 +1424,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. - const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); bool isPlayer = iter->first == player; bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -1437,22 +1438,24 @@ namespace MWMechanics inAnimationRange = true; active = std::max(1, active); } - iter->second->getCharacterController()->setActive(active); + + CharacterController* ctrl = iter->second->getCharacterController(); + ctrl->setActive(active); if (!inAnimationRange) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) - iter->second->getCharacterController()->skipAnim(); + ctrl->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(); + playerCharacter = ctrl; continue; } - iter->second->getCharacterController()->update(duration); + ctrl->update(duration); } if (playerCharacter) @@ -1512,7 +1515,7 @@ namespace MWMechanics continue; // is the player in range and can they be detected - if ((observer.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= radius*radius + if ((observer.getRefData().getPosition().asVec3() - playerPos).length2() <= radius*radius && MWBase::Environment::get().getWorld()->getLOS(player, observer)) { if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) @@ -1656,7 +1659,8 @@ namespace MWMechanics void Actors::rest(bool sleep) { float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + 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) { @@ -1666,7 +1670,7 @@ namespace MWMechanics restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || - (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) + (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) continue; adjustMagicEffects (iter->first); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 8c94ce45f..a1e0e511d 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -28,8 +28,6 @@ namespace MWMechanics void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); - void updateNpc(const MWWorld::Ptr &ptr, float duration); - void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); @@ -39,7 +37,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); - void updateDrowning (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip);