From 1676bf917e60f8a0d0614d628be779b0f65d6e28 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:06:52 +1200 Subject: [PATCH 1/7] CombatMove logic moved into AiCombatStorage. Basically, copied from mrcheko's 1d4be08f6e4c2dbd89cc0c3408a8231ee4497277 --- apps/openmw/mwmechanics/aicombat.cpp | 91 +++++++++++++++++----------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f386ec81be..c8876c8420 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -110,6 +110,10 @@ namespace MWMechanics mForceNoShortcut(false), mLastActorPos(0,0,0), mMovement(){} + + void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack); + void updateCombatMove(float duration); + void stopCombatMove(); }; AiCombat::AiCombat(const MWWorld::Ptr& actor) : @@ -192,19 +196,8 @@ namespace MWMechanics //Update every frame - bool& combatMove = storage.mCombatMove; - float& timerCombatMove = storage.mTimerCombatMove; + storage.updateCombatMove(duration); MWMechanics::Movement& movement = storage.mMovement; - if(combatMove) - { - timerCombatMove -= duration; - if( timerCombatMove <= 0) - { - timerCombatMove = 0; - movement.mPosition[1] = movement.mPosition[0] = 0; - combatMove = false; - } - } UpdateActorsMovement(actor, movement); @@ -454,31 +447,12 @@ namespace MWMechanics if (followTarget && distToTarget > rangeAttack) { //Close-up combat: just run up on target + storage.stopCombatMove(); movement.mPosition[1] = 1; } else // (within attack dist) { - if(movement.mPosition[0] || movement.mPosition[1]) - { - storage.mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); - storage.mCombatMove = true; - } - // only NPCs are smart enough to use dodge movements - else if(actorClass.isNpc() && (!distantCombat || (distantCombat && distToTarget < rangeAttack/2))) - { - //apply sideway movement (kind of dodging) with some probability - if (Misc::Rng::rollClosedProbability() < 0.25) - { - movement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; - storage.mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); - storage.mCombatMove = true; - } - } - - if(distantCombat && distToTarget < rangeAttack/4) - { - movement.mPosition[1] = -1; - } + storage.startCombatMove(actorClass.isNpc(), distantCombat, distToTarget, rangeAttack); readyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting @@ -551,14 +525,13 @@ namespace MWMechanics } } - movement.mPosition[1] = 1; if (readyToAttack) { // to stop possible sideway moving after target moved out of attack range - storage.mCombatMove = true; - storage.mTimerCombatMove = 0; + storage.stopCombatMove(); + readyToAttack = false; } - readyToAttack = false; + movement.mPosition[1] = 1; } return false; @@ -663,6 +636,50 @@ namespace MWMechanics package.mPackage = combat.release(); sequence.mPackages.push_back(package); } + + void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack) + { + if (mMovement.mPosition[0] || mMovement.mPosition[1]) + { + mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); + mCombatMove = true; + } + // only NPCs are smart enough to use dodge movements + else if (isNpc && (!isDistantCombat || (distToTarget < rangeAttack / 2))) + { + //apply sideway movement (kind of dodging) with some probability + if (Misc::Rng::rollClosedProbability() < 0.25) + { + mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; + mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); + mCombatMove = true; + } + } + + if (isDistantCombat && distToTarget < rangeAttack / 4) + { + mMovement.mPosition[1] = -1; + } + } + + void AiCombatStorage::updateCombatMove(float duration) + { + if (mCombatMove) + { + mTimerCombatMove -= duration; + if (mTimerCombatMove <= 0) + { + stopCombatMove(); + } + } + } + + void AiCombatStorage::stopCombatMove() + { + mTimerCombatMove = 0; + mMovement.mPosition[1] = mMovement.mPosition[0] = 0; + mCombatMove = false; + } } From 0735e3e06e3c3a1241ea78ec402a28e38f8331e1 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:08:42 +1200 Subject: [PATCH 2/7] move start attack logic to AiCombatStorage. Basically, copied from mrchenko's 1d4be08f6e4c2dbd89cc0c3408a8231ee4497277 --- apps/openmw/mwmechanics/aicombat.cpp | 82 +++++++++++++++------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c8876c8420..03dc91c0a5 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -114,6 +114,8 @@ namespace MWMechanics void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack); void updateCombatMove(float duration); void stopCombatMove(); + void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, + const ESM::Weapon* weapon, bool distantCombat); }; AiCombat::AiCombat(const MWWorld::Ptr& actor) : @@ -194,7 +196,6 @@ namespace MWMechanics || target.getClass().getCreatureStats(target).isDead()) return true; - //Update every frame storage.updateCombatMove(duration); MWMechanics::Movement& movement = storage.mMovement; @@ -318,41 +319,9 @@ namespace MWMechanics } - float& strength = storage.mStrength; bool& readyToAttack = storage.mReadyToAttack; // start new attack - if(readyToAttack && characterController.readyToStartAttack()) - { - if (storage.mAttackCooldown <= 0) - { - storage.mAttack = true; // attack starts just now - characterController.setAttackingOrSpell(true); - - if (!distantCombat) - chooseBestAttack(weapon, movement); - - strength = Misc::Rng::rollClosedProbability(); - - const MWWorld::ESMStore &store = world->getStore(); - - //say a provoking combat phrase - if (actor.getClass().isNpc()) - { - int chance = store.get().find("iVoiceAttackOdds")->getInt(); - if (Misc::Rng::roll0to99() < chance) - { - MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); - } - } - float baseDelay = store.get().find("fCombatDelayCreature")->getFloat(); - if (actor.getClass().isNpc()) - baseDelay = store.get().find("fCombatDelayNPC")->getFloat(); - storage.mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9); - } - else - storage.mAttackCooldown -= REACTION_INTERVAL; - } - + storage.startAttackIfReady(actor, characterController, weapon, distantCombat); /* * Some notes on meanings of variables: @@ -403,9 +372,6 @@ namespace MWMechanics bool canMoveByZ = (actorClass.canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor); - // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack - bool inLOS = distantCombat ? world->getLOS(actor, target) : true; - // can't fight if attacker can't go where target is. E.g. A fish can't attack person on land. if (distToTarget >= rangeAttack && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target)) @@ -419,6 +385,9 @@ namespace MWMechanics return false; } + // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack + bool inLOS = distantCombat ? world->getLOS(actor, target) : true; + // (within attack dist) || (not quite attack dist while following) if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck))) { @@ -432,7 +401,8 @@ namespace MWMechanics if (distantCombat) { osg::Vec3f& lastTargetPos = storage.mLastTargetPos; - osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, REACTION_INTERVAL, weaptype, strength); + osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, REACTION_INTERVAL, weaptype, + storage.mStrength); lastTargetPos = vTargetPos; movement.mRotation[0] = getXAngleToDir(vAimDir); movement.mRotation[2] = getZAngleToDir(vAimDir); @@ -680,6 +650,42 @@ namespace MWMechanics mMovement.mPosition[1] = mMovement.mPosition[0] = 0; mCombatMove = false; } + + void AiCombatStorage::startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, + const ESM::Weapon* weapon, bool distantCombat) + { + if (mReadyToAttack && characterController.readyToStartAttack()) + { + if (mAttackCooldown <= 0) + { + mAttack = true; // attack starts just now + characterController.setAttackingOrSpell(true); + + if (!distantCombat) + chooseBestAttack(weapon, mMovement); + + mStrength = Misc::Rng::rollClosedProbability(); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + float baseDelay = store.get().find("fCombatDelayCreature")->getFloat(); + if (actor.getClass().isNpc()) + { + baseDelay = store.get().find("fCombatDelayNPC")->getFloat(); + + //say a provoking combat phrase + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + if (Misc::Rng::roll0to99() < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } + mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9); + } + else + mAttackCooldown -= REACTION_INTERVAL; + } + } } From 50ddcd19532756016d668f38baa071009f98d208 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:10:08 +1200 Subject: [PATCH 3/7] more attack logic moved into AiCombatStorage. --- apps/openmw/mwmechanics/aicombat.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 03dc91c0a5..a5a48991a8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -116,6 +116,7 @@ namespace MWMechanics void stopCombatMove(); void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, const ESM::Weapon* weapon, bool distantCombat); + void updateAttack(CharacterController& characterController); }; AiCombat::AiCombat(const MWWorld::Ptr& actor) : @@ -201,12 +202,7 @@ namespace MWMechanics MWMechanics::Movement& movement = storage.mMovement; UpdateActorsMovement(actor, movement); - - bool& attack = storage.mAttack; - if (attack && (characterController.getAttackStrength() >= storage.mStrength || characterController.readyToPrepareAttack())) - attack = false; - - characterController.setAttackingOrSpell(attack); + storage.updateAttack(characterController); float& actionCooldown = storage.mActionCooldown; actionCooldown -= duration; @@ -686,6 +682,15 @@ namespace MWMechanics mAttackCooldown -= REACTION_INTERVAL; } } + + void AiCombatStorage::updateAttack(CharacterController& characterController) + { + if (mAttack && (characterController.getAttackStrength() >= mStrength || characterController.readyToPrepareAttack())) + { + mAttack = false; + } + characterController.setAttackingOrSpell(mAttack); + } } From 038851420d5615d7ce470bd83312e0b3d00ee180 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:18:55 +1200 Subject: [PATCH 4/7] Removed unneeded temp variables. Corrected case of function names. --- apps/openmw/mwmechanics/aicombat.cpp | 16 ++++++---------- apps/openmw/mwmechanics/aicombat.hpp | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index a5a48991a8..b1889605d8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -199,13 +199,9 @@ namespace MWMechanics //Update every frame storage.updateCombatMove(duration); - MWMechanics::Movement& movement = storage.mMovement; - - UpdateActorsMovement(actor, movement); + updateActorsMovement(actor, storage.mMovement); storage.updateAttack(characterController); - - float& actionCooldown = storage.mActionCooldown; - actionCooldown -= duration; + storage.mActionCooldown -= duration; float& timerReact = storage.mTimerReact; if(timerReact < REACTION_INTERVAL) @@ -503,7 +499,7 @@ namespace MWMechanics return false; } - void AiCombat::UpdateActorsMovement(const MWWorld::Ptr& actor, MWMechanics::Movement& desiredMovement) + void AiCombat::updateActorsMovement(const MWWorld::Ptr& actor, MWMechanics::Movement& desiredMovement) { MWMechanics::Movement& actorMovementSettings = actor.getClass().getMovementSettings(actor); if (mPathFinder.isPathConstructed()) @@ -522,12 +518,12 @@ namespace MWMechanics else { actorMovementSettings = desiredMovement; - RotateActorOnAxis(actor, 2, actorMovementSettings, desiredMovement); - RotateActorOnAxis(actor, 0, actorMovementSettings, desiredMovement); + rotateActorOnAxis(actor, 2, actorMovementSettings, desiredMovement); + rotateActorOnAxis(actor, 0, actorMovementSettings, desiredMovement); } } - void AiCombat::RotateActorOnAxis(const MWWorld::Ptr& actor, int axis, + void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement) { actorMovementSettings.mRotation[axis] = 0; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 4e40608196..73d0254f3b 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -65,8 +65,8 @@ namespace MWMechanics AiCombatStorage& storage, MWWorld::Ptr target); /// Transfer desired movement (from AiCombatStorage) to Actor - void UpdateActorsMovement(const MWWorld::Ptr& actor, MWMechanics::Movement& movement); - void RotateActorOnAxis(const MWWorld::Ptr& actor, int axis, + void updateActorsMovement(const MWWorld::Ptr& actor, MWMechanics::Movement& movement); + void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement); }; From 0884a3796f7e6174bf93b1b4311680faa1c67b0a Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:20:55 +1200 Subject: [PATCH 5/7] extracted function isTargetMagicallyHidden(). --- apps/openmw/mwmechanics/aicombat.cpp | 3 +-- apps/openmw/mwmechanics/aipackage.cpp | 8 ++++++++ apps/openmw/mwmechanics/aipackage.hpp | 2 ++ apps/openmw/mwmechanics/aipursue.cpp | 3 +-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index b1889605d8..8a51a4cb07 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -222,8 +222,7 @@ namespace MWMechanics MWMechanics::Movement& movement = storage.mMovement; // Stop attacking if target is not seen - if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0 - || target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75) + if (isTargetMagicallyHidden(target)) { movement.mPosition[1] = movement.mPosition[0] = 0; return false; // TODO: run away instead of doing nothing diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 77efe62a88..d10edd5891 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -129,3 +130,10 @@ bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const { return distance(mPrevDest, dest) > 10; } + +bool MWMechanics::AiPackage::isTargetMagicallyHidden(const MWWorld::Ptr& target) +{ + MagicEffects& magicEffects(target.getClass().getCreatureStats(target).getMagicEffects()); + return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0) + || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75); +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index da43dc6dae..d73833b948 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -69,6 +69,8 @@ namespace MWMechanics /// Simulates the passing of time virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {} + bool isTargetMagicallyHidden(const MWWorld::Ptr& target); + protected: /// Causes the actor to attempt to walk to the specified location /** \return If the actor has arrived at his destination **/ diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index e936505c93..055801fde0 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -43,8 +43,7 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte ) return true; //Target doesn't exist - if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0 - || target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75) + if (isTargetMagicallyHidden(target)) return true; if(target.getClass().getCreatureStats(target).isDead()) From 2b9e22f593ebc862cfdc1df070249725e760a460 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 9 Aug 2015 14:29:38 +1200 Subject: [PATCH 6/7] extracted function stopAttack(). --- apps/openmw/mwmechanics/aicombat.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8a51a4cb07..5ea3ba6a3b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -117,6 +117,7 @@ namespace MWMechanics void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, const ESM::Weapon* weapon, bool distantCombat); void updateAttack(CharacterController& characterController); + void stopAttack(); }; AiCombat::AiCombat(const MWWorld::Ptr& actor) : @@ -221,10 +222,9 @@ namespace MWMechanics { MWMechanics::Movement& movement = storage.mMovement; - // Stop attacking if target is not seen if (isTargetMagicallyHidden(target)) { - movement.mPosition[1] = movement.mPosition[0] = 0; + storage.stopAttack(); return false; // TODO: run away instead of doing nothing } @@ -368,11 +368,7 @@ namespace MWMechanics && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target)) { // TODO: start fleeing? - movement.mPosition[0] = 0; - movement.mPosition[1] = 0; - movement.mPosition[2] = 0; - readyToAttack = false; - characterController.setAttackingOrSpell(false); + storage.stopAttack(); return false; } @@ -686,6 +682,15 @@ namespace MWMechanics } characterController.setAttackingOrSpell(mAttack); } + + void AiCombatStorage::stopAttack() + { + mMovement.mPosition[0] = 0; + mMovement.mPosition[1] = 0; + mMovement.mPosition[2] = 0; + mReadyToAttack = false; + mAttack = false; + } } From 55e3aaaa35c491ecd24edc74094c66c42ab3c3ef Mon Sep 17 00:00:00 2001 From: dteviot Date: Mon, 10 Aug 2015 20:30:43 +1200 Subject: [PATCH 7/7] made variable const. --- apps/openmw/mwmechanics/aipackage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index d10edd5891..1ab3264b7b 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -133,7 +133,7 @@ bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const bool MWMechanics::AiPackage::isTargetMagicallyHidden(const MWWorld::Ptr& target) { - MagicEffects& magicEffects(target.getClass().getCreatureStats(target).getMagicEffects()); + const MagicEffects& magicEffects(target.getClass().getCreatureStats(target).getMagicEffects()); return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0) || (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75); }