diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9c7d28fc9..88fee8ef1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -280,7 +280,7 @@ namespace MWMechanics } } - void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) + void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map >& cachedAllies, bool againstPlayer) { CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); if (creatureStats1.getAiSequence().isInCombat(actor2)) @@ -306,7 +306,8 @@ namespace MWMechanics // Get actors allied with actor1. Includes those following or escorting actor1, actors following or escorting those actors, (recursive) // and any actor currently being followed or escorted by actor1 std::set allies1; - getActorsSidingWith(actor1, allies1); + + getActorsSidingWith(actor1, allies1, cachedAllies); // If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2 for (std::set::const_iterator it = allies1.begin(); it != allies1.end(); ++it) @@ -328,10 +329,10 @@ namespace MWMechanics aggressive = true; } - std::set playerFollowersAndEscorters; - getActorsSidingWith(MWMechanics::getPlayer(), playerFollowersAndEscorters); + std::set playerAllies; + getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies); - bool isPlayerFollowerOrEscorter = std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end(); + bool isPlayerFollowerOrEscorter = std::find(playerAllies.begin(), playerAllies.end(), actor1) != playerAllies.end(); // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them // Doesn't apply for player followers/escorters @@ -341,7 +342,9 @@ namespace MWMechanics if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(actor1)) { std::set allies2; - getActorsSidingWith(actor2, allies2); + + getActorsSidingWith(actor2, allies2, cachedAllies); + // Check that an ally of actor2 is also in combat with actor1 for (std::set::const_iterator it = allies2.begin(); it != allies2.end(); ++it) { @@ -383,11 +386,11 @@ namespace MWMechanics // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { - if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + if (againstPlayer || std::find(playerAllies.begin(), playerAllies.end(), actor2) != playerAllies.end()) { // Player followers and escorters with high fight should not initiate combat with the player or with // other player followers or escorters - if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) == playerFollowersAndEscorters.end()) + if (std::find(playerAllies.begin(), playerAllies.end(), actor1) == playerAllies.end()) aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } } @@ -1075,6 +1078,8 @@ namespace MWMechanics /// \todo move update logic to Actor class where appropriate + std::map > cachedAllies; // will be filled as engageCombat iterates + // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1126,7 +1131,7 @@ namespace MWMechanics { if (it->first == iter->first || iter->first == player) // player is not AI-controlled continue; - engageCombat(iter->first, it->first, it->first == player); + engageCombat(iter->first, it->first, cachedAllies, it->first == player); } } if (timerUpdateHeadTrack == 0) @@ -1591,6 +1596,29 @@ namespace MWMechanics getActorsSidingWith(*it, out); } + void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, std::map >& cachedAllies) { + // If we have already found actor's allies, use the cache + std::map >::const_iterator search = cachedAllies.find(actor); + if (search != cachedAllies.end()) + out.insert(search->second.begin(), search->second.end()); + else + { + std::list followers = getActorsSidingWith(actor); + for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) + if (out.insert(*it).second) + getActorsSidingWith(*it, out, cachedAllies); + + // Cache ptrs and their sets of allies + cachedAllies.insert(std::make_pair(actor, out)); + for (std::set::const_iterator it = out.begin(); it != out.end(); ++it) + { + search = cachedAllies.find(*it); + if (search == cachedAllies.end()) + cachedAllies.insert(std::make_pair(*it, out)); + } + } + } + std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) { std::list list; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 20aef4c17..362c2f126 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -88,7 +88,7 @@ namespace MWMechanics @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 engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map >& cachedAllies, bool againstPlayer); void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance); @@ -127,6 +127,8 @@ namespace MWMechanics void getActorsFollowing(const MWWorld::Ptr &actor, std::set& out); /// Recursive version of getActorsSidingWith void getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out); + /// Recursive version of getActorsSidingWith that takes, adds to and returns a cache of actors mapped to their allies + void getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, std::map >& cachedAllies); /// Get the list of AiFollow::mFollowIndex for all actors following this target std::list getActorsFollowingIndices(const MWWorld::Ptr& actor);