From 10840765d91eeda98227866a1f991ad015ba1a1c Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 8 May 2013 15:27:20 -0700 Subject: [PATCH 1/5] Framework - duration and pathing implemented, no waiting for follower yet. --- apps/openmw/mwmechanics/aiescort.cpp | 194 +++++++++++++++++++++++---- apps/openmw/mwmechanics/aiescort.hpp | 45 +++++-- 2 files changed, 207 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index ebbea55b0..6ffdb960e 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,30 +1,178 @@ #include "aiescort.hpp" -#include - -MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) -: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) -{ -} +#include + +#include "character.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/timestamp.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" +#include "movement.hpp" + +#include +#include +#include "boost/tuple/tuple.hpp" + +namespace +{ + float sgn(float a) + { + if(a>0) return 1.; + else return -1.; + } +} + +// TODO: Fix all commenting! +// TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. +// TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. +// Necessary? MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, true); + +MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) +: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) +{ + //\ < The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + std::cout << "AiEscort Started at: " << mStartingSecond << " For: " + << duration << "Started At: " << mStartingSecond << std::endl; + } +} MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) { + //\ < The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + std::cout << "AiEscort Started at: " << mStartingSecond << " For: " + << duration << "Started At: " << mStartingSecond << std::endl; + } +} + + +MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const +{ + return new AiEscort(*this); +} + +bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) +{ + // If AiEscort has ran for as long or longer then the duration specified + // and the duration is not infinite, the package is complete. + if(mDuration != 0) + { + MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); + unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); + std::cout << "AiEscort: " << currentSecond << " time: " << currentSecond - mStartingSecond << std::endl; + if(currentSecond - mStartingSecond >= mDuration) + { + std::cout << "AiEscort Has Run It's Duration: " << currentSecond - mStartingSecond + << " >= " << mDuration << std::endl; + return true; + } + } + + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + + if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) + { + int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); + // Check if actor is near the border of an inactive cell. If so, disable AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) + { + int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); + // Check if actor is near the border of an inactive cell. If so, disable AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + + if(!mPathFinder.isPathConstructed() || cellChange) + { + cellX = actor.getCell()->mCell->mData.mX; + cellY = actor.getCell()->mCell->mData.mY; + float xCell = 0; + float yCell = 0; + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + dest.mY = mY; + dest.mZ = mZ; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + } + + if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + +/* + if(getDistanceBetween() <= ???) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + } + // Stop moving if the player is to far away + else + { + // TODO: Set the actor to do the equivilent of AIWander 0 0 0 playing the "look back" idle animation. + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + } +*/ + // Delete these three when above code is enabled! + + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + return false; +} + +int MWMechanics::AiEscort::getTypeId() const +{ + return 2; } - -MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const -{ - return new AiEscort(*this); -} - -bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) -{ - std::cout << "AiEscort completed. \n"; - return true; -} - -int MWMechanics::AiEscort::getTypeId() const -{ - return 2; -} - diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index d89a9586c..69e24e9fb 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -2,13 +2,36 @@ #define GAME_MWMECHANICS_AIESCORT_H #include "aipackage.hpp" +#include "pathfinding.hpp" #include +/* From CS: +Escort + +Makes the actor escort another actor to a location or for a specified period of time. During this time the actor will also protect the actor it is escorting. + +If you are not doing this package with the player as the target, you’ll want to also put a follow package on the target Actor, since escorting an actor makes the escorter wait for the other actor. If the Target does not know they are supposed to follow, the escorter will most likely just stand there. + +Target: The ActorID to Escort. Remember that since all ActorIDs share the same AI packages, putting this on an Actor with multiple references will cause ALL references of that actor to attempt to escort the same actor. Thus, this type of AI should only be placed on specific or unique sets of Actors. + +Duration: The duration the actor should escort for. Trumped by providing a location. + +Escort to: Check this to use location data for the escort. + +Cell: The Cell to escort to. + +XYZ: Like Travel, specify the XYZ location to escort to. + +View Location: A red X will appear in the render window that you can move around with the standard render window object controls. Place the X on the escort destination. + + +*/ + namespace MWMechanics -{ - class AiEscort : public AiPackage - { - public: +{ + class AiEscort : public AiPackage + { + public: AiEscort(const std::string &actorId,int duration, float x, float y, float z); ///< \implement AiEscort AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); @@ -27,8 +50,12 @@ namespace MWMechanics float mX; float mY; float mZ; - int mDuration; - - }; -} -#endif + unsigned int mStartingSecond; + unsigned int mDuration; + + PathFinder mPathFinder; + int cellX; + int cellY; + }; +} +#endif From c03dca47f5a5df24c58cee243ef80b34d5d352dc Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 8 May 2013 17:19:47 -0700 Subject: [PATCH 2/5] AIEscort implemented and working - only touchups are needed. --- apps/openmw/mwmechanics/aiescort.cpp | 58 +++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 6ffdb960e..6366f5da2 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,5 +1,6 @@ #include "aiescort.hpp" #include +#include #include "character.hpp" @@ -21,6 +22,42 @@ namespace if(a>0) return 1.; else return -1.; } + + /* + + MWWorld::Ptr InterpreterContext::getReference ( + const std::string& id, bool activeOnly) + { + if (!id.empty()) + { + return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); + } + else + { + if (mReference.isEmpty()) + throw std::runtime_error ("no implicit reference"); + + return mReference; + } + } + + float InterpreterContext::getDistance (const std::string& name, const std::string& id) const + { + // TODO handle exterior cells (when ref and ref2 are located in different cells) + /const MWWorld::Ptr ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); + + double diff[3]; + + const float* const pos1 = actor.getRefData().getPosition().pos; + const float* const pos2 = ref2.getRefData().getPosition().pos; + + for (int i=0; i<3; ++i) + diff[i] = pos1[i] - pos2[i]; + + return std::sqrt (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]); + } + */ + } // TODO: Fix all commenting! @@ -147,8 +184,18 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) return true; } -/* - if(getDistanceBetween() <= ???) + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const float* const leaderPos = actor.getRefData().getPosition().pos; + const float* const followerPos = follower.getRefData().getPosition().pos; + double differenceBetween[3]; + + for (short i = 0; i < 3; ++i) + differenceBetween[i] = (leaderPos[i] - followerPos[i]); + + float distanceBetweenResult = + std::sqrt((differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2])); + + if(distanceBetweenResult <= 500) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); @@ -160,13 +207,6 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) // TODO: Set the actor to do the equivilent of AIWander 0 0 0 playing the "look back" idle animation. MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; } -*/ - // Delete these three when above code is enabled! - - - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); - MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; return false; } From 133964f0e48910508f2cb2cbcbf84ed88bf3a1c0 Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 8 May 2013 20:02:24 -0700 Subject: [PATCH 3/5] AIEscort complete --- apps/openmw/mwmechanics/aiescort.cpp | 70 +++++++++------------------- apps/openmw/mwmechanics/aiescort.hpp | 1 + 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 6366f5da2..c30146e6e 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -3,13 +3,14 @@ #include #include "character.hpp" +#include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwbase/world.hpp" -#include "../mwworld/timestamp.hpp" -#include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" -#include "movement.hpp" +#include "../mwworld/timestamp.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include #include @@ -22,53 +23,20 @@ namespace if(a>0) return 1.; else return -1.; } - - /* - - MWWorld::Ptr InterpreterContext::getReference ( - const std::string& id, bool activeOnly) - { - if (!id.empty()) - { - return MWBase::Environment::get().getWorld()->getPtr (id, activeOnly); - } - else - { - if (mReference.isEmpty()) - throw std::runtime_error ("no implicit reference"); - - return mReference; - } - } - - float InterpreterContext::getDistance (const std::string& name, const std::string& id) const - { - // TODO handle exterior cells (when ref and ref2 are located in different cells) - /const MWWorld::Ptr ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); - - double diff[3]; - - const float* const pos1 = actor.getRefData().getPosition().pos; - const float* const pos2 = ref2.getRefData().getPosition().pos; - - for (int i=0; i<3; ++i) - diff[i] = pos1[i] - pos2[i]; - - return std::sqrt (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]); - } - */ - } -// TODO: Fix all commenting! -// TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. -// TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. -// Necessary? MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, true); +/* + TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. + TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. + TODO: Take account for actors being in different cells. +*/ MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) { - //\ < The CS Help File states that if a duration is givin, the AI package will run for that long + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -85,7 +53,9 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) { - //\ < The CS Help File states that if a duration is givin, the AI package will run for that long + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -195,17 +165,19 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) float distanceBetweenResult = std::sqrt((differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2])); - if(distanceBetweenResult <= 500) + if(distanceBetweenResult <= mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMaxDist = 470; } - // Stop moving if the player is to far away else { - // TODO: Set the actor to do the equivilent of AIWander 0 0 0 playing the "look back" idle animation. + // Stop moving if the player is to far away + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mMaxDist = 330; } return false; diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 69e24e9fb..667b88e31 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -50,6 +50,7 @@ namespace MWMechanics float mX; float mY; float mZ; + float mMaxDist; unsigned int mStartingSecond; unsigned int mDuration; From d20178dd56a53a03a844ec615e2eb669d1923c44 Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 8 May 2013 20:05:45 -0700 Subject: [PATCH 4/5] Removed unnecessary std::cout messages --- apps/openmw/mwmechanics/aiescort.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index c30146e6e..cd4ea7a61 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,5 +1,4 @@ #include "aiescort.hpp" -#include #include #include "character.hpp" @@ -45,8 +44,6 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x { MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - std::cout << "AiEscort Started at: " << mStartingSecond << " For: " - << duration << "Started At: " << mStartingSecond << std::endl; } } @@ -64,8 +61,6 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &ce { MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - std::cout << "AiEscort Started at: " << mStartingSecond << " For: " - << duration << "Started At: " << mStartingSecond << std::endl; } } @@ -86,8 +81,6 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) std::cout << "AiEscort: " << currentSecond << " time: " << currentSecond - mStartingSecond << std::endl; if(currentSecond - mStartingSecond >= mDuration) { - std::cout << "AiEscort Has Run It's Duration: " << currentSecond - mStartingSecond - << " >= " << mDuration << std::endl; return true; } } From 4928c9d079bab7f59f90b05812706daf3943aa4c Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 8 May 2013 20:47:31 -0700 Subject: [PATCH 5/5] Removed sqrt function call (minor optimization thanks to Chris) --- apps/openmw/mwmechanics/aiescort.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index cd4ea7a61..7c28dd216 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,5 +1,4 @@ #include "aiescort.hpp" -#include #include "character.hpp" #include "movement.hpp" @@ -156,9 +155,9 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) differenceBetween[i] = (leaderPos[i] - followerPos[i]); float distanceBetweenResult = - std::sqrt((differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2])); + (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); - if(distanceBetweenResult <= mMaxDist) + if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);