From 588442b6ccf24e7ff731c32249e28949666205e7 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 25 Dec 2016 20:14:21 +0900 Subject: [PATCH 1/6] Make enemies start combat with player followers Recreates vanilla behavior of enemies starting combat with player followers and escorters. (Fixes #3691) --- apps/openmw/mwmechanics/actors.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 23a6f497ff..8c1a88e3fc 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -310,13 +310,14 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); bool aggressive; - if (againstPlayer) + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) { - // followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon) - const std::list& followers = getActorsSidingWith(actor2); - if (std::find(followers.begin(), followers.end(), actor1) != followers.end()) + // Player followers and escorters with high fight should not initiate combat with the player or with + // other player followers or escorters + if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) return; aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); @@ -369,7 +370,8 @@ namespace MWMechanics { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); if (LOS) { From e8c7ad2f4b2c29d7bbd8769c6d304572700462f7 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 25 Dec 2016 23:31:44 +0900 Subject: [PATCH 2/6] Change environment check to canFight check Instead of just checking that combatants are in compatible environments, allow combat if in attack range using canFight. Together with previous commit, fixes #3690. --- apps/openmw/mwmechanics/actors.cpp | 59 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8c1a88e3fc..2f1c55a2a9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -29,6 +29,7 @@ #include "movement.hpp" #include "character.hpp" #include "aicombat.hpp" +#include "aicombataction.hpp" #include "aifollow.hpp" #include "aipursue.hpp" #include "actor.hpp" @@ -298,41 +299,12 @@ namespace MWMechanics if (sqrDist > sqrAiProcessingDistance) return; - // pure water creatures won't try to fight with the target on the ground - // except that creature is already hostile - if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target - { - return; - } - // no combat for totally static creatures (they have no movement or attack animations anyway) if (!actor1.getClass().isMobile(actor1)) return; const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); - bool aggressive; - - if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) - { - // Player followers and escorters with high fight should not initiate combat with the player or with - // other player followers or escorters - if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) - return; - - aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); - } - else - { - aggressive = false; - - // Make guards fight aggressive creatures - if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) - { - if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) - aggressive = true; - } - } + bool aggressive = false; // start combat if target actor is in combat with one of our followers const std::list& followers = getActorsSidingWith(actor1); @@ -366,6 +338,33 @@ namespace MWMechanics aggressive = true; } + // pure water creatures won't try to fight with the target on the ground + // except that creature is already hostile + if (!aggressive && (againstPlayer || !creatureStats.getAiSequence().isInCombat()) + && !MWMechanics::canFight(actor1,actor2)) // creature can't swim to target + { + return; + } + + if (!aggressive && againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + { + // Player followers and escorters with high fight should not initiate combat with the player or with + // other player followers or escorters + if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) + return; + + aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); + } + else + { + // Make guards fight aggressive creatures + if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) + { + if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) + aggressive = true; + } + } + if(aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); From e10c4d8814a043a83c6b569c544b41f012407667 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 00:18:52 +0900 Subject: [PATCH 3/6] Stop combat between AI when canFight is false --- apps/openmw/mwmechanics/aicombat.cpp | 18 ++++++++++++------ apps/openmw/mwmechanics/aicombat.hpp | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index d8dae8b79a..fbd3819e24 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -19,6 +19,7 @@ #include "aicombataction.hpp" #include "combat.hpp" #include "coordinateconverter.hpp" +#include "actorutil.hpp" namespace { @@ -210,13 +211,14 @@ namespace MWMechanics else { timerReact = 0; - attack(actor, target, storage, characterController); + if (attack(actor, target, storage, characterController)) + return true; } return false; } - void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) + bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController) { const MWWorld::CellStore*& currentCell = storage.mCell; bool cellChange = currentCell && (actor.getCell() != currentCell); @@ -231,7 +233,10 @@ namespace MWMechanics storage.stopAttack(); characterController.setAttackingOrSpell(false); storage.mActionCooldown = 0.f; - forceFlee = true; + if (target == MWMechanics::getPlayer()) + forceFlee = true; + else + return true; } const MWWorld::Class& actorClass = actor.getClass(); @@ -243,7 +248,7 @@ namespace MWMechanics if (!forceFlee) { if (actionCooldown > 0) - return; + return false; if (characterController.readyToPrepareAttack()) { @@ -258,7 +263,7 @@ namespace MWMechanics } if (!currentAction) - return; + return false; if (storage.isFleeing() != currentAction->isFleeing()) { @@ -266,7 +271,7 @@ namespace MWMechanics { storage.startFleeing(); MWBase::Environment::get().getDialogueManager()->say(actor, "flee"); - return; + return false; } else storage.stopFleeing(); @@ -311,6 +316,7 @@ namespace MWMechanics storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } } + return false; } void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index a2e995cb38..fbe864ca07 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -59,7 +59,8 @@ namespace MWMechanics int mTargetActorId; - void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); + /// Returns true if combat should end + bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); From 5a6ea4e84e37349223aaf813b5311f3fbb7175ea Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 02:03:17 +0900 Subject: [PATCH 4/6] Cleanup --- apps/openmw/mwmechanics/actors.cpp | 47 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2f1c55a2a9..d2d07cf28c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -287,10 +287,12 @@ namespace MWMechanics void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { - CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); + const CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); + if (creatureStats1.getAiSequence().isInCombat(actor2)) + return; - if (actor2.getClass().getCreatureStats(actor2).isDead() - || actor1.getClass().getCreatureStats(actor1).isDead()) + const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); + if (creatureStats1.isDead() || creatureStats2.isDead()) return; const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); @@ -299,27 +301,25 @@ namespace MWMechanics if (sqrDist > sqrAiProcessingDistance) return; - // no combat for totally static creatures (they have no movement or attack animations anyway) + // No combat for totally static creatures if (!actor1.getClass().isMobile(actor1)) return; - const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); bool aggressive = false; - // start combat if target actor is in combat with one of our followers - const std::list& followers = getActorsSidingWith(actor1); - const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); - for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) + // Start combat if target actor is in combat with one of our followers or escorters + const std::list& followersAndEscorters = getActorsSidingWith(actor1); + for (std::list::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it) { - // need to check both ways since player doesn't use AI packages + // Need to check both ways since player doesn't use AI packages if ((creatureStats2.getAiSequence().isInCombat(*it) || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) - && !creatureStats.getAiSequence().isInCombat(*it)) + && !creatureStats1.getAiSequence().isInCombat(*it)) aggressive = true; } - // start combat if target actor is in combat with someone we are following - for (std::list::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it) + // Start combat if target actor is in combat with someone we are following through a follow package + for (std::list::const_iterator it = creatureStats1.getAiSequence().begin(); it != creatureStats1.getAiSequence().end(); ++it) { if (!(*it)->sideWithTarget()) continue; @@ -329,26 +329,23 @@ namespace MWMechanics if (followTarget.isEmpty()) continue; - if (creatureStats.getAiSequence().isInCombat(followTarget)) + if (creatureStats1.getAiSequence().isInCombat(followTarget)) continue; - // need to check both ways since player doesn't use AI packages + // Need to check both ways since player doesn't use AI packages if (creatureStats2.getAiSequence().isInCombat(followTarget) || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) aggressive = true; } - // pure water creatures won't try to fight with the target on the ground - // except that creature is already hostile - if (!aggressive && (againstPlayer || !creatureStats.getAiSequence().isInCombat()) - && !MWMechanics::canFight(actor1,actor2)) // creature can't swim to target - { + // Otherwise, don't initiate combat with an unreachable target + if (!aggressive && !MWMechanics::canFight(actor1,actor2)) return; - } - if (!aggressive && againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); + if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) { - // Player followers and escorters with high fight should not initiate combat with the player or with + // Player followers and escorters with high fight should not initiate combat here with the player or with // other player followers or escorters if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end()) return; @@ -360,12 +357,12 @@ namespace MWMechanics // Make guards fight aggressive creatures if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { - if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) + if (creatureStats1.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) aggressive = true; } } - if(aggressive) + if (aggressive) { bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); From 6fa0354a179b110e2bc12e465dab51e93feaeeae Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 04:50:22 +0900 Subject: [PATCH 5/6] Make AI attack player also if it attacks follower --- apps/openmw/mwmechanics/actors.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d2d07cf28c..441e7d0f47 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -338,11 +338,24 @@ namespace MWMechanics aggressive = true; } + // Initiate combat with the player if we are already in combat with a player follower or escorter + const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); + if (!aggressive && againstPlayer) + { + for (std::list::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it) + { + if (creatureStats1.getAiSequence().isInCombat(*it)) + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } + } + } + // Otherwise, don't initiate combat with an unreachable target if (!aggressive && !MWMechanics::canFight(actor1,actor2)) return; - const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) { // Player followers and escorters with high fight should not initiate combat here with the player or with From 54fa921dad66844127f701569a53ce7442f72220 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 26 Dec 2016 05:11:06 +0900 Subject: [PATCH 6/6] Change some AI combat engagements to not need LOS --- apps/openmw/mwmechanics/actors.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 441e7d0f47..497ec73849 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -305,8 +305,6 @@ namespace MWMechanics if (!actor1.getClass().isMobile(actor1)) return; - bool aggressive = false; - // Start combat if target actor is in combat with one of our followers or escorters const std::list& followersAndEscorters = getActorsSidingWith(actor1); for (std::list::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it) @@ -315,7 +313,10 @@ namespace MWMechanics if ((creatureStats2.getAiSequence().isInCombat(*it) || it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) && !creatureStats1.getAiSequence().isInCombat(*it)) - aggressive = true; + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } } // Start combat if target actor is in combat with someone we are following through a follow package @@ -335,12 +336,15 @@ namespace MWMechanics // Need to check both ways since player doesn't use AI packages if (creatureStats2.getAiSequence().isInCombat(followTarget) || followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2)) - aggressive = true; + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + return; + } } - - // Initiate combat with the player if we are already in combat with a player follower or escorter + + // Start combat with the player if we are already in combat with a player follower or escorter const std::list& playerFollowersAndEscorters = getActorsSidingWith(getPlayer()); - if (!aggressive && againstPlayer) + if (againstPlayer) { for (std::list::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it) { @@ -353,10 +357,12 @@ namespace MWMechanics } // Otherwise, don't initiate combat with an unreachable target - if (!aggressive && !MWMechanics::canFight(actor1,actor2)) + if (!MWMechanics::canFight(actor1,actor2)) return; - if (!aggressive && (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())) + bool aggressive = false; + + if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end()) { // Player followers and escorters with high fight should not initiate combat here with the player or with // other player followers or escorters