Savegame: Store AiSequence

deque
scrawl 11 years ago
parent be6f1fe4fe
commit a54ac579a5

@ -1,5 +1,7 @@
#include "aiactivate.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
@ -19,26 +21,26 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
return new AiActivate(*this);
}
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{
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
if(target == MWWorld::Ptr())
return true; //Target doesn't exist
//Set the target desition from the actor
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
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else {
pathTo(actor, dest, duration); //Go to the destination
}
{
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
if(target == MWWorld::Ptr())
return true; //Target doesn't exist
//Set the target desition from the actor
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
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else {
pathTo(actor, dest, duration); //Go to the destination
}
return false;
}
@ -46,3 +48,20 @@ int MWMechanics::AiActivate::getTypeId() const
{
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"
namespace ESM
{
namespace AiSequence
{
struct AiActivate;
}
}
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 **/
class AiActivate : public AiPackage
{
public:
/// Constructor
public:
/// Constructor
/** \param objectId Reference to object to activate **/
AiActivate(const std::string &objectId);
AiActivate(const ESM::AiSequence::AiActivate* activate);
virtual AiActivate *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const;
virtual void writeState(ESM::AiSequence::AiSequence& sequence) const;
private:
std::string mObjectId;
int mCellX;

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

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

@ -3,6 +3,7 @@
#include <OgreMath.h>
#include <OgreVector3.h>
#include <components/esm/aisequence.hpp>
#include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp"
@ -81,23 +82,22 @@ namespace MWMechanics
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
AiCombat::AiCombat(const MWWorld::Ptr& actor) :
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)
mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
{
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
//
// TODO: Add a parameter to vary DURATION_SAME_SPOT?
MWWorld::CellStore *cell = actor.getCell();
if((distToTarget > rangeAttack || mFollowTarget) &&
mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
{
// first check if we're walking into a door
mDoorCheckDuration += 1.0f; // add time taken for obstacle check
if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior())
{
mDoorCheckDuration = 0;
// Check all the doors in this cell
mDoors = cell->get<ESM::Door>(); // update
mDoorIter = mDoors.mList.begin();
for (; mDoorIter != mDoors.mList.end(); ++mDoorIter)
{
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility
if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.getPosition().pos)) < minSqr &&
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;
}
// 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
}
return false;
@ -647,6 +599,23 @@ namespace MWMechanics
{
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"
namespace ESM
{
namespace AiSequence
{
struct AiCombat;
}
}
namespace MWMechanics
{
/// \brief Causes the actor to fight another actor
@ -22,6 +30,10 @@ namespace MWMechanics
/** \param actor Actor to fight **/
AiCombat(const MWWorld::Ptr& actor);
AiCombat (const ESM::AiSequence::AiCombat* combat);
void init();
virtual AiCombat *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
@ -33,6 +45,8 @@ namespace MWMechanics
///Returns target ID
MWWorld::Ptr getTarget() const;
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
private:
PathFinder mPathFinder;
// controls duration of the actual strike
@ -46,7 +60,6 @@ namespace MWMechanics
bool mReadyToAttack, mAttack;
bool mFollowTarget;
bool mCombatMove;
bool mBackOffDoor;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
@ -57,11 +70,6 @@ namespace MWMechanics
const MWWorld::CellStore* mCell;
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);
};

@ -1,12 +1,13 @@
#include "aiescort.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp"
#include "steering.hpp"
#include "movement.hpp"
@ -20,7 +21,7 @@
namespace 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)
: mActorId(actorId), mX(x), mY(y), mZ(z), mRemainingDuration(duration)
, mCellX(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
// 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);
}
mRemainingDuration = 0;
}
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())
, mCellY(std::numeric_limits<int>::max())
{
mMaxDist = 470;
// 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)
mDuration = 0;
mRemainingDuration = 0;
}
else
{
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
: mActorId(escort->mTargetId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
, mCellX(std::numeric_limits<int>::max())
, 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
// and the duration is not infinite, the package is complete.
if(mDuration != 0)
if(mRemainingDuration != 0)
{
MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp();
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100);
if(currentSecond - mStartingSecond >= mDuration)
mRemainingDuration -= duration;
if (duration <= 0)
return true;
}
@ -89,7 +86,7 @@ namespace MWMechanics
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;
mMaxDist = 470;
}
@ -108,5 +105,21 @@ namespace MWMechanics
{
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"
namespace ESM
{
namespace AiSequence
{
struct AiEscort;
}
}
namespace MWMechanics
{
/// \brief AI Package to have an NPC lead the player to a specific point
@ -21,12 +29,16 @@ namespace MWMechanics
\implement AiEscortCell **/
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 bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const;
void writeState(ESM::AiSequence::AiSequence &sequence) const;
private:
std::string mActorId;
std::string mCellId;
@ -34,8 +46,7 @@ namespace MWMechanics
float mY;
float mZ;
float mMaxDist;
unsigned int mStartingSecond;
unsigned int mDuration;
float mRemainingDuration; // In seconds
PathFinder mPathFinder;
int mCellX;

@ -1,5 +1,9 @@
#include "aifollow.hpp"
#include <iostream>
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
@ -12,16 +16,16 @@
#include "steering.hpp"
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)
: 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)
: 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(mTotalTime > mDuration && mDuration != 0) //Check if we've run out of time
return true;
//Check if we've run out of time
if (mRemainingDuration != 0)
{
mRemainingDuration -= duration;
if (duration <= 0)
return true;
}
if((pos.pos[0]-mX)*(pos.pos[0]-mX) +
(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;
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);
}
int MWMechanics::AiFollow::getTypeId() const
int MWMechanics::AiFollow::getTypeId() const
{
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 <components/esm/defs.hpp>
namespace ESM
{
namespace AiSequence
{
struct AiFollow;
}
}
namespace MWMechanics
{
/// \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
**/
class AiFollow : public AiPackage
{
**/
class AiFollow : public AiPackage
{
public:
/// 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);
@ -21,6 +29,8 @@ namespace MWMechanics
/// Follow Actor indefinitively
AiFollow(const std::string &ActorId);
AiFollow(const ESM::AiSequence::AiFollow* follow);
virtual AiFollow *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
@ -30,16 +40,18 @@ namespace MWMechanics
/// Returns the actor being followed
std::string getFollowedActor();
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
private:
/// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow;
float mDuration;
float mRemainingDuration; // Seconds
float mX;
float mY;
float mZ;
std::string mActorId;
std::string mCellId;
};
}
#endif
std::string mCellId;
};
}
#endif

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

@ -12,6 +12,14 @@ namespace MWWorld
class Ptr;
}
namespace ESM
{
namespace AiSequence
{
class AiSequence;
}
}
namespace MWMechanics
{
/// \brief Base class for AI packages
@ -51,6 +59,8 @@ namespace MWMechanics
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;}
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
protected:
/// Causes the actor to attempt to walk to the specified location
/** \return If the actor has arrived at his destination **/
@ -60,12 +70,11 @@ namespace MWMechanics
ObstacleCheck mObstacleCheck;
float mDoorCheckDuration;
float mTimer;
float mStuckTimer;
float mTotalTime;
float mTimer;
float mStuckTimer;
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::Pathgrid::Point mPrevDest;
};

@ -1,5 +1,7 @@
#include "aipursue.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
@ -18,6 +20,12 @@ AiPursue::AiPursue(const MWWorld::Ptr& actor)
: mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
{
}
AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue)
: mTargetActorId(pursue->mTargetActorId)
{
}
AiPursue *MWMechanics::AiPursue::clone() const
{
return new AiPursue(*this);
@ -57,4 +65,15 @@ MWWorld::Ptr AiPursue::getTarget() const
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

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

@ -11,6 +11,8 @@
#include "aicombat.hpp"
#include "aipursue.hpp"
#include <components/esm/aisequence.hpp>
#include "../mwworld/class.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
@ -258,7 +260,8 @@ void AiSequence::fill(const ESM::AIPackageList &list)
if (it->mType == ESM::AI_Wander)
{
ESM::AIWander data = it->mWander;
std::vector<int> idles;
std::vector<unsigned char> idles;
idles.reserve(8);
for (int i=0; i<8; ++i)
idles.push_back(data.mIdle[i]);
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

@ -10,6 +10,14 @@ namespace MWWorld
class Ptr;
}
namespace ESM
{
namespace AiSequence
{
class AiSequence;
}
}
namespace MWMechanics
{
class AiPackage;
@ -90,6 +98,9 @@ namespace MWMechanics
/** Typically used for loading from the ESM
\see ESM::AIPackageList **/
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 <components/esm/aisequence.hpp>
#include "../mwbase/world.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
{
return new AiTravel(*this);
@ -92,5 +103,18 @@ namespace MWMechanics
{
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"
namespace ESM
{
namespace AiSequence
{
struct AiTravel;
}
}
namespace MWMechanics
{
{
/// \brief Causes the AI to travel to the specified point
class AiTravel : public AiPackage
{
public:
public:
/// Default constructor
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 bool execute (const MWWorld::Ptr& actor,float duration);

@ -3,6 +3,8 @@
#include <OgreVector3.h>
#include <OgreSceneNode.h>
#include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.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 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)
, 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++)
{
if(mIdle[counter] >= 127 || mIdle[counter] < 0)
mIdle[counter] = 0;
}
init();
}
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)
mDistance = 0;
@ -56,15 +55,6 @@ namespace MWMechanics
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
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;
mChooseAction = true;
@ -219,11 +209,12 @@ namespace MWMechanics
}
mReaction += duration;
if(mReaction > 0.25f) // FIXME: hard coded constant
if(mReaction < 0.25f) // FIXME: hard coded constant
{
mReaction = 0;
return false;
}
else
mReaction = 0;
// NOTE: everything below get updated every 0.25 seconds
@ -394,7 +385,10 @@ namespace MWMechanics
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// 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");
}
}
@ -406,7 +400,10 @@ namespace MWMechanics
// Play a random voice greeting if the player gets too close
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
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();
Ogre::Vector3 playerPos(player.getRefData().getPosition().pos);
@ -455,7 +452,10 @@ namespace MWMechanics
}
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;
}
@ -632,8 +632,11 @@ namespace MWMechanics
for(unsigned int counter = 0; counter < mIdle.size(); counter++)
{
unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter];
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.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)
{
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"
namespace ESM
{
namespace AiSequence
{
struct AiWander;
}
}
namespace MWMechanics
{
/// \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 idle Chances of each idle to play (9 in total)
\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;
@ -36,6 +48,8 @@ namespace MWMechanics
/** In case another AI package moved the actor elsewhere **/
void setReturnPosition (const Ogre::Vector3& position);
virtual void writeState(ESM::AiSequence::AiSequence &sequence) const;
private:
void stopWalking(const MWWorld::Ptr& actor);
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 mDuration;
int mTimeOfDay;
std::vector<int> mIdle;
std::vector<unsigned char> mIdle;
bool mRepeat;
bool mSaidGreeting;
int mGreetDistanceMultiplier;
float mGreetDistanceReset;
float mChance;
bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position,
// if we had the actor in the AiWander constructor...
@ -74,7 +85,6 @@ namespace MWMechanics
bool mMoveNow;
bool mWalking;
float mIdleChanceMultiplier;
unsigned short mPlayedIdle;
MWWorld::TimeStamp mStartTime;

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

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

@ -42,6 +42,7 @@ add_component_dir (esm
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
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate
aisequence
)
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);
mActiveSpells.load(esm);
mAiSequence.load(esm);
}
void ESM::CreatureStats::save (ESMWriter &esm) const
@ -160,4 +161,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
mSpells.save(esm);
mActiveSpells.save(esm);
mAiSequence.save(esm);
}

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

Loading…
Cancel
Save