1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-05 12:06:40 +00:00

Remove allies from combat when stopping combat

This commit is contained in:
Evil Eye 2021-11-25 22:00:52 +01:00
parent a231037449
commit d6f06fbf9a
9 changed files with 50 additions and 5 deletions

View file

@ -2,6 +2,7 @@
------ ------
Bug #1751: Birthsign abilities increase modified attribute values instead of base ones Bug #1751: Birthsign abilities increase modified attribute values instead of base ones
Bug #1930: Followers are still fighting if a target stops combat with a leader
Bug #3246: ESSImporter: Most NPCs are dead on save load Bug #3246: ESSImporter: Most NPCs are dead on save load
Bug #3488: AI combat aiming is too slow Bug #3488: AI combat aiming is too slow
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear

View file

@ -112,6 +112,9 @@ namespace MWBase
/// Makes \a ptr fight \a target. Also shouts a combat taunt. /// Makes \a ptr fight \a target. Also shouts a combat taunt.
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
/// Removes an actor and its allies from combat with the actor's targets.
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
enum OffenseType enum OffenseType
{ {
OT_Theft, // Taking items owned by an NPC or a faction you are not a member of OT_Theft, // Taking items owned by an NPC or a faction you are not a member of

View file

@ -443,6 +443,22 @@ namespace MWMechanics
} }
} }
void Actors::stopCombat(const MWWorld::Ptr& ptr)
{
auto& ai = ptr.getClass().getCreatureStats(ptr).getAiSequence();
std::vector<MWWorld::Ptr> targets;
if(ai.getCombatTargets(targets))
{
std::set<MWWorld::Ptr> allySet;
getActorsSidingWith(ptr, allySet);
std::vector<MWWorld::Ptr> allies(allySet.begin(), allySet.end());
for(const auto& ally : allies)
ally.getClass().getCreatureStats(ally).getAiSequence().stopCombat(targets);
for(const auto& target : targets)
target.getClass().getCreatureStats(target).getAiSequence().stopCombat(allies);
}
}
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer) void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
{ {
// No combat for totally static creatures // No combat for totally static creatures
@ -979,7 +995,7 @@ namespace MWMechanics
// Calm witness down // Calm witness down
if (ptr.getClass().isClass(ptr, "Guard")) if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stopPursuit(); creatureStats.getAiSequence().stopPursuit();
creatureStats.getAiSequence().stopCombat(); stopCombat(ptr);
// Reset factors to attack // Reset factors to attack
creatureStats.setAttacked(false); creatureStats.setAttacked(false);
@ -1967,7 +1983,7 @@ namespace MWMechanics
if (stats.isDead()) if (stats.isDead())
continue; continue;
// 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 // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Wander packages before the Follow/Escort package
// Actors that are targeted by this actor's Follow or Escort packages also side with them // Actors that are targeted by this actor's Follow or Escort packages also side with them
for (const auto& package : stats.getAiSequence()) for (const auto& package : stats.getAiSequence())
{ {
@ -1983,7 +1999,7 @@ namespace MWMechanics
} }
break; break;
} }
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) else if (package->getTypeId() > AiPackageTypeId::Wander && package->getTypeId() <= AiPackageTypeId::Activate) // Don't count "fake" package types
break; break;
} }
} }

View file

@ -111,6 +111,8 @@ namespace MWMechanics
///< This function is normally called automatically during the update process, but it can ///< This function is normally called automatically during the update process, but it can
/// also be called explicitly at any time to force an update. /// also be called explicitly at any time to force an update.
/// 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 /** Start combat between two actors
@Notes: If againstPlayer = true then actor2 should be the Player. @Notes: If againstPlayer = true then actor2 should be the Player.
If one of the combatants is creature it should be actor1. If one of the combatants is creature it should be actor1.

View file

@ -158,6 +158,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
return false; return false;
} }
// TODO: use std::list::remove_if for all these methods when we switch to C++20
void AiSequence::stopCombat() void AiSequence::stopCombat()
{ {
for(auto it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )
@ -171,6 +172,19 @@ void AiSequence::stopCombat()
} }
} }
void AiSequence::stopCombat(const std::vector<MWWorld::Ptr>& targets)
{
for(auto it = mPackages.begin(); it != mPackages.end(); )
{
if ((*it)->getTypeId() == AiPackageTypeId::Combat && std::find(targets.begin(), targets.end(), (*it)->getTarget()) != targets.end())
{
it = mPackages.erase(it);
}
else
++it;
}
}
void AiSequence::stopPursuit() void AiSequence::stopPursuit()
{ {
for(auto it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )

View file

@ -102,6 +102,9 @@ namespace MWMechanics
/// Removes all combat packages until first non-combat or stack empty. /// Removes all combat packages until first non-combat or stack empty.
void stopCombat(); void stopCombat();
/// Removes all combat packages with the given targets
void stopCombat(const std::vector<MWWorld::Ptr>& targets);
/// Has a package been completed during the last update? /// Has a package been completed during the last update?
bool isPackageDone() const; bool isPackageDone() const;

View file

@ -1607,6 +1607,11 @@ namespace MWMechanics
MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); MWBase::Environment::get().getDialogueManager()->say(ptr, "attack");
} }
void MechanicsManager::stopCombat(const MWWorld::Ptr& actor)
{
mActors.stopCombat(actor);
}
void MechanicsManager::getObjectsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects) void MechanicsManager::getObjectsInRange(const osg::Vec3f &position, float radius, std::vector<MWWorld::Ptr> &objects)
{ {
mActors.getObjectsInRange(position, radius, objects); mActors.getObjectsInRange(position, radius, objects);

View file

@ -102,6 +102,8 @@ namespace MWMechanics
/// Makes \a ptr fight \a target. Also shouts a combat taunt. /// Makes \a ptr fight \a target. Also shouts a combat taunt.
void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override; void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;
void stopCombat(const MWWorld::Ptr& ptr) override;
/** /**
* @note victim may be empty * @note victim may be empty
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.

View file

@ -515,8 +515,7 @@ namespace MWScript
MWWorld::Ptr actor = R()(runtime); MWWorld::Ptr actor = R()(runtime);
if (!actor.getClass().isActor()) if (!actor.getClass().isActor())
return; return;
MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); MWBase::Environment::get().getMechanicsManager()->stopCombat(actor);
creatureStats.getAiSequence().stopCombat();
} }
}; };