diff --git a/CHANGELOG.md b/CHANGELOG.md index a95e960528..4d6918cffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Bug #3792: 1 frame late magicka recalc breaks early scripted magicka reactions to Intelligence change Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes Bug #3855: AI sometimes spams defensive spells + Bug #3867: All followers attack player when one follower enters combat with player Bug #3905: Great House Dagoth issues Bug #4203: Resurrecting an actor doesn't close the loot GUI Bug #4376: Moved actors don't respawn in their original cells @@ -26,6 +27,7 @@ Bug #5192: Actor turn rate is too slow Bug #5207: Loose summons can be present in scene Bug #5279: Ingame console stops auto-scrolling after clicking output + Bug #5318: Aiescort behaves differently from vanilla Bug #5371: 'Dead' slaughterfish added by mod are animated/alive Bug #5377: Console does not appear after using menutest in inventory Bug #5379: Wandering NPCs falling through cantons diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2353f3729d..b24149827c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1980,7 +1980,7 @@ namespace MWMechanics return false; } - std::vector Actors::getActorsSidingWith(const MWWorld::Ptr& actorPtr) + std::vector Actors::getActorsSidingWith(const MWWorld::Ptr& actorPtr, bool excludeInfighting) { std::vector list; for (const Actor& actor : mActors) @@ -1999,10 +1999,20 @@ namespace MWMechanics // Actors that are targeted by this actor's Follow or Escort packages also side with them for (const auto& package : stats.getAiSequence()) { + if (excludeInfighting && !sameActor && package->getTypeId() == AiPackageTypeId::Combat && package->getTarget() == actorPtr) + break; if (package->sideWithTarget() && !package->getTarget().isEmpty()) { if (sameActor) { + if(excludeInfighting) + { + MWWorld::Ptr ally = package->getTarget(); + std::vector enemies; + if(ally.getClass().getCreatureStats(ally).getAiSequence().getCombatTargets(enemies) + && std::find(enemies.begin(), enemies.end(), actorPtr) != enemies.end()) + break; + } list.push_back(package->getTarget()); } else if (package->getTarget() == actorPtr) @@ -2039,11 +2049,11 @@ namespace MWMechanics getActorsFollowing(follower, out); } - void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out) { - auto followers = getActorsSidingWith(actor); + void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, bool excludeInfighting) { + auto followers = getActorsSidingWith(actor, excludeInfighting); for(const MWWorld::Ptr &follower : followers) if (out.insert(follower).second) - getActorsSidingWith(follower, out); + getActorsSidingWith(follower, out, excludeInfighting); } void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, std::map >& cachedAllies) { @@ -2053,7 +2063,7 @@ namespace MWMechanics out.insert(search->second.begin(), search->second.end()); else { - auto followers = getActorsSidingWith(actor); + auto followers = getActorsSidingWith(actor, true); for (const MWWorld::Ptr &follower : followers) if (out.insert(follower).second) getActorsSidingWith(follower, out, cachedAllies); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 547c2916e2..7b54a98198 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -93,11 +93,6 @@ namespace MWMechanics /// Removes an actor from combat and makes all of their allies stop fighting the actor's targets void stopCombat(const MWWorld::Ptr& ptr); - /** Start combat between two actors - @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 >& cachedAllies, bool againstPlayer); void playIdleDialogue(const MWWorld::Ptr& actor); void updateMovementSpeed(const MWWorld::Ptr& actor); @@ -144,15 +139,13 @@ namespace MWMechanics ///Returns the list of actors which are siding with the given actor in fights /**ie AiFollow or AiEscort is active and the target is the actor **/ - std::vector getActorsSidingWith(const MWWorld::Ptr& actor); + std::vector getActorsSidingWith(const MWWorld::Ptr& actor, bool excludeInfighting = false); std::vector getActorsFollowing(const MWWorld::Ptr& actor); /// Recursive version of getActorsFollowing 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); + void getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, bool excludeInfighting = false); /// Get the list of AiFollow::mFollowIndex for all actors following this target std::vector getActorsFollowingIndices(const MWWorld::Ptr& actor); @@ -218,6 +211,16 @@ namespace MWMechanics void purgeSpellEffects (int casterActorId); void predictAndAvoidCollisions(float duration); + + /** Start combat between two actors + @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 >& cachedAllies, bool againstPlayer); + + /// 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& out, std::map >& cachedAllies); + }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2fd5a81f0d..f20381ea91 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1457,7 +1457,14 @@ namespace MWMechanics } if (!peaceful) + { startCombat(target, attacker); + // Force friendly actors into combat to prevent infighting between followers + std::set followersTarget; + getActorsSidingWith(target, followersTarget); + for(const auto& follower : followersTarget) + startCombat(follower, attacker); + } } }