1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-05 03:45:35 +00:00
This commit is contained in:
scrawl 2014-05-14 23:54:25 +02:00
commit 9b67fcc4d7
46 changed files with 635 additions and 505 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

@ -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;
};

View file

@ -111,13 +111,17 @@ void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::Univers
{ {
std::vector<std::pair<std::string, std::vector<std::string> > > filterSource; 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::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); mFilterBox->createFilterRequest(filterSource, action);
} }

View file

@ -67,7 +67,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects 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 aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle disease pickpocket levelledlist combat steering obstacle
) )

View file

@ -393,10 +393,14 @@ namespace MWBase
virtual void setupPlayer() = 0; virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0; virtual void renderPlayer() = 0;
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0; 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; 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 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 virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object

View file

@ -19,83 +19,26 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
return new AiActivate(*this); return new AiActivate(*this);
} }
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
ESM::Position pos = actor.getRefData().getPosition(); const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
Movement &movement = actor.getClass().getMovementSettings(actor);
const ESM::Cell *cell = actor.getCell()->getCell(); if(target == MWWorld::Ptr())
return true; //Target doesn't exist
MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX) //Set the target desition from the actor
{ ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
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(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
{ MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
movement.mPosition[1] = 0; return true;
return false; }
} else {
} pathTo(actor, dest, duration); //Go to the destination
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;
return false; return false;
} }

View file

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

View file

@ -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;
}

View file

@ -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

View file

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

View file

@ -75,58 +75,6 @@ namespace MWMechanics
return true; 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 MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const leaderPos = actor.getRefData().getPosition().pos;
const float* const followerPos = follower.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos;
@ -141,9 +89,8 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist) if(distanceBetweenResult <= mMaxDist * mMaxDist)
{ {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete
zTurn(actor, Ogre::Degree(zAngle)); return true;
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470; mMaxDist = 470;
} }
else else

View file

@ -8,18 +8,22 @@
namespace MWMechanics namespace MWMechanics
{ {
/// \brief AI Package to have an NPC lead the player to a specific point
class AiEscort : public AiPackage class AiEscort : public AiPackage
{ {
public: 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); 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); AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z);
///< \implement AiEscortCell
virtual AiEscort *clone() const; virtual AiEscort *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const; virtual int getTypeId() const;

View file

@ -12,16 +12,16 @@
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), 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) 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) 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 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 ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
if(!mAlwaysFollow) //Update if you only follow for a bit 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 //Set the target desition from the actor
ESM::Pathgrid::Point dest; ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
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 if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close
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;
}
//************************
// 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)
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
else else {
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; pathTo(actor, dest, duration); //Go to the destination
}
//Check if you're far away //Check if you're far away
if((dest.mX - start.mX)*(dest.mX - start.mX) if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000)
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) > 1000*1000)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run 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 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
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) < 800*800)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
return false; return false;

View file

@ -1,43 +1,45 @@
#ifndef GAME_MWMECHANICS_AIFALLOW_H #ifndef GAME_MWMECHANICS_AIFOLLOW_H
#define GAME_MWMECHANICS_AIFALLOW_H #define GAME_MWMECHANICS_AIFOLLOW_H
#include "aipackage.hpp" #include "aipackage.hpp"
#include <string> #include <string>
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include "../../../components/esm/defs.hpp" #include <components/esm/defs.hpp>
namespace MWMechanics 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: 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); 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); 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); AiFollow(const std::string &ActorId);
virtual AiFollow *clone() const; virtual AiFollow *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const; virtual int getTypeId() const;
/// Returns the actor being followed
std::string getFollowedActor(); std::string getFollowedActor();
private: 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 mDuration;
float mX; float mX;
float mY; float mY;
float mZ; float mZ;
std::string mActorId; std::string mActorId;
std::string mCellId; std::string mCellId;
float mTimer;
float mStuckTimer;
float mTotalTime;
ESM::Position mStuckPos;
PathFinder mPathFinder;
}; };
} }
#endif #endif

View file

@ -1,4 +1,131 @@
#include "aipackage.hpp" #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() {}
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;
}

View file

@ -1,6 +1,12 @@
#ifndef GAME_MWMECHANICS_AIPACKAGE_H #ifndef GAME_MWMECHANICS_AIPACKAGE_H
#define 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 namespace MWWorld
{ {
class Ptr; class Ptr;
@ -12,6 +18,7 @@ namespace MWMechanics
class AiPackage class AiPackage
{ {
public: public:
///Enumerates the various AITypes availible.
enum TypeId { enum TypeId {
TypeIdNone = -1, TypeIdNone = -1,
TypeIdWander = 0, TypeIdWander = 0,
@ -20,21 +27,47 @@ namespace MWMechanics
TypeIdFollow = 3, TypeIdFollow = 3,
TypeIdActivate = 4, TypeIdActivate = 4,
TypeIdCombat = 5, TypeIdCombat = 5,
TypeIdPursue = 6 TypeIdPursue = 6,
TypeIdAvoidDoor = 7
}; };
virtual ~AiPackage(); ///Default constructor
AiPackage();
virtual AiPackage *clone() const = 0;
virtual bool execute (const MWWorld::Ptr& actor,float duration) = 0;
///< \return Package completed?
virtual int getTypeId() const = 0;
///< @see enum TypeId
///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;
/// Returns the TypeID of the AiPackage
/// \see enum TypeId
virtual int getTypeId() const = 0;
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;} 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;
}; };
} }

View file

@ -21,75 +21,27 @@ MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
} }
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) 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(target == MWWorld::Ptr())
if(cell->mData.mX != player.getCell()->getCell()->mData.mX) return true; //Target doesn't exist
{
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) //Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
MWWorld::Ptr target = world->getPtr(mObjectId,false); if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
ESM::Position targetPos = target.getRefData().getPosition(); actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
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]) < 100*100)
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
return true; return true;
} }
else {
pathTo(actor, dest, duration); //Go to the destination
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
movement.mPosition[1] = 1;
return false; return false;
} }

View file

@ -8,20 +8,22 @@
namespace MWMechanics 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 class AiPursue : public AiPackage
{ {
public: public:
///Constructor
/** \param objectId Actor to pursue **/
AiPursue(const std::string &objectId); AiPursue(const std::string &objectId);
virtual AiPursue *clone() const; virtual AiPursue *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const; virtual int getTypeId() const;
private: private:
std::string mObjectId; std::string mObjectId;
PathFinder mPathFinder;
int mCellX; int mCellX;
int mCellY; int mCellY;
}; };

View file

@ -13,66 +13,79 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
class AiPackage; class AiPackage;
/// \brief Sequence of AI-packages for a single actor /// \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 class AiSequence
{ {
///AiPackages to run though
std::list<AiPackage *> mPackages; std::list<AiPackage *> mPackages;
///Finished with top AIPackage, set for one frame
bool mDone; bool mDone;
///Copy AiSequence
void copy (const AiSequence& sequence); void copy (const AiSequence& sequence);
// The type of AI package that ran last /// The type of AI package that ran last
int mLastAiPackage; int mLastAiPackage;
public: public:
///Default constructor
AiSequence(); AiSequence();
/// Copy Constructor
AiSequence (const AiSequence& sequence); AiSequence (const AiSequence& sequence);
/// Assignment operator
AiSequence& operator= (const AiSequence& sequence); AiSequence& operator= (const AiSequence& sequence);
virtual ~AiSequence(); virtual ~AiSequence();
/// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/
int getTypeId() const; 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; } 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; 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(); void stopCombat();
///< Removes all combat packages until first non-combat or stack empty.
void stopPursuit(); /// Has a package been completed during the last update?
///< Removes all pursue packages until first non-pursue or stack empty.
bool isPackageDone() const; 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); void execute (const MWWorld::Ptr& actor,float duration);
///< Execute package.
/// Remove all packages.
void clear(); 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); 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); 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(); 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); void fill (const ESM::AIPackageList& list);
}; };
} }

View file

@ -6,15 +6,16 @@
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes the AI to travel to the specified point
class AiTravel : public AiPackage class AiTravel : public AiPackage
{ {
public: public:
/// Default constructor
AiTravel(float x, float y, float z); AiTravel(float x, float y, float z);
virtual AiTravel *clone() const; virtual AiTravel *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual bool execute (const MWWorld::Ptr& actor,float duration);
///< \return Package completed?
virtual int getTypeId() const; virtual int getTypeId() const;

View file

@ -14,20 +14,27 @@
namespace MWMechanics namespace MWMechanics
{ {
/// \brief Causes the Actor to wander within a specified range
class AiWander : public AiPackage class AiWander : public AiPackage
{ {
public: 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); 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
virtual AiPackage *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
virtual int getTypeId() const;
/// 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); 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: private:
void stopWalking(const MWWorld::Ptr& actor); void stopWalking(const MWWorld::Ptr& actor);

View file

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

View file

@ -1,6 +1,10 @@
#ifndef OPENMW_MECHANICS_OBSTACLE_H #ifndef OPENMW_MECHANICS_OBSTACLE_H
#define OPENMW_MECHANICS_OBSTACLE_H #define OPENMW_MECHANICS_OBSTACLE_H
//#include "../mwbase/world.hpp"
//#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
@ -8,14 +12,20 @@ namespace MWWorld
namespace MWMechanics 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; 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, bool proximityToDoor(const MWWorld::Ptr& actor,
float minSqr = MIN_DIST_TO_DOOR_SQUARED, float minSqr = MIN_DIST_TO_DOOR_SQUARED,
bool closed = true); 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 class ObstacleCheck
{ {
public: public:

View file

@ -11,30 +11,6 @@
namespace 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. // Slightly cheaper version for comparisons.
// Caller needs to be careful for very short distances (i.e. less than 1) // 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 // or when accumuating the results i.e. (a + b)^2 != a^2 + b^2
@ -114,6 +90,30 @@ namespace
namespace MWMechanics 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() PathFinder::PathFinder()
: mIsPathConstructed(false), : mIsPathConstructed(false),
mPathgrid(NULL), mPathgrid(NULL),

View file

@ -13,6 +13,8 @@ namespace MWWorld
namespace MWMechanics 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 class PathFinder
{ {
public: public:

View file

@ -30,6 +30,7 @@
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/levelledlist.hpp"
#include "../mwmechanics/combat.hpp" #include "../mwmechanics/combat.hpp"
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
@ -1216,9 +1217,16 @@ namespace MWWorld
MWWorld::Ptr ptr = getPtrViaHandle(*cit); MWWorld::Ptr ptr = getPtrViaHandle(*cit);
if (MWWorld::Class::get(ptr).isActor()) 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); 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; 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) bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
{ {
MWWorld::Ptr player = mPlayer->getPlayer(); MWWorld::Ptr player = mPlayer->getPlayer();

View file

@ -496,10 +496,13 @@ namespace MWWorld
virtual void setupPlayer(); virtual void setupPlayer();
virtual void renderPlayer(); virtual void renderPlayer();
/// if activated, should this door be opened or closed?
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door); 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); 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 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 virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object

View file

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

View file

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

View file

@ -11,14 +11,35 @@ namespace Compiler
{ {
class Literals; 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 class Extensions
{ {
struct Function struct Function
{ {
char mReturn; char mReturn;
std::string mArguments; ScriptArgs mArguments;
int mCode; int mCode;
int mCodeExplicit; int mCodeExplicit;
int mSegment; int mSegment;
@ -26,7 +47,7 @@ namespace Compiler
struct Instruction struct Instruction
{ {
std::string mArguments; ScriptArgs mArguments;
int mCode; int mCode;
int mCodeExplicit; int mCodeExplicit;
int mSegment; int mSegment;
@ -46,21 +67,21 @@ namespace Compiler
/// - if no match is found 0 is returned. /// - if no match is found 0 is returned.
/// - keyword must be all lower case. /// - 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; bool& explicitReference) const;
///< Is this keyword registered with a function? If yes, return return and argument ///< Is this keyword registered with a function? If yes, return return and argument
/// types. /// types.
/// \param explicitReference In: has explicit reference; Out: set to false, if /// \param explicitReference In: has explicit reference; Out: set to false, if
/// explicit reference is not available for this instruction. /// explicit reference is not available for this instruction.
bool isInstruction (int keyword, std::string& argumentType, bool isInstruction (int keyword, ScriptArgs& argumentType,
bool& explicitReference) const; bool& explicitReference) const;
///< Is this keyword registered with a function? If yes, return argument types. ///< Is this keyword registered with a function? If yes, return argument types.
/// \param explicitReference In: has explicit reference; Out: set to false, if /// \param explicitReference In: has explicit reference; Out: set to false, if
/// explicit reference is not available for this instruction. /// explicit reference is not available for this instruction.
void registerFunction (const std::string& keyword, char returnType, void registerFunction (const std::string& keyword, ScriptReturn returnType,
const std::string& argumentType, int code, int codeExplicit = -1); const ScriptArgs& argumentType, int code, int codeExplicit = -1);
///< Register a custom function ///< Register a custom function
/// - keyword must be all lower case. /// - keyword must be all lower case.
/// - keyword must be unique /// - keyword must be unique
@ -68,7 +89,7 @@ namespace Compiler
/// \note Currently only segment 3 and segment 5 opcodes are supported. /// \note Currently only segment 3 and segment 5 opcodes are supported.
void registerInstruction (const std::string& keyword, 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 ///< Register a custom instruction
/// - keyword must be all lower case. /// - keyword must be all lower case.
/// - keyword must be unique /// - keyword must be unique

View file

@ -8,6 +8,20 @@ namespace ESM
{ {
unsigned int Pathgrid::sRecordId = REC_PGRD; 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) void Pathgrid::load(ESMReader &esm)
{ {
esm.getHNT(mData, "DATA", 12); esm.getHNT(mData, "DATA", 12);

View file

@ -31,6 +31,10 @@ struct Pathgrid
unsigned char mAutogenerated; // autogenerated vs. user coloring flag? unsigned char mAutogenerated; // autogenerated vs. user coloring flag?
unsigned char mConnectionNum; // number of connections for this point unsigned char mConnectionNum; // number of connections for this point
short mUnknown; 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 }; // 16 bytes
struct Edge // path grid edge struct Edge // path grid edge

View file

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