1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-03 16:49:54 +00:00

Merge branch 'master' of https://github.com/zinnschlag/openmw into sdl_input2

Conflicts:
	apps/openmw/mwinput/inputmanagerimp.cpp
This commit is contained in:
scrawl 2013-06-15 17:41:42 +02:00
commit 4c8a04d9cb
34 changed files with 1093 additions and 749 deletions

View file

@ -4,6 +4,10 @@ if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks")
set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks")
set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks")
endif (APPLE) endif (APPLE)
# Macros # Macros
@ -15,7 +19,7 @@ include (OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 23) set (OPENMW_VERSION_MINOR 24)
set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")

View file

@ -310,6 +310,8 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
bool MainDialog::setupLauncherSettings() bool MainDialog::setupLauncherSettings()
{ {
mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QStringList paths; QStringList paths;
@ -427,6 +429,8 @@ bool MainDialog::setupGameSettings()
bool MainDialog::setupGraphicsSettings() bool MainDialog::setupGraphicsSettings()
{ {
mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());

View file

@ -103,8 +103,8 @@ bool GameSettings::readFile(QTextStream &stream)
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1); QString key = keyRe.cap(1).trimmed();
QString value = keyRe.cap(2); QString value = keyRe.cap(2).trimmed();
// Don't remove existing data entries // Don't remove existing data entries
if (key != QLatin1String("data")) if (key != QLatin1String("data"))

View file

@ -14,7 +14,7 @@ class SettingsBase
{ {
public: public:
SettingsBase() {} SettingsBase() { mMultiValue = false; }
~SettingsBase() {} ~SettingsBase() {}
inline QString value(const QString &key, const QString &defaultValue = QString()) inline QString value(const QString &key, const QString &defaultValue = QString())
@ -36,6 +36,11 @@ public:
mSettings.insertMulti(key, value); mSettings.insertMulti(key, value);
} }
inline void setMultiValueEnabled(bool enable)
{
mMultiValue = enable;
}
inline void remove(const QString &key) inline void remove(const QString &key)
{ {
mSettings.remove(key); mSettings.remove(key);
@ -66,8 +71,8 @@ public:
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1); QString key = keyRe.cap(1).trimmed();
QString value = keyRe.cap(2); QString value = keyRe.cap(2).trimmed();
if (!sectionPrefix.isEmpty()) if (!sectionPrefix.isEmpty())
key.prepend(sectionPrefix); key.prepend(sectionPrefix);
@ -75,8 +80,13 @@ public:
mSettings.remove(key); mSettings.remove(key);
QStringList values = mCache.values(key); QStringList values = mCache.values(key);
if (!values.contains(value)) { if (!values.contains(value)) {
mCache.insertMulti(key, value); if (mMultiValue) {
mCache.insertMulti(key, value);
} else {
mCache.insert(key, value);
}
} }
} }
} }
@ -94,6 +104,8 @@ public:
private: private:
Map mSettings; Map mSettings;
Map mCache; Map mCache;
bool mMultiValue;
}; };
#endif // SETTINGSBASE_HPP #endif // SETTINGSBASE_HPP

View file

@ -79,6 +79,7 @@ opencs_units (view/settings
abstractwidget abstractwidget
usersettingsdialog usersettingsdialog
editorpage editorpage
windowpage
) )
opencs_units_noqt (view/settings opencs_units_noqt (view/settings

View file

@ -61,6 +61,17 @@ void CS::Editor::setupDataFiles()
QString path = QString::fromStdString(iter->string()); QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path); mFileDialog.addFiles(path);
} }
//Settings setup
QStringList settingFiles;
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
settingFiles.append(QString("opencs.cfg"));
settingFiles.append(userPath + QString("opencs.cfg"));
mUserSettings.setSettingsFiles(settingFiles);
mUserSettings.readSettings();
} }
void CS::Editor::createDocument() void CS::Editor::createDocument()

View file

@ -10,6 +10,7 @@
#include "view/doc/viewmanager.hpp" #include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp" #include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp" #include "view/doc/filedialog.hpp"
#include "model/settings/usersettings.hpp"
namespace CS namespace CS
{ {
@ -17,6 +18,7 @@ namespace CS
{ {
Q_OBJECT Q_OBJECT
CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager; CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager; CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup; CSVDoc::StartupDialogue mStartup;

View file

@ -7,6 +7,7 @@
#include <QMap> #include <QMap>
#include <QMessageBox> #include <QMessageBox>
#include <QTextCodec> #include <QTextCodec>
#include <QDebug>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -29,17 +30,25 @@ namespace boost
} /* namespace boost */ } /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */ #endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings() CSMSettings::UserSettings::UserSettings()
{ {
assert(!mUserSettingsInstance);
mUserSettingsInstance = this; mUserSettingsInstance = this;
} }
CSMSettings::UserSettings::~UserSettings() CSMSettings::UserSettings::~UserSettings()
{ {
mUserSettingsInstance = 0;
} }
QFile *CSMSettings::UserSettings::openFile (const QString &filename) CSMSettings::SectionMap CSMSettings::UserSettings::getSettingsMap() const
{
return mSectionMap;
}
QFile *CSMSettings::UserSettings::openFile (const QString &filename) const
{ {
QFile *file = new QFile(filename); QFile *file = new QFile(filename);
@ -63,7 +72,7 @@ QFile *CSMSettings::UserSettings::openFile (const QString &filename)
return file; return file;
} }
bool CSMSettings::UserSettings::writeFile(QFile *file, QMap<QString, CSMSettings::SettingList *> &settings) bool CSMSettings::UserSettings::writeFile(QFile *file, QMap<QString, CSMSettings::SettingList *> &settings) const
{ {
if (!file) if (!file)
return false; return false;
@ -88,7 +97,7 @@ bool CSMSettings::UserSettings::writeFile(QFile *file, QMap<QString, CSMSettings
return true; return true;
} }
void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap &sections) void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap &sections) const
{ {
//looks for a square bracket, "'\\[" //looks for a square bracket, "'\\["
//that has one or more "not nothing" in it, "([^]]+)" //that has one or more "not nothing" in it, "([^]]+)"
@ -135,3 +144,70 @@ void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap &sec
} }
sections.insert(section, settings); sections.insert(section, settings);
} }
void CSMSettings::UserSettings::readSettings()
{
CSMSettings::SectionMap sectionMap;
foreach (const QString &path, mSettingsFiles)
{
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenCS configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
getSettings(stream, mSectionMap);
}
file.close();
}
}
void CSMSettings::UserSettings::setSettingsFiles(QStringList files)
{
mSettingsFiles = files;
}
QStringList CSMSettings::UserSettings::getSettingsFiles () const
{
return mSettingsFiles;
}
QString CSMSettings::UserSettings::getSettingValue(QString section, QString setting) const
{
if(mSectionMap.find(section) == mSectionMap.end())
return QString();
CSMSettings::SettingMap *settings = mSectionMap.value(section);
if(settings->find(setting) == settings->end())
return QString();
CSMSettings::SettingContainer *settingContainer = settings->value(setting);
return settingContainer->getValue();
}
const CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
{
assert(mUserSettingsInstance);
return *mUserSettingsInstance;
}

View file

@ -24,22 +24,27 @@ namespace CSMSettings {
public: public:
static UserSettings &instance() UserSettings();
{ ~UserSettings();
static UserSettings instance;
return instance; static const UserSettings& instance();
}
QFile *openFile (const QString &); void readSettings();
bool writeFile(QFile *file, QMap<QString, SettingList *> &sections); void setSettingsFiles(QStringList files);
void getSettings (QTextStream &stream, SectionMap &settings);
QFile *openFile (const QString &) const;
bool writeFile(QFile *file, QMap<QString, SettingList *> &sections) const;
void getSettings (QTextStream &stream, SectionMap &settings) const;
QStringList getSettingsFiles () const;
CSMSettings::SectionMap getSettingsMap() const;
QString getSettingValue(QString section, QString setting) const;
private: private:
UserSettings *mUserSettingsInstance; static UserSettings *mUserSettingsInstance;
UserSettings();
~UserSettings(); CSMSettings::SectionMap mSectionMap;
QStringList mSettingsFiles;
UserSettings (UserSettings const &); //not implemented UserSettings (UserSettings const &); //not implemented
void operator= (UserSettings const &); //not implemented void operator= (UserSettings const &); //not implemented

View file

@ -8,6 +8,7 @@
#include <QMdiArea> #include <QMdiArea>
#include <QDockWidget> #include <QDockWidget>
#include <QtGui/QApplication> #include <QtGui/QApplication>
#include <QDebug>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../world/subviews.hpp" #include "../world/subviews.hpp"
@ -179,7 +180,12 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews) mViewTotal (totalViews)
{ {
resize (300, 300); /// \todo get default size from settings and set reasonable minimal size QString width = CSMSettings::UserSettings::instance().getSettingValue(QString("Window Size"), QString("Width"));
QString height = CSMSettings::UserSettings::instance().getSettingValue(QString("Window Size"), QString("Height"));
if(width==QString() || height==QString())
resize(800, 600);
else
resize (width.toInt(), height.toInt());
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);

View file

@ -9,20 +9,21 @@
#include <QFile> #include <QFile>
#include <QPushButton> #include <QPushButton>
#include <QDockWidget> #include <QDockWidget>
#include <QDebug>
#include "blankpage.hpp" #include "blankpage.hpp"
#include "editorpage.hpp" #include "editorpage.hpp"
#include "windowpage.hpp"
#include "../../model/settings/support.hpp" #include "../../model/settings/support.hpp"
#include "settingwidget.hpp" #include "settingwidget.hpp"
#include <QDebug>
CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) :
QMainWindow (parent), mStackedWidget (0) QMainWindow (parent), mStackedWidget (0)
{ {
setWindowTitle(QString::fromUtf8 ("User Settings")); setWindowTitle(QString::fromUtf8 ("User Settings"));
buildPages(); buildPages();
setWidgetStates (loadSettings()); setWidgetStates (CSMSettings::UserSettings::instance().getSettingsMap());
positionWindow (); positionWindow ();
connect (mListWidget, connect (mListWidget,
@ -76,9 +77,10 @@ void CSVSettings::UserSettingsDialog::buildPages()
setDockOptions (QMainWindow::AllowNestedDocks); setDockOptions (QMainWindow::AllowNestedDocks);
//uncomment to test with sample editor page. //uncomment to test with sample editor page.
//createSamplePage(); //createSamplePage();
createPage<BlankPage>("Page1"); /*createPage<BlankPage>("Page1");
createPage<BlankPage>("Page2"); createPage<BlankPage>("Page2");
createPage<BlankPage>("Page3"); createPage<BlankPage>("Page3");*/
createWindowPage();
} }
void CSVSettings::UserSettingsDialog::createSamplePage() void CSVSettings::UserSettingsDialog::createSamplePage()
@ -95,6 +97,20 @@ void CSVSettings::UserSettingsDialog::createSamplePage()
&(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); &(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)));
} }
void CSVSettings::UserSettingsDialog::createWindowPage()
{
//add pages to stackedwidget and items to listwidget
CSVSettings::AbstractPage *page
= new CSVSettings::WindowPage(this);
mStackedWidget->addWidget (page);
new QListWidgetItem (page->objectName(), mListWidget);
connect ( page, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)),
&(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)));
}
void CSVSettings::UserSettingsDialog::positionWindow () void CSVSettings::UserSettingsDialog::positionWindow ()
{ {
QRect scr = QApplication::desktop()->screenGeometry(); QRect scr = QApplication::desktop()->screenGeometry();
@ -103,46 +119,6 @@ void CSVSettings::UserSettingsDialog::positionWindow ()
} }
CSMSettings::SectionMap CSVSettings::UserSettingsDialog::loadSettings ()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
mPaths.append(QString("opencs.cfg"));
mPaths.append(userPath + QString("opencs.cfg"));
CSMSettings::SectionMap settingsMap;
foreach (const QString &path, mPaths)
{
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists())
{
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenCS configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return settingsMap;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
CSMSettings::UserSettings::instance().getSettings(stream, settingsMap);
}
file.close();
}
return settingsMap;
}
void CSVSettings::UserSettingsDialog::writeSettings() void CSVSettings::UserSettingsDialog::writeSettings()
{ {
@ -154,7 +130,9 @@ void CSVSettings::UserSettingsDialog::writeSettings()
settings [page->objectName()] = page->getSettings(); settings [page->objectName()] = page->getSettings();
} }
CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(mPaths.back()), settings); QStringList paths = CSMSettings::UserSettings::instance().getSettingsFiles();
CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(paths.back()), settings);
} }

View file

@ -25,7 +25,6 @@ namespace CSVSettings {
{ {
Q_OBJECT Q_OBJECT
QStringList mPaths;
QListWidget *mListWidget; QListWidget *mListWidget;
QStackedWidget *mStackedWidget; QStackedWidget *mStackedWidget;
Files::ConfigurationManager mCfgMgr; Files::ConfigurationManager mCfgMgr;
@ -41,10 +40,12 @@ namespace CSVSettings {
void setWidgetStates (CSMSettings::SectionMap settingsMap); void setWidgetStates (CSMSettings::SectionMap settingsMap);
void buildPages(); void buildPages();
void positionWindow (); void positionWindow ();
CSMSettings::SectionMap loadSettings();
void writeSettings(); void writeSettings();
void createSamplePage(); void createSamplePage();
//Pages
void createWindowPage();
template <typename T> template <typename T>
void createPage (const QString &title) void createPage (const QString &title)
{ {

View file

@ -0,0 +1,114 @@
#include "windowpage.hpp"
#include <QList>
#include <QListView>
#include <QGroupBox>
#include <QRadioButton>
#include <QDockWidget>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QStyle>
#ifdef Q_OS_MAC
#include <QPlastiqueStyle>
#endif
#include "../../model/settings/usersettings.hpp"
#include "groupblock.hpp"
#include "toggleblock.hpp"
CSVSettings::WindowPage::WindowPage(QWidget *parent):
AbstractPage("Window Size", parent)
{
// Hacks to get the stylesheet look properly
#ifdef Q_OS_MAC
QPlastiqueStyle *style = new QPlastiqueStyle;
//profilesComboBox->setStyle(style);
#endif
setupUi();
}
void CSVSettings::WindowPage::setupUi()
{
GroupBlockDef customWindowSize (QString ("Custom Window Size"));
GroupBlockDef definedWindowSize (QString ("Pre-Defined Window Size"));
GroupBlockDef windowSizeToggle (QString ("Window Size"));
CustomBlockDef windowSize (QString ("Window Size"));
///////////////////////////////
//custom window size properties
///////////////////////////////
//custom width
SettingsItemDef *widthItem = new SettingsItemDef ("Width", "640");
widthItem->widget = WidgetDef (Widget_LineEdit);
widthItem->widget.widgetWidth = 45;
//custom height
SettingsItemDef *heightItem = new SettingsItemDef ("Height", "480");
heightItem->widget = WidgetDef (Widget_LineEdit);
heightItem->widget.widgetWidth = 45;
heightItem->widget.caption = "x";
customWindowSize.properties << widthItem << heightItem;
customWindowSize.widgetOrientation = Orient_Horizontal;
customWindowSize.isVisible = false;
//pre-defined
SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480");
WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox);
widthByHeightWidget.widgetWidth = 90;
*(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768" << "1440x900";
QStringList *widthProxy = new QStringList;
QStringList *heightProxy = new QStringList;
(*widthProxy) << "Width" << "640" << "800" << "1024" << "1440";
(*heightProxy) << "Height" << "480" << "600" << "768" << "900";
*(widthByHeightItem->proxyList) << widthProxy << heightProxy;
widthByHeightItem->widget = widthByHeightWidget;
definedWindowSize.properties << widthByHeightItem;
definedWindowSize.isProxy = true;
definedWindowSize.isVisible = false;
// window size toggle
windowSizeToggle.captions << "Pre-Defined" << "Custom";
windowSizeToggle.widgetOrientation = Orient_Vertical;
windowSizeToggle.isVisible = false;
//define a widget for each group in the toggle
for (int i = 0; i < 2; i++)
windowSizeToggle.widgets << new WidgetDef (Widget_RadioButton);
windowSizeToggle.widgets.at(0)->isDefault = false;
windowSize.blockDefList << &windowSizeToggle << &definedWindowSize << &customWindowSize;
windowSize.defaultValue = "Custom";
QGridLayout *pageLayout = new QGridLayout(this);
setLayout (pageLayout);
mAbstractBlocks << buildBlock<ToggleBlock> (windowSize);
foreach (AbstractBlock *block, mAbstractBlocks)
{
connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)),
this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) );
}
}
void CSVSettings::WindowPage::initializeWidgets (const CSMSettings::SettingMap &settings)
{
//iterate each item in each blocks in this section
//validate the corresponding setting against the defined valuelist if any.
for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin();
it_block != mAbstractBlocks.end(); ++it_block)
(*it_block)->updateSettings (settings);
}

View file

@ -0,0 +1,28 @@
#ifndef WINDOWPAGE_H
#define WINDOWPAGE_H
#include "abstractpage.hpp"
class QGroupBox;
namespace CSVSettings {
class UserSettings;
class AbstractBlock;
class WindowPage : public AbstractPage
{
Q_OBJECT
public:
WindowPage(QWidget *parent = 0);
void setupUi();
void initializeWidgets (const CSMSettings::SettingMap &settings);
signals:
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
};
}
#endif //WINDOWPAGE_H

View file

@ -130,26 +130,12 @@ namespace MWGui
void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender)
{ {
if ((mCurrentPage+1)*2 < mPages.size()) nextPage();
{
MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0);
++mCurrentPage;
updatePages();
}
} }
void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender)
{ {
if (mCurrentPage > 0) prevPage();
{
MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0);
--mCurrentPage;
updatePages();
}
} }
void BookWindow::updatePages() void BookWindow::updatePages()
@ -194,5 +180,28 @@ namespace MWGui
if (button->getAlign().isRight()) if (button->getAlign().isRight())
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
} }
void BookWindow::nextPage()
{
if ((mCurrentPage+1)*2 < mPages.size())
{
MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0);
++mCurrentPage;
updatePages();
}
}
void BookWindow::prevPage()
{
if (mCurrentPage > 0)
{
MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0);
--mCurrentPage;
updatePages();
}
}
} }

View file

@ -16,7 +16,8 @@ namespace MWGui
void open(MWWorld::Ptr book); void open(MWWorld::Ptr book);
void setTakeButtonShow(bool show); void setTakeButtonShow(bool show);
void nextPage();
void prevPage();
void setInventoryAllowed(bool allowed); void setInventoryAllowed(bool allowed);
protected: protected:

View file

@ -251,10 +251,8 @@ namespace MWGui
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
} }
boost::algorithm::replace_all(text, "\n", "\n"); boost::algorithm::replace_all(text, "<BR>", "\n");
boost::algorithm::replace_all(text, "\r", "\r"); boost::algorithm::replace_all(text, "<P>", "\n\n");
boost::algorithm::replace_all(text, "<BR>", "\n\n");
boost::algorithm::replace_all(text, "<P>", "\n\n"); // tweaking by adding another newline to see if that spaces out better
boost::algorithm::trim_left(text); boost::algorithm::trim_left(text);
// remove trailing " // remove trailing "

View file

@ -183,7 +183,7 @@ namespace
if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())
{ {
MWBase::Environment::get().getWindowManager()->popGuiMode (); MWBase::Environment::get().getWindowManager()->popGuiMode ();
} }
mModel->load (); mModel->load ();
setBookMode (); setBookMode ();
@ -433,7 +433,6 @@ namespace
void notifyClose(MyGUI::Widget* _sender) void notifyClose(MyGUI::Widget* _sender)
{ {
MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
MWBase::Environment::get().getWindowManager ()->popGuiMode (); MWBase::Environment::get().getWindowManager ()->popGuiMode ();
} }

View file

@ -34,6 +34,10 @@ namespace MWGui
{ {
} }
LocalMapBase::~LocalMapBase()
{
}
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop)
{ {
mLocalMap = widget; mLocalMap = widget;

View file

@ -14,6 +14,7 @@ namespace MWGui
{ {
public: public:
LocalMapBase(); LocalMapBase();
virtual ~LocalMapBase();
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop=false); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop=false);
void setCellPrefix(const std::string& prefix); void setCellPrefix(const std::string& prefix);

View file

@ -416,6 +416,10 @@ namespace MWGui
mAddEffectDialog.setVisible (false); mAddEffectDialog.setVisible (false);
} }
EffectEditorBase::~EffectEditorBase()
{
}
void EffectEditorBase::startEditing () void EffectEditorBase::startEditing ()
{ {
// get the list of magic effects that are known to the player // get the list of magic effects that are known to the player

View file

@ -84,7 +84,7 @@ namespace MWGui
{ {
public: public:
EffectEditorBase(); EffectEditorBase();
virtual ~EffectEditorBase();
protected: protected:
std::map<int, short> mButtonMapping; // maps button ID to effect ID std::map<int, short> mButtonMapping; // maps button ID to effect ID

View file

@ -19,6 +19,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwgui/bookwindow.hpp"
using namespace ICS; using namespace ICS;
@ -488,6 +489,15 @@ namespace MWInput
mMouseWheel = int(arg.z); mMouseWheel = int(arg.z);
MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel);
//if the player is reading a book and flicking the mouse wheel
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Book && arg.zrel)
{
if (arg.zrel < 0)
MWBase::Environment::get().getWindowManager()->getBookWindow()->nextPage();
else
MWBase::Environment::get().getWindowManager()->getBookWindow()->prevPage();
}
} }
if (mMouseLookEnabled) if (mMouseLookEnabled)
@ -636,7 +646,7 @@ namespace MWInput
// Toggle between game mode and journal mode // Toggle between game mode and journal mode
bool gameMode = !mWindows.isGuiMode(); bool gameMode = !mWindows.isGuiMode();
if(gameMode) if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{ {
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
mWindows.pushGuiMode(MWGui::GM_Journal); mWindows.pushGuiMode(MWGui::GM_Journal);

View file

@ -13,8 +13,9 @@ namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.0; if(a > 0)
else return -1.0; return 1.0;
return -1.0;
} }
} }
@ -24,151 +25,156 @@ namespace
TODO: Take account for actors being in different cells. TODO: Take account for actors being in different cells.
*/ */
MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) namespace MWMechanics
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
mMaxDist = 470; AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
// The CS Help File states that if a duration is givin, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
else
{ {
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
}
MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z)
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
{
mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
else
{
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
}
MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const
{
return new AiEscort(*this);
}
bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
{
// If AiEscort has ran for as long or longer then the duration specified
// and the duration is not infinite, the package is complete.
if(mDuration != 0)
{
MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp();
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100);
if(currentSecond - mStartingSecond >= mDuration)
return true;
}
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2. - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2. - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
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,pathgrid,xCell,yCell);
}
if(mPathFinder.checkIfNextPointReached(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;
double differenceBetween[3];
for (short i = 0; i < 3; ++i)
differenceBetween[i] = (leaderPos[i] - followerPos[i]);
float distanceBetweenResult =
(differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]);
if(distanceBetweenResult <= mMaxDist * mMaxDist)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470; mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
else
{
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
} }
else
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z)
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
// Stop moving if the player is to far away mMaxDist = 470;
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; // The CS Help File states that if a duration is givin, the AI package will run for that long
mMaxDist = 330; // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0;
else
{
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
} }
return false;
} AiEscort *MWMechanics::AiEscort::clone() const
{
int MWMechanics::AiEscort::getTypeId() const return new AiEscort(*this);
{ }
return 2;
bool AiEscort::execute (const MWWorld::Ptr& actor)
{
// If AiEscort has ran for as long or longer then the duration specified
// and the duration is not infinite, the package is complete.
if(mDuration != 0)
{
MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp();
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100);
if(currentSecond - mStartingSecond >= mDuration)
return true;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
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, pathgrid, xCell, yCell, 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;
double differenceBetween[3];
for (short counter = 0; counter < 3; counter++)
differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]);
float distanceBetweenResult =
(differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] *
differenceBetween[2]);
if(distanceBetweenResult <= mMaxDist * mMaxDist)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470;
}
else
{
// Stop moving if the player is to far away
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
mMaxDist = 330;
}
return false;
}
int AiEscort::getTypeId() const
{
return 2;
}
} }

View file

@ -2,99 +2,106 @@
#include "movement.hpp" #include "movement.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.; if(a > 0)
else return -1.; return 1.0;
return -1.0;
} }
} }
MWMechanics::AiTravel::AiTravel(float x, float y, float z) namespace MWMechanics
: mX(x),mY(y),mZ(z),mPathFinder()
{ {
} AiTravel::AiTravel(float x, float y, float z)
: mX(x),mY(y),mZ(z),mPathFinder()
{
}
MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const AiTravel *MWMechanics::AiTravel::clone() const
{ {
return new AiTravel(*this); return new AiTravel(*this);
} }
bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) bool AiTravel::execute (const MWWorld::Ptr& actor)
{ {
const ESM::Pathgrid *pathgrid = MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); ESM::Position pos = actor.getRefData().getPosition();
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) {
{ int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); //check if actor is near the border of an inactive cell. If so, disable aitravel.
//check if actor is near the border of an inactive cell. If so, disable aitravel. if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) 2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
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, pathgrid, xCell, yCell, true);
}
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{ {
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
} }
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
return false;
} }
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
int AiTravel::getTypeId() const
{ {
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); return 1;
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
} }
if(!mPathFinder.isPathConstructed() ||cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
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,pathgrid,xCell,yCell);
}
if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2]))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
return false;
} }
int MWMechanics::AiTravel::getTypeId() const
{
return 1;
}

View file

@ -14,310 +14,324 @@ namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.0; if(a > 0)
else return -1.0; return 1.0;
return -1.0;
} }
} }
MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): namespace MWMechanics
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{ {
for(unsigned short counter = 0; counter < mIdle.size(); counter++) AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{ {
if(mIdle[counter] >= 127 || mIdle[counter] < 0) for(unsigned short counter = 0; counter < mIdle.size(); counter++)
mIdle[counter] = 0;
}
if(mDistance < 0)
mDistance = 0;
if(mDuration < 0)
mDuration = 0;
if(mDuration == 0)
mTimeOfDay = 0;
srand(time(NULL));
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mPlayedIdle = 0;
mPathgrid = NULL;
mIdleChanceMultiplier =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
mStoredAvailableNodes = false;
mChooseAction = true;
mIdleNow = false;
mMoveNow = false;
mWalking = false;
}
MWMechanics::AiPackage * MWMechanics::AiWander::clone() const
{
return new AiWander(*this);
}
bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
{
if(mDuration)
{
// End package if duration is complete or mid-night hits:
MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp();
if(currentTime.getHour() >= mStartTime.getHour() + mDuration)
{ {
if(!mRepeat) if(mIdle[counter] >= 127 || mIdle[counter] < 0)
{ mIdle[counter] = 0;
stopWalking(actor, mPathFinder);
return true;
}
else
mStartTime = currentTime;
} }
else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
{
if(!mRepeat)
{
stopWalking(actor, mPathFinder);
return true;
}
else
mStartTime = currentTime;
}
}
ESM::Position pos = actor.getRefData().getPosition(); if(mDistance < 0)
if(!mStoredAvailableNodes)
{
mStoredAvailableNodes = true;
mPathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
mCellX = actor.getCell()->mCell->mData.mX;
mCellY = actor.getCell()->mCell->mData.mY;
if(!mPathgrid)
mDistance = 0;
else if(mPathgrid->mPoints.empty())
mDistance = 0; mDistance = 0;
if(mDuration < 0)
mDuration = 0;
if(mDuration == 0)
mTimeOfDay = 0;
if(mDistance) srand(time(NULL));
{ mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mXCell = 0;
mYCell = 0;
if(actor.getCell()->mCell->isExterior())
{
mXCell = mCellX * ESM::Land::REAL_SIZE;
mYCell = mCellY * ESM::Land::REAL_SIZE;
}
Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos);
npcPos[0] = npcPos[0] - mXCell;
npcPos[1] = npcPos[1] - mYCell;
for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++)
{
Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, mPathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(mPathgrid->mPoints[counter]);
}
if(!mAllowedNodes.empty())
{
Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
float closestNode = npcPos.squaredDistance(firstNodePos);
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
}
if(mAllowedNodes.empty())
mDistance = 0;
}
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY;
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
// Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY)))
mDistance = 0;
if(mChooseAction)
{
mPlayedIdle = 0; mPlayedIdle = 0;
unsigned short idleRoll = 0; mPathgrid = NULL;
mIdleChanceMultiplier =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat();
for(unsigned int counter = 1; counter < mIdle.size(); counter++) mStoredAvailableNodes = false;
mChooseAction = true;
mIdleNow = false;
mMoveNow = false;
mWalking = false;
}
AiPackage * MWMechanics::AiWander::clone() const
{
return new AiWander(*this);
}
bool AiWander::execute (const MWWorld::Ptr& actor)
{
if(mDuration)
{ {
unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; // End package if duration is complete or mid-night hits:
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp();
if(randSelect < idleChance && randSelect > idleRoll) if(currentTime.getHour() >= mStartTime.getHour() + mDuration)
{ {
mPlayedIdle = counter; if(!mRepeat)
idleRoll = randSelect; {
stopWalking(actor);
return true;
}
else
mStartTime = currentTime;
}
else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
{
if(!mRepeat)
{
stopWalking(actor);
return true;
}
else
mStartTime = currentTime;
} }
} }
if(!mPlayedIdle && mDistance) ESM::Position pos = actor.getRefData().getPosition();
{
mChooseAction = false;
mMoveNow = true;
}
else
{
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartTime = currentTime;
playIdle(actor, mPlayedIdle + 1);
mChooseAction = false;
mIdleNow = true;
}
}
if(mIdleNow) if(!mStoredAvailableNodes)
{ {
if(!checkIdle(actor, mPlayedIdle + 1)) mStoredAvailableNodes = true;
mPathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
mCellX = actor.getCell()->mCell->mData.mX;
mCellY = actor.getCell()->mCell->mData.mY;
if(!mPathgrid)
mDistance = 0;
else if(mPathgrid->mPoints.empty())
mDistance = 0;
if(mDistance)
{
mXCell = 0;
mYCell = 0;
if(actor.getCell()->mCell->isExterior())
{
mXCell = mCellX * ESM::Land::REAL_SIZE;
mYCell = mCellY * ESM::Land::REAL_SIZE;
}
Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos);
npcPos[0] = npcPos[0] - mXCell;
npcPos[1] = npcPos[1] - mYCell;
for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++)
{
Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY,
mPathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(mPathgrid->mPoints[counter]);
}
if(!mAllowedNodes.empty())
{
Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
float closestNode = npcPos.squaredDistance(firstNodePos);
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
}
if(mAllowedNodes.empty())
mDistance = 0;
}
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY;
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
stopWalking(actor);
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
stopWalking(actor);
return true;
}
}
// Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY)))
mDistance = 0;
if(mChooseAction)
{ {
mPlayedIdle = 0; mPlayedIdle = 0;
mIdleNow = false; unsigned short idleRoll = 0;
mChooseAction = true;
}
}
if(mMoveNow && mDistance) for(unsigned int counter = 1; counter < mIdle.size(); counter++)
{ {
if(!mPathFinder.isPathConstructed()) unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter];
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
if(randSelect < idleChance && randSelect > idleRoll)
{
mPlayedIdle = counter;
idleRoll = randSelect;
}
}
if(!mPlayedIdle && mDistance)
{
mChooseAction = false;
mMoveNow = true;
}
else
{
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartTime = currentTime;
playIdle(actor, mPlayedIdle + 1);
mChooseAction = false;
mIdleNow = true;
}
}
if(mIdleNow)
{ {
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); if(!checkIdle(actor, mPlayedIdle + 1))
Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); {
mPlayedIdle = 0;
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node): mIdleNow = false;
ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; mChooseAction = true;
mAllowedNodes.erase(mAllowedNodes.begin() + randNode); }
mAllowedNodes.push_back(mCurrentNode);
mCurrentNode = temp;
ESM::Pathgrid::Point dest;
dest.mX = destNodePos[0] + mXCell;
dest.mY = destNodePos[1] + mYCell;
dest.mZ = destNodePos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start,dest,mPathgrid,mXCell,mYCell);
mWalking = true;
} }
}
if(mWalking) if(mMoveNow && mDistance)
{
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
// Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be
// at the same path node at the same time and both will complete instead of endlessly walking into eachother:
Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ);
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
actorPos[0] = actorPos[0] - mXCell;
actorPos[1] = actorPos[1] - mYCell;
float distance = actorPos.squaredDistance(destNodePos);
if(distance < 1200 || mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2]))
{ {
stopWalking(actor, mPathFinder); if(!mPathFinder.isPathConstructed())
mMoveNow = false; {
mWalking = false; unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
mChooseAction = true; Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ);
ESM::Pathgrid::Point dest;
dest.mX = destNodePos[0] + mXCell;
dest.mY = destNodePos[1] + mYCell;
dest.mZ = destNodePos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false);
if(mPathFinder.isPathConstructed())
{
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
mAllowedNodes.push_back(mCurrentNode);
mCurrentNode = temp;
mMoveNow = false;
mWalking = true;
}
// Choose a different node and delete this one from possible nodes because it is uncreachable:
else
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
}
} }
}
return false; if(mWalking)
} {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
int MWMechanics::AiWander::getTypeId() const // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be
{ // at the same path node at the same time and both will complete instead of endlessly walking into eachother:
return 0; Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ);
} Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
actorPos[0] = actorPos[0] - mXCell;
actorPos[1] = actorPos[1] - mYCell;
float distance = actorPos.squaredDistance(destNodePos);
void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{ {
PathFinder pathClearer; stopWalking(actor);
path = pathClearer; mMoveNow = false;
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; mWalking = false;
} mChooseAction = true;
}
}
void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{
if(idleSelect == 2)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1);
else if(idleSelect == 3)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
else if(idleSelect == 4)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1);
else if(idleSelect == 5)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1);
else if(idleSelect == 6)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1);
else if(idleSelect == 7)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1);
else if(idleSelect == 8)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1);
else if(idleSelect == 9)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1);
}
bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{
if(idleSelect == 2)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2");
else if(idleSelect == 3)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3");
else if(idleSelect == 4)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4");
else if(idleSelect == 5)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5");
else if(idleSelect == 6)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6");
else if(idleSelect == 7)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7");
else if(idleSelect == 8)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8");
else if(idleSelect == 9)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9");
else
return false; return false;
}
int AiWander::getTypeId() const
{
return 0;
}
void AiWander::stopWalking(const MWWorld::Ptr& actor)
{
mPathFinder.clearPath();
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
}
void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{
if(idleSelect == 2)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1);
else if(idleSelect == 3)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
else if(idleSelect == 4)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1);
else if(idleSelect == 5)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1);
else if(idleSelect == 6)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1);
else if(idleSelect == 7)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1);
else if(idleSelect == 8)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1);
else if(idleSelect == 9)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1);
}
bool AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{
if(idleSelect == 2)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2");
else if(idleSelect == 3)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3");
else if(idleSelect == 4)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4");
else if(idleSelect == 5)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5");
else if(idleSelect == 6)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6");
else if(idleSelect == 7)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7");
else if(idleSelect == 8)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8");
else if(idleSelect == 9)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9");
else
return false;
}
} }

View file

@ -22,7 +22,7 @@ namespace MWMechanics
///< 0: Wander ///< 0: Wander
private: private:
void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); void stopWalking(const MWWorld::Ptr& actor);
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);

View file

@ -3,164 +3,134 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "OgreMath.h"
#include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/adjacency_list.hpp> #include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp"
#include "OgreMath.h"
namespace namespace
{ {
//helpers functions struct found_path {};
float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z)
typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS,
boost::property<boost::vertex_index_t, int, ESM::Pathgrid::Point>, boost::property<boost::edge_weight_t, float> >
PathGridGraph;
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
typedef PathGridGraph::vertex_descriptor PointID;
typedef PathGridGraph::edge_descriptor PointConnectionID;
class goalVisited : public boost::default_dijkstra_visitor
{ {
return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z)); public:
goalVisited(PointID goal) {mGoal = goal;};
void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();};
private:
PointID mGoal;
};
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) float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{ {
return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+(point.mZ - z)*(point.mZ - 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 distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{ {
return sqrt(float(a.mX - b.mX)*(a.mX - b.mX)+(a.mY - b.mY)*(a.mY - b.mY)+(a.mZ - b.mZ)*(a.mZ - b.mZ)); 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);
} }
static float sgn(float a) static float sgn(float a)
{ {
if(a>0) return 1.; if(a > 0)
else return -1.; return 1.0;
return -1.0;
} }
int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z)
{ {
if(!grid) return -1; if(!grid || grid->mPoints.empty())
if(grid->mPoints.empty()) return -1; return -1;
float m = distance(grid->mPoints[0],x,y,z); float distanceBetween = distance(grid->mPoints[0], x, y, z);
int i0 = 0; int closestIndex = 0;
for(unsigned int i=1; i<grid->mPoints.size();++i) for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++)
{ {
if(distance(grid->mPoints[i],x,y,z)<m) if(distance(grid->mPoints[counter], x, y, z) < distanceBetween)
{ {
m = distance(grid->mPoints[i],x,y,z); distanceBetween = distance(grid->mPoints[counter], x, y, z);
i0 = i; closestIndex = counter;
} }
} }
return i0;
return closestIndex;
} }
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS, PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0)
boost::property<boost::vertex_index_t,int,ESM::Pathgrid::Point>,boost::property<boost::edge_weight_t,float> > PathGridGraph;
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
typedef PathGridGraph::vertex_descriptor PointID;
typedef PathGridGraph::edge_descriptor PointConnectionID;
struct found_path {};
/*class goalVisited : public boost::default_astar_visitor
{
public:
goalVisited(PointID goal) : mGoal(goal) {}
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private:
PointID mGoal;
};
class DistanceHeuristic : public boost::atasr_heuristic <PathGridGraph, float>
{
public:
DistanceHeuristic(const PathGridGraph & l, PointID goal)
: mGraph(l), mGoal(goal) {}
float operator()(PointID u)
{
const ESM::Pathgrid::Point & U = mGraph[u];
const ESM::Pathgrid::Point & V = mGraph[mGoal];
float dx = U.mX - V.mX;
float dy = U.mY - V.mY;
float dz = U.mZ - V.mZ;
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
const PathGridGraph & mGraph;
PointID mGoal;
};*/
class goalVisited : public boost::default_dijkstra_visitor
{
public:
goalVisited(PointID goal) : mGoal(goal) {}
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private:
PointID mGoal;
};
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0)
{ {
PathGridGraph graph; PathGridGraph graph;
for(unsigned int i = 0;i<pathgrid->mPoints.size();++i) for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{ {
PointID pID = boost::add_vertex(graph); PointID pID = boost::add_vertex(graph);
graph[pID].mX = pathgrid->mPoints[i].mX + xCell; graph[pID].mX = pathgrid->mPoints[counter].mX + xCell;
graph[pID].mY = pathgrid->mPoints[i].mY + yCell; graph[pID].mY = pathgrid->mPoints[counter].mY + yCell;
graph[pID].mZ = pathgrid->mPoints[i].mZ; graph[pID].mZ = pathgrid->mPoints[counter].mZ;
} }
for(unsigned int i = 0;i<pathgrid->mEdges.size();++i) for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++)
{ {
PointID u = pathgrid->mEdges[i].mV0; PointID u = pathgrid->mEdges[counterTwo].mV0;
PointID v = pathgrid->mEdges[i].mV1; PointID v = pathgrid->mEdges[counterTwo].mV1;
PointConnectionID edge; PointConnectionID edge;
bool done; bool done;
boost::tie(edge,done) = boost::add_edge(u,v,graph); boost::tie(edge, done) = boost::add_edge(u, v, graph);
WeightMap weightmap = boost::get(boost::edge_weight, graph); WeightMap weightmap = boost::get(boost::edge_weight, graph);
weightmap[edge] = distance(graph[u],graph[v]); weightmap[edge] = distance(graph[u], graph[v]);
} }
return graph; return graph;
} }
std::list<ESM::Pathgrid::Point> findPath(PointID start,PointID end,PathGridGraph graph){ std::list<ESM::Pathgrid::Point> findPath(PointID start, PointID end, PathGridGraph graph)
{
std::vector<PointID> p(boost::num_vertices(graph)); std::vector<PointID> p(boost::num_vertices(graph));
std::vector<float> d(boost::num_vertices(graph)); std::vector<float> d(boost::num_vertices(graph));
std::list<ESM::Pathgrid::Point> shortest_path; std::list<ESM::Pathgrid::Point> shortest_path;
try { try
boost::dijkstra_shortest_paths {
( boost::dijkstra_shortest_paths(graph, start,
graph, boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end)));
start, }
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
);
} catch(found_path fg) { catch(found_path fg)
for(PointID v = end;; v = p[v]) { {
for(PointID v = end; ; v = p[v])
{
shortest_path.push_front(graph[v]); shortest_path.push_front(graph[v]);
if(p[v] == v) if(p[v] == v)
break; break;
} }
} }
return shortest_path; return shortest_path;
} }
//end of helpers functions
} }
namespace MWMechanics namespace MWMechanics
@ -170,55 +140,81 @@ namespace MWMechanics
mIsPathConstructed = false; mIsPathConstructed = false;
} }
void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, void PathFinder::clearPath()
const ESM::Pathgrid* pathGrid,float xCell,float yCell)
{ {
//first check if there is an obstacle if(!mPath.empty())
if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, mPath.clear();
endPoint.mX,endPoint.mY,endPoint.mZ) ) mIsPathConstructed = false;
{ }
int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ);
int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ);
if(start != -1 && end != -1) void PathFinder::buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint,
const ESM::Pathgrid* pathGrid, float xCell, float yCell, bool allowShortcuts)
{
if(allowShortcuts)
{
if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, endPoint.mX, endPoint.mY,
endPoint.mZ))
allowShortcuts = false;
}
if(!allowShortcuts)
{
int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ);
int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ);
if(startNode != -1 && endNode != -1)
{ {
PathGridGraph graph = buildGraph(pathGrid,xCell,yCell); PathGridGraph graph = buildGraph(pathGrid, xCell, yCell);
mPath = findPath(start,end,graph); mPath = findPath(startNode, endNode, graph);
if(!mPath.empty())
{
mPath.push_back(endPoint);
mIsPathConstructed = true;
}
} }
} }
mPath.push_back(endPoint); else
mIsPathConstructed = true;
}
float PathFinder::getZAngleToNext(float x,float y,float z)
{
if(mPath.empty())
{ {
return 0;/// shouldn't happen! mPath.push_back(endPoint);
mIsPathConstructed = true;
} }
ESM::Pathgrid::Point nextPoint = *mPath.begin();
float dX = nextPoint.mX - x; if(mPath.empty())
float dY = nextPoint.mY - y; mIsPathConstructed = false;
float h = sqrt(dX*dX+dY*dY);
return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees();
} }
bool PathFinder::checkIfNextPointReached(float x,float y,float z) float PathFinder::getZAngleToNext(float x, float y)
{
// This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call
// if otherwise).
if(mPath.empty())
return 0;
ESM::Pathgrid::Point nextPoint = *mPath.begin();
float directionX = nextPoint.mX - x;
float directionY = nextPoint.mY - y;
float directionResult = sqrt(directionX * directionX + directionY * directionY);
return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees();
}
bool PathFinder::checkPathCompleted(float x, float y, float z)
{ {
if(mPath.empty()) if(mPath.empty())
{
return true; return true;
}
ESM::Pathgrid::Point nextPoint = *mPath.begin(); ESM::Pathgrid::Point nextPoint = *mPath.begin();
if(distanceZCorrected(nextPoint,x,y,z) < 20) if(distanceZCorrected(nextPoint, x, y, z) < 40)
{ {
mPath.pop_front(); mPath.pop_front();
if(mPath.empty()) if(mPath.empty())
{ {
mIsPathConstructed = false;
return true; return true;
} }
nextPoint = *mPath.begin();
} }
return false; return false;
} }
@ -226,8 +222,10 @@ namespace MWMechanics
{ {
return mPath; return mPath;
} }
bool PathFinder::isPathConstructed() bool PathFinder::isPathConstructed()
{ {
return mIsPathConstructed; return mIsPathConstructed;
} }
} }

View file

@ -8,22 +8,24 @@ namespace MWMechanics
{ {
class PathFinder class PathFinder
{ {
public: public:
PathFinder(); PathFinder();
void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, void clearPath();
const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); void buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint,
const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0, bool allowShortcuts = 1);
bool checkIfNextPointReached(float x,float y,float z);//returns true if the last point of the path has been reached. bool checkPathCompleted(float x, float y, float z);
float getZAngleToNext(float x,float y,float z); ///< \Returns true if the last point of the path has been reached.
float getZAngleToNext(float x, float y);
std::list<ESM::Pathgrid::Point> getPath(); std::list<ESM::Pathgrid::Point> getPath();
bool isPathConstructed(); bool isPathConstructed();
private: private:
std::list<ESM::Pathgrid::Point> mPath; std::list<ESM::Pathgrid::Point> mPath;
bool mIsPathConstructed; bool mIsPathConstructed;
}; };
} }
#endif #endif

View file

@ -401,29 +401,24 @@ void RenderingManager::setWaterHeight(const float height)
void RenderingManager::skyEnable () void RenderingManager::skyEnable ()
{ {
if(mSkyManager)
mSkyManager->enable(); mSkyManager->enable();
mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); mOcclusionQuery->setSunNode(mSkyManager->getSunNode());
} }
void RenderingManager::skyDisable () void RenderingManager::skyDisable ()
{ {
if(mSkyManager) mSkyManager->disable();
mSkyManager->disable();
} }
void RenderingManager::skySetHour (double hour) void RenderingManager::skySetHour (double hour)
{ {
if(mSkyManager) mSkyManager->setHour(hour);
mSkyManager->setHour(hour);
} }
void RenderingManager::skySetDate (int day, int month) void RenderingManager::skySetDate (int day, int month)
{ {
if(mSkyManager) mSkyManager->setDate(day, month);
mSkyManager->setDate(day, month);
} }
int RenderingManager::skyGetMasserPhase() const int RenderingManager::skyGetMasserPhase() const
@ -438,8 +433,7 @@ int RenderingManager::skyGetSecundaPhase() const
} }
void RenderingManager::skySetMoonColour (bool red){ void RenderingManager::skySetMoonColour (bool red){
if(mSkyManager) mSkyManager->setMoonColour(red);
mSkyManager->setMoonColour(red);
} }
bool RenderingManager::toggleRenderMode(int mode) bool RenderingManager::toggleRenderMode(int mode)

View file

@ -32,9 +32,16 @@ namespace MWRender
extern "C" extern "C"
{ {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
// From libavformat version 55.0.100 and onward the declaration of av_gettime() is removed from libavformat/avformat.h and moved
// to libavutil/time.h
// https://github.com/FFmpeg/FFmpeg/commit/06a83505992d5f49846c18507a6c3eb8a47c650e
#if AV_VERSION_INT(55, 0, 100) <= AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO)
#include <libavutil/time.h>
#endif
} }
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)

View file

@ -32,6 +32,7 @@ Jacob Essex (Yacoby)
Jannik Heller (scrawl) Jannik Heller (scrawl)
Jason Hooks (jhooks) Jason Hooks (jhooks)
Joel Graff (graffy) Joel Graff (graffy)
John Blomberg (fstp)
Jordan Milne Jordan Milne
Julien Voisin (jvoisin/ap0) Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll) Karl-Felix Glatzer (k1ll)

View file

@ -2,9 +2,17 @@
<ui version="4.0"> <ui version="4.0">
<class>PlayPage</class> <class>PlayPage</class>
<widget class="QWidget" name="PlayPage"> <widget class="QWidget" name="PlayPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>274</width>
<height>317</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QFrame" name="Scroll"> <widget class="QWidget" name="Scroll" native="true">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">#Scroll { <string notr="true">#Scroll {
background-image: url(&quot;:/images/playpage-background.png&quot;); background-image: url(&quot;:/images/playpage-background.png&quot;);
@ -13,15 +21,6 @@
} }
</string> </string>
</property> </property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>30</number> <number>30</number>

View file

@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is an attempt at recreating the engine for the popular role-playing game OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
Version: 0.23.0 Version: 0.24.0
License: GPL (see GPL3.txt for more information) License: GPL (see GPL3.txt for more information)
Website: http://www.openmw.org Website: http://www.openmw.org
@ -94,6 +94,54 @@ Allowed options:
CHANGELOG CHANGELOG
0.24.0
Bug #284: Book's text misalignment
Bug #445: Camera able to get slightly below floor / terrain
Bug #582: Seam issue in Red Mountain
Bug #632: Journal Next Button shows white square
Bug #653: IndexedStore ignores index
Bug #694: Parser does not recognize float values starting with .
Bug #699: Resource handling broken with Ogre 1.9 trunk
Bug #718: components/esm/loadcell is using the mwworld subsystem
Bug #729: Levelled item list tries to add nonexistent item
Bug #730: Arrow buttons in the settings menu do not work.
Bug #732: Erroneous behavior when binding keys
Bug #733: Unclickable dialogue topic
Bug #734: Book empty line problem
Bug #738: OnDeath only works with implicit references
Bug #740: Script compiler fails on scripts with special names
Bug #742: Wait while no clipping
Bug #743: Problem with changeweather console command
Bug #744: No wait dialogue after starting a new game
Bug #748: Player is not able to unselect objects with the console
Bug #751: AddItem should only spawn a message box when called from dialogue
Bug #752: The enter button has several functions in trade and looting that is not impelemted.
Bug #753: Fargoth's Ring Quest Strange Behavior
Bug #755: Launcher writes duplicate lines into settings.cfg
Bug #759: Second quest in mages guild does not work
Bug #763: Enchantment cast cost is wrong
Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly
Bug #773: AIWander Isn't Being Passed The Correct idle Values
Bug #778: The journal can be opened at the start of a new game
Bug #779: Divayth Fyr starts as dead
Bug #787: "Batch count" on detailed FPS counter gets cut-off
Bug #788: chargen scroll layout does not match vanilla
Feature #60: Atlethics Skill
Feature #65: Security Skill
Feature #74: Interaction with non-load-doors
Feature #98: Render Weapon and Shield
Feature #102: AI Package: Escort, EscortCell
Feature #182: Advanced Journal GUI
Feature #288: Trading enhancements
Feature #405: Integrate "new game" into the menu
Feature #537: Highlight dialogue topic links
Feature #658: Rotate, RotateWorld script instructions and local rotations
Feature #690: Animation Layering
Feature #722: Night Eye/Blind magic effects
Feature #735: Move, MoveWorld script instructions.
Feature #760: Non-removable corpses
0.23.0 0.23.0
Bug #522: Player collides with placeable items Bug #522: Player collides with placeable items