deque
scrawl 11 years ago
commit 9b67fcc4d7

@ -20,7 +20,7 @@
#include "model/world/data.hpp"
CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager),
: mUserSettings (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS")
{
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -90,6 +90,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mCfgMgr.readConfiguration(variables, desc);
mDocumentManager.setEncoding (
ToUTF8::calculateEncoding (variables["encoding"].as<std::string>()));
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>());
mFsStrict = variables["fs-strict"].as<bool>();

@ -2204,11 +2204,13 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mTools (mData), mResDir(resDir),
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding), mTools (mData),
mResDir(resDir),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
mSaving (*this, mProjectPath, encoding)
{
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");

@ -9,6 +9,8 @@
#include <QObject>
#include <QTimer>
#include <components/to_utf8/to_utf8.hpp>
#include "../world/data.hpp"
#include "../tools/tools.hpp"
@ -70,7 +72,8 @@ namespace CSMDoc
Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir);
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
ToUTF8::FromType encoding);
~Document();

@ -13,7 +13,7 @@
#include "document.hpp"
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration)
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252)
{
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
@ -52,7 +52,7 @@ CSMDoc::DocumentManager::~DocumentManager()
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir);
Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding);
mDocuments.push_back (document);
@ -80,6 +80,11 @@ void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& par
mResDir = boost::filesystem::system_complete(parResDir);
}
void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
{
mEncoding = encoding;
}
void CSMDoc::DocumentManager::documentLoaded (Document *document)
{
emit documentAdded (document);

@ -9,6 +9,8 @@
#include <QObject>
#include <QThread>
#include <components/to_utf8/to_utf8.hpp>
#include "loader.hpp"
namespace Files
@ -28,6 +30,7 @@ namespace CSMDoc
const Files::ConfigurationManager& mConfiguration;
QThread mLoaderThread;
Loader mLoader;
ToUTF8::FromType mEncoding;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
@ -45,6 +48,8 @@ namespace CSMDoc
void setResourceDir (const boost::filesystem::path& parResDir);
void setEncoding (ToUTF8::FromType encoding);
private:
boost::filesystem::path mResDir;

@ -8,8 +8,9 @@
#include "savingstages.hpp"
#include "document.hpp"
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath)
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath)
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath,
ToUTF8::FromType encoding)
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath, encoding)
{
// save project file
appendStage (new OpenSaveStage (mDocument, mState, true));

@ -3,6 +3,8 @@
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "operation.hpp"
#include "savingstate.hpp"
@ -19,7 +21,8 @@ namespace CSMDoc
public:
Saving (Document& document, const boost::filesystem::path& projectPath);
Saving (Document& document, const boost::filesystem::path& projectPath,
ToUTF8::FromType encoding);
};
}

@ -4,11 +4,9 @@
#include "operation.hpp"
#include "document.hpp"
CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath)
: mOperation (operation),
/// \todo set encoding properly, once config implementation has been fixed.
mEncoder (ToUTF8::calculateEncoding ("win1252")),
mProjectPath (projectPath), mProjectFile (false)
CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath,
ToUTF8::FromType encoding)
: mOperation (operation), mEncoder (encoding), mProjectPath (projectPath), mProjectFile (false)
{
mWriter.setEncoder (&mEncoder);
}

@ -7,6 +7,8 @@
#include <components/esm/esmwriter.hpp>
#include <components/to_utf8/to_utf8.hpp>
namespace CSMDoc
{
class Operation;
@ -25,7 +27,8 @@ namespace CSMDoc
public:
SavingState (Operation& operation, const boost::filesystem::path& projectPath);
SavingState (Operation& operation, const boost::filesystem::path& projectPath,
ToUTF8::FromType encoding);
bool hasError() const;

@ -28,7 +28,8 @@ namespace boost
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings()
CSMSettings::UserSettings::UserSettings (const Files::ConfigurationManager& configurationManager)
: mCfgMgr (configurationManager)
{
assert(!mUserSettingsInstance);
mUserSettingsInstance = this;

@ -30,7 +30,7 @@ namespace CSMSettings {
Q_OBJECT
static UserSettings *mUserSettingsInstance;
Files::ConfigurationManager mCfgMgr;
const Files::ConfigurationManager& mCfgMgr;
QSettings *mSettingDefinitions;
QList <Setting *> mSettings;
@ -40,11 +40,11 @@ namespace CSMSettings {
/// Singleton implementation
static UserSettings& instance();
UserSettings();
UserSettings (const Files::ConfigurationManager& configurationManager);
~UserSettings();
UserSettings (UserSettings const &); //not implemented
void operator= (UserSettings const &); //not implemented
UserSettings (UserSettings const &); //not implemented
UserSettings& operator= (UserSettings const &); //not implemented
/// Retrieves the settings file at all three levels (global, local and user).
void loadSettings (const QString &fileName);

@ -55,10 +55,8 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
return number;
}
CSMWorld::Data::Data()
/// \todo set encoding properly, once config implementation has been fixed.
: mEncoder (ToUTF8::calculateEncoding ("win1252")),
mRefs (mCells), mReader (0), mDialogue (0)
CSMWorld::Data::Data (ToUTF8::FromType encoding)
: mEncoder (encoding), mRefs (mCells), mReader (0), mDialogue (0)
{
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>);

@ -22,6 +22,8 @@
#include <components/esm/loadspel.hpp>
#include <components/esm/loaddial.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "../filter/filter.hpp"
#include "../doc/stage.hpp"
@ -91,7 +93,7 @@ namespace CSMWorld
public:
Data();
Data (ToUTF8::FromType encoding);
virtual ~Data();

@ -1,62 +0,0 @@
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <components/fileorderlist/datafileslist.hpp>
#include "opendialog.hpp"
OpenDialog::OpenDialog(QWidget * parent) : QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
mFileSelector = new DataFilesList(mCfgMgr, this);
layout->addWidget(mFileSelector);
/// \todo move config to Editor class and add command line options.
// We use the Configuration Manager to retrieve the configuration values
boost::program_options::variables_map variables;
boost::program_options::options_description desc;
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
Files::PathContainer mDataDirs, mDataLocal;
if (!variables["data"].empty()) {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
}
std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local));
}
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
// Set the charset for reading the esm/esp files
QString encoding = QString::fromUtf8 (variables["encoding"].as<std::string>().c_str());
Files::PathContainer dataDirs;
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
mFileSelector->setupDataFiles(dataDirs, encoding);
buttonBox = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel, Qt::Horizontal, this);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
layout->addWidget(buttonBox);
setLayout(layout);
setWindowTitle(tr("Open"));
}
void OpenDialog::getFileList(std::vector<boost::filesystem::path>& paths)
{
mFileSelector->selectedFiles(paths);
}

@ -1,17 +0,0 @@
#include <qdialog.h>
#include <components/files/configurationmanager.hpp>
class DataFilesList;
class QDialogButtonBox;
class OpenDialog : public QDialog {
Q_OBJECT
public:
OpenDialog(QWidget * parent = 0);
void getFileList(std::vector<boost::filesystem::path>& paths);
private:
DataFilesList * mFileSelector;
QDialogButtonBox * buttonBox;
Files::ConfigurationManager mCfgMgr;
};

@ -111,13 +111,17 @@ void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::Univers
{
std::vector<std::pair<std::string, std::vector<std::string> > > filterSource;
for (std::vector<CSMWorld::UniversalId>::iterator it = types.begin(); it != types.end(); ++it)
for (std::vector<CSMWorld::UniversalId>::iterator it(types.begin()); it != types.end(); ++it)
{
std::pair<std::string, std::vector<std::string> > pair( //splited long line
std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType()))));
std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType()))));
filterSource.push_back(pair);
if(!pair.second.empty())
{
filterSource.push_back(pair);
}
}
mFilterBox->createFilterRequest(filterSource, action);
}

@ -67,7 +67,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle
)

@ -393,10 +393,14 @@ namespace MWBase
virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0;
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
///< if activated, should this door be opened or closed?
/// activate (open or close) an non-teleport door
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
///< activate (open or close) an non-teleport door
/// Is door currently opening/closing?
virtual bool getIsMovingDoor(const MWWorld::Ptr& door) = 0;
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object

@ -19,83 +19,26 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
return new AiActivate(*this);
}
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos = actor.getRefData().getPosition();
Movement &movement = actor.getClass().getMovementSettings(actor);
const ESM::Cell *cell = actor.getCell()->getCell();
MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
MWWorld::Ptr target = world->searchPtr(mObjectId,false);
if(target == MWWorld::Ptr()) return true;
ESM::Position targetPos = target.getRefData().getPosition();
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
if(!mPathFinder.isPathConstructed() || cellChange)
{
mCellX = cell->mData.mX;
mCellY = cell->mData.mY;
ESM::Pathgrid::Point dest;
dest.mX = targetPos.pos[0];
dest.mY = targetPos.pos[1];
dest.mZ = targetPos.pos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200)
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
return true;
}
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
return true;
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
movement.mPosition[1] = 1;
{
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);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else {
pathTo(actor, dest, duration); //Go to the destination
}
return false;
}

@ -7,21 +7,21 @@
#include "pathfinding.hpp"
namespace MWMechanics
{
{
/// \brief Causes actor to walk to activatable object and activate it
/** Will activate when close to object **/
class AiActivate : public AiPackage
{
public:
public:
/// Constructor
/** \param objectId Reference to object to activate **/
AiActivate(const std::string &objectId);
virtual AiActivate *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;
private:
std::string mObjectId;
PathFinder mPathFinder;
int mCellX;
int mCellY;
};

@ -0,0 +1,90 @@
#include "aiavoiddoor.hpp"
#include <iostream>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "creaturestats.hpp"
#include "movement.hpp"
#include "mechanicsmanagerimp.hpp"
#include <OgreMath.h>
#include "steering.hpp"
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::Ptr& doorPtr)
: AiPackage(), mDoorPtr(doorPtr), mDuration(1), mAdjAngle(0)
{
}
bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration)
{
ESM::Position pos = actor.getRefData().getPosition();
if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing
mLastPos = pos;
mDuration -= duration; //Update timer
if(mDuration < 0) {
float x = pos.pos[0] - mLastPos.pos[0];
float y = pos.pos[1] - mLastPos.pos[1];
float z = pos.pos[2] - mLastPos.pos[2];
int distance = x * x + y * y + z * z;
if(distance < 10 * 10) { //Got stuck, didn't move
if(mAdjAngle == 0) //Try going in various directions
mAdjAngle = 1.57079632679f; //pi/2
else if (mAdjAngle == 1.57079632679f)
mAdjAngle = -1.57079632679;
else
mAdjAngle = 0;
mDuration = 1; //reset timer
}
else //Not stuck
return true; // We have tried backing up for more than one second, we've probably cleared it
}
if(!MWBase::Environment::get().getWorld()->getIsMovingDoor(mDoorPtr))
return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
float x = pos.pos[0] - tPos.pos[0];
float y = pos.pos[1] - tPos.pos[1];
float dirToDoor = std::atan2(x,y) + pos.rot[2] + mAdjAngle; //Calculates the direction to the door, relative to the direction of the NPC
// For example, if the NPC is directly facing the door this will be pi/2
// Make actor move away from the door
actor.getClass().getMovementSettings(actor).mPosition[1] = -1 * std::sin(dirToDoor); //I knew I'd use trig someday
actor.getClass().getMovementSettings(actor).mPosition[0] = -1 * std::cos(dirToDoor);
//Make all nearby actors also avoid the door
std::vector<MWWorld::Ptr> actors;
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(Ogre::Vector3(pos.pos[0],pos.pos[1],pos.pos[2]),100,actors);
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); it++) {
if(*it != MWBase::Environment::get().getWorld()->getPlayerPtr()) { //Not the player
MWMechanics::AiSequence& seq = MWWorld::Class::get(*it).getCreatureStats(*it).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it);
}
}
}
return false;
}
std::string MWMechanics::AiAvoidDoor::getAvoidedDoor()
{
return mDoorPtr.getCellRef().mRefID;
}
MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const
{
return new AiAvoidDoor(*this);
}
int MWMechanics::AiAvoidDoor::getTypeId() const
{
return TypeIdAvoidDoor;
}

@ -0,0 +1,38 @@
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
#define GAME_MWMECHANICS_AIAVOIDDOOR_H
#include "aipackage.hpp"
#include <string>
#include "pathfinding.hpp"
#include <components/esm/defs.hpp>
#include "../mwworld/class.hpp"
namespace MWMechanics
{
/// \brief AiPackage to have an actor avoid an opening door
/** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it
**/
class AiAvoidDoor : public AiPackage
{
public:
/// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::Ptr& doorPtr);
virtual AiAvoidDoor *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const;
/// Returns the door being avoided
std::string getAvoidedDoor();
private:
float mDuration;
const MWWorld::Ptr& mDoorPtr;
ESM::Position mLastPos;
float mAdjAngle;
};
}
#endif

@ -14,20 +14,23 @@
namespace MWMechanics
{
/// \brief Causes the actor to fight another actor
class AiCombat : public AiPackage
{
public:
///Constructor
/** \param actor Actor to fight **/
AiCombat(const MWWorld::Ptr& actor);
virtual AiCombat *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;
virtual unsigned int getPriority() const;
///Returns target ID
const std::string &getTargetId() const;
private:

@ -75,58 +75,6 @@ namespace MWMechanics
return true;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->getCell()->mData.mX != mCellX || actor.getCell()->getCell()->mData.mY != mCellY;
if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX);
// Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return false;
}
}
if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY);
// Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return false;
}
}
if(!mPathFinder.isPathConstructed() || cellChange)
{
mCellX = actor.getCell()->getCell()->mData.mX;
mCellY = actor.getCell()->getCell()->mData.mY;
ESM::Pathgrid::Point dest;
dest.mX = mX;
dest.mY = mY;
dest.mZ = mZ;
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
const float* const leaderPos = actor.getRefData().getPosition().pos;
const float* const followerPos = follower.getRefData().getPosition().pos;
@ -141,9 +89,8 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete
return true;
mMaxDist = 470;
}
else

@ -8,18 +8,22 @@
namespace MWMechanics
{
/// \brief AI Package to have an NPC lead the player to a specific point
class AiEscort : public AiPackage
{
public:
/// Implementation of AiEscort
/** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time
\implement AiEscort **/
AiEscort(const std::string &actorId,int duration, float x, float y, float z);
///< \implement AiEscort
/// Implementation of AiEscortCell
/** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time
\implement AiEscortCell **/
AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z);
///< \implement AiEscortCell
virtual AiEscort *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;

@ -12,16 +12,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(""), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage()
{
}
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), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage()
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage()
{
}
@ -31,10 +31,6 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
if(target == MWWorld::Ptr()) return true; //Target doesn't exist
mTimer = mTimer + duration; //Update timer
mStuckTimer = mStuckTimer + duration; //Update stuck timer
mTotalTime = mTotalTime + duration; //Update total time following
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
if(!mAlwaysFollow) //Update if you only follow for a bit
@ -60,74 +56,18 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
}
//Set the target desition from the actor
ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1];
dest.mZ = target.getRefData().getPosition().pos[2];
//Current position, for pathfilding stuff
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
//Build the path to get to the destination
if(mPathFinder.getPath().empty())
mPathFinder.buildPath(start, dest, actor.getCell(), true);
//***********************
// Checks if you can't get to the end position at all
//***********************
if(mTimer > 0.25)
{
if(!mPathFinder.getPath().empty()) //Path has points in it
{
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX)
+(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY)
+(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ)
> 100*100) //End of the path is far from the destination
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
}
mTimer = 0;
}
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
//************************
// Checks if you aren't moving; you're stuck
//************************
if(mStuckTimer>0.5) //Checks every half of a second
{
if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0])
+(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1])
+(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck
mPathFinder.buildPath(start, dest, actor.getCell(), true);
mStuckTimer = 0;
mStuckPos = pos;
}
//Checks if the path isn't over, turn tomards the direction that you're going
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
else
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
else {
pathTo(actor, dest, duration); //Go to the destination
}
//Check if you're far away
if((dest.mX - start.mX)*(dest.mX - start.mX)
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) > 1000*1000)
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
else if((dest.mX - start.mX)*(dest.mX - start.mX) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) < 800*800)
else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 800) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
return false;

@ -1,43 +1,45 @@
#ifndef GAME_MWMECHANICS_AIFALLOW_H
#define GAME_MWMECHANICS_AIFALLOW_H
#ifndef GAME_MWMECHANICS_AIFOLLOW_H
#define GAME_MWMECHANICS_AIFOLLOW_H
#include "aipackage.hpp"
#include <string>
#include "pathfinding.hpp"
#include "../../../components/esm/defs.hpp"
#include <components/esm/defs.hpp>
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
{
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);
/// Follow Actor for duration or until you arrive at a position in a cell
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
/// Follow Actor indefinitively
AiFollow(const std::string &ActorId);
virtual AiFollow *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;
/// Returns the actor being followed
std::string getFollowedActor();
private:
bool mAlwaysFollow; //this will make the actor always follow, thus ignoring mDuration and mX,mY,mZ (used for summoned creatures).
/// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow;
float mDuration;
float mX;
float mY;
float mZ;
std::string mActorId;
std::string mCellId;
float mTimer;
float mStuckTimer;
float mTotalTime;
ESM::Position mStuckPos;
PathFinder mPathFinder;
};
}
#endif

@ -1,4 +1,131 @@
#include "aipackage.hpp"
#include <cmath>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "creaturestats.hpp"
#include "movement.hpp"
#include "../mwworld/action.hpp"
#include <OgreMath.h>
#include "steering.hpp"
MWMechanics::AiPackage::~AiPackage() {}
MWMechanics::AiPackage::AiPackage() : mLastDoorChecked(MWWorld::Ptr()), mTimer(.26), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild
}
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration)
{
//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
/// Stops the actor when it gets too close to a unloaded cell
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const ESM::Cell *cell = actor.getCell()->getCell();
Movement &movement = actor.getClass().getMovementSettings(actor);
//Ensure pursuer doesn't leave loaded cells
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
}
//Start position
ESM::Pathgrid::Point start = pos.pos;
//***********************
/// Checks if you can't get to the end position at all, adds end position to end of path
/// Rebuilds path every quarter of a second, in case the target has moved
//***********************
if(mTimer > 0.25)
{
if(distance(mPrevDest, dest) > 10) { //Only rebuild path if it's moved
mPathFinder.buildPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved
mPrevDest = dest;
}
if(!mPathFinder.getPath().empty()) //Path has points in it
{
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
if(distance(dest, lastPos) > 100) //End of the path is far from the destination
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
}
mTimer = 0;
}
//************************
/// Checks if you aren't moving; attempts to unstick you
//************************
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) //Path finished?
return true;
else if(mStuckTimer>0.5) //Every half second see if we need to take action to avoid something
{
/// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason
//if(mObstacleCheck.check(actor, duration)) {
if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < 10 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care
// first check if we're walking into a door
MWWorld::Ptr door = getNearbyDoor(actor);
if(door != MWWorld::Ptr()) // NOTE: checks interior cells only
{
if(door.getCellRef().mTrap.empty() && mLastDoorChecked != door) { //Open the door if untrapped
door.getClass().activate(door, actor).get()->execute(actor);
mLastDoorChecked = door;
}
}
else // probably walking into another NPC
{
// 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
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
}
}
else { //Not stuck, so reset things
mStuckTimer = 0;
mStuckPos = pos;
mLastDoorChecked = MWWorld::Ptr(); //Resets it, in case he gets stuck behind the door again
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward
}
}
else {
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time
}
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
return false;
}

@ -1,6 +1,12 @@
#ifndef GAME_MWMECHANICS_AIPACKAGE_H
#define GAME_MWMECHANICS_AIPACKAGE_H
#include "pathfinding.hpp"
#include <components/esm/defs.hpp>
#include "../mwbase/world.hpp"
#include "obstacle.hpp"
namespace MWWorld
{
class Ptr;
@ -12,6 +18,7 @@ namespace MWMechanics
class AiPackage
{
public:
///Enumerates the various AITypes availible.
enum TypeId {
TypeIdNone = -1,
TypeIdWander = 0,
@ -20,21 +27,47 @@ namespace MWMechanics
TypeIdFollow = 3,
TypeIdActivate = 4,
TypeIdCombat = 5,
TypeIdPursue = 6
TypeIdPursue = 6,
TypeIdAvoidDoor = 7
};
///Default constructor
AiPackage();
///Default Deconstructor
virtual ~AiPackage();
///Clones the package
virtual AiPackage *clone() const = 0;
/// Updates and runs the package (Should run every frame)
/// \return Package completed?
virtual bool execute (const MWWorld::Ptr& actor,float duration) = 0;
///< \return Package completed?
/// Returns the TypeID of the AiPackage
/// \see enum TypeId
virtual int getTypeId() const = 0;
///< @see enum TypeId
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;}
///< higher number is higher priority (0 beeing the lowest)
protected:
/// Causes the actor to attempt to walk to the specified location
/** \return If the actor has arrived at his destination **/
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
PathFinder mPathFinder;
ObstacleCheck mObstacleCheck;
float mDoorCheckDuration;
float mTimer;
float mStuckTimer;
float mTotalTime;
MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door
ESM::Position mStuckPos;
ESM::Pathgrid::Point mPrevDest;
};
}

@ -21,75 +21,27 @@ MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
}
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos = actor.getRefData().getPosition();
Movement &movement = actor.getClass().getMovementSettings(actor);
const ESM::Cell *cell = actor.getCell()->getCell();
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
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
MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
// Big TODO: Sync this with current AiFollow. Move common code to a shared base class or helpers (applies to all AI packages, way too much duplicated code)
MWWorld::Ptr target = world->getPtr(mObjectId,false);
ESM::Position targetPos = target.getRefData().getPosition();
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
mCellX = cell->mData.mX;
mCellY = cell->mData.mY;
if(target == MWWorld::Ptr())
return true; //Target doesn't exist
ESM::Pathgrid::Point dest;
dest.mX = targetPos.pos[0];
dest.mY = targetPos.pos[1];
dest.mZ = targetPos.pos[2];
//Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 100*100)
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else {
pathTo(actor, dest, duration); //Go to the destination
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
movement.mPosition[1] = 1;
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
return false;
}

@ -8,20 +8,22 @@
namespace MWMechanics
{
/// \brief Makes the actor very closely follow the actor
/** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them.
Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the
path is completed). **/
class AiPursue : public AiPackage
{
public:
///Constructor
/** \param objectId Actor to pursue **/
AiPursue(const std::string &objectId);
virtual AiPursue *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;
private:
std::string mObjectId;
PathFinder mPathFinder;
int mCellX;
int mCellY;
};

@ -13,66 +13,79 @@ namespace MWWorld
namespace MWMechanics
{
class AiPackage;
/// \brief Sequence of AI-packages for a single actor
/** The top-most AI package is run each frame. When completed, it is removed from the stack. **/
class AiSequence
{
///AiPackages to run though
std::list<AiPackage *> mPackages;
///Finished with top AIPackage, set for one frame
bool mDone;
///Copy AiSequence
void copy (const AiSequence& sequence);
// The type of AI package that ran last
/// The type of AI package that ran last
int mLastAiPackage;
public:
///Default constructor
AiSequence();
/// Copy Constructor
AiSequence (const AiSequence& sequence);
/// Assignment operator
AiSequence& operator= (const AiSequence& sequence);
virtual ~AiSequence();
/// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/
int getTypeId() const;
///< @see enum AiPackage::TypeId
/// Get the typeid of the Ai package that ran last
/** NOT the currently "active" Ai package that will be run in the next frame.
This difference is important when an Ai package has just finished and been removed.
\see enum AiPackage::TypeId **/
int getLastRunTypeId() const { return mLastAiPackage; }
///< Get the typeid of the Ai package that ran last, NOT the currently "active" Ai package that will be run in the next frame.
/// This difference is important when an Ai package has just finished and been removed.
/// Return true and assign target if combat package is currently active, return false otherwise
bool getCombatTarget (std::string &targetActorId) const;
///< Return true and assign target if combat package is currently
/// active, return false otherwise
/// Removes all combat packages until first non-combat or stack empty.
void stopCombat();
///< Removes all combat packages until first non-combat or stack empty.
void stopPursuit();
///< Removes all pursue packages until first non-pursue or stack empty.
/// Has a package been completed during the last update?
bool isPackageDone() const;
///< Has a package been completed during the last update?
/// Removes all pursue packages until first non-pursue or stack empty.
void stopPursuit();
/// Execute current package, switching if needed.
void execute (const MWWorld::Ptr& actor,float duration);
///< Execute package.
/// Remove all packages.
void clear();
///< Remove all packages.
///< Add \a package to the front of the sequence
/** Suspends current package
@param actor The actor that owns this AiSequence **/
void stack (const AiPackage& package, const MWWorld::Ptr& actor);
///< Add \a package to the front of the sequence (suspends current package)
/// @param actor The actor that owns this AiSequence
/// Add \a package to the end of the sequence
/** Executed after all other packages have been completed **/
void queue (const AiPackage& package);
///< Add \a package to the end of the sequence (executed after all other packages have been
/// completed)
/// Return the current active package.
/** If there is no active package, it will throw an exception **/
AiPackage* getActivePackage();
///< return the current active package. If there is no active package, throw an exeption
/// Fills the AiSequence with packages
/** Typically used for loading from the ESM
\see ESM::AIPackageList **/
void fill (const ESM::AIPackageList& list);
};
}

@ -6,15 +6,16 @@
#include "pathfinding.hpp"
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);
virtual AiTravel *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;

@ -14,20 +14,27 @@
namespace MWMechanics
{
/// \brief Causes the Actor to wander within a specified range
class AiWander : public AiPackage
{
public:
/// Constructor
/** \param distance Max distance the ACtor will wander
\param duration Time, in hours, that this package will be preformed
\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);
virtual AiPackage *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const;
///< 0: Wander
/// Set the position to return to for a stationary (non-wandering) actor
/** In case another AI package moved the actor elsewhere **/
void setReturnPosition (const Ogre::Vector3& position);
///< Set the position to return to for a stationary (non-wandering) actor, in case
/// another AI package moved the actor elsewhere
private:
void stopWalking(const MWWorld::Ptr& actor);

@ -19,11 +19,19 @@ namespace MWMechanics
// Limitation: there can be false detections, and does not test whether the
// actor is facing the door.
bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr, bool closed)
{
if(getNearbyDoor(actor, minSqr, closed)!=MWWorld::Ptr())
return true;
else
return false;
}
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr, bool closed)
{
MWWorld::CellStore *cell = actor.getCell();
if(cell->getCell()->isExterior())
return false; // check interior cells only
return MWWorld::Ptr(); // check interior cells only
// Check all the doors in this cell
MWWorld::CellRefList<ESM::Door>& doors = cell->get<ESM::Door>();
@ -31,14 +39,14 @@ namespace MWMechanics
MWWorld::CellRefList<ESM::Door>::List::iterator it = refList.begin();
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
// TODO: How to check whether the actor is facing a door? Below code is for
// the player, perhaps it can be adapted.
/// TODO: How to check whether the actor is facing a door? Below code is for
/// the player, perhaps it can be adapted.
//MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
//if(!ptr.isEmpty())
//std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl;
// TODO: The in-game observation of rot[2] value seems to be the
// opposite of the code in World::activateDoor() ::confused::
/// TODO: The in-game observation of rot[2] value seems to be the
/// opposite of the code in World::activateDoor() ::confused::
for (; it != refList.end(); ++it)
{
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
@ -46,10 +54,10 @@ namespace MWMechanics
if((closed && ref.mData.getLocalRotation().rot[2] == 0) ||
(!closed && ref.mData.getLocalRotation().rot[2] >= 1))
{
return true; // found, stop searching
return MWWorld::Ptr(&ref, actor.getCell()); // found, stop searching
}
}
return false; // none found
return MWWorld::Ptr(); // none found
}
ObstacleCheck::ObstacleCheck():

@ -1,6 +1,10 @@
#ifndef OPENMW_MECHANICS_OBSTACLE_H
#define OPENMW_MECHANICS_OBSTACLE_H
//#include "../mwbase/world.hpp"
//#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
namespace MWWorld
{
class Ptr;
@ -8,14 +12,20 @@ namespace MWWorld
namespace MWMechanics
{
// NOTE: determined empirically based on in-game behaviour
/// NOTE: determined empirically based on in-game behaviour
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
// tests actor's proximity to a closed door by default
/// tests actor's proximity to a closed door by default
bool proximityToDoor(const MWWorld::Ptr& actor,
float minSqr = MIN_DIST_TO_DOOR_SQUARED,
bool closed = true);
/// Returns door pointer within range. No guarentee is given as too which one
/** \return Pointer to the door, or NULL if none exists **/
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor,
float minSqr = MIN_DIST_TO_DOOR_SQUARED,
bool closed = true);
class ObstacleCheck
{
public:

@ -11,30 +11,6 @@
namespace
{
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + 0.1 * z * z);
}
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + z * z);
}
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{
float x = a.mX - b.mX;
float y = a.mY - b.mY;
float z = a.mZ - b.mZ;
return sqrt(x * x + y * y + z * z);
}
// Slightly cheaper version for comparisons.
// Caller needs to be careful for very short distances (i.e. less than 1)
// or when accumuating the results i.e. (a + b)^2 != a^2 + b^2
@ -114,6 +90,30 @@ namespace
namespace MWMechanics
{
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + 0.1 * z * z);
}
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + z * z);
}
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{
float x = a.mX - b.mX;
float y = a.mY - b.mY;
float z = a.mZ - b.mZ;
return sqrt(x * x + y * y + z * z);
}
PathFinder::PathFinder()
: mIsPathConstructed(false),
mPathgrid(NULL),

@ -13,6 +13,8 @@ namespace MWWorld
namespace MWMechanics
{
float distance(ESM::Pathgrid::Point point, float x, float y, float);
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b);
class PathFinder
{
public:

@ -30,6 +30,7 @@
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/levelledlist.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
#include "../mwrender/sky.hpp"
#include "../mwrender/animation.hpp"
@ -1216,9 +1217,16 @@ namespace MWWorld
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
if (MWWorld::Class::get(ptr).isActor())
{
// we collided with an actor, we need to undo the rotation
// Collided with actor, ask actor to try to avoid door
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) {
MWMechanics::AiSequence& seq = MWWorld::Class::get(ptr).getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
}
// we need to undo the rotation
localRotateObject(it->first, 0, 0, oldRot);
break;
//break; //Removed in case multiple actors are touching
}
}
@ -1862,6 +1870,12 @@ namespace MWWorld
return door.getRefData().getLocalRotation().rot[2] == 0;
}
bool World::getIsMovingDoor(const Ptr& door)
{
bool result = mDoorStates.find(door) != mDoorStates.end();
return result;
}
bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
{
MWWorld::Ptr player = mPlayer->getPlayer();

@ -496,10 +496,13 @@ namespace MWWorld
virtual void setupPlayer();
virtual void renderPlayer();
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
///< if activated, should this door be opened or closed?
/// activate (open or close) an non-teleport door
virtual void activateDoor(const MWWorld::Ptr& door);
///< activate (open or close) an non-teleport door
virtual bool getIsMovingDoor(const MWWorld::Ptr& door);
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object

@ -98,10 +98,8 @@ namespace Compiler
int parseArguments (const std::string& arguments, Scanner& scanner,
std::vector<Interpreter::Type_Code>& code, bool invert = false);
///< Parse sequence of arguments specified by \a arguments.
/// \param arguments Each character represents one arguments ('l': integer,
/// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are
/// optional)
/// 'x': optional string that will be ignored (die in a fire, MW script compiler!)
/// \param arguments Uses ScriptArgs typedef
/// \see Compiler::ScriptArgs
/// \param invert Store arguments in reverted order.
/// \return number of optional arguments
};

@ -21,7 +21,7 @@ namespace Compiler
return iter->second;
}
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType,
bool Extensions::isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType,
bool& explicitReference) const
{
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
@ -37,7 +37,7 @@ namespace Compiler
return true;
}
bool Extensions::isInstruction (int keyword, std::string& argumentType,
bool Extensions::isInstruction (int keyword, ScriptArgs& argumentType,
bool& explicitReference) const
{
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
@ -52,8 +52,8 @@ namespace Compiler
return true;
}
void Extensions::registerFunction (const std::string& keyword, char returnType,
const std::string& argumentType, int code, int codeExplicit)
void Extensions::registerFunction (const std::string& keyword, ScriptReturn returnType,
const ScriptArgs& argumentType, int code, int codeExplicit)
{
Function function;
@ -83,7 +83,7 @@ namespace Compiler
}
void Extensions::registerInstruction (const std::string& keyword,
const std::string& argumentType, int code, int codeExplicit)
const ScriptArgs& argumentType, int code, int codeExplicit)
{
Instruction instruction;

@ -11,14 +11,35 @@ namespace Compiler
{
class Literals;
/// \brief Collection of compiler extensions
/// Typedef for script arguments string
/** Every character reperesents an argument to the command. All arguments are required until a /, after which
every argument is optional. <BR>
Eg: fff/f represents 3 required floats followed by one optional float <BR>
f - Float <BR>
c - String, case smashed <BR>
l - Integer <BR>
s - Short <BR>
S - String, case preserved <BR>
x - Optional, ignored argument
**/
typedef std::string ScriptArgs;
/// Typedef for script return char
/** The character represents the type of data being returned. <BR>
f - float <BR>
S - String (Cell names) <BR>
l - Integer
**/
typedef char ScriptReturn;
/// \brief Collection of compiler extensions
class Extensions
{
struct Function
{
char mReturn;
std::string mArguments;
ScriptArgs mArguments;
int mCode;
int mCodeExplicit;
int mSegment;
@ -26,7 +47,7 @@ namespace Compiler
struct Instruction
{
std::string mArguments;
ScriptArgs mArguments;
int mCode;
int mCodeExplicit;
int mSegment;
@ -46,21 +67,21 @@ namespace Compiler
/// - if no match is found 0 is returned.
/// - keyword must be all lower case.
bool isFunction (int keyword, char& returnType, std::string& argumentType,
bool isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType,
bool& explicitReference) const;
///< Is this keyword registered with a function? If yes, return return and argument
/// types.
/// \param explicitReference In: has explicit reference; Out: set to false, if
/// explicit reference is not available for this instruction.
bool isInstruction (int keyword, std::string& argumentType,
bool isInstruction (int keyword, ScriptArgs& argumentType,
bool& explicitReference) const;
///< Is this keyword registered with a function? If yes, return argument types.
/// \param explicitReference In: has explicit reference; Out: set to false, if
/// explicit reference is not available for this instruction.
void registerFunction (const std::string& keyword, char returnType,
const std::string& argumentType, int code, int codeExplicit = -1);
void registerFunction (const std::string& keyword, ScriptReturn returnType,
const ScriptArgs& argumentType, int code, int codeExplicit = -1);
///< Register a custom function
/// - keyword must be all lower case.
/// - keyword must be unique
@ -68,7 +89,7 @@ namespace Compiler
/// \note Currently only segment 3 and segment 5 opcodes are supported.
void registerInstruction (const std::string& keyword,
const std::string& argumentType, int code, int codeExplicit = -1);
const ScriptArgs& argumentType, int code, int codeExplicit = -1);
///< Register a custom instruction
/// - keyword must be all lower case.
/// - keyword must be unique

@ -8,6 +8,20 @@ namespace ESM
{
unsigned int Pathgrid::sRecordId = REC_PGRD;
Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3]) {
mX = rhs[0];
mY = rhs[1];
mZ = rhs[2];
return *this;
}
Pathgrid::Point::Point(const float rhs[3]) {
mX = rhs[0];
mY = rhs[1];
mZ = rhs[2];
}
Pathgrid::Point::Point():mX(0),mY(0),mZ(0) {
}
void Pathgrid::load(ESMReader &esm)
{
esm.getHNT(mData, "DATA", 12);

@ -31,6 +31,10 @@ struct Pathgrid
unsigned char mAutogenerated; // autogenerated vs. user coloring flag?
unsigned char mConnectionNum; // number of connections for this point
short mUnknown;
Point& operator=(const float[3]);
Point(const float[3]);
Point();
Point(int x, int y, int z) : mX(x), mY(y), mZ(z) {}
}; // 16 bytes
struct Edge // path grid edge

@ -33,6 +33,7 @@ Edmondo Tommasina (edmondo)
Eduard Cot (trombonecot)
Eli2
Emanuel Guével (potatoesmaster)
Fil Krynicki (filkry)
gugus/gus
Jacob Essex (Yacoby)
Jannik Heller (scrawl)

Loading…
Cancel
Save