1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-31 00:11:31 +00:00

Move the caching getActorsSidingWith to its own type

This commit is contained in:
Evil Eye 2024-01-25 20:44:18 +01:00
parent bdc6119b31
commit a8ee3dfae8
3 changed files with 65 additions and 56 deletions

View file

@ -577,8 +577,8 @@ namespace MWMechanics
}
}
void Actors::engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies, bool againstPlayer) const
void Actors::engageCombat(
const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, SidingCache& cachedAllies, bool againstPlayer) const
{
// No combat for totally static creatures
if (!actor1.getClass().isMobile(actor1))
@ -606,9 +606,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<MWWorld::Ptr> allies1;
getActorsSidingWith(actor1, allies1, cachedAllies);
const std::set<MWWorld::Ptr> allies1 = cachedAllies.getActorsSidingWith(actor1);
const std::set<MWWorld::Ptr> allies2 = cachedAllies.getActorsSidingWith(actor2);
const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager();
// If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and
@ -620,7 +619,7 @@ namespace MWMechanics
if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
{
mechanicsManager->startCombat(actor1, actor2, nullptr);
mechanicsManager->startCombat(actor1, actor2, &allies2);
// Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
// if the player gets out of reach, while the ally would continue combat with the player
creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
@ -633,9 +632,8 @@ namespace MWMechanics
aggressive = true;
}
std::set<MWWorld::Ptr> playerAllies;
MWWorld::Ptr player = MWMechanics::getPlayer();
getActorsSidingWith(player, playerAllies, cachedAllies);
const std::set<MWWorld::Ptr>& playerAllies = cachedAllies.getActorsSidingWith(player);
bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
@ -646,10 +644,6 @@ namespace MWMechanics
// Check that actor2 is in combat with actor1
if (creatureStats2.getAiSequence().isInCombat(actor1))
{
std::set<MWWorld::Ptr> allies2;
getActorsSidingWith(actor2, allies2, cachedAllies);
// Check that an ally of actor2 is also in combat with actor1
for (const MWWorld::Ptr& ally2 : allies2)
{
@ -747,7 +741,7 @@ namespace MWMechanics
bool LOS = world->getLOS(actor1, actor2) && mechanicsManager->awarenessCheck(actor2, actor1);
if (LOS)
mechanicsManager->startCombat(actor1, actor2, nullptr);
mechanicsManager->startCombat(actor1, actor2, &allies2);
}
}
@ -1093,7 +1087,7 @@ namespace MWMechanics
}
}
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration) const
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration, SidingCache& cachedAllies) const
{
const MWWorld::Ptr player = getPlayer();
if (ptr == player)
@ -1133,7 +1127,7 @@ namespace MWMechanics
= esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
if (playerStats.getBounty() >= cutoff * iCrimeThresholdMultiplier)
{
mechanicsManager->startCombat(ptr, player, nullptr);
mechanicsManager->startCombat(ptr, player, &cachedAllies.getActorsSidingWith(player));
creatureStats.setHitAttemptActorId(
playerClass.getCreatureStats(player)
.getActorId()); // Stops the guard from quitting combat if player is unreachable
@ -1508,8 +1502,7 @@ namespace MWMechanics
/// \todo move update logic to Actor class where appropriate
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>
cachedAllies; // will be filled as engageCombat iterates
SidingCache cachedAllies{ *this, true }; // will be filled as engageCombat iterates
const bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive();
const int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId();
@ -1597,7 +1590,7 @@ namespace MWMechanics
updateHeadTracking(actor.getPtr(), mActors, isPlayer, ctrl);
if (actor.getPtr().getClass().isNpc() && !isPlayer)
updateCrimePursuit(actor.getPtr(), duration);
updateCrimePursuit(actor.getPtr(), duration, cachedAllies);
if (!isPlayer)
{
@ -2156,32 +2149,6 @@ namespace MWMechanics
getActorsSidingWith(follower, out, excludeInfighting);
}
void Actors::getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies) const
{
// If we have already found actor's allies, use the cache
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>::const_iterator search = cachedAllies.find(actor);
if (search != cachedAllies.end())
out.insert(search->second.begin(), search->second.end());
else
{
for (const MWWorld::Ptr& follower : getActorsSidingWith(actor, true))
if (out.insert(follower).second && follower != actor)
getActorsSidingWith(follower, out, cachedAllies);
// Cache ptrs and their sets of allies
cachedAllies.insert(std::make_pair(actor, out));
for (const MWWorld::Ptr& iter : out)
{
if (iter == actor)
continue;
search = cachedAllies.find(iter);
if (search == cachedAllies.end())
cachedAllies.insert(std::make_pair(iter, out));
}
}
}
std::vector<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr& actor) const
{
std::vector<int> list;
@ -2380,4 +2347,32 @@ namespace MWMechanics
seq.fastForward(ptr);
}
}
const std::set<MWWorld::Ptr>& SidingCache::getActorsSidingWith(const MWWorld::Ptr& actor)
{
// If we have already found actor's allies, use the cache
auto search = mCache.find(actor);
if (search != mCache.end())
return search->second;
std::set<MWWorld::Ptr>& out = mCache[actor];
for (const MWWorld::Ptr& follower : mActors.getActorsSidingWith(actor, mExcludeInfighting))
{
if (out.insert(follower).second && follower != actor)
{
const auto& allies = getActorsSidingWith(follower);
out.insert(allies.begin(), allies.end());
}
}
// Cache ptrs and their sets of allies
for (const MWWorld::Ptr& iter : out)
{
if (iter == actor)
continue;
search = mCache.find(iter);
if (search == mCache.end())
mCache.emplace(iter, out);
}
return out;
}
}

View file

@ -36,6 +36,7 @@ namespace MWMechanics
class Actor;
class CharacterController;
class CreatureStats;
class SidingCache;
class Actors
{
@ -182,7 +183,7 @@ namespace MWMechanics
void calculateRestoration(const MWWorld::Ptr& ptr, float duration) const;
void updateCrimePursuit(const MWWorld::Ptr& ptr, float duration) const;
void updateCrimePursuit(const MWWorld::Ptr& ptr, float duration, SidingCache& cachedAllies) const;
void killDeadActors();
@ -194,13 +195,25 @@ 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,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies, bool againstPlayer) const;
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, SidingCache& cachedAllies,
bool againstPlayer) const;
};
/// Recursive version of getActorsSidingWith that takes, adds to and returns a cache of
/// actors mapped to their allies. Excludes infighting
void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies) const;
class SidingCache
{
const Actors& mActors;
const bool mExcludeInfighting;
std::map<MWWorld::Ptr, std::set<MWWorld::Ptr>> mCache;
public:
SidingCache(const Actors& actors, bool excludeInfighting)
: mActors(actors)
, mExcludeInfighting(excludeInfighting)
{
}
/// Recursive version of getActorsSidingWith that takes, returns a cached set of allies
const std::set<MWWorld::Ptr>& getActorsSidingWith(const MWWorld::Ptr& actor);
};
}

View file

@ -30,6 +30,7 @@
#include "../mwbase/world.hpp"
#include "actor.hpp"
#include "actors.hpp"
#include "actorutil.hpp"
#include "aicombat.hpp"
#include "aipursue.hpp"
@ -1508,14 +1509,14 @@ namespace MWMechanics
if (!peaceful)
{
startCombat(target, attacker, nullptr);
SidingCache cachedAllies{ mActors, false };
const std::set<MWWorld::Ptr>& attackerAllies = cachedAllies.getActorsSidingWith(attacker);
startCombat(target, attacker, &attackerAllies);
// Force friendly actors into combat to prevent infighting between followers
std::set<MWWorld::Ptr> followersTarget;
getActorsSidingWith(target, followersTarget);
for (const auto& follower : followersTarget)
for (const auto& follower : cachedAllies.getActorsSidingWith(target))
{
if (follower != attacker && follower != player)
startCombat(follower, attacker, nullptr);
startCombat(follower, attacker, &attackerAllies);
}
}
}