Make followers keep a distance dependant on the fattest party member

pull/3036/head
Evil Eye 4 years ago
parent a4f6448f34
commit 7cac7fa870

@ -199,6 +199,7 @@ namespace MWBase
virtual std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) = 0; virtual std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) = 0;
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0; virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
virtual std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0; virtual std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0;
virtual std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) = 0;
///Returns a list of actors who are fighting the given actor within the fAlarmDistance ///Returns a list of actors who are fighting the given actor within the fAlarmDistance
/** ie AiCombat is active and the target is the actor **/ /** ie AiCombat is active and the target is the actor **/

@ -142,6 +142,29 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
} }
template<class T>
void forEachFollowingPackage(MWMechanics::Actors::PtrActorMap& actors, const MWWorld::Ptr& actor, const MWWorld::Ptr& player, T&& func)
{
for(auto& iter : actors)
{
const MWWorld::Ptr &iteratedActor = iter.first;
if (iteratedActor == player || iteratedActor == actor)
continue;
const MWMechanics::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 and Wander packages before the AiFollow package
for (const auto& package : stats.getAiSequence())
{
if(!func(iter, package))
break;
}
}
}
} }
namespace MWMechanics namespace MWMechanics
@ -2512,26 +2535,14 @@ namespace MWMechanics
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor) std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
{ {
std::list<MWWorld::Ptr> list; std::list<MWWorld::Ptr> list;
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
{ {
const MWWorld::Ptr &iteratedActor = iter->first; if (package->followTargetThroughDoors() && package->getTarget() == actor)
if (iteratedActor == getPlayer() || iteratedActor == actor) list.push_back(iter.first);
continue; else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
return false;
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); return true;
if (stats.isDead()) });
continue;
// An actor counts as following if AiFollow is the current AiPackage,
// or there are only Combat and Wander packages before the AiFollow package
for (const auto& package : stats.getAiSequence())
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iteratedActor);
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
break;
}
}
return list; return list;
} }
@ -2575,32 +2586,38 @@ namespace MWMechanics
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
{ {
std::list<int> list; std::list<int> list;
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
{ {
const MWWorld::Ptr &iteratedActor = iter->first; if (package->followTargetThroughDoors() && package->getTarget() == actor)
if (iteratedActor == getPlayer() || iteratedActor == actor)
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 and Wander packages before the AiFollow package
for (const auto& package : stats.getAiSequence())
{ {
if (package->followTargetThroughDoors() && package->getTarget() == actor) list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
{ return false;
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
break;
}
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
break;
} }
} else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
return false;
return true;
});
return list; return list;
} }
std::map<int, MWWorld::Ptr> Actors::getActorsFollowingByIndex(const MWWorld::Ptr &actor)
{
std::map<int, MWWorld::Ptr> map;
forEachFollowingPackage(mActors, actor, getPlayer(), [&] (auto& iter, const std::unique_ptr<AiPackage>& package)
{
if (package->followTargetThroughDoors() && package->getTarget() == actor)
{
int index = static_cast<const AiFollow*>(package.get())->getFollowIndex();
map[index] = iter.first;
return false;
}
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
return false;
return true;
});
return map;
}
std::list<MWWorld::Ptr> Actors::getActorsFighting(const MWWorld::Ptr& actor) { std::list<MWWorld::Ptr> Actors::getActorsFighting(const MWWorld::Ptr& actor) {
std::list<MWWorld::Ptr> list; std::list<MWWorld::Ptr> list;
std::vector<MWWorld::Ptr> neighbors; std::vector<MWWorld::Ptr> neighbors;

@ -181,6 +181,7 @@ namespace MWMechanics
/// Get the list of AiFollow::mFollowIndex for all actors following this target /// Get the list of AiFollow::mFollowIndex for all actors following this target
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor); std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);
std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor);
///Returns the list of actors which are fighting the given actor ///Returns the list of actors which are fighting the given actor
/**ie AiCombat is active and the target is the actor **/ /**ie AiCombat is active and the target is the actor **/

@ -113,24 +113,23 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
if (!mActive) if (!mActive)
return false; return false;
// The distances below are approximations based on observations of the original engine. // In the original engine the first follower stays closer to the player than any subsequent followers.
// If only one actor is following the target, it uses 186. // Followers beyond the first usually attempt to stand inside each other.
// If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target. osg::Vec3f::value_type floatingDistance = 0;
auto followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingByIndex(target);
short followDistance = 186; if (followers.size() >= 2 && followers.cbegin()->first != mFollowIndex)
std::list<int> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target);
if (followers.size() >= 2)
{ {
followDistance = 313; osg::Vec3f::value_type maxSize = 0;
short i = 0; for(auto& follower : followers)
followers.sort();
for (int followIndex : followers)
{ {
if (followIndex == mFollowIndex) auto halfExtent = MWBase::Environment::get().getWorld()->getHalfExtents(follower.second).y();
followDistance += 130 * i; if(halfExtent > floatingDistance)
++i; floatingDistance = halfExtent;
} }
} }
floatingDistance += MWBase::Environment::get().getWorld()->getHalfExtents(target).y();
floatingDistance += MWBase::Environment::get().getWorld()->getHalfExtents(actor).y() * 2;
short followDistance = static_cast<short>(floatingDistance);
if (!mAlwaysFollow) //Update if you only follow for a bit if (!mAlwaysFollow) //Update if you only follow for a bit
{ {

@ -1654,6 +1654,11 @@ namespace MWMechanics
return mActors.getActorsFollowingIndices(actor); return mActors.getActorsFollowingIndices(actor);
} }
std::map<int, MWWorld::Ptr> MechanicsManager::getActorsFollowingByIndex(const MWWorld::Ptr& actor)
{
return mActors.getActorsFollowingByIndex(actor);
}
std::list<MWWorld::Ptr> MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { std::list<MWWorld::Ptr> MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) {
return mActors.getActorsFighting(actor); return mActors.getActorsFighting(actor);
} }

@ -150,6 +150,7 @@ namespace MWMechanics
std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) override; std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor) override;
std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) override; std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) override;
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) override; std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor) override;
std::map<int, MWWorld::Ptr> getActorsFollowingByIndex(const MWWorld::Ptr& actor) override;
std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) override; std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) override;
std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) override; std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) override;

Loading…
Cancel
Save