mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 09:15:38 +00:00
Savegame: Store AiSequence
This commit is contained in:
parent
be6f1fe4fe
commit
a54ac579a5
27 changed files with 868 additions and 202 deletions
|
@ -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])));
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
|
|
211
components/esm/aisequence.cpp
Normal file
211
components/esm/aisequence.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
154
components/esm/aisequence.hpp
Normal file
154
components/esm/aisequence.hpp
Normal file
|
@ -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…
Reference in a new issue