diff --git a/CHANGELOG.md b/CHANGELOG.md index c8eef0d45..30e4409bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Bug #4286: Scripted animations can be interrupted Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter + Bug #4304: "Follow" not working as a second AI package Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6a605c056..fc08e1958 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2,7 +2,6 @@ #include #include - #include #include #include @@ -404,7 +403,7 @@ namespace MWMechanics std::set playerAllies; getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies); - bool isPlayerFollowerOrEscorter = std::find(playerAllies.begin(), playerAllies.end(), actor1) != playerAllies.end(); + bool isPlayerFollowerOrEscorter = playerAllies.find(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 @@ -458,7 +457,7 @@ namespace MWMechanics // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { - if (againstPlayer || std::find(playerAllies.begin(), playerAllies.end(), actor2) != playerAllies.end()) + if (againstPlayer || playerAllies.find(actor2) != playerAllies.end()) { // Player followers and escorters with high fight should not initiate combat with the player or with // other player followers or escorters @@ -1777,38 +1776,35 @@ namespace MWMechanics std::list Actors::getActorsSidingWith(const MWWorld::Ptr& actor) { std::list list; - for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - const CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat packages before the Follow/Escort package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) - { - if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) - { - list.push_back(iter->first); - break; - } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; - } + // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - if (actor != getPlayer()) + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const CreatureStats &stats2 = actor.getClass().getCreatureStats(actor); - for (std::list::const_iterator it2 = stats2.getAiSequence().begin(); it2 != stats2.getAiSequence().end(); ++it2) + const MWWorld::Ptr &target = (*package)->getTarget(); + if ((*package)->sideWithTarget() && !target.isEmpty()) { - if ((*it2)->sideWithTarget() && !(*it2)->getTarget().isEmpty()) + if (iteratedActor == actor) { - list.push_back((*it2)->getTarget()); - break; + list.push_back(target); } - else if ((*it2)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; + else if (target == actor) + { + list.push_back(iteratedActor); + } + break; } + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + break; } } return list; @@ -1819,17 +1815,21 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) - list.push_back(iter->first); - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) + list.push_back(iteratedActor); + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1878,24 +1878,24 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) { - MWWorld::Ptr followTarget = (*it)->getTarget(); - if (followTarget.isEmpty()) - continue; - if (followTarget == actor) - list.push_back(static_cast(*it)->getFollowIndex()); + list.push_back(static_cast(*package)->getFollowIndex()); break; } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1907,14 +1907,14 @@ namespace MWMechanics std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const MWWorld::Class &cls = iter->getClass(); - const CreatureStats &stats = cls.getCreatureStats(*iter); - if (stats.isDead() || *iter == actor) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor) continue; + if (stats.getAiSequence().isInCombat(actor)) - list.push_front(*iter); + list.push_front(*neighbor); } return list; } @@ -1926,15 +1926,18 @@ namespace MWMechanics osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - std::list followers = getActorsFollowing(actor); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + std::set followers; + getActorsFollowing(actor, followers); + for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const CreatureStats &stats = iter->getClass().getCreatureStats(*iter); - if (stats.isDead() || *iter == actor || iter->getClass().isPureWaterCreature(*iter)) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor)) continue; - const bool isFollower = std::find(followers.begin(), followers.end(), *iter) != followers.end(); - if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*iter, actor) && !isFollower)) - list.push_back(*iter); + + const bool isFollower = followers.find(*neighbor) != followers.end(); + + if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower)) + list.push_back(*neighbor); } return list; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7814b4a91..e9915397a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,6 +1,7 @@ #include "mechanicsmanagerimp.hpp" #include +#include #include @@ -1445,11 +1446,12 @@ namespace MWMechanics if (target == getPlayer() || !attacker.getClass().isActor()) return false; - std::list followersAttacker = getActorsSidingWith(attacker); + std::set followersAttacker; + getActorsSidingWith(attacker, followersAttacker); MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); - if (std::find(followersAttacker.begin(), followersAttacker.end(), target) != followersAttacker.end()) + if (followersAttacker.find(target) != followersAttacker.end()) { statsTarget.friendlyHit();