Savegame: Store AiSequence

deque
scrawl 11 years ago
parent be6f1fe4fe
commit a54ac579a5

@ -1,5 +1,7 @@
#include "aiactivate.hpp" #include "aiactivate.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -19,26 +21,26 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
return new AiActivate(*this); return new AiActivate(*this);
} }
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{ {
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
if(target == MWWorld::Ptr()) if(target == MWWorld::Ptr())
return true; //Target doesn't exist return true; //Target doesn't exist
//Set the target desition from the actor //Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
return true; return true;
} }
else { else {
pathTo(actor, dest, duration); //Go to the destination pathTo(actor, dest, duration); //Go to the destination
} }
return false; return false;
} }
@ -46,3 +48,20 @@ int MWMechanics::AiActivate::getTypeId() const
{ {
return TypeIdActivate; return TypeIdActivate;
} }
void MWMechanics::AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());
activate->mTargetId = mObjectId;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Activate;
package.mPackage = activate.release();
sequence.mPackages.push_back(package);
}
MWMechanics::AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate)
: mObjectId(activate->mTargetId)
{
}

@ -6,20 +6,33 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiActivate;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes actor to walk to activatable object and activate it /// \brief Causes actor to walk to activatable object and activate it
/** Will activate when close to object **/ /** Will activate when close to object **/
class AiActivate : public AiPackage class AiActivate : public AiPackage
{ {
public: public:
/// Constructor /// Constructor
/** \param objectId Reference to object to activate **/ /** \param objectId Reference to object to activate **/
AiActivate(const std::string &objectId); AiActivate(const std::string &objectId);
AiActivate(const ESM::AiSequence::AiActivate* activate);
virtual AiActivate *clone() const; virtual AiActivate *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const; virtual int getTypeId() const;
virtual void writeState(ESM::AiSequence::AiSequence& sequence) const;
private: private:
std::string mObjectId; std::string mObjectId;
int mCellX; int mCellX;

@ -78,8 +78,14 @@ MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const
return new AiAvoidDoor(*this); return new AiAvoidDoor(*this);
} }
int MWMechanics::AiAvoidDoor::getTypeId() const int MWMechanics::AiAvoidDoor::getTypeId() const
{ {
return TypeIdAvoidDoor; return TypeIdAvoidDoor;
} }
unsigned int MWMechanics::AiAvoidDoor::getPriority() const
{
return 2;
}

@ -24,6 +24,8 @@ namespace MWMechanics
virtual int getTypeId() const; virtual int getTypeId() const;
virtual unsigned int getPriority() const;
private: private:
float mDuration; float mDuration;
MWWorld::Ptr mDoorPtr; MWWorld::Ptr mDoorPtr;

@ -3,6 +3,7 @@
#include <OgreMath.h> #include <OgreMath.h>
#include <OgreVector3.h> #include <OgreVector3.h>
#include <components/esm/aisequence.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp" #include "../mwworld/timestamp.hpp"
@ -81,23 +82,22 @@ namespace MWMechanics
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
AiCombat::AiCombat(const MWWorld::Ptr& actor) : AiCombat::AiCombat(const MWWorld::Ptr& actor) :
mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
mTimerAttack(0),
mTimerReact(0),
mTimerCombatMove(0),
mFollowTarget(false),
mReadyToAttack(false),
mAttack(false),
mCombatMove(false),
mMovement(),
mForceNoShortcut(false),
mShortcutFailPos(),
mBackOffDoor(false),
mCell(NULL),
mDoorIter(actor.getCell()->get<ESM::Door>().mList.end()),
mDoors(actor.getCell()->get<ESM::Door>()),
mDoorCheckDuration(0)
{ {
init();
}
void AiCombat::init()
{
mTimerAttack = 0;
mTimerReact = 0;
mTimerCombatMove = 0;
mFollowTarget = false;
mReadyToAttack = false;
mAttack = false;
mCombatMove = false;
mForceNoShortcut = false;
mCell = NULL;
} }
/* /*
@ -511,70 +511,22 @@ namespace MWMechanics
// coded at 250ms or 1/4 second // coded at 250ms or 1/4 second
// //
// TODO: Add a parameter to vary DURATION_SAME_SPOT? // TODO: Add a parameter to vary DURATION_SAME_SPOT?
MWWorld::CellStore *cell = actor.getCell();
if((distToTarget > rangeAttack || mFollowTarget) && if((distToTarget > rangeAttack || mFollowTarget) &&
mObstacleCheck.check(actor, tReaction)) // check if evasive action needed mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
{ {
// first check if we're walking into a door // probably walking into another NPC TODO: untested in combat situation
mDoorCheckDuration += 1.0f; // add time taken for obstacle check // TODO: diagonal should have same animation as walk forward
if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior()) // but doesn't seem to do that?
{ actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
mDoorCheckDuration = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
// Check all the doors in this cell // change the angle a bit, too
mDoors = cell->get<ESM::Door>(); // update if(mPathFinder.isPathConstructed())
mDoorIter = mDoors.mList.begin(); zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
for (; mDoorIter != mDoors.mList.end(); ++mDoorIter)
{ if(mFollowTarget)
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter; mFollowTarget = false;
float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility // FIXME: can fool actors to stay behind doors, etc.
if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.getPosition().pos)) < minSqr && // Related to Bug#1102 and to some degree #1155 as well
ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening
{
//std::cout<<"closed door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
mBackOffDoor = true;
mObstacleCheck.clear();
if(mFollowTarget)
mFollowTarget = false;
break;
}
}
}
else // probably walking into another NPC TODO: untested in combat situation
{
// TODO: diagonal should have same animation as walk forward
// but doesn't seem to do that?
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
// change the angle a bit, too
if(mPathFinder.isPathConstructed())
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
if(mFollowTarget)
mFollowTarget = false;
// FIXME: can fool actors to stay behind doors, etc.
// Related to Bug#1102 and to some degree #1155 as well
}
}
if(!cell->getCell()->isExterior() && !mDoors.mList.empty())
{
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility
// TODO: add reaction to checking open doors
if(mBackOffDoor &&
vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.getPosition().pos)) < minSqr)
{
mMovement.mPosition[1] = -0.2; // back off, but slowly
}
else if(mBackOffDoor &&
mDoorIter != mDoors.mList.end() &&
ref.mData.getLocalRotation().rot[2] >= 1)
{
mDoorIter = mDoors.mList.end();
mBackOffDoor = false;
//std::cout<<"open door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
mMovement.mPosition[1] = 1;
}
} }
return false; return false;
@ -647,6 +599,23 @@ namespace MWMechanics
{ {
return new AiCombat(*this); return new AiCombat(*this);
} }
AiCombat::AiCombat(const ESM::AiSequence::AiCombat *combat)
{
mTargetActorId = combat->mTargetActorId;
init();
}
void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat());
combat->mTargetActorId = mTargetActorId;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Combat;
package.mPackage = combat.release();
sequence.mPackages.push_back(package);
}
} }

@ -12,6 +12,14 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiCombat;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes the actor to fight another actor /// \brief Causes the actor to fight another actor
@ -22,6 +30,10 @@ namespace MWMechanics
/** \param actor Actor to fight **/ /** \param actor Actor to fight **/
AiCombat(const MWWorld::Ptr& actor); AiCombat(const MWWorld::Ptr& actor);
AiCombat (const ESM::AiSequence::AiCombat* combat);
void init();
virtual AiCombat *clone() const; virtual AiCombat *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
@ -33,6 +45,8 @@ namespace MWMechanics
///Returns target ID ///Returns target ID
MWWorld::Ptr getTarget() const; MWWorld::Ptr getTarget() const;
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
private: private:
PathFinder mPathFinder; PathFinder mPathFinder;
// controls duration of the actual strike // controls duration of the actual strike
@ -46,7 +60,6 @@ namespace MWMechanics
bool mReadyToAttack, mAttack; bool mReadyToAttack, mAttack;
bool mFollowTarget; bool mFollowTarget;
bool mCombatMove; bool mCombatMove;
bool mBackOffDoor;
bool mForceNoShortcut; bool mForceNoShortcut;
ESM::Position mShortcutFailPos; ESM::Position mShortcutFailPos;
@ -57,11 +70,6 @@ namespace MWMechanics
const MWWorld::CellStore* mCell; const MWWorld::CellStore* mCell;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;
float mDoorCheckDuration;
// TODO: for some reason mDoors.searchViaHandle() returns
// null pointers, workaround by keeping an iterator
MWWorld::CellRefList<ESM::Door>::List::iterator mDoorIter;
MWWorld::CellRefList<ESM::Door>& mDoors;
void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
}; };

@ -1,12 +1,13 @@
#include "aiescort.hpp" #include "aiescort.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -20,7 +21,7 @@
namespace MWMechanics namespace MWMechanics
{ {
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) : mActorId(actorId), mX(x), mY(y), mZ(z), mRemainingDuration(duration)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
@ -29,32 +30,29 @@ namespace MWMechanics
// The CS Help File states that if a duration is given, the AI package will run for that long // The CS Help File states that if a duration is given, 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. // 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) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mRemainingDuration = 0;
else
{
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
} }
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) 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) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mRemainingDuration(duration)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
mMaxDist = 470; mMaxDist = 470;
// The CS Help File states that if a duration is given, the AI package will run for that long // The CS Help File states that if a duration is given, 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. // BUT if a location is given, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mRemainingDuration = 0;
}
else AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
{ : mActorId(escort->mTargetId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); , mCellX(std::numeric_limits<int>::max())
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); , mCellY(std::numeric_limits<int>::max())
} , mCellId(escort->mCellId)
, mRemainingDuration(escort->mRemainingDuration)
{
} }
@ -67,11 +65,10 @@ namespace MWMechanics
{ {
// If AiEscort has ran for as long or longer then the duration specified // If AiEscort has ran for as long or longer then the duration specified
// and the duration is not infinite, the package is complete. // and the duration is not infinite, the package is complete.
if(mDuration != 0) if(mRemainingDuration != 0)
{ {
MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); mRemainingDuration -= duration;
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); if (duration <= 0)
if(currentSecond - mStartingSecond >= mDuration)
return true; return true;
} }
@ -89,7 +86,7 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist) if(distanceBetweenResult <= mMaxDist * mMaxDist)
{ {
if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete
return true; return true;
mMaxDist = 470; mMaxDist = 470;
} }
@ -108,5 +105,21 @@ namespace MWMechanics
{ {
return TypeIdEscort; return TypeIdEscort;
} }
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());
escort->mData.mX = mX;
escort->mData.mY = mY;
escort->mData.mZ = mZ;
escort->mTargetId = mActorId;
escort->mRemainingDuration = mRemainingDuration;
escort->mCellId = mCellId;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Escort;
package.mPackage = escort.release();
sequence.mPackages.push_back(package);
}
} }

@ -6,6 +6,14 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiEscort;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief AI Package to have an NPC lead the player to a specific point /// \brief AI Package to have an NPC lead the player to a specific point
@ -21,12 +29,16 @@ namespace MWMechanics
\implement AiEscortCell **/ \implement AiEscortCell **/
AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z);
AiEscort(const ESM::AiSequence::AiEscort* escort);
virtual AiEscort *clone() const; virtual AiEscort *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const; virtual int getTypeId() const;
void writeState(ESM::AiSequence::AiSequence &sequence) const;
private: private:
std::string mActorId; std::string mActorId;
std::string mCellId; std::string mCellId;
@ -34,8 +46,7 @@ namespace MWMechanics
float mY; float mY;
float mZ; float mZ;
float mMaxDist; float mMaxDist;
unsigned int mStartingSecond; float mRemainingDuration; // In seconds
unsigned int mDuration;
PathFinder mPathFinder; PathFinder mPathFinder;
int mCellX; int mCellX;

@ -1,5 +1,9 @@
#include "aifollow.hpp" #include "aifollow.hpp"
#include <iostream> #include <iostream>
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -12,16 +16,16 @@
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage() : mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
{ {
} }
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage() : mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
{ {
} }
MWMechanics::AiFollow::AiFollow(const std::string &actorId) MWMechanics::AiFollow::AiFollow(const std::string &actorId)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() : mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
{ {
} }
@ -35,8 +39,13 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
if(!mAlwaysFollow) //Update if you only follow for a bit if(!mAlwaysFollow) //Update if you only follow for a bit
{ {
if(mTotalTime > mDuration && mDuration != 0) //Check if we've run out of time //Check if we've run out of time
return true; if (mRemainingDuration != 0)
{
mRemainingDuration -= duration;
if (duration <= 0)
return true;
}
if((pos.pos[0]-mX)*(pos.pos[0]-mX) + if((pos.pos[0]-mX)*(pos.pos[0]-mX) +
(pos.pos[1]-mY)*(pos.pos[1]-mY) + (pos.pos[1]-mY)*(pos.pos[1]-mY) +
@ -55,7 +64,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
} }
} }
//Set the target desition from the actor //Set the target destination from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close
@ -83,7 +92,32 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
return new AiFollow(*this); return new AiFollow(*this);
} }
int MWMechanics::AiFollow::getTypeId() const int MWMechanics::AiFollow::getTypeId() const
{ {
return TypeIdFollow; return TypeIdFollow;
} }
void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiFollow> follow(new ESM::AiSequence::AiFollow());
follow->mData.mX = mX;
follow->mData.mY = mY;
follow->mData.mZ = mZ;
follow->mTargetId = mActorId;
follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Follow;
package.mPackage = follow.release();
sequence.mPackages.push_back(package);
}
MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mActorId(follow->mTargetId), mCellId(follow->mCellId)
{
}

@ -6,13 +6,21 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
namespace ESM
{
namespace AiSequence
{
struct AiFollow;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief AiPackage for an actor to follow another actor/the PC /// \brief AiPackage for an actor to follow another actor/the PC
/** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely
**/ **/
class AiFollow : public AiPackage class AiFollow : public AiPackage
{ {
public: public:
/// Follow Actor for duration or until you arrive at a world position /// Follow Actor for duration or until you arrive at a world position
AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z);
@ -21,6 +29,8 @@ namespace MWMechanics
/// Follow Actor indefinitively /// Follow Actor indefinitively
AiFollow(const std::string &ActorId); AiFollow(const std::string &ActorId);
AiFollow(const ESM::AiSequence::AiFollow* follow);
virtual AiFollow *clone() const; virtual AiFollow *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
@ -30,16 +40,18 @@ namespace MWMechanics
/// Returns the actor being followed /// Returns the actor being followed
std::string getFollowedActor(); std::string getFollowedActor();
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
private: private:
/// This will make the actor always follow. /// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow; bool mAlwaysFollow;
float mDuration; float mRemainingDuration; // Seconds
float mX; float mX;
float mY; float mY;
float mZ; float mZ;
std::string mActorId; std::string mActorId;
std::string mCellId; std::string mCellId;
}; };
} }
#endif #endif

@ -26,8 +26,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
//Update various Timers //Update various Timers
mTimer += duration; //Update timer mTimer += duration; //Update timer
mStuckTimer += duration; //Update stuck timer mStuckTimer += duration; //Update stuck timer
mTotalTime += duration; //Update total time following
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor ESM::Position pos = actor.getRefData().getPosition(); //position of the actor

@ -12,6 +12,14 @@ namespace MWWorld
class Ptr; class Ptr;
} }
namespace ESM
{
namespace AiSequence
{
class AiSequence;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Base class for AI packages /// \brief Base class for AI packages
@ -51,6 +59,8 @@ namespace MWMechanics
/// Higher number is higher priority (0 being the lowest) /// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;} virtual unsigned int getPriority() const {return 0;}
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
protected: protected:
/// Causes the actor to attempt to walk to the specified location /// Causes the actor to attempt to walk to the specified location
/** \return If the actor has arrived at his destination **/ /** \return If the actor has arrived at his destination **/
@ -60,12 +70,11 @@ namespace MWMechanics
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;
float mDoorCheckDuration; float mDoorCheckDuration;
float mTimer; float mTimer;
float mStuckTimer; float mStuckTimer;
float mTotalTime;
MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door
MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door
ESM::Position mStuckPos; ESM::Position mStuckPos;
ESM::Pathgrid::Point mPrevDest; ESM::Pathgrid::Point mPrevDest;
}; };

@ -1,5 +1,7 @@
#include "aipursue.hpp" #include "aipursue.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -18,6 +20,12 @@ AiPursue::AiPursue(const MWWorld::Ptr& actor)
: mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
{ {
} }
AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue)
: mTargetActorId(pursue->mTargetActorId)
{
}
AiPursue *MWMechanics::AiPursue::clone() const AiPursue *MWMechanics::AiPursue::clone() const
{ {
return new AiPursue(*this); return new AiPursue(*this);
@ -57,4 +65,15 @@ MWWorld::Ptr AiPursue::getTarget() const
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
} }
void AiPursue::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiPursue> pursue(new ESM::AiSequence::AiPursue());
pursue->mTargetActorId = mTargetActorId;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Pursue;
package.mPackage = pursue.release();
sequence.mPackages.push_back(package);
}
} // namespace MWMechanics } // namespace MWMechanics

@ -7,6 +7,14 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiPursue;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Makes the actor very closely follow the actor /// \brief Makes the actor very closely follow the actor
@ -20,12 +28,16 @@ namespace MWMechanics
/** \param actor Actor to pursue **/ /** \param actor Actor to pursue **/
AiPursue(const MWWorld::Ptr& actor); AiPursue(const MWWorld::Ptr& actor);
AiPursue(const ESM::AiSequence::AiPursue* pursue);
virtual AiPursue *clone() const; virtual AiPursue *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const; virtual int getTypeId() const;
MWWorld::Ptr getTarget() const; MWWorld::Ptr getTarget() const;
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
private: private:
int mTargetActorId; // The actor to pursue int mTargetActorId; // The actor to pursue

@ -11,6 +11,8 @@
#include "aicombat.hpp" #include "aicombat.hpp"
#include "aipursue.hpp" #include "aipursue.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
@ -258,7 +260,8 @@ void AiSequence::fill(const ESM::AIPackageList &list)
if (it->mType == ESM::AI_Wander) if (it->mType == ESM::AI_Wander)
{ {
ESM::AIWander data = it->mWander; ESM::AIWander data = it->mWander;
std::vector<int> idles; std::vector<unsigned char> idles;
idles.reserve(8);
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
idles.push_back(data.mIdle[i]); idles.push_back(data.mIdle[i]);
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat); package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat);
@ -287,4 +290,76 @@ void AiSequence::fill(const ESM::AIPackageList &list)
} }
} }
void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const
{
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
{
(*iter)->writeState(sequence);
}
}
void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
{
clear();
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
it != sequence.mPackages.end(); ++it)
{
switch (it->mType)
{
case ESM::AiSequence::Ai_Wander:
{
MWMechanics::AiWander* wander = new AiWander(
dynamic_cast<ESM::AiSequence::AiWander*>(it->mPackage));
mPackages.push_back(wander);
break;
}
case ESM::AiSequence::Ai_Travel:
{
MWMechanics::AiTravel* travel = new AiTravel(
dynamic_cast<ESM::AiSequence::AiTravel*>(it->mPackage));
mPackages.push_back(travel);
break;
}
case ESM::AiSequence::Ai_Escort:
{
MWMechanics::AiEscort* escort = new AiEscort(
dynamic_cast<ESM::AiSequence::AiEscort*>(it->mPackage));
mPackages.push_back(escort);
break;
}
case ESM::AiSequence::Ai_Follow:
{
MWMechanics::AiFollow* follow = new AiFollow(
dynamic_cast<ESM::AiSequence::AiFollow*>(it->mPackage));
mPackages.push_back(follow);
break;
}
case ESM::AiSequence::Ai_Activate:
{
MWMechanics::AiActivate* activate = new AiActivate(
dynamic_cast<ESM::AiSequence::AiActivate*>(it->mPackage));
mPackages.push_back(activate);
break;
}
case ESM::AiSequence::Ai_Combat:
{
MWMechanics::AiCombat* combat = new AiCombat(
dynamic_cast<ESM::AiSequence::AiCombat*>(it->mPackage));
mPackages.push_back(combat);
break;
}
case ESM::AiSequence::Ai_Pursue:
{
MWMechanics::AiPursue* pursue = new AiPursue(
dynamic_cast<ESM::AiSequence::AiPursue*>(it->mPackage));
mPackages.push_back(pursue);
break;
}
default:
break;
}
}
}
} // namespace MWMechanics } // namespace MWMechanics

@ -10,6 +10,14 @@ namespace MWWorld
class Ptr; class Ptr;
} }
namespace ESM
{
namespace AiSequence
{
class AiSequence;
}
}
namespace MWMechanics namespace MWMechanics
{ {
class AiPackage; class AiPackage;
@ -90,6 +98,9 @@ namespace MWMechanics
/** Typically used for loading from the ESM /** Typically used for loading from the ESM
\see ESM::AIPackageList **/ \see ESM::AIPackageList **/
void fill (const ESM::AIPackageList& list); void fill (const ESM::AIPackageList& list);
void writeState (ESM::AiSequence::AiSequence& sequence) const;
void readState (const ESM::AiSequence::AiSequence& sequence);
}; };
} }

@ -1,5 +1,7 @@
#include "aitravel.hpp" #include "aitravel.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -19,6 +21,15 @@ namespace MWMechanics
{ {
} }
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ)
, mPathFinder()
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
}
AiTravel *MWMechanics::AiTravel::clone() const AiTravel *MWMechanics::AiTravel::clone() const
{ {
return new AiTravel(*this); return new AiTravel(*this);
@ -92,5 +103,18 @@ namespace MWMechanics
{ {
return TypeIdTravel; return TypeIdTravel;
} }
void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiTravel> travel(new ESM::AiSequence::AiTravel());
travel->mData.mX = mX;
travel->mData.mY = mY;
travel->mData.mZ = mZ;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Travel;
package.mPackage = travel.release();
sequence.mPackages.push_back(package);
}
} }

@ -5,14 +5,26 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiTravel;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes the AI to travel to the specified point /// \brief Causes the AI to travel to the specified point
class AiTravel : public AiPackage class AiTravel : public AiPackage
{ {
public: public:
/// Default constructor /// Default constructor
AiTravel(float x, float y, float z); AiTravel(float x, float y, float z);
AiTravel(const ESM::AiSequence::AiTravel* travel);
void writeState(ESM::AiSequence::AiSequence &sequence) const;
virtual AiTravel *clone() const; virtual AiTravel *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);

@ -3,6 +3,8 @@
#include <OgreVector3.h> #include <OgreVector3.h>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -21,31 +23,28 @@ namespace MWMechanics
static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed
static const float DOOR_CHECK_INTERVAL = 1.5f; static const float DOOR_CHECK_INTERVAL = 1.5f;
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
, mXCell(0)
, mYCell(0)
, mCell(NULL)
, mStuckCount(0) // TODO: maybe no longer needed
, mDoorCheckDuration(0)
, mTrimCurrentNode(false)
, mReaction(0)
, mGreetDistanceMultiplier(0)
, mGreetDistanceReset(0)
, mChance(0)
, mRotate(false)
, mTargetAngle(0)
, mSaidGreeting(false)
, mHasReturnPosition(false)
, mReturnPosition(0,0,0)
{ {
for(unsigned short counter = 0; counter < mIdle.size(); counter++) init();
{ }
if(mIdle[counter] >= 127 || mIdle[counter] < 0)
mIdle[counter] = 0; void AiWander::init()
} {
mCellX = std::numeric_limits<int>::max();
mCellY = std::numeric_limits<int>::max();
mXCell = 0;
mYCell = 0;
mCell = NULL;
mStuckCount = 0;// TODO: maybe no longer needed
mDoorCheckDuration = 0;
mTrimCurrentNode = false;
mReaction = 0;
mRotate = false;
mTargetAngle = 0;
mSaidGreeting = false;
mHasReturnPosition = false;
mReturnPosition = Ogre::Vector3(0,0,0);
if(mDistance < 0) if(mDistance < 0)
mDistance = 0; mDistance = 0;
@ -56,15 +55,6 @@ namespace MWMechanics
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mPlayedIdle = 0; mPlayedIdle = 0;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
mIdleChanceMultiplier =
store.get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
mGreetDistanceMultiplier =
store.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
mGreetDistanceReset =
store.get<ESM::GameSetting>().find("fGreetDistanceReset")->getFloat();
mChance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
mStoredAvailableNodes = false; mStoredAvailableNodes = false;
mChooseAction = true; mChooseAction = true;
@ -219,11 +209,12 @@ namespace MWMechanics
} }
mReaction += duration; mReaction += duration;
if(mReaction > 0.25f) // FIXME: hard coded constant if(mReaction < 0.25f) // FIXME: hard coded constant
{ {
mReaction = 0;
return false; return false;
} }
else
mReaction = 0;
// NOTE: everything below get updated every 0.25 seconds // NOTE: everything below get updated every 0.25 seconds
@ -394,7 +385,10 @@ namespace MWMechanics
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// Don't bother if the player is out of hearing range // Don't bother if the player is out of hearing range
if (roll < mChance && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500) static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500)
MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
} }
} }
@ -406,7 +400,10 @@ namespace MWMechanics
// Play a random voice greeting if the player gets too close // Play a random voice greeting if the player gets too close
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
float helloDistance = hello; float helloDistance = hello;
helloDistance *= mGreetDistanceMultiplier; static int iGreetDistanceMultiplier =MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
helloDistance *= iGreetDistanceMultiplier;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
Ogre::Vector3 playerPos(player.getRefData().getPosition().pos); Ogre::Vector3 playerPos(player.getRefData().getPosition().pos);
@ -455,7 +452,10 @@ namespace MWMechanics
} }
else else
{ {
if (playerDistSqr >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier) static float fGreetDistanceReset = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fGreetDistanceReset")->getFloat();
if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset * iGreetDistanceMultiplier*iGreetDistanceMultiplier)
mSaidGreeting = false; mSaidGreeting = false;
} }
@ -632,8 +632,11 @@ namespace MWMechanics
for(unsigned int counter = 0; counter < mIdle.size(); counter++) for(unsigned int counter = 0; counter < mIdle.size(); counter++)
{ {
unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); .get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
unsigned short idleChance = fIdleChanceMultiplier * mIdle[counter];
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / fIdleChanceMultiplier));
if(randSelect < idleChance && randSelect > idleRoll) if(randSelect < idleChance && randSelect > idleRoll)
{ {
mPlayedIdle = counter+2; mPlayedIdle = counter+2;
@ -641,5 +644,37 @@ namespace MWMechanics
} }
} }
} }
void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiWander> wander(new ESM::AiSequence::AiWander());
wander->mData.mDistance = mDistance;
wander->mData.mDuration = mDuration;
wander->mData.mTimeOfDay = mTimeOfDay;
wander->mStartTime = mStartTime.toEsm();
assert (mIdle.size() >= 8);
for (int i=0; i<8; ++i)
wander->mData.mIdle[i] = mIdle[i];
wander->mData.mShouldRepeat = mRepeat;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Wander;
package.mPackage = wander.release();
sequence.mPackages.push_back(package);
}
AiWander::AiWander (const ESM::AiSequence::AiWander* wander)
{
init();
mDistance = wander->mData.mDistance;
mDuration = wander->mData.mDuration;
mStartTime = MWWorld::TimeStamp(wander->mStartTime);
mTimeOfDay = wander->mData.mTimeOfDay;
for (int i=0; i<8; ++i)
mIdle.push_back(wander->mData.mIdle[i]);
mRepeat = wander->mData.mShouldRepeat;
}
} }

@ -12,6 +12,14 @@
#include "../mwworld/timestamp.hpp" #include "../mwworld/timestamp.hpp"
namespace ESM
{
namespace AiSequence
{
struct AiWander;
}
}
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes the Actor to wander within a specified range /// \brief Causes the Actor to wander within a specified range
@ -24,7 +32,11 @@ namespace MWMechanics
\param timeOfDay Start time of the package, if it has a duration. Currently unimplemented \param timeOfDay Start time of the package, if it has a duration. Currently unimplemented
\param idle Chances of each idle to play (9 in total) \param idle Chances of each idle to play (9 in total)
\param repeat Repeat wander or not **/ \param repeat Repeat wander or not **/
AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat); AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat);
AiWander (const ESM::AiSequence::AiWander* wander);
void init();
virtual AiPackage *clone() const; virtual AiPackage *clone() const;
@ -36,6 +48,8 @@ namespace MWMechanics
/** In case another AI package moved the actor elsewhere **/ /** In case another AI package moved the actor elsewhere **/
void setReturnPosition (const Ogre::Vector3& position); void setReturnPosition (const Ogre::Vector3& position);
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
private: private:
void stopWalking(const MWWorld::Ptr& actor); void stopWalking(const MWWorld::Ptr& actor);
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
@ -45,13 +59,10 @@ namespace MWMechanics
int mDistance; // how far the actor can wander from the spawn point int mDistance; // how far the actor can wander from the spawn point
int mDuration; int mDuration;
int mTimeOfDay; int mTimeOfDay;
std::vector<int> mIdle; std::vector<unsigned char> mIdle;
bool mRepeat; bool mRepeat;
bool mSaidGreeting; bool mSaidGreeting;
int mGreetDistanceMultiplier;
float mGreetDistanceReset;
float mChance;
bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position,
// if we had the actor in the AiWander constructor... // if we had the actor in the AiWander constructor...
@ -74,7 +85,6 @@ namespace MWMechanics
bool mMoveNow; bool mMoveNow;
bool mWalking; bool mWalking;
float mIdleChanceMultiplier;
unsigned short mPlayedIdle; unsigned short mPlayedIdle;
MWWorld::TimeStamp mStartTime; MWWorld::TimeStamp mStartTime;

@ -503,6 +503,7 @@ namespace MWMechanics
mSpells.writeState(state.mSpells); mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells); mActiveSpells.writeState(state.mActiveSpells);
mAiSequence.writeState(state.mAiSequence);
} }
void CreatureStats::readState (const ESM::CreatureStats& state) void CreatureStats::readState (const ESM::CreatureStats& state)
@ -543,6 +544,7 @@ namespace MWMechanics
mSpells.readState(state.mSpells); mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells); mActiveSpells.readState(state.mActiveSpells);
mAiSequence.readState(state.mAiSequence);
} }
void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)

@ -186,7 +186,7 @@ namespace MWScript
Interpreter::Type_Integer time = runtime[0].mFloat; Interpreter::Type_Integer time = runtime[0].mFloat;
runtime.pop(); runtime.pop();
std::vector<int> idleList; std::vector<unsigned char> idleList;
bool repeat = false; bool repeat = false;
for(int i=1; i < 10 && arg0; ++i) for(int i=1; i < 10 && arg0; ++i)
@ -194,6 +194,7 @@ namespace MWScript
if(!repeat) if(!repeat)
repeat = true; repeat = true;
Interpreter::Type_Integer idleValue = runtime[0].mInteger; Interpreter::Type_Integer idleValue = runtime[0].mInteger;
idleValue = std::min(255, std::max(0, idleValue));
idleList.push_back(idleValue); idleList.push_back(idleValue);
runtime.pop(); runtime.pop();
--arg0; --arg0;

@ -42,6 +42,7 @@ add_component_dir (esm
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate
aisequence
) )
add_component_dir (misc add_component_dir (misc

@ -0,0 +1,211 @@
#include "aisequence.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
#include <memory>
namespace ESM
{
namespace AiSequence
{
void AiWander::load(ESMReader &esm)
{
esm.getHNT (mData, "DATA");
esm.getHNT(mStartTime, "STAR");
}
void AiWander::save(ESMWriter &esm) const
{
esm.writeHNT ("DATA", mData);
esm.writeHNT ("STAR", mStartTime);
}
void AiTravel::load(ESMReader &esm)
{
esm.getHNT (mData, "DATA");
}
void AiTravel::save(ESMWriter &esm) const
{
esm.writeHNT ("DATA", mData);
}
void AiEscort::load(ESMReader &esm)
{
esm.getHNT (mData, "DATA");
mTargetId = esm.getHNString("TARG");
esm.getHNT (mRemainingDuration, "DURA");
mCellId = esm.getHNOString ("CELL");
}
void AiEscort::save(ESMWriter &esm) const
{
esm.writeHNT ("DATA", mData);
esm.writeHNString ("TARG", mTargetId);
esm.writeHNT ("DURA", mRemainingDuration);
if (!mCellId.empty())
esm.writeHNString ("CELL", mCellId);
}
void AiFollow::load(ESMReader &esm)
{
esm.getHNT (mData, "DATA");
mTargetId = esm.getHNString("TARG");
esm.getHNT (mRemainingDuration, "DURA");
mCellId = esm.getHNOString ("CELL");
esm.getHNT (mAlwaysFollow, "ALWY");
}
void AiFollow::save(ESMWriter &esm) const
{
esm.writeHNT ("DATA", mData);
esm.writeHNString("TARG", mTargetId);
esm.writeHNT ("DURA", mRemainingDuration);
if (!mCellId.empty())
esm.writeHNString ("CELL", mCellId);
esm.writeHNT ("ALWY", mAlwaysFollow);
}
void AiActivate::load(ESMReader &esm)
{
mTargetId = esm.getHNString("TARG");
}
void AiActivate::save(ESMWriter &esm) const
{
esm.writeHNString("TARG", mTargetId);
}
void AiCombat::load(ESMReader &esm)
{
esm.getHNT (mTargetActorId, "TARG");
}
void AiCombat::save(ESMWriter &esm) const
{
esm.writeHNT ("TARG", mTargetActorId);
}
void AiPursue::load(ESMReader &esm)
{
esm.getHNT (mTargetActorId, "TARG");
}
void AiPursue::save(ESMWriter &esm) const
{
esm.writeHNT ("TARG", mTargetActorId);
}
AiSequence::~AiSequence()
{
for (std::vector<AiPackageContainer>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
delete it->mPackage;
}
void AiSequence::save(ESMWriter &esm) const
{
for (std::vector<AiPackageContainer>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
{
esm.writeHNT ("AIPK", it->mType);
switch (it->mType)
{
case Ai_Wander:
static_cast<const AiWander*>(it->mPackage)->save(esm);
break;
case Ai_Travel:
static_cast<const AiTravel*>(it->mPackage)->save(esm);
break;
case Ai_Escort:
static_cast<const AiEscort*>(it->mPackage)->save(esm);
break;
case Ai_Follow:
static_cast<const AiFollow*>(it->mPackage)->save(esm);
break;
case Ai_Activate:
static_cast<const AiActivate*>(it->mPackage)->save(esm);
break;
case Ai_Combat:
static_cast<const AiCombat*>(it->mPackage)->save(esm);
break;
case Ai_Pursue:
static_cast<const AiPursue*>(it->mPackage)->save(esm);
break;
default:
break;
}
}
}
void AiSequence::load(ESMReader &esm)
{
while (esm.isNextSub("AIPK"))
{
int type;
esm.getHT(type);
mPackages.push_back(AiPackageContainer());
mPackages.back().mType = type;
switch (type)
{
case Ai_Wander:
{
std::auto_ptr<AiWander> ptr (new AiWander());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Travel:
{
std::auto_ptr<AiTravel> ptr (new AiTravel());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Escort:
{
std::auto_ptr<AiEscort> ptr (new AiEscort());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Follow:
{
std::auto_ptr<AiFollow> ptr (new AiFollow());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Activate:
{
std::auto_ptr<AiActivate> ptr (new AiActivate());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Combat:
{
std::auto_ptr<AiCombat> ptr (new AiCombat());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
case Ai_Pursue:
{
std::auto_ptr<AiPursue> ptr (new AiPursue());
ptr->load(esm);
mPackages.back().mPackage = ptr.release();
break;
}
default:
return;
}
}
}
}
}

@ -0,0 +1,154 @@
#ifndef OPENMW_COMPONENTS_ESM_AISEQUENCE_H
#define OPENMW_COMPONENTS_ESM_AISEQUENCE_H
#include <vector>
#include <string>
#include "defs.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
namespace AiSequence
{
// format 0, saved games only
// As opposed to AiPackageList, this stores the "live" version of AI packages.
enum AiPackages
{
Ai_Wander = ESM::FourCC<'W','A','N','D'>::value,
Ai_Travel = ESM::FourCC<'T','R','A','V'>::value,
Ai_Escort = ESM::FourCC<'E','S','C','O'>::value,
Ai_Follow = ESM::FourCC<'F','O','L','L'>::value,
Ai_Activate = ESM::FourCC<'A','C','T','I'>::value,
Ai_Combat = ESM::FourCC<'C','O','M','B'>::value,
Ai_Pursue = ESM::FourCC<'P','U','R','S'>::value
};
struct AiPackage
{
virtual ~AiPackage() {}
};
#pragma pack(push,1)
struct AiWanderData
{
short mDistance;
short mDuration;
unsigned char mTimeOfDay;
unsigned char mIdle[8];
unsigned char mShouldRepeat;
};
struct AiTravelData
{
float mX, mY, mZ;
};
struct AiEscortData
{
float mX, mY, mZ;
short mDuration;
};
#pragma pack(pop)
struct AiWander : AiPackage
{
AiWanderData mData;
ESM::TimeStamp mStartTime;
/// \todo add more AiWander state
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiTravel : AiPackage
{
AiTravelData mData;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiEscort : AiPackage
{
AiEscortData mData;
std::string mTargetId;
std::string mCellId;
float mRemainingDuration;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiFollow : AiPackage
{
AiEscortData mData;
std::string mTargetId;
std::string mCellId;
float mRemainingDuration;
bool mAlwaysFollow;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiActivate : AiPackage
{
std::string mTargetId;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiCombat : AiPackage
{
int mTargetActorId;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiPursue : AiPackage
{
int mTargetActorId;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
};
struct AiPackageContainer
{
int mType;
AiPackage* mPackage;
};
struct AiSequence
{
AiSequence() {}
~AiSequence();
std::vector<AiPackageContainer> mPackages;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
private:
AiSequence(const AiSequence&);
AiSequence& operator=(const AiSequence&);
};
}
}
#endif

@ -79,6 +79,7 @@ void ESM::CreatureStats::load (ESMReader &esm)
mSpells.load(esm); mSpells.load(esm);
mActiveSpells.load(esm); mActiveSpells.load(esm);
mAiSequence.load(esm);
} }
void ESM::CreatureStats::save (ESMWriter &esm) const void ESM::CreatureStats::save (ESMWriter &esm) const
@ -160,4 +161,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
mSpells.save(esm); mSpells.save(esm);
mActiveSpells.save(esm); mActiveSpells.save(esm);
mAiSequence.save(esm);
} }

@ -11,6 +11,7 @@
#include "spellstate.hpp" #include "spellstate.hpp"
#include "activespells.hpp" #include "activespells.hpp"
#include "aisequence.hpp"
namespace ESM namespace ESM
{ {
@ -23,6 +24,8 @@ namespace ESM
StatState<int> mAttributes[8]; StatState<int> mAttributes[8];
StatState<float> mDynamic[3]; StatState<float> mDynamic[3];
AiSequence::AiSequence mAiSequence;
ESM::TimeStamp mTradeTime; ESM::TimeStamp mTradeTime;
int mGoldPool; int mGoldPool;
int mActorId; int mActorId;

Loading…
Cancel
Save