From f596b698d96a07ed4916ee056f2d4a8a00884d43 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 4 May 2014 18:15:07 +0400 Subject: [PATCH 1/8] fixes to slow-down at jump startup and infinite air intertia growth --- apps/openmw/mwmechanics/character.cpp | 35 +++++++++++++++------------ apps/openmw/mwworld/physicssystem.cpp | 11 ++++++++- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8ad2b4a67..42ed235c5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1006,7 +1006,16 @@ void CharacterController::update(float duration) bool flying = world->isFlying(mPtr); //Ogre::Vector3 vec = cls.getMovementVector(mPtr); Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition); - vec.normalise(); + if(vec.z > 0.0f) // to avoid slow-down when jumping + { + Ogre::Vector2 vecXY = Ogre::Vector2(vec.x, vec.y); + vecXY.normalise(); + vec.x = vecXY.x; + vec.y = vecXY.y; + } + else + vec.normalise(); + if(mHitState != CharState_None && mJumpState == JumpState_None) vec = Ogre::Vector3(0.0f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); @@ -1107,9 +1116,12 @@ void CharacterController::update(float duration) if(cls.isNpc()) { const NpcStats &stats = cls.getNpcStats(mPtr); - mult = gmst.find("fJumpMoveBase")->getFloat() + + static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); + static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); + + mult = fJumpMoveBase + (stats.getSkill(ESM::Skill::Acrobatics).getModified()/100.0f * - gmst.find("fJumpMoveMult")->getFloat()); + fJumpMoveMult); } vec.x *= mult; @@ -1119,14 +1131,7 @@ void CharacterController::update(float duration) else if(vec.z > 0.0f && mJumpState == JumpState_None) { // Started a jump. - float z = cls.getJump(mPtr); - if(vec.x == 0 && vec.y == 0) - vec = Ogre::Vector3(0.0f, 0.0f, z); - else - { - Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); - vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; - } + vec.z = cls.getJump(mPtr); // advance acrobatics if (mPtr.getRefData().getHandle() == "player") @@ -1438,14 +1443,14 @@ void CharacterController::updateVisibility() void CharacterController::determineAttackType() { - float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; + float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; if(mPtr.getClass().hasInventoryStore(mPtr)) { - if (move[0] && !move[1]) //sideway - mAttackType = "slash"; - else if (move[1]) //forward + if (move[1]) // forward-backward mAttackType = "thrust"; + else if (move[0]) //sideway + mAttackType = "slash"; else mAttackType = "chop"; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 247c0d4bd..5b85e203c 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -246,6 +246,15 @@ namespace MWWorld // If falling, add part of the incoming velocity with the current inertia // TODO: but we could be jumping up? velocity = velocity * time + physicActor->getInertialForce(); + + // avoid getting infinite inertia in air + float actorSpeed = ptr.getClass().getSpeed(ptr); + float speedXY = Ogre::Vector2(velocity.x, velocity.y).length(); + if (speedXY > actorSpeed) + { + velocity.x *= actorSpeed / speedXY; + velocity.y *= actorSpeed / speedXY; + } } inertia = velocity; // NOTE: velocity is for z axis only in this code block @@ -314,7 +323,7 @@ namespace MWWorld if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) { // don't let pure water creatures move out of water after stepMove - if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr)) + if((ptr.getClass().canSwim(ptr) && !canWalk) && newPosition.z > (waterlevel - halfExtents.z * 0.5)) newPosition = oldPosition; else // Only on the ground if there's gravity From 01810f24b04b60e5c38262881d8df0f85bf89045 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 6 May 2014 23:29:39 +0400 Subject: [PATCH 2/8] A* misses 1st closest node fix --- apps/openmw/mwmechanics/pathgrid.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 82d815d68..c3fa0a662 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -328,6 +328,12 @@ namespace MWMechanics path.push_front(pt); current = graphParent[current]; } + + // add first node to path explicitly + ESM::Pathgrid::Point pt = mPathgrid->mPoints[start]; + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); return path; } } From 725f6cac5e5c54fc1c41ddce88fd303663f62eb4 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 13 May 2014 00:05:30 +0400 Subject: [PATCH 3/8] AiPursue infinite package updating bug resolved --- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/aipursue.cpp | 16 +++++++++------- apps/openmw/mwmechanics/aipursue.hpp | 10 +++++++--- apps/openmw/mwmechanics/aisequence.cpp | 8 +++++++- apps/openmw/mwworld/physicssystem.cpp | 2 +- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6375bdd1b..6ec2f64d0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -730,7 +730,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } @@ -759,7 +759,7 @@ namespace MWMechanics else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); else { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 0c10bd81d..8b71c8072 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -1,6 +1,5 @@ #include "aipursue.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" @@ -11,8 +10,8 @@ #include "movement.hpp" #include "creaturestats.hpp" -MWMechanics::AiPursue::AiPursue(const std::string &objectId) - : mObjectId(objectId) +MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr target) + : mTarget(target) { } MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const @@ -54,8 +53,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) // Big TODO: Sync this with current AiFollow. Move common code to a shared base class or helpers (applies to all AI packages, way too much duplicated code) - MWWorld::Ptr target = world->getPtr(mObjectId,false); - ESM::Position targetPos = target.getRefData().getPosition(); + ESM::Position targetPos = mTarget.getRefData().getPosition(); bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) @@ -81,8 +79,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) (pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 100*100) { movement.mPosition[1] = 0; - MWWorld::Ptr target = world->getPtr(mObjectId,false); - MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); + MWWorld::Class::get(mTarget).activate(mTarget,actor).get()->execute(actor); return true; } @@ -98,3 +95,8 @@ int MWMechanics::AiPursue::getTypeId() const { return TypeIdPursue; } + +MWWorld::Ptr MWMechanics::AiPursue::getTarget() const +{ + return mTarget; +} diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 86750acca..4cac6c008 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -2,7 +2,8 @@ #define GAME_MWMECHANICS_AIPURSUE_H #include "aipackage.hpp" -#include + +#include "../mwbase/world.hpp" #include "pathfinding.hpp" @@ -12,14 +13,17 @@ namespace MWMechanics class AiPursue : public AiPackage { public: - AiPursue(const std::string &objectId); + AiPursue(const MWWorld::Ptr target); virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? virtual int getTypeId() const; + virtual MWWorld::Ptr getTarget() const; + private: - std::string mObjectId; + + MWWorld::Ptr mTarget; PathFinder mPathFinder; int mCellX; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 0b1da180d..2134b7bba 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -9,6 +9,7 @@ #include "aifollow.hpp" #include "aiactivate.hpp" #include "aicombat.hpp" +#include "aipursue.hpp" #include "../mwworld/class.hpp" #include "creaturestats.hpp" @@ -128,7 +129,12 @@ void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Pt // Notify AiWander of our current position so we can return to it after combat finished for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) { - if ((*iter)->getTypeId() == AiPackage::TypeIdWander) + if((*iter)->getTypeId() == AiPackage::TypeIdPursue && package.getTypeId() == AiPackage::TypeIdPursue + && static_cast(*iter)->getTarget() == static_cast(&package)->getTarget()) + { + return; // target is already pursued + } + else if ((*iter)->getTypeId() == AiPackage::TypeIdWander) static_cast(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos)); } } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 17f118b11..787653f11 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -316,7 +316,7 @@ namespace MWWorld if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) { // don't let pure water creatures move out of water after stepMove - if((ptr.getClass().canSwim(ptr) && !canWalk) + if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr)) && newPosition.z > (waterlevel - halfExtents.z * 0.5)) newPosition = oldPosition; else // Only on the ground if there's gravity From 5be37f04ef1709565dbe4138bc4e603499ed481b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 16 May 2014 00:03:48 +0400 Subject: [PATCH 4/8] Feature 1314: make npc fight creatures --- apps/openmw/mwmechanics/actors.cpp | 161 +++++++++++++++++-------- apps/openmw/mwmechanics/actors.hpp | 8 +- apps/openmw/mwmechanics/aicombat.cpp | 11 +- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aisequence.cpp | 64 +++++++++- apps/openmw/mwmechanics/aisequence.hpp | 3 + 6 files changed, 187 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6ec2f64d0..d988b9703 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -178,54 +178,65 @@ namespace MWMechanics calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); - - // AI - if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) - { - CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - //engage combat or not? - if(ptr != player && !creatureStats.isHostile()) - { - ESM::Position playerpos = player.getRefData().getPosition(); - ESM::Position actorpos = ptr.getRefData().getPosition(); - float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) - +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) - +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); - float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); - - if( (fight == 100 ) - || (fight >= 95 && d <= 3000) - || (fight >= 90 && d <= 2000) - || (fight >= 80 && d <= 1000) - ) - { - bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr); - - if (LOS) - { - MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); - } - } - } - updateCrimePersuit(ptr, duration); - creatureStats.getAiSequence().execute (ptr,duration); - } - // fatigue restoration calculateRestoration(ptr, duration, false); } - void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) + void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) { - if(!paused) + CreatureStats& creatureStats = MWWorld::Class::get(actor1).getCreatureStats(actor1); + + if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player + + float fight; + + if (againstPlayer) + fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified(); + else { - updateDrowning(ptr, duration); - calculateNpcStatModifiers(ptr); - updateEquippedLight(ptr, duration); + fight = 0; + // if one of actors is creature then we should make a decision to start combat or not + // NOTE: function doesn't take into account combat between 2 creatures + if (!actor1.getClass().isNpc()) + { + // if creature is hostile then it is necessarily to start combat + if (creatureStats.isHostile()) fight = 100; + else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified(); + } } + + ESM::Position actor1Pos = actor1.getRefData().getPosition(); + ESM::Position actor2Pos = actor2.getRefData().getPosition(); + float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); + + if( (fight == 100 && d <= 5000) + || (fight >= 95 && d <= 3000) + || (fight >= 90 && d <= 2000) + || (fight >= 80 && d <= 1000)) + { + if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d)) + { + bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); + + if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + + if (LOS) + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + if (!againstPlayer) // start combat between each other + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); + } + } + } + } + } + + void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) + { + updateDrowning(ptr, duration); + calculateNpcStatModifiers(ptr); + updateEquippedLight(ptr, duration); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -830,10 +841,24 @@ namespace MWMechanics } } + static Ogre::Vector3 sBasePoint; + bool comparePtrDist (const MWWorld::Ptr& ptr1, const MWWorld::Ptr& ptr2) + { + return (sBasePoint.squaredDistance(Ogre::Vector3(ptr1.getRefData().getPosition().pos)) + < sBasePoint.squaredDistance(Ogre::Vector3(ptr2.getRefData().getPosition().pos))); + } + void Actors::update (float duration, bool paused) { if(!paused) { + std::list listGuards; // at the moment only guards certainly will fight with creatures + + static float timerUpdateAITargets = 0; + + // target lists get updated once every 1.0 sec + if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0; + // Reset data from previous frame for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -841,29 +866,64 @@ namespace MWMechanics // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation // (below) iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); + + // add guards to list to later make them fight with creatures + if (timerUpdateAITargets == 0 && iter->first.getClass().isClass(iter->first, "Guard")) + listGuards.push_back(iter->first); } - // AI and magic effects update - for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + listGuards.push_back(player); + + // AI and magic effects update + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { updateActor(iter->first, duration); - if (iter->first.getTypeName() == typeid(ESM::NPC).name()) - updateNpc(iter->first, duration, paused); + + if (MWBase::Environment::get().getMechanicsManager()->isAIActive()) + { + // make guards and creatures fight each other + if (timerUpdateAITargets == 0 && !iter->first.getClass().isNpc() && !listGuards.empty()) + { + //findNthClosest + sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos); + listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest creature + + for (std::list::const_iterator it = listGuards.cbegin(); it != listGuards.cend(); ++it) + { + engageCombat(iter->first, *it, false); + } + } + + if (iter->first != player) engageCombat(iter->first, player, true); + + if (iter->first.getClass().isNpc() && iter->first != player) + updateCrimePersuit(iter->first, duration); + + if (iter->first != player) + iter->first.getClass().getCreatureStats(iter->first).getAiSequence().execute(iter->first, duration); + } + + if(iter->first.getTypeName() == typeid(ESM::NPC).name()) + updateNpc(iter->first, duration); } } + timerUpdateAITargets += duration; + // Looping magic VFX update // Note: we need to do this before any of the animations are updated. // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), // so updating VFX immediately after that would just remove the particle effects instantly. // There needs to be a magic effect update in between. - for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) iter->second->updateContinuousVfx(); // Animation/movement update - for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( ESM::MagicEffect::Paralyze).mMagnitude > 0) @@ -872,7 +932,7 @@ namespace MWMechanics } // Kill dead actors, update some variables - for (PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = MWWorld::Class::get(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first); @@ -945,7 +1005,6 @@ namespace MWMechanics } // if player is in sneak state see if anyone detects him - const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak)) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -1075,9 +1134,7 @@ namespace MWMechanics if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat) { MWMechanics::AiCombat* package = static_cast(stats.getAiSequence().getActivePackage()); - // TODO: This is wrong! It's comparing Ref IDs with Ogre handles. The only case where this (coincidentally) works is the player. - // possibly applies to other code using getTargetId. - if(package->getTargetId() == actor.getCellRef().mRefID) + if(package->getTarget() == actor) list.push_front(*iter); } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index f7dff1058..c6b72c958 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -27,7 +27,7 @@ namespace MWMechanics { std::map mDeathCount; - void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); + void updateNpc(const MWWorld::Ptr &ptr, float duration); void adjustMagicEffects (const MWWorld::Ptr& creature); @@ -81,6 +81,12 @@ namespace MWMechanics ///< This function is normally called automatically during the update process, but it can /// also be called explicitly at any time to force an update. + /** Start combat between two actors + @Notes: If againstPlayer = true then actor2 should be the Player. + If one of the combatants is creature it should be actor1. + */ + void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer); + void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8464cc419..9b2e73edb 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -149,11 +149,8 @@ namespace MWMechanics bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { //General description - if(!actor.getClass().getCreatureStats(actor).isHostile() - || actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) - return true; - - if(mTarget.getClass().getCreatureStats(mTarget).isDead()) + if(actor.getClass().getCreatureStats(actor).isDead() + || mTarget.getClass().getCreatureStats(mTarget).isDead() ) return true; //Update every frame @@ -627,9 +624,9 @@ namespace MWMechanics return 1; } - const std::string &AiCombat::getTargetId() const + const MWWorld::Ptr &AiCombat::getTarget() const { - return mTarget.getRefData().getHandle(); + return mTarget; } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 30b72acd9..388e6c348 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -28,7 +28,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; - const std::string &getTargetId() const; + const MWWorld::Ptr &getTarget() const; private: PathFinder mPathFinder; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2134b7bba..1ed135a67 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -61,7 +61,35 @@ bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const if (getTypeId() != AiPackage::TypeIdCombat) return false; const AiCombat *combat = static_cast(mPackages.front()); - targetActorId = combat->getTargetId(); + targetActorId = combat->getTarget().getRefData().getHandle(); + + return true; +} + +bool MWMechanics::AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const +{ + bool firstCombatFound = false; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + { + firstCombatFound = true; + + const AiCombat *combat = static_cast(*it); + if (combat->getTarget() != player) return false; // only 1 non-player target allowed + else + { + // add new target only if current target (player) is farther + ESM::Position &targetPos = combat->getTarget().getRefData().getPosition(); + + float distToCurrTarget = (Ogre::Vector3(targetPos.pos) - Ogre::Vector3(actorPos.pos)).length(); + return (distToCurrTarget > distToTarget); + } + } + else if (firstCombatFound) break; // assumes combat packages go one-by-one in packages list + } return true; } @@ -96,6 +124,40 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { MWMechanics::AiPackage* package = mPackages.front(); mLastAiPackage = package->getTypeId(); + + // if active package is combat one, choose nearest target + if (mLastAiPackage == AiPackage::TypeIdCombat) + { + std::list::const_iterator itActualCombat; + + float nearestDist = std::numeric_limits::max(); + Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos); + + const AiCombat *package; + + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + package = static_cast(*it); + + if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; + + ESM::Position &targetPos = package->getTarget().getRefData().getPosition(); + + float distTo = (Ogre::Vector3(targetPos.pos) - vActorPos).length(); + if (distTo < nearestDist) + { + nearestDist = distTo; + itActualCombat = it; + } + } + + if (mPackages.cbegin() != itActualCombat) + { + // move combat package with nearest target to the front + mPackages.splice(mPackages.begin(), mPackages, itActualCombat); + } + } + if (package->execute (actor,duration)) { // To account for the rare case where AiPackage::execute() queued another AI package diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 37e0c7f17..50cd3bea8 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -47,6 +47,9 @@ namespace MWMechanics ///< Return true and assign target if combat package is currently /// active, return false otherwise + bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const; + ///< Function assumes that actor can have only 1 target apart player + void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. From 69c1eb28c51ba04f50a0764fb07bd25342f11a55 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 18 May 2014 14:39:04 +0400 Subject: [PATCH 5/8] travis compile fix --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/aisequence.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9eb5f55cc..2c3540462 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -922,7 +922,7 @@ namespace MWMechanics sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos); listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest creature - for (std::list::const_iterator it = listGuards.cbegin(); it != listGuards.cend(); ++it) + for (std::list::iterator it = listGuards.begin(); it != listGuards.end(); ++it) { engageCombat(iter->first, *it, false); } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index f93dae4d1..b74a5da5a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -156,7 +156,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) } } - if (mPackages.cbegin() != itActualCombat) + if (mPackages.begin() != itActualCombat) { // move combat package with nearest target to the front mPackages.splice(mPackages.begin(), mPackages, itActualCombat); From 74697f811692456cc1e82e77c8016c2423fb79a4 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 18 May 2014 15:41:15 +0400 Subject: [PATCH 6/8] travis fix #2 --- apps/openmw/mwmechanics/aisequence.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index b74a5da5a..2da213529 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -159,7 +159,9 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) if (mPackages.begin() != itActualCombat) { // move combat package with nearest target to the front - mPackages.splice(mPackages.begin(), mPackages, itActualCombat); + AiPackage *package = (*itActualCombat)->clone(); + mPackages.erase(itActualCombat); + mPackages.push_front(package); } } From 66307dd8894c95176eedfc5757642c6a14c2c88d Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 18 May 2014 16:10:14 +0400 Subject: [PATCH 7/8] travis fix #3 :( --- apps/openmw/mwmechanics/aisequence.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2da213529..f0f6bffc1 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -133,14 +133,14 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) // if active package is combat one, choose nearest target if (mLastAiPackage == AiPackage::TypeIdCombat) { - std::list::const_iterator itActualCombat; + std::list::iterator itActualCombat; float nearestDist = std::numeric_limits::max(); Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos); const AiCombat *package; - for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { package = static_cast(*it); @@ -159,9 +159,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) if (mPackages.begin() != itActualCombat) { // move combat package with nearest target to the front - AiPackage *package = (*itActualCombat)->clone(); - mPackages.erase(itActualCombat); - mPackages.push_front(package); + mPackages.splice(mPackages.begin(), mPackages, itActualCombat); } } From e1249f6a31087e6a127b5d8d3dd3218d87ee1b58 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 18 May 2014 20:13:46 +0400 Subject: [PATCH 8/8] actor handle and id confusing fix --- apps/openmw/mwmechanics/aisequence.cpp | 11 +++-------- apps/openmw/mwmechanics/aisequence.hpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 6 ++---- apps/openmw/mwscript/aiextensions.cpp | 5 +++-- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index f0f6bffc1..9dc2380f8 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -59,14 +59,13 @@ int AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } -bool AiSequence::getCombatTarget(std::string &targetActorId) const +bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const { if (getTypeId() != AiPackage::TypeIdCombat) return false; const AiCombat *combat = static_cast(mPackages.front()); - MWWorld::Ptr target = combat->getTarget(); - targetActorId = target.getClass().getCreatureStats(target).getActorId(); + targetActor = combat->getTarget(); return true; } @@ -138,15 +137,11 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) float nearestDist = std::numeric_limits::max(); Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos); - const AiCombat *package; - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) { - package = static_cast(*it); - if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; - ESM::Position &targetPos = package->getTarget().getRefData().getPosition(); + ESM::Position &targetPos = static_cast(*it)->getTarget().getRefData().getPosition(); float distTo = (Ogre::Vector3(targetPos.pos) - vActorPos).length(); if (distTo < nearestDist) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 7e977cf58..41a280da8 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -53,7 +53,7 @@ namespace MWMechanics int getLastRunTypeId() const { return mLastAiPackage; } /// Return true and assign target if combat package is currently active, return false otherwise - bool getCombatTarget (std::string &targetActorId) const; + bool getCombatTarget (MWWorld::Ptr &targetActor) const; bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const; ///< Function assumes that actor can have only 1 target apart player diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 57618e6e9..d8dfa158d 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -322,11 +322,9 @@ namespace MWMechanics bool CreatureStats::getCreatureTargetted() const { - std::string target; - if (mAiSequence.getCombatTarget(target)) + MWWorld::Ptr targetPtr; + if (mAiSequence.getCombatTarget(targetPtr)) { - MWWorld::Ptr targetPtr; - targetPtr = MWBase::Environment::get().getWorld()->getPtr(target, true); return targetPtr.getTypeName() == typeid(ESM::Creature).name(); } return false; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8ed19925e..695954bc2 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -419,9 +419,10 @@ namespace MWScript std::string currentTargetId; bool targetsAreEqual = false; - if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) + MWWorld::Ptr targetPtr; + if (creatureStats.getAiSequence().getCombatTarget (targetPtr)) { - if (currentTargetId == testedTargetId) + if (targetPtr.getRefData().getHandle() == testedTargetId) targetsAreEqual = true; } runtime.push(int(targetsAreEqual));