From 19143f9bc7c48f8154ee1fcb86d879d11ef7334c Mon Sep 17 00:00:00 2001 From: Allofich Date: Tue, 6 Dec 2016 22:23:06 +0900 Subject: [PATCH] Require line of sight for AI attacks (Fixes #3646) --- apps/openmw/mwmechanics/aicombat.cpp | 54 +++++++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 2 ++ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 80b343d4f..21eaa0de5 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -60,30 +60,30 @@ namespace MWMechanics FleeState_RunToDestination }; FleeState mFleeState; - bool mFleeLOS; - float mFleeUpdateLOSTimer; + bool mLOS; + float mUpdateLOSTimer; float mFleeBlindRunTimer; ESM::Pathgrid::Point mFleeDest; AiCombatStorage(): - mAttackCooldown(0), + mAttackCooldown(0.0f), mTimerReact(AI_REACTION_TIME), - mTimerCombatMove(0), + mTimerCombatMove(0.0f), mReadyToAttack(false), mAttack(false), - mAttackRange(0), + mAttackRange(0.0f), mCombatMove(false), mLastTargetPos(0,0,0), mCell(NULL), mCurrentAction(), - mActionCooldown(0), + mActionCooldown(0.0f), mStrength(), mForceNoShortcut(false), mShortcutFailPos(), mMovement(), mFleeState(FleeState_None), - mFleeLOS(false), - mFleeUpdateLOSTimer(0.0f), + mLOS(false), + mUpdateLOSTimer(0.0f), mFleeBlindRunTimer(0.0f) {} @@ -181,10 +181,14 @@ namespace MWMechanics if (!storage.isFleeing()) { - if (storage.mCurrentAction.get()) // need to wait to init action with it's attack range + if (storage.mCurrentAction.get()) // need to wait to init action with its attack range { - //Update every frame - bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, storage.mAttackRange); + //Update every frame. UpdateLOS uses a timer, so the LOS check does not happen every frame. + updateLOS(actor, target, duration, storage); + float targetReachedTolerance = 0.0f; + if (storage.mLOS) + targetReachedTolerance = storage.mAttackRange; + bool is_target_reached = pathTo(actor, target.getRefData().getPosition().pos, duration, targetReachedTolerance); if (is_target_reached) storage.mReadyToAttack = true; } @@ -283,7 +287,7 @@ namespace MWMechanics osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); - storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack); + storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS); if (storage.mReadyToAttack) { @@ -309,18 +313,23 @@ namespace MWMechanics } } - void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) + void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) { static const float LOS_UPDATE_DURATION = 0.5f; - static const float BLIND_RUN_DURATION = 1.0f; - - if (storage.mFleeUpdateLOSTimer <= 0.f) + if (storage.mUpdateLOSTimer <= 0.f) { - storage.mFleeLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); - storage.mFleeUpdateLOSTimer = LOS_UPDATE_DURATION; + storage.mLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); + storage.mUpdateLOSTimer = LOS_UPDATE_DURATION; } else - storage.mFleeUpdateLOSTimer -= duration; + storage.mUpdateLOSTimer -= duration; + } + + void MWMechanics::AiCombat::updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage) + { + static const float BLIND_RUN_DURATION = 1.0f; + + updateLOS(actor, target, duration, storage); AiCombatStorage::FleeState& state = storage.mFleeState; switch (state) @@ -332,7 +341,7 @@ namespace MWMechanics { float triggerDist = getMaxAttackDistance(target); - if (storage.mFleeLOS && + if (storage.mLOS && (triggerDist >= 1000 || getDistanceMinusHalfExtents(actor, target) <= triggerDist)) { const ESM::Pathgrid* pathgrid = @@ -399,7 +408,7 @@ namespace MWMechanics static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->getFloat(); float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); - if ((dist > fFleeDistance && !storage.mFleeLOS) + if ((dist > fFleeDistance && !storage.mLOS) || pathTo(actor, storage.mFleeDest, duration)) { state = AiCombatStorage::FleeState_Idle; @@ -602,9 +611,6 @@ namespace MWMechanics mMovement.mPosition[2] = 0; mFleeState = FleeState_None; mFleeDest = ESM::Pathgrid::Point(0, 0, 0); - mFleeLOS = false; - mFleeUpdateLOSTimer = 0.0f; - mFleeUpdateLOSTimer = 0.0f; } bool AiCombatStorage::isFleeing() diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 3f2bde776..a2e995cb3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -61,6 +61,8 @@ namespace MWMechanics void 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); + void updateFleeing(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage); /// Transfer desired movement (from AiCombatStorage) to Actor