diff --git a/CMakeLists.txt b/CMakeLists.txt index e8cfdcbdf..df3b95eee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ if (APPLE) set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") 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) # Macros @@ -15,7 +19,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 23) +set (OPENMW_VERSION_MINOR 24) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index e5da3431a..8cc476f53 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -310,6 +310,8 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) bool MainDialog::setupLauncherSettings() { + mLauncherSettings.setMultiValueEnabled(true); + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QStringList paths; @@ -427,6 +429,8 @@ bool MainDialog::setupGameSettings() bool MainDialog::setupGraphicsSettings() { + mGraphicsSettings.setMultiValueEnabled(false); + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 9a9b8df41..1b0a2e9bf 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -103,8 +103,8 @@ bool GameSettings::readFile(QTextStream &stream) if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1); - QString value = keyRe.cap(2); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); // Don't remove existing data entries if (key != QLatin1String("data")) diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index 21029b3ad..6bf2eff66 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -14,7 +14,7 @@ class SettingsBase { public: - SettingsBase() {} + SettingsBase() { mMultiValue = false; } ~SettingsBase() {} inline QString value(const QString &key, const QString &defaultValue = QString()) @@ -36,6 +36,11 @@ public: mSettings.insertMulti(key, value); } + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } + inline void remove(const QString &key) { mSettings.remove(key); @@ -66,8 +71,8 @@ public: if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1); - QString value = keyRe.cap(2); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); @@ -75,8 +80,13 @@ public: mSettings.remove(key); QStringList values = mCache.values(key); + 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: Map mSettings; Map mCache; + + bool mMultiValue; }; #endif // SETTINGSBASE_HPP diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 9787719af..158f7d7f1 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -79,6 +79,7 @@ opencs_units (view/settings abstractwidget usersettingsdialog editorpage + windowpage ) opencs_units_noqt (view/settings diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 8dc5366a9..991d59537 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -61,6 +61,17 @@ void CS::Editor::setupDataFiles() QString path = QString::fromStdString(iter->string()); 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() diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 1c4bcb1ee..380e434c2 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -10,6 +10,7 @@ #include "view/doc/viewmanager.hpp" #include "view/doc/startup.hpp" #include "view/doc/filedialog.hpp" +#include "model/settings/usersettings.hpp" namespace CS { @@ -17,6 +18,7 @@ namespace CS { Q_OBJECT + CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index aabda86b3..79de124c2 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -29,17 +30,25 @@ namespace boost } /* namespace boost */ #endif /* (BOOST_VERSION <= 104600) */ +CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0; CSMSettings::UserSettings::UserSettings() { + assert(!mUserSettingsInstance); mUserSettingsInstance = this; } 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); @@ -63,7 +72,7 @@ QFile *CSMSettings::UserSettings::openFile (const QString &filename) return file; } -bool CSMSettings::UserSettings::writeFile(QFile *file, QMap &settings) +bool CSMSettings::UserSettings::writeFile(QFile *file, QMap &settings) const { if (!file) return false; @@ -88,7 +97,7 @@ bool CSMSettings::UserSettings::writeFile(QFile *file, QMapCould not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").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; +} + diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 343702eda..6a2628fd1 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -24,22 +24,27 @@ namespace CSMSettings { public: - static UserSettings &instance() - { - static UserSettings instance; + UserSettings(); + ~UserSettings(); - return instance; - } + static const UserSettings& instance(); - QFile *openFile (const QString &); - bool writeFile(QFile *file, QMap §ions); - void getSettings (QTextStream &stream, SectionMap &settings); + void readSettings(); + void setSettingsFiles(QStringList files); + + QFile *openFile (const QString &) const; + bool writeFile(QFile *file, QMap §ions) const; + void getSettings (QTextStream &stream, SectionMap &settings) const; + QStringList getSettingsFiles () const; + CSMSettings::SectionMap getSettingsMap() const; + QString getSettingValue(QString section, QString setting) const; private: - UserSettings *mUserSettingsInstance; - UserSettings(); - ~UserSettings(); + static UserSettings *mUserSettingsInstance; + + CSMSettings::SectionMap mSectionMap; + QStringList mSettingsFiles; UserSettings (UserSettings const &); //not implemented void operator= (UserSettings const &); //not implemented diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 2857f4a54..1e19e6db4 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "../../model/doc/document.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), 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); diff --git a/apps/opencs/view/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp index 012fc0408..d657ba06b 100644 --- a/apps/opencs/view/settings/usersettingsdialog.cpp +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -9,20 +9,21 @@ #include #include #include +#include #include "blankpage.hpp" #include "editorpage.hpp" +#include "windowpage.hpp" #include "../../model/settings/support.hpp" #include "settingwidget.hpp" -#include CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : QMainWindow (parent), mStackedWidget (0) { setWindowTitle(QString::fromUtf8 ("User Settings")); buildPages(); - setWidgetStates (loadSettings()); + setWidgetStates (CSMSettings::UserSettings::instance().getSettingsMap()); positionWindow (); connect (mListWidget, @@ -76,9 +77,10 @@ void CSVSettings::UserSettingsDialog::buildPages() setDockOptions (QMainWindow::AllowNestedDocks); //uncomment to test with sample editor page. //createSamplePage(); - createPage("Page1"); + /*createPage("Page1"); createPage("Page2"); - createPage("Page3"); + createPage("Page3");*/ + createWindowPage(); } void CSVSettings::UserSettingsDialog::createSamplePage() @@ -95,6 +97,20 @@ void CSVSettings::UserSettingsDialog::createSamplePage() &(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 () { 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("
Could not open %0 for reading

\ - Please make sure you have the right permissions \ - and try again.
").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() { @@ -154,7 +130,9 @@ void CSVSettings::UserSettingsDialog::writeSettings() 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); } diff --git a/apps/opencs/view/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp index 31d40ca01..71a4cb2f8 100644 --- a/apps/opencs/view/settings/usersettingsdialog.hpp +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -25,7 +25,6 @@ namespace CSVSettings { { Q_OBJECT - QStringList mPaths; QListWidget *mListWidget; QStackedWidget *mStackedWidget; Files::ConfigurationManager mCfgMgr; @@ -41,10 +40,12 @@ namespace CSVSettings { void setWidgetStates (CSMSettings::SectionMap settingsMap); void buildPages(); void positionWindow (); - CSMSettings::SectionMap loadSettings(); void writeSettings(); void createSamplePage(); + //Pages + void createWindowPage(); + template void createPage (const QString &title) { diff --git a/apps/opencs/view/settings/windowpage.cpp b/apps/opencs/view/settings/windowpage.cpp new file mode 100644 index 000000000..e8677fa42 --- /dev/null +++ b/apps/opencs/view/settings/windowpage.cpp @@ -0,0 +1,114 @@ +#include "windowpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#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 (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); +} diff --git a/apps/opencs/view/settings/windowpage.hpp b/apps/opencs/view/settings/windowpage.hpp new file mode 100644 index 000000000..7978263fc --- /dev/null +++ b/apps/opencs/view/settings/windowpage.hpp @@ -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 diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 3b5deb5ac..f24922e23 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -130,26 +130,12 @@ namespace MWGui void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) { - if ((mCurrentPage+1)*2 < mPages.size()) - { - MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); - - ++mCurrentPage; - - updatePages(); - } + nextPage(); } void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) { - if (mCurrentPage > 0) - { - MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); - - --mCurrentPage; - - updatePages(); - } + prevPage(); } void BookWindow::updatePages() @@ -194,5 +180,28 @@ namespace MWGui if (button->getAlign().isRight()) 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(); + } + } } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index c6ea486d4..ef87dd9c4 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -16,7 +16,8 @@ namespace MWGui void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); - + void nextPage(); + void prevPage(); void setInventoryAllowed(bool allowed); protected: diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index aeff573ae..58d963ce8 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -251,10 +251,8 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); } - boost::algorithm::replace_all(text, "\n", "\n"); - boost::algorithm::replace_all(text, "\r", "\r"); - boost::algorithm::replace_all(text, "
", "\n\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); // tweaking by adding another newline to see if that spaces out better + boost::algorithm::replace_all(text, "
", "\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); boost::algorithm::trim_left(text); // remove trailing " diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 5b84c99a5..ab8dc1584 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -183,7 +183,7 @@ namespace if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) { MWBase::Environment::get().getWindowManager()->popGuiMode (); - } + } mModel->load (); setBookMode (); @@ -433,7 +433,6 @@ namespace void notifyClose(MyGUI::Widget* _sender) { MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); - MWBase::Environment::get().getWindowManager ()->popGuiMode (); } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 4a78238ce..7098853af 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -34,6 +34,10 @@ namespace MWGui { } + LocalMapBase::~LocalMapBase() + { + } + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) { mLocalMap = widget; diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 18c81a060..3aefc398c 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -14,6 +14,7 @@ namespace MWGui { public: LocalMapBase(); + virtual ~LocalMapBase(); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop=false); void setCellPrefix(const std::string& prefix); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 45cf1b0aa..c4c1be711 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -416,6 +416,10 @@ namespace MWGui mAddEffectDialog.setVisible (false); } + EffectEditorBase::~EffectEditorBase() + { + } + void EffectEditorBase::startEditing () { // get the list of magic effects that are known to the player diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 5ad306fbe..61b888491 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -84,7 +84,7 @@ namespace MWGui { public: EffectEditorBase(); - + virtual ~EffectEditorBase(); protected: std::map mButtonMapping; // maps button ID to effect ID diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e3fc0ba2a..f53508f86 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwgui/bookwindow.hpp" using namespace ICS; @@ -488,6 +489,15 @@ namespace MWInput mMouseWheel = int(arg.z); 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) @@ -636,7 +646,7 @@ namespace MWInput // Toggle between game mode and journal mode bool gameMode = !mWindows.isGuiMode(); - if(gameMode) + if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); mWindows.pushGuiMode(MWGui::GM_Journal); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5b94c4938..6af87e8bd 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -13,8 +13,9 @@ namespace { float sgn(float a) { - if(a > 0) return 1.0; - else return -1.0; + if(a > 0) + return 1.0; + return -1.0; } } @@ -24,151 +25,156 @@ namespace TODO: Take account for actors being in different cells. */ -MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) -: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) +namespace MWMechanics { - 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 + AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) + : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) { - 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().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; + + // 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 - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - mMaxDist = 330; + 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); + } } - return false; -} - -int MWMechanics::AiEscort::getTypeId() const -{ - return 2; + + AiEscort *MWMechanics::AiEscort::clone() const + { + return new AiEscort(*this); + } + + 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().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; + } } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index fbae5c1d2..90365c16b 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -2,99 +2,106 @@ #include "movement.hpp" -#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" namespace { float sgn(float a) { - if(a > 0) return 1.; - else return -1.; + if(a > 0) + return 1.0; + return -1.0; } } -MWMechanics::AiTravel::AiTravel(float x, float y, float z) -: mX(x),mY(y),mZ(z),mPathFinder() +namespace MWMechanics { -} + AiTravel::AiTravel(float x, float y, float z) + : mX(x),mY(y),mZ(z),mPathFinder() + { + } -MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const -{ - return new AiTravel(*this); -} + AiTravel *MWMechanics::AiTravel::clone() const + { + return new AiTravel(*this); + } -bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) -{ - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - ESM::Position pos = actor.getRefData().getPosition(); + bool AiTravel::execute (const MWWorld::Ptr& actor) + { + 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().search(*actor.getCell()->mCell); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - 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 aitravel. - if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) + 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 aitravel. + 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 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; 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); - //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; - } + return 1; } - - 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; -} diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 46c5598cc..8f7926236 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -14,310 +14,324 @@ namespace { float sgn(float a) { - if(a > 0) return 1.0; - else return -1.0; + if(a > 0) + return 1.0; + return -1.0; } } -MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): - mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) +namespace MWMechanics { - for(unsigned short counter = 0; counter < mIdle.size(); counter++) + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) { - if(mIdle[counter] >= 127 || mIdle[counter] < 0) - 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().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) + for(unsigned short counter = 0; counter < mIdle.size(); counter++) { - if(!mRepeat) - { - stopWalking(actor, mPathFinder); - return true; - } - else - mStartTime = currentTime; + if(mIdle[counter] >= 127 || mIdle[counter] < 0) + mIdle[counter] = 0; } - 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(!mStoredAvailableNodes) - { - mStoredAvailableNodes = true; - mPathgrid = - MWBase::Environment::get().getWorld()->getStore().get().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()) + if(mDistance < 0) mDistance = 0; + if(mDuration < 0) + mDuration = 0; + if(mDuration == 0) + mTimeOfDay = 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)) - { - 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) - { + srand(time(NULL)); + mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; - unsigned short idleRoll = 0; + mPathgrid = NULL; + mIdleChanceMultiplier = + MWBase::Environment::get().getWorld()->getStore().get().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]; - unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); - if(randSelect < idleChance && randSelect > idleRoll) + // End package if duration is complete or mid-night hits: + MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + if(currentTime.getHour() >= mStartTime.getHour() + mDuration) { - mPlayedIdle = counter; - idleRoll = randSelect; + if(!mRepeat) + { + 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) - { - 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; - } - } + ESM::Position pos = actor.getRefData().getPosition(); - if(mIdleNow) - { - if(!checkIdle(actor, mPlayedIdle + 1)) + if(!mStoredAvailableNodes) + { + mStoredAvailableNodes = true; + mPathgrid = + MWBase::Environment::get().getWorld()->getStore().get().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; - mIdleNow = false; - mChooseAction = true; - } - } + unsigned short idleRoll = 0; - if(mMoveNow && mDistance) - { - if(!mPathFinder.isPathConstructed()) + for(unsigned int counter = 1; counter < mIdle.size(); counter++) + { + 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()); - Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); - - // 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; - - 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(!checkIdle(actor, mPlayedIdle + 1)) + { + mPlayedIdle = 0; + mIdleNow = false; + mChooseAction = true; + } } - } - if(mWalking) - { - 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])) + if(mMoveNow && mDistance) { - stopWalking(actor, mPathFinder); - mMoveNow = false; - mWalking = false; - mChooseAction = true; + if(!mPathFinder.isPathConstructed()) + { + unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); + 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 -{ - return 0; -} + // 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); -void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) -{ - PathFinder pathClearer; - path = pathClearer; - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; -} + if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + stopWalking(actor); + mMoveNow = false; + 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; + } + + 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; + } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index cf3820527..c82ccc215 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -22,7 +22,7 @@ namespace MWMechanics ///< 0: Wander private: - void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); + void stopWalking(const MWWorld::Ptr& actor); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 62c825be7..986595a9a 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -3,164 +3,134 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "OgreMath.h" + #include #include -#include "boost/tuple/tuple.hpp" -#include "OgreMath.h" namespace { - //helpers functions - float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z) + struct found_path {}; + + typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, + boost::property, boost::property > + PathGridGraph; + typedef boost::property_map::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) { - if(a>0) return 1.; - else return -1.; + if(a > 0) + 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->mPoints.empty()) return -1; + if(!grid || grid->mPoints.empty()) + return -1; - float m = distance(grid->mPoints[0],x,y,z); - int i0 = 0; + float distanceBetween = distance(grid->mPoints[0], x, y, z); + int closestIndex = 0; - for(unsigned int i=1; imPoints.size();++i) + for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) { - if(distance(grid->mPoints[i],x,y,z)mPoints[counter], x, y, z) < distanceBetween) { - m = distance(grid->mPoints[i],x,y,z); - i0 = i; + distanceBetween = distance(grid->mPoints[counter], x, y, z); + closestIndex = counter; } } - return i0; + + return closestIndex; } - typedef boost::adjacency_list,boost::property > PathGridGraph; - typedef boost::property_map::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 - { - 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 buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0) { PathGridGraph graph; - for(unsigned int i = 0;imPoints.size();++i) + for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { PointID pID = boost::add_vertex(graph); - graph[pID].mX = pathgrid->mPoints[i].mX + xCell; - graph[pID].mY = pathgrid->mPoints[i].mY + yCell; - graph[pID].mZ = pathgrid->mPoints[i].mZ; + graph[pID].mX = pathgrid->mPoints[counter].mX + xCell; + graph[pID].mY = pathgrid->mPoints[counter].mY + yCell; + graph[pID].mZ = pathgrid->mPoints[counter].mZ; } - for(unsigned int i = 0;imEdges.size();++i) + for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++) { - PointID u = pathgrid->mEdges[i].mV0; - PointID v = pathgrid->mEdges[i].mV1; + PointID u = pathgrid->mEdges[counterTwo].mV0; + PointID v = pathgrid->mEdges[counterTwo].mV1; PointConnectionID edge; 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[edge] = distance(graph[u],graph[v]); - + weightmap[edge] = distance(graph[u], graph[v]); } return graph; } - std::list findPath(PointID start,PointID end,PathGridGraph graph){ + std::list findPath(PointID start, PointID end, PathGridGraph graph) + { std::vector p(boost::num_vertices(graph)); std::vector d(boost::num_vertices(graph)); std::list shortest_path; - try { - boost::dijkstra_shortest_paths - ( - graph, - start, - boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph)) - ); + try + { + boost::dijkstra_shortest_paths(graph, start, + boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))); + } - } catch(found_path fg) { - for(PointID v = end;; v = p[v]) { + catch(found_path fg) + { + for(PointID v = end; ; v = p[v]) + { shortest_path.push_front(graph[v]); if(p[v] == v) break; } } + return shortest_path; } - - //end of helpers functions - } namespace MWMechanics @@ -170,55 +140,81 @@ namespace MWMechanics mIsPathConstructed = false; } - void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid,float xCell,float yCell) + void PathFinder::clearPath() { - //first check if there is an obstacle - if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, - endPoint.mX,endPoint.mY,endPoint.mZ) ) - { - 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(!mPath.empty()) + mPath.clear(); + mIsPathConstructed = false; + } - 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); - mPath = findPath(start,end,graph); + PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); + mPath = findPath(startNode, endNode, graph); + + if(!mPath.empty()) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } } } - mPath.push_back(endPoint); - mIsPathConstructed = true; - } - - float PathFinder::getZAngleToNext(float x,float y,float z) - { - if(mPath.empty()) + else { - return 0;/// shouldn't happen! + mPath.push_back(endPoint); + mIsPathConstructed = true; } - ESM::Pathgrid::Point nextPoint = *mPath.begin(); - float dX = nextPoint.mX - x; - float dY = nextPoint.mY - y; - float h = sqrt(dX*dX+dY*dY); - return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees(); + + if(mPath.empty()) + mIsPathConstructed = false; } - 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()) - { return true; - } + ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(distanceZCorrected(nextPoint,x,y,z) < 20) + if(distanceZCorrected(nextPoint, x, y, z) < 40) { mPath.pop_front(); if(mPath.empty()) { + mIsPathConstructed = false; return true; } - nextPoint = *mPath.begin(); } + return false; } @@ -226,8 +222,10 @@ namespace MWMechanics { return mPath; } + bool PathFinder::isPathConstructed() { return mIsPathConstructed; } -} \ No newline at end of file +} + diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index b1bbab37a..1727c650f 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -8,22 +8,24 @@ namespace MWMechanics { class PathFinder { - public: - PathFinder(); + public: + PathFinder(); - void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); + void clearPath(); + 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. - float getZAngleToNext(float x,float y,float z); + bool checkPathCompleted(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 getPath(); - bool isPathConstructed(); + std::list getPath(); + bool isPathConstructed(); - private: - std::list mPath; - bool mIsPathConstructed; + private: + std::list mPath; + bool mIsPathConstructed; }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9de183978..c8b496f6b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -401,29 +401,24 @@ void RenderingManager::setWaterHeight(const float height) void RenderingManager::skyEnable () { - if(mSkyManager) mSkyManager->enable(); - mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); } void RenderingManager::skyDisable () { - if(mSkyManager) - mSkyManager->disable(); + mSkyManager->disable(); } void RenderingManager::skySetHour (double hour) { - if(mSkyManager) - mSkyManager->setHour(hour); + mSkyManager->setHour(hour); } void RenderingManager::skySetDate (int day, int month) { - if(mSkyManager) - mSkyManager->setDate(day, month); + mSkyManager->setDate(day, month); } int RenderingManager::skyGetMasserPhase() const @@ -438,8 +433,7 @@ int RenderingManager::skyGetSecundaPhase() const } void RenderingManager::skySetMoonColour (bool red){ - if(mSkyManager) - mSkyManager->setMoonColour(red); + mSkyManager->setMoonColour(red); } bool RenderingManager::toggleRenderMode(int mode) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 87ae8175d..1b5eddc21 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -32,9 +32,16 @@ namespace MWRender extern "C" { -#include -#include -#include + #include + #include + #include + + // 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 + #endif } #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) diff --git a/credits.txt b/credits.txt index 390a2d721..630f6ff39 100644 --- a/credits.txt +++ b/credits.txt @@ -32,6 +32,7 @@ Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) Joel Graff (graffy) +John Blomberg (fstp) Jordan Milne Julien Voisin (jvoisin/ap0) Karl-Felix Glatzer (k1ll) diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui index c0320de1e..bf883b96e 100644 --- a/files/ui/playpage.ui +++ b/files/ui/playpage.ui @@ -2,9 +2,17 @@ PlayPage + + + 0 + 0 + 274 + 317 + + - + #Scroll { background-image: url(":/images/playpage-background.png"); @@ -13,15 +21,6 @@ } - - QFrame::StyledPanel - - - QFrame::Plain - - - 0 - 30 diff --git a/readme.txt b/readme.txt index f8361780c..67c02cc61 100644 --- a/readme.txt +++ b/readme.txt @@ -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 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) Website: http://www.openmw.org @@ -94,6 +94,54 @@ Allowed options: 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 Bug #522: Player collides with placeable items