mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-05 03:45:35 +00:00
Merge branch 'master' of https://github.com/OpenMW/openmw
This commit is contained in:
commit
9b67fcc4d7
46 changed files with 635 additions and 505 deletions
|
@ -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& 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()))));
|
||||
|
||||
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
|
||||
|
|
|
@ -20,82 +20,25 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
|
|||
}
|
||||
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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
if(target == MWWorld::Ptr())
|
||||
return true; //Target doesn't exist
|
||||
|
||||
MWWorld::Ptr target = world->searchPtr(mObjectId,false);
|
||||
if(target == MWWorld::Ptr()) return true;
|
||||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,20 +8,20 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
/// \brief Causes actor to walk to activatable object and activate it
|
||||
/** Will activate when close to object **/
|
||||
class AiActivate : public AiPackage
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
|
90
apps/openmw/mwmechanics/aiavoiddoor.cpp
Normal file
90
apps/openmw/mwmechanics/aiavoiddoor.cpp
Normal 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;
|
||||
}
|
||||
|
38
apps/openmw/mwmechanics/aiavoiddoor.hpp
Normal file
38
apps/openmw/mwmechanics/aiavoiddoor.hpp
Normal 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
|
||||
|
|
@ -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];
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//************************
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if(target == MWWorld::Ptr())
|
||||
return true; //Target doesn't exist
|
||||
|
||||
// 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);
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -15,64 +15,77 @@ 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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief Causes the AI to travel to the specified point
|
||||
class AiTravel : public AiPackage
|
||||
{
|
||||
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
|
||||
|
||||
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);
|
||||
///< 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…
Reference in a new issue