From 72786fef9d935df0d2b28dd74d1beafc8f134d7f Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 9 Sep 2016 23:57:19 +0300 Subject: [PATCH 1/9] prevent running in circles around path points addresses http://bugs.openmw.org/issues/2229 --- apps/openmw/mwmechanics/aipackage.cpp | 43 +++++++++++++++++++++++++-- apps/openmw/mwmechanics/aipackage.hpp | 5 ++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 9f6ef2597d..fa952c414a 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -19,12 +19,15 @@ #include "actorutil.hpp" #include "coordinateconverter.hpp" +#include + MWMechanics::AiPackage::~AiPackage() {} MWMechanics::AiPackage::AiPackage() : mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mIsShortcutting(false), - mShortcutProhibited(false), mShortcutFailPos() + mShortcutProhibited(false), mShortcutFailPos(), + mRotateOnTheRunChecks(0) { } @@ -104,6 +107,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path { mPathFinder.buildSyncedPath(start, dest, actor.getCell()); + mRotateOnTheRunChecks = 3; // give priority to go directly on target if there is minimal opportunity if (destInLOS && mPathFinder.getPath().size() > 1) @@ -143,7 +147,13 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr } else { - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // run to target + if (mRotateOnTheRunChecks == 0 + || isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // move to the target + if (mRotateOnTheRunChecks > 0) mRotateOnTheRunChecks--; + } + // handle obstacles on the way evadeObstacles(actor, duration, pos); } @@ -292,3 +302,32 @@ bool MWMechanics::AiPackage::isNearInactiveCell(const ESM::Position& actorPos) return false; } } + +bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest) +{ + // get actor's shortest radius for moving in circle + float speed = actor.getClass().getSpeed(actor); + speed += speed * 0.1f; // 10% real speed inaccuracy + float radius = speed / MAX_VEL_ANGULAR_RADIANS; + + // get radius direction to the center + const float* rot = actor.getRefData().getPosition().rot; + osg::Quat quatRot(rot[0], -osg::X_AXIS, rot[1], -osg::Y_AXIS, rot[2], -osg::Z_AXIS); + osg::Vec3f dir = quatRot * osg::Y_AXIS; // actor's orientation direction is a tangent to circle + osg::Vec3f radiusDir = dir ^ osg::Z_AXIS; // radius is perpendicular to a tangent + radiusDir.normalize(); + radiusDir *= radius; + + // pick up the nearest center candidate + osg::Vec3f dest_ = PathFinder::MakeOsgVec3(dest); + osg::Vec3f pos = actor.getRefData().getPosition().asVec3(); + osg::Vec3f center1 = pos - radiusDir; + osg::Vec3f center2 = pos + radiusDir; + osg::Vec3f center = (center1 - dest_).length2() < (center2 - dest_).length2() ? center1 : center2; + + float distToDest = (center - dest_).length(); + + // if pathpoint is reachable for the actor rotating on the run: + // no points of actor's circle should be farther from the center than destination point + return (radius <= distToDest); +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index c0df76c327..4960c14c33 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -97,6 +97,9 @@ namespace MWMechanics bool isTargetMagicallyHidden(const MWWorld::Ptr& target); + /// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing. + static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest); + protected: /// Handles path building and shortcutting with obstacles avoiding /** \return If the actor has arrived at his destination **/ @@ -123,6 +126,8 @@ namespace MWMechanics osg::Vec3f mLastActorPos; + short mRotateOnTheRunChecks; // attempts to check rotation to the pathpoint on the run possibility + bool mIsShortcutting; // if shortcutting at the moment bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt ESM::Pathgrid::Point mShortcutFailPos; // position of last shortcut fail From 7bc4535c0d9d6ff7887a54a58eda58430d342ba4 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sat, 10 Sep 2016 23:30:46 +0900 Subject: [PATCH 2/9] Make NPCs dodge according to target's weapon reach --- apps/openmw/mwmechanics/aicombat.cpp | 34 ++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 379119f601..ca3ab746e6 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -69,7 +69,7 @@ namespace MWMechanics mMovement() {} - void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack); + void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& target); void updateCombatMove(float duration); void stopCombatMove(); void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, @@ -242,7 +242,7 @@ namespace MWMechanics if (storage.mReadyToAttack) { - storage.startCombatMove(actorClass.isNpc(), isRangedCombat, distToTarget, rangeAttack); + storage.startCombatMove(actorClass.isNpc(), isRangedCombat, distToTarget, rangeAttack, target); // start new attack storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); @@ -306,7 +306,6 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } - AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); @@ -323,7 +322,7 @@ namespace MWMechanics sequence.mPackages.push_back(package); } - void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack) + void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& target) { if (mMovement.mPosition[0] || mMovement.mPosition[1]) { @@ -331,10 +330,28 @@ namespace MWMechanics mCombatMove = true; } // dodge movements (for NPCs only) - else if (isNpc && (!isDistantCombat || (distToTarget < rangeAttack / 2))) + else if (isNpc) { - //apply sideway movement (kind of dodging) with some probability - if (Misc::Rng::rollClosedProbability() < 0.25) + // get the range of the target's weapon + float rangeAttackOfTarget = 0.f; + MWWorld::Ptr targetWeapon = MWWorld::Ptr(); + bool isRangedCombat = false; + const MWWorld::Class& targetClass = target.getClass(); + if (targetClass.hasInventoryStore(target)) + { + MWMechanics::WeaponType weapType = WeapType_None; + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(targetClass.getCreatureStats(target), targetClass.getInventoryStore(target), &weapType); + if (weapType != WeapType_PickProbe && weapType != WeapType_Spell && weapType != WeapType_None && weapType != WeapType_HandToHand) + targetWeapon = *weaponSlot; + } + boost::shared_ptr targetWeaponAction (new ActionWeapon(targetWeapon)); + if (targetWeaponAction.get()) + rangeAttackOfTarget = targetWeaponAction->getCombatRange(isRangedCombat); + + // apply sideway movement (kind of dodging) with some probability + // if NPC is within range of target's weapon + if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) { mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); @@ -342,6 +359,9 @@ namespace MWMechanics } } + // Original engine behavior seems to be to back up during ranged combat + // according to fCombatDistance or opponent's weapon range, unless opponent + // is also using a ranged weapon if (isDistantCombat && distToTarget < rangeAttack / 4) { mMovement.mPosition[1] = -1; From c98d4e04738c29149ca19cf69ae0ee98db100d46 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sat, 10 Sep 2016 23:53:02 +0900 Subject: [PATCH 3/9] Allow dodging for bipedal creatures --- apps/openmw/mwmechanics/aicombat.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ca3ab746e6..e51502f847 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -69,7 +69,7 @@ namespace MWMechanics mMovement() {} - void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& target); + void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); void updateCombatMove(float duration); void stopCombatMove(); void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, @@ -242,7 +242,7 @@ namespace MWMechanics if (storage.mReadyToAttack) { - storage.startCombatMove(actorClass.isNpc(), isRangedCombat, distToTarget, rangeAttack, target); + storage.startCombatMove(actorClass.isNpc(), isRangedCombat, distToTarget, rangeAttack, actor, target); // start new attack storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); @@ -322,21 +322,22 @@ namespace MWMechanics sequence.mPackages.push_back(package); } - void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& target) + void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { if (mMovement.mPosition[0] || mMovement.mPosition[1]) { mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } - // dodge movements (for NPCs only) - else if (isNpc) + // dodge movements (for NPCs and bipedal creatures) + else if (isNpc || ((actor.get()->mBase->mFlags & ESM::Creature::Bipedal) != 0)) { // get the range of the target's weapon float rangeAttackOfTarget = 0.f; - MWWorld::Ptr targetWeapon = MWWorld::Ptr(); bool isRangedCombat = false; + MWWorld::Ptr targetWeapon = MWWorld::Ptr(); const MWWorld::Class& targetClass = target.getClass(); + if (targetClass.hasInventoryStore(target)) { MWMechanics::WeaponType weapType = WeapType_None; @@ -345,12 +346,14 @@ namespace MWMechanics if (weapType != WeapType_PickProbe && weapType != WeapType_Spell && weapType != WeapType_None && weapType != WeapType_HandToHand) targetWeapon = *weaponSlot; } + boost::shared_ptr targetWeaponAction (new ActionWeapon(targetWeapon)); + if (targetWeaponAction.get()) rangeAttackOfTarget = targetWeaponAction->getCombatRange(isRangedCombat); // apply sideway movement (kind of dodging) with some probability - // if NPC is within range of target's weapon + // if actor is within range of target's weapon if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) { mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right From 3f6543860a870f6c7005bb34c63848f289c233c9 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 11 Sep 2016 00:00:12 +0900 Subject: [PATCH 4/9] Make creatures use fHandToHandReach --- apps/openmw/mwmechanics/aicombataction.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index a704100357..6dea53e989 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -491,17 +491,9 @@ namespace MWMechanics if (mWeapon.isEmpty()) { - if (!mIsNpc) - { - return fCombatDistance; - } - else - { - static float fHandToHandReach = - MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); - - return fHandToHandReach * fCombatDistance; - } + static float fHandToHandReach = + MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + return fHandToHandReach * fCombatDistance; } const ESM::Weapon* weapon = mWeapon.get()->mBase; From 3bbde312b97eb9a635ac9812830ade1708796980 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 12 Sep 2016 19:54:06 +0900 Subject: [PATCH 5/9] Remove unneeded code --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/aicombataction.cpp | 2 -- apps/openmw/mwmechanics/aicombataction.hpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index e51502f847..5f5685bb07 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -330,7 +330,7 @@ namespace MWMechanics mCombatMove = true; } // dodge movements (for NPCs and bipedal creatures) - else if (isNpc || ((actor.get()->mBase->mFlags & ESM::Creature::Bipedal) != 0)) + else if (actor.getClass().isBipedal(actor)) { // get the range of the target's weapon float rangeAttackOfTarget = 0.f; diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 6dea53e989..094df1db37 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -462,8 +462,6 @@ namespace MWMechanics void ActionWeapon::prepare(const MWWorld::Ptr &actor) { - mIsNpc = actor.getClass().isNpc(); - if (actor.getClass().hasInventoryStore(actor)) { if (mWeapon.isEmpty()) diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index e4ce443464..c280d3c118 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -63,7 +63,6 @@ namespace MWMechanics private: MWWorld::Ptr mAmmunition; MWWorld::Ptr mWeapon; - bool mIsNpc; public: /// \a weapon may be empty for hand-to-hand combat From bce01669310344b7a020ae1122a3206d285f68a2 Mon Sep 17 00:00:00 2001 From: Allofich Date: Mon, 12 Sep 2016 20:40:40 +0900 Subject: [PATCH 6/9] Don't play blood effects for resisted hits --- apps/openmw/mwclass/creature.cpp | 20 +++++++++--------- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 27 ++++++++++++------------ apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 7 ++---- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- 9 files changed, 31 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 51c83eee1c..934ec62f5b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -264,7 +264,7 @@ namespace MWClass if(Misc::Rng::roll0to99() >= hitchance) { - victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); + victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr); return; } @@ -318,15 +318,12 @@ namespace MWClass if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; - if (damage > 0) - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - MWMechanics::diseaseContact(victim, ptr); - victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, true); + victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); } - void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const { // NOTE: 'object' and/or 'attacker' may be empty. @@ -342,10 +339,10 @@ namespace MWClass setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } - if(!object.isEmpty()) + if (!object.isEmpty()) getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); - if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) + if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { const std::string &script = ptr.get()->mBase->mScript; /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ @@ -353,14 +350,14 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - if(!successful) + if (!successful) { // Missed MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); return; } - if(!object.isEmpty()) + if (!object.isEmpty()) getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); if (damage > 0.0f && !object.isEmpty()) @@ -391,7 +388,10 @@ namespace MWClass if(ishealth) { if (!attacker.isEmpty()) + { damage = scaleDamage(damage, attacker, ptr); + MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition); + } MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index bea56887a0..c6c699166c 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -58,7 +58,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 98fdd76713..0189f6e362 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -591,7 +591,7 @@ namespace MWClass if (Misc::Rng::roll0to99() >= hitchance) { - othercls.onHit(victim, 0.0f, false, weapon, ptr, false); + othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr); return; } @@ -646,15 +646,12 @@ namespace MWClass if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; - if (healthdmg && damage > 0) - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - MWMechanics::diseaseContact(victim, ptr); - othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); + othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); } - void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); @@ -671,10 +668,10 @@ namespace MWClass setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); } - if(!object.isEmpty()) + if (!object.isEmpty()) getCreatureStats(ptr).setLastHitAttemptObject(object.getCellRef().getRefId()); - if(setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) + if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { const std::string &script = ptr.getClass().getScript(ptr); /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ @@ -682,14 +679,14 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - if(!successful) + if (!successful) { // Missed sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); return; } - if(!object.isEmpty()) + if (!object.isEmpty()) getCreatureStats(ptr).setLastHitObject(object.getCellRef().getRefId()); @@ -699,7 +696,7 @@ namespace MWClass if (damage < 0.001f) damage = 0; - if(damage > 0.0f && !attacker.isEmpty()) + if (damage > 0.0f && !attacker.isEmpty()) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. @@ -725,7 +722,7 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(damage > 0 && ishealth) + if (damage > 0 && ishealth) { // Hit percentages: // cuirass = 30% @@ -787,16 +784,18 @@ namespace MWClass } } - if(ishealth) + if (ishealth) { if (!attacker.isEmpty()) damage = scaleDamage(damage, attacker, ptr); - if(damage > 0.0f) + if (damage > 0.0f) { sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); if (ptr == MWMechanics::getPlayer()) MWBase::Environment::get().getWindowManager()->activateHitOverlay(); + if (!attacker.isEmpty()) + MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition); } MWMechanics::DynamicStat health(getCreatureStats(ptr).getHealth()); health.setCurrent(health.getCurrent() - damage); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 95edbd4089..339ee056af 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -73,7 +73,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index bb8929f8bf..e351229d0e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1771,7 +1771,7 @@ void CharacterController::update(float duration) float realHealthLost = static_cast(healthLost * (1.0f - 0.25f * fatigueTerm)); health.setCurrent(health.getCurrent() - realHealthLost); cls.getCreatureStats(mPtr).setHealth(health); - cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); + cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index df2793bcb7..b454f8e3a7 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -190,7 +190,7 @@ namespace MWMechanics if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue)) { - victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false); + victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, osg::Vec3f(), false); MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker); return; } @@ -218,9 +218,6 @@ namespace MWMechanics if (weapon != projectile) appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition); - if (damage > 0) - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory if (victim != getPlayer() && !appliedEnchantment) @@ -230,7 +227,7 @@ namespace MWMechanics victim.getClass().getContainerStore(victim).add(projectile, 1, victim); } - victim.getClass().onHit(victim, damage, true, projectile, attacker, true); + victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true); } float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 95f2d940fa..5024dfeec4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -569,7 +569,7 @@ namespace MWMechanics // Notify the target actor they've been hit if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor()) - target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); + target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); } bool CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index ecfe7cb7c4..699b8a27e3 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -98,7 +98,7 @@ namespace MWWorld throw std::runtime_error("class cannot block"); } - void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, osg::Vec3f hitPosition, bool successful) const { throw std::runtime_error("class cannot be hit"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 23128ea9dd..51afca0bb4 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -120,7 +120,7 @@ namespace MWWorld /// enums. ignored for creature attacks. /// (default implementation: throw an exception) - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is /// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the /// actor responsible for the attack, and \a successful specifies if the hit is From 34851349de2361ccd292fce9c52364a5e4e0c7b1 Mon Sep 17 00:00:00 2001 From: Allofich Date: Tue, 13 Sep 2016 00:49:31 +0900 Subject: [PATCH 7/9] Pass hitPosition by const reference --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 934ec62f5b..ff4ae61486 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -323,7 +323,7 @@ namespace MWClass victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); } - void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const + void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { // NOTE: 'object' and/or 'attacker' may be empty. diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index c6c699166c..654df60b60 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -58,7 +58,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0189f6e362..52debfb346 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -651,7 +651,7 @@ namespace MWClass othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); } - void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const + void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 339ee056af..9416051766 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -73,7 +73,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const; - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const; virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector& models) const; ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 699b8a27e3..12b69c7cac 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -98,7 +98,7 @@ namespace MWWorld throw std::runtime_error("class cannot block"); } - void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, osg::Vec3f hitPosition, bool successful) const + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const { throw std::runtime_error("class cannot be hit"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 51afca0bb4..01d20032bc 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -120,7 +120,7 @@ namespace MWWorld /// enums. ignored for creature attacks. /// (default implementation: throw an exception) - virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, osg::Vec3f hitPosition, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const; ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is /// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the /// actor responsible for the attack, and \a successful specifies if the hit is From 65dc12cdd61ebf0984313251b100c9e052449b47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Sep 2016 02:48:36 +0200 Subject: [PATCH 8/9] Remove unused parameter --- apps/openmw/mwmechanics/aicombat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 5f5685bb07..c821eac0d2 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -69,7 +69,7 @@ namespace MWMechanics mMovement() {} - void startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); + void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target); void updateCombatMove(float duration); void stopCombatMove(); void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController, @@ -242,7 +242,7 @@ namespace MWMechanics if (storage.mReadyToAttack) { - storage.startCombatMove(actorClass.isNpc(), isRangedCombat, distToTarget, rangeAttack, actor, target); + storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target); // start new attack storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat); @@ -322,7 +322,7 @@ namespace MWMechanics sequence.mPackages.push_back(package); } - void AiCombatStorage::startCombatMove(bool isNpc, bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) + void AiCombatStorage::startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { if (mMovement.mPosition[0] || mMovement.mPosition[1]) { From 1362264561731371b685ded5928ecb913c4fbe06 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Sep 2016 02:49:19 +0200 Subject: [PATCH 9/9] Fix warning --- apps/openmw/mwmechanics/aipackage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index fa952c414a..abdc570423 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -25,9 +25,9 @@ MWMechanics::AiPackage::~AiPackage() {} MWMechanics::AiPackage::AiPackage() : mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild + mRotateOnTheRunChecks(0), mIsShortcutting(false), - mShortcutProhibited(false), mShortcutFailPos(), - mRotateOnTheRunChecks(0) + mShortcutProhibited(false), mShortcutFailPos() { }