diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwbase/mechanicsmanager.hpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwbase/mechanicsmanager.hpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwbase/mechanicsmanager.hpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwbase/mechanicsmanager.hpp 2026-06-27 11:19:50.000000000 +0000 @@ -254,4 +254,6 @@ virtual void clear() = 0; + virtual bool isAggressive(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; + /// Resurrects the player if necessary virtual void resurrect(const MWWorld::Ptr& ptr) = 0; diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwdialogue/filter.cpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwdialogue/filter.cpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwdialogue/filter.cpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwdialogue/filter.cpp 2026-06-27 11:19:50.000000000 +0000 @@ -21,5 +21,4 @@ #include "../mwmechanics/actorutil.hpp" -#include "../mwmechanics/combat.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" @@ -669,5 +668,5 @@ case ESM::DialogueCondition::Function_ShouldAttack: - return MWMechanics::isAggressive(mActor, MWMechanics::getPlayer()); + return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, MWMechanics::getPlayer()); case ESM::DialogueCondition::Function_Werewolf: diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwinput/keyboardmanager.cpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwinput/keyboardmanager.cpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwinput/keyboardmanager.cpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwinput/keyboardmanager.cpp 2026-06-27 11:19:50.000000000 +0000 @@ -37,6 +37,5 @@ auto kc = SDLUtil::sdlKeyToMyGUI(arg.keysym.sym); if (mBindingsManager->getKeyBinding(A_Console) == arg.keysym.scancode - // HACK: allow upper case variant of console keybinding. - && (arg.keysym.mod & KMOD_SHIFT) == 0 && MWBase::Environment::get().getWindowManager()->isConsoleMode()) + && MWBase::Environment::get().getWindowManager()->isConsoleMode()) SDL_StopTextInput(); diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/actors.cpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/actors.cpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/actors.cpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/actors.cpp 2026-06-27 11:19:50.000000000 +0000 @@ -51,5 +51,4 @@ #include "attacktype.hpp" #include "character.hpp" -#include "combat.hpp" #include "creaturestats.hpp" #include "greetingstate.hpp" @@ -727,9 +726,9 @@ // other player followers or escorters if (!isPlayerFollowerOrEscorter) - aggressive = isAggressive(actor1, actor2); + aggressive = mechanicsManager->isAggressive(actor1, actor2); } } - // Make guards go aggressive with hostile creatures and werewolves that are in combat + // Make guards go aggressive with creatures and werewolves that are in combat const auto world = MWBase::Environment::get().getWorld(); if (!aggressive && actor1.getClass().isClass(actor1, "Guard") && creatureStats2.getAiSequence().isInCombat()) @@ -741,17 +740,6 @@ return; - // Check if the guard is under a calm spell - if (creatureStats1.getMagicEffects().getOrDefault(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0) - return; - - // Check if the target is immobile or under a calm spell - if (!isAggressionCapable(actor2)) - return; - - // Check if the target is a werewolf or aggressive creature bool targetIsCreature = !actor2.getClass().isNpc(); - bool targetIsAggressiveCreature = targetIsCreature ? getFightTerm(actor2, player) >= 100 - : actor2.getClass().getNpcStats(actor2).isWerewolf(); - if (targetIsAggressiveCreature) + if (targetIsCreature || actor2.getClass().getNpcStats(actor2).isWerewolf()) { bool followerOrEscorter = false; @@ -2300,5 +2288,6 @@ const bool isFollower = followers.find(neighbor) != followers.end(); - if (stats.getAiSequence().isInCombat(actor) || (MWMechanics::isAggressive(neighbor, actor) && !isFollower)) + if (stats.getAiSequence().isInCombat(actor) + || (MWBase::Environment::get().getMechanicsManager()->isAggressive(neighbor, actor) && !isFollower)) list.push_back(neighbor); } diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/combat.cpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/combat.cpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/combat.cpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/combat.cpp 2026-06-27 11:19:50.000000000 +0000 @@ -548,25 +548,4 @@ } - int getFightTerm(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) - { - const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager(); - int disposition = actor.getClass().isNpc() ? mechanicsManager->getDerivedDisposition(actor) : 50; - int fight = actor.getClass().getCreatureStats(actor).getAiSetting(AiSetting::Fight).getModified() - + static_cast( - getFightDistanceBias(actor, target) + getFightDispositionBias(static_cast(disposition))); - - return fight; - } - - float getFightDispositionBias(float disposition) - { - static const float fFightDispMult = MWBase::Environment::get() - .getESMStore() - ->get() - .find("fFightDispMult") - ->mValue.getFloat(); - return ((50.f - disposition) * fFightDispMult); - } - float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2) { @@ -597,48 +576,4 @@ } - bool isAggressionCapable(const MWWorld::Ptr& actor) - { - // Immobile creatures can enter combat, but are never aggressive - if (!actor.getClass().isMobile(actor)) - return false; - - const MWWorld::Class& actorClass = actor.getClass(); - MagicEffects magEffects = actorClass.getCreatureStats(actor).getMagicEffects(); - - // Actor isn't aggressive if a calm effect is active, as it would cause combat to cycle on/off - // when combat is activated here and then canceled by the calm effect - if ((actorClass.isNpc() && magEffects.getOrDefault(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0) - || (!actorClass.isNpc() && magEffects.getOrDefault(ESM::MagicEffect::CalmCreature).getMagnitude() > 0)) - return false; - - return true; - } - - bool isAggressive(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) - { - // If already in combat with target, consider aggressive - if (actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(target)) - return true; - - if (!isAggressionCapable(actor)) - return false; - - // Aggression is easier if the target is a werewolf - int fight = getFightTerm(actor, target); - if (actor.getClass().isNpc() && target.getClass().isNpc()) - { - if (target.getClass().getNpcStats(target).isWerewolf() - || (target == getPlayer() - && MWBase::Environment::get().getWorld()->getGlobalInt(MWWorld::Globals::sPCKnownWerewolf))) - { - const ESM::GameSetting* iWerewolfFightMod - = MWBase::Environment::get().getESMStore()->get().find("iWerewolfFightMod"); - fight += iWerewolfFightMod->mValue.getInteger(); - } - } - - return (fight >= 100); - } - float getDistanceToBounds(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/combat.hpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/combat.hpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/combat.hpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/combat.hpp 2026-06-27 11:19:50.000000000 +0000 @@ -58,10 +58,7 @@ void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float attackStrength); - int getFightTerm(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); - float getFightDispositionBias(float disposition); float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); + float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs); - bool isAggressionCapable(const MWWorld::Ptr& actor); - bool isAggressive(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); // Cursed distance calculation used for combat proximity and hit checks in Morrowind diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp 2026-06-27 11:19:50.000000000 +0000 @@ -45,4 +45,14 @@ { + float getFightDispositionBias(float disposition) + { + static const float fFightDispMult = MWBase::Environment::get() + .getESMStore() + ->get() + .find("fFightDispMult") + ->mValue.getFloat(); + return ((50.f - disposition) * fFightDispMult); + } + void getPersuasionRatings( const MWMechanics::NpcStats& stats, float& rating1, float& rating2, float& rating3, bool player) @@ -1851,4 +1861,51 @@ } + bool MechanicsManager::isAggressive(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) + { + // If already in combat with target, consider aggressive + if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target)) + return true; + + // Don't become aggressive if a calm effect is active, since it would cause combat to cycle on/off as + // combat is activated here and then canceled by the calm effect + if ((ptr.getClass().isNpc() + && ptr.getClass() + .getCreatureStats(ptr) + .getMagicEffects() + .getOrDefault(ESM::MagicEffect::CalmHumanoid) + .getMagnitude() + > 0) + || (!ptr.getClass().isNpc() + && ptr.getClass() + .getCreatureStats(ptr) + .getMagicEffects() + .getOrDefault(ESM::MagicEffect::CalmCreature) + .getMagnitude() + > 0)) + return false; + + int disposition = 50; + if (ptr.getClass().isNpc()) + disposition = getDerivedDisposition(ptr); + + int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(AiSetting::Fight).getModified() + + static_cast( + getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast(disposition))); + + if (ptr.getClass().isNpc() && target.getClass().isNpc()) + { + if (target.getClass().getNpcStats(target).isWerewolf() + || (target == getPlayer() + && MWBase::Environment::get().getWorld()->getGlobalInt(MWWorld::Globals::sPCKnownWerewolf))) + { + const ESM::GameSetting* iWerewolfFightMod + = MWBase::Environment::get().getESMStore()->get().find("iWerewolfFightMod"); + fight += iWerewolfFightMod->mValue.getInteger(); + } + } + + return (fight >= 100); + } + void MechanicsManager::resurrect(const MWWorld::Ptr& ptr) { diff -U2 -r /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp --- /var/lib/copr-rpmbuild/results/openmw-git/upstream-unpacked/Source0/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp 2026-06-28 00:21:35.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/openmw-git/srpm-unpacked/openmw-master.tar.bz2-extract/openmw-master/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp 2026-06-27 11:19:50.000000000 +0000 @@ -191,4 +191,6 @@ void clear() override; + bool isAggressive(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override; + void resurrect(const MWWorld::Ptr& ptr) override;