From 8fa7fcdbee3d63d61120526b24ef3b104c01e718 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 25 May 2014 21:03:37 +0400 Subject: [PATCH] fix bug http://bugs.openmw.org/issues/1335 (1304 as well) though fish detects if actor is swimming, but in MW it attacks even if the player is knee-deep in water --- apps/openmw/mwmechanics/actors.cpp | 8 +++++ apps/openmw/mwmechanics/aicombat.cpp | 29 +++++++++++++------ .../mwmechanics/mechanicsmanagerimp.cpp | 4 ++- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 35667a23c..fe89fda11 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -191,6 +191,14 @@ namespace MWMechanics if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player + // pure water creatures won't try to fight with the target on the ground + // except that creature is already hostile + if ((againstPlayer || !creatureStats.isHostile()) + && ((actor1.getClass().canSwim(actor1) && !actor1.getClass().canWalk(actor1) // pure water creature + && !MWBase::Environment::get().getWorld()->isSwimming(actor2)) + || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target + return; + float fight; if (againstPlayer) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9fe92f807..8f8188559 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -53,7 +53,7 @@ namespace // cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target; // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH - bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offset) + bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY) { if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH) { @@ -61,7 +61,7 @@ namespace dir.z = 0; dir.normalise(); float verticalOffset = 200; // instead of '200' here we want the height of the actor - Ogre::Vector3 _from = from + dir*offset + Ogre::Vector3::UNIT_Z * verticalOffset; + Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset; // cast up-down ray and find height in world space of hit float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1); @@ -149,13 +149,24 @@ namespace MWMechanics bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { //General description - if(actor.getClass().getCreatureStats(actor).isDead()) return true; + if(actor.getClass().getCreatureStats(actor).isDead()) + return true; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); if(target.getClass().getCreatureStats(target).isDead()) return true; + if (!actor.getClass().isNpc() && target == MWBase::Environment::get().getWorld()->getPlayerPtr() && + (actor.getClass().canSwim(actor) && !actor.getClass().canWalk(actor) // pure water creature + && !MWBase::Environment::get().getWorld()->isSwimming(target)) // Player moved out of water + || (!actor.getClass().canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(target))) // creature can't swim to Player + { + actor.getClass().getCreatureStats(actor).setHostile(false); + actor.getClass().getCreatureStats(actor).setAttackingOrSpell(false); + return true; + } + //Update every frame if(mCombatMove) { @@ -327,6 +338,7 @@ namespace MWMechanics Ogre::Vector3 vActorPos(pos.pos); Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos); Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; + float distToTarget = vDirToTarget.length(); bool isStuck = false; float speed = 0.0f; @@ -342,16 +354,15 @@ namespace MWMechanics // determine vertical angle to target // if actor can move along z-axis it will control movement dir // if can't - it will control correct aiming - mMovement.mRotation[0] = getXAngleToDir(vDirToTarget); - - vDirToTarget.z = 0; - float distToTarget = vDirToTarget.length(); + mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget); // (within strike dist) || (not quite strike dist while following) if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) ) { //Melee and Close-up combat - mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget); + + // if we preserve dir.z then horizontal angle can be inaccurate + mMovement.mRotation[2] = getZAngleToDir(Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0)); // (not quite strike dist while following) if (mFollowTarget && distToTarget > rangeAttack) @@ -405,7 +416,7 @@ namespace MWMechanics if(speed == 0.0f) speed = actorCls.getSpeed(actor); // maximum dist before pit/obstacle for actor to avoid them depending on his speed float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability - preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2); + preferShortcut = checkWayIsClear(vActorPos, vTargetPos, Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0).length() > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2); } // don't use pathgrid when actor can move in 3 dimensions diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5a72c8883..c9a181461 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1024,7 +1024,9 @@ namespace MWMechanics void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { - MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); + if (ptr.getClass().isNpc()) + MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); + ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) ptr.getClass().getCreatureStats(ptr).setHostile(true);