diff --git a/CMakeLists.txt b/CMakeLists.txt index b989297b39..f86d92aef0 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}") @@ -181,6 +185,12 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() +include (CheckIncludeFileCXX) +check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) +if (HAVE_UNORDERED_MAP) + add_definitions(-DHAVE_UNORDERED_MAP) +endif () + set(BOOST_COMPONENTS system filesystem program_options thread date_time wave) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index e5da3431ab..8cc476f53f 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 9a9b8df41b..1b0a2e9bf7 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 21029b3ad9..6bf2eff66a 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 85b5ba3b60..501958714e 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -80,6 +80,7 @@ opencs_units (view/settings usersettingsdialog samplepage editorpage + windowpage ) opencs_units_noqt (view/settings diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 8dc5366a98..991d59537a 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 1c4bcb1eeb..380e434c24 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 faaa2509b1..ecdfcd71b8 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -7,8 +7,11 @@ #include #include #include + #include +#include + #include #include "settingcontainer.hpp" @@ -32,8 +35,11 @@ namespace boost } /* namespace boost */ #endif /* (BOOST_VERSION <= 104600) */ +CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0; + CSMSettings::UserSettings::UserSettings() { + assert(!mUserSettingsInstance); mUserSettingsInstance = this; mReadWriteMessage = QObject::tr("
Could not open or create file for writing

\ @@ -45,9 +51,18 @@ CSMSettings::UserSettings::UserSettings() CSMSettings::UserSettings::~UserSettings() { + mUserSettingsInstance = 0; } -QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) + +//QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) + +CSMSettings::SectionMap CSMSettings::UserSettings::getSettingsMap() const +{ + return mSectionMap; +} + +QFile *CSMSettings::UserSettings::openFile (const QString &filename) const { QFile *file = new QFile(filePath); @@ -91,6 +106,9 @@ QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, } bool CSMSettings::UserSettings::writeFile(QMap &settings) + +//bool CSMSettings::UserSettings::writeFile(QFile *file, QMap &settings) const + { QTextStream *stream = openFileStream(mPaths.back()); @@ -111,7 +129,11 @@ bool CSMSettings::UserSettings::writeFile(QMapobjectName(), setting->getValue()); } } + +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("
Could 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 3a7bbd1eb2..f5eb5e80d6 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -36,13 +36,15 @@ namespace CSMSettings { public: - static UserSettings &instance() - { - static UserSettings instance; + UserSettings(); + ~UserSettings(); - return instance; - } + static const UserSettings& instance(); + void readSettings(); + void setSettingsFiles(QStringList files); + +<<<<<<< HEAD bool writeFile(QMap §ions); const SectionMap &getSettings (); void updateSettings (const QString §ionName, const QString &settingName = ""); @@ -52,6 +54,21 @@ namespace CSMSettings { UserSettings(); ~UserSettings(); +======= + 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: + + static UserSettings *mUserSettingsInstance; + + CSMSettings::SectionMap mSectionMap; + QStringList mSettingsFiles; +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 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 f85a822d8d..7e5a14c9ce 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 c48e0ee977..ac1a5ead7c 100644 --- a/apps/opencs/view/settings/usersettingsdialog.cpp +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -9,10 +9,19 @@ #include #include #include +<<<<<<< HEAD #include #include "blankpage.hpp" #include "samplepage.hpp" +======= +#include + +#include "blankpage.hpp" +#include "editorpage.hpp" +#include "windowpage.hpp" +#include "../../model/settings/support.hpp" +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 #include "../../model/settings/support.hpp" #include @@ -23,7 +32,11 @@ CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : { setWindowTitle(QString::fromUtf8 ("User Settings")); buildPages(); +<<<<<<< HEAD setWidgetStates (); +======= + setWidgetStates (CSMSettings::UserSettings::instance().getSettingsMap()); +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 positionWindow (); connect (mListWidget, @@ -85,11 +98,17 @@ void CSVSettings::UserSettingsDialog::buildPages() setDockOptions (QMainWindow::AllowNestedDocks); //uncomment to test with sample editor page. +<<<<<<< HEAD // TODO: Reimplement sample page using createPage function //createPage("Sample"); createPage("Editor"); +======= + //createSamplePage(); + /*createPage("Page1"); +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 createPage("Page2"); - createPage("Page3"); + createPage("Page3");*/ + createWindowPage(); } void CSVSettings::UserSettingsDialog::createSamplePage() @@ -108,6 +127,22 @@ void CSVSettings::UserSettingsDialog::createSamplePage() new QListWidgetItem (page->objectName(), mListWidget); } +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 &))); +} + +<<<<<<< HEAD +======= void CSVSettings::UserSettingsDialog::positionWindow () { QRect scr = QApplication::desktop()->screenGeometry(); @@ -116,6 +151,8 @@ void CSVSettings::UserSettingsDialog::positionWindow () } + +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 void CSVSettings::UserSettingsDialog::writeSettings() { QMap settings; @@ -126,7 +163,13 @@ void CSVSettings::UserSettingsDialog::writeSettings() settings [page->objectName()] = page->getSettings(); } +<<<<<<< HEAD CSMSettings::UserSettings::instance().writeFile(settings); +======= + QStringList paths = CSMSettings::UserSettings::instance().getSettingsFiles(); + + CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(paths.back()), settings); +>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826 } diff --git a/apps/opencs/view/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp index 5e73fd1235..620bf2f432 100644 --- a/apps/opencs/view/settings/usersettingsdialog.hpp +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -24,7 +24,6 @@ namespace CSVSettings { { Q_OBJECT - QStringList mPaths; QListWidget *mListWidget; QStackedWidget *mStackedWidget; @@ -42,6 +41,9 @@ namespace CSVSettings { 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 0000000000..e8677fa428 --- /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 0000000000..7978263fc7 --- /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/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 660e451156..31f6fca79e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -33,7 +33,7 @@ add_openmw_dir (mwgui enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview - tradeitemmodel companionitemmodel pickpocketitemmodel + tradeitemmodel companionitemmodel pickpocketitemmodel fontloader ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index abc9517269..89d34bd410 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -380,7 +380,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setWindowManager (new MWGui::WindowManager( mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), - mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage)); + mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding)); if (mNewGame) mEnvironment.getWindowManager()->setNewGame(true); diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 38794269b9..7e09f9b4d7 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -112,6 +112,8 @@ namespace MWBase virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the scene should be ignored. + + virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index de1a347925..86a6a89d21 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -342,6 +342,8 @@ namespace MWBase virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; ///< get all containers in active cells owned by this Npc + virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + ///< get all items in active cells owned by this Npc virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a4eda71267..a263af4645 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -5,6 +5,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" +#include "../mwmechanics/movement.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -29,6 +30,7 @@ namespace { MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore mContainerStore; + MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; }; @@ -47,6 +49,18 @@ namespace MWClass { std::auto_ptr data (new CustomData); + static bool inited = false; + if(!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); + fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); + + inited = true; + } + MWWorld::LiveCellRef *ref = ptr.get(); // creature stats @@ -181,6 +195,42 @@ namespace MWClass return true; } + float Creature::getSpeed(const MWWorld::Ptr &ptr) const + { + MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); + /// \todo what about the rest? + return walkSpeed; + } + + MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const + { + ensureCustomData (ptr); + + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + } + + Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mPosition); + movement.mPosition[0] = 0.0f; + movement.mPosition[1] = 0.0f; + movement.mPosition[2] = 0.0f; + return vec; + } + + Ogre::Vector3 Creature::getRotationVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mRotation); + movement.mRotation[0] = 0.0f; + movement.mRotation[1] = 0.0f; + movement.mRotation[2] = 0.0f; + return vec; + } + MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -249,4 +299,7 @@ namespace MWClass return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); } + + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; + const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index b5657e265a..5c30004cd0 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -12,6 +12,9 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + static const ESM::GameSetting *fMinWalkSpeedCreature; + static const ESM::GameSetting *fMaxWalkSpeedCreature; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -66,6 +69,18 @@ namespace MWClass virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; + ///< Return desired movement. + + virtual Ogre::Vector3 getMovementVector (const MWWorld::Ptr& ptr) const; + ///< Return desired movement vector (determined based on movement settings, + /// stance and stats). + + virtual Ogre::Vector3 getRotationVector (const MWWorld::Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + + float getSpeed (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index ef688be1bd..94f834a4d2 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -223,6 +223,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Style * createStyle (char const * fontName, Colour fontColour) { + if (strcmp(fontName, "") == 0) + return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); + for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) if (i->match (fontName, fontColour, fontColour, fontColour, 0)) return &*i; @@ -405,7 +408,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - space_width += gi->advance; + if (gi) + space_width += gi->advance + gi->bearingX; stream.consume (); } @@ -414,7 +418,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - word_width += gi->advance + gi->bearingX; + if (gi) + word_width += gi->advance + gi->bearingX; word_height = line_height; ++character_count; stream.consume (); @@ -628,6 +633,9 @@ namespace { MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + if (!gi) + return; + MyGUI::FloatRect vr; vr.left = mCursor.left + gi->bearingX; @@ -647,7 +655,8 @@ namespace { MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); - mCursor.left += gi->bearingX + gi->advance; + if (gi) + mCursor.left += gi->bearingX + gi->advance; } private: diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 79351c6ca0..5d23744b23 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -3,11 +3,39 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +namespace +{ + + bool stacks (const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + if (left == right) + return true; + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (left.getContainerStore() && right.getContainerStore()) + return left.getContainerStore()->stacks(left, right) + && right.getContainerStore()->stacks(left, right); + + if (left.getContainerStore()) + return left.getContainerStore()->stacks(left, right); + if (right.getContainerStore()) + return right.getContainerStore()->stacks(left, right); + + MWWorld::ContainerStore store; + return store.stacks(left, right); + } + +} + namespace MWGui { -ContainerItemModel::ContainerItemModel(const std::vector& itemSources) +ContainerItemModel::ContainerItemModel(const std::vector& itemSources, const std::vector& worldItems) : mItemSources(itemSources) + , mWorldItems(worldItems) { assert (mItemSources.size()); } @@ -65,8 +93,7 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count) for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure - if (*it == item.mBase || (store.stacks(*it, item.mBase) && item.mBase.getContainerStore()->stacks(*it, item.mBase))) + if (stacks(*it, item.mBase)) { int refCount = it->getRefData().getCount(); it->getRefData().setCount(std::max(0, refCount - toRemove)); @@ -76,6 +103,21 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count) } } } + for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) + { + if (stacks(*source, item.mBase)) + { + int refCount = source->getRefData().getCount(); + if (refCount - toRemove <= 0) + MWBase::Environment::get().getWorld()->deleteObject(*source); + else + source->getRefData().setCount(std::max(0, refCount - toRemove)); + toRemove -= refCount; + if (toRemove <= 0) + return; + } + } + throw std::runtime_error("Not enough items to remove could be found"); } @@ -91,8 +133,7 @@ void ContainerItemModel::update() std::vector::iterator itemStack = mItems.begin(); for (; itemStack != mItems.end(); ++itemStack) { - // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure - if (store.stacks(itemStack->mBase, *it) && it->getContainerStore()->stacks(itemStack->mBase, *it)) + if (stacks(*it, itemStack->mBase)) { // we already have an item stack of this kind, add to it itemStack->mCount += it->getRefData().getCount(); @@ -108,6 +149,26 @@ void ContainerItemModel::update() } } } + for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) + { + std::vector::iterator itemStack = mItems.begin(); + for (; itemStack != mItems.end(); ++itemStack) + { + if (stacks(*source, itemStack->mBase)) + { + // we already have an item stack of this kind, add to it + itemStack->mCount += source->getRefData().getCount(); + break; + } + } + + if (itemStack == mItems.end()) + { + // no stack yet, create one + ItemStack newItem (*source, this, source->getRefData().getCount()); + mItems.push_back(newItem); + } + } } } diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 2f4c708bae..22595582ef 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -11,7 +11,7 @@ namespace MWGui class ContainerItemModel : public ItemModel { public: - ContainerItemModel (const std::vector& itemSources); + ContainerItemModel (const std::vector& itemSources, const std::vector& worldItems); ///< @note The order of elements \a itemSources matters here. The first element has the highest priority for removal, /// while the last element will be used to add new items to. @@ -28,6 +28,7 @@ namespace MWGui private: std::vector mItemSources; + std::vector mWorldItems; std::vector mItems; }; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 2eacd973e8..64c61abc09 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -115,7 +115,7 @@ namespace MWGui void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { - BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + BookTypesetter::Style* title = typesetter->createStyle("", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); typesetter->sectionBreak(9); if (mTitle != "") typesetter->write(title, to_utf8_span(mTitle.c_str())); @@ -159,7 +159,7 @@ namespace MWGui if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) { - BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); + BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { @@ -197,7 +197,7 @@ namespace MWGui void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const { - BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); + BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f); const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f); @@ -215,7 +215,7 @@ namespace MWGui void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { - BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + BookTypesetter::Style* title = typesetter->createStyle("", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); typesetter->sectionBreak(9); typesetter->write(title, to_utf8_span(mText.c_str())); } @@ -465,7 +465,7 @@ namespace MWGui (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); - BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White); + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); // choices const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f); diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 799a89ab5a..98ba8ec2f2 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -61,30 +61,32 @@ namespace MWGui void EnchantingDialog::updateLabels() { std::stringstream enchantCost; - enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost(); + enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantPoints(); mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); - mCastCost->setCaption(boost::lexical_cast(mEnchanting.getEnchantCost())); + std::stringstream castCost; + castCost << std::setprecision(1) << std::fixed << mEnchanting.getCastCost(); + mCastCost->setCaption(boost::lexical_cast(castCost.str())); mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); - switch(mEnchanting.getEnchantType()) + switch(mEnchanting.getCastStyle()) { - case 0: + case ESM::Enchantment::CastOnce: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); mAddEffectDialog.constantEffect=false; break; - case 1: + case ESM::Enchantment::WhenStrikes: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); mAddEffectDialog.constantEffect=false; break; - case 2: + case ESM::Enchantment::WhenUsed: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); mAddEffectDialog.constantEffect=false; break; - case 3: + case ESM::Enchantment::ConstantEffect: mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); mAddEffectDialog.constantEffect=true; break; @@ -169,7 +171,7 @@ namespace MWGui image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem); mEnchanting.setOldItem(item); - mEnchanting.nextEnchantType(); + mEnchanting.nextCastStyle(); updateLabels(); } @@ -248,7 +250,7 @@ namespace MWGui void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) { - mEnchanting.nextEnchantType(); + mEnchanting.nextCastStyle(); updateLabels(); } @@ -278,7 +280,7 @@ namespace MWGui return; } - if (mEnchanting.getEnchantCost() > mEnchanting.getMaxEnchantValue()) + if (mEnchanting.getEnchantPoints() > mEnchanting.getMaxEnchantValue()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); return; diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp new file mode 100644 index 0000000000..ff160105a3 --- /dev/null +++ b/apps/openmw/mwgui/fontloader.cpp @@ -0,0 +1,238 @@ +#include "fontloader.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace +{ + unsigned long utf8ToUnicode(const std::string& utf8) + { + size_t i = 0; + unsigned long unicode; + size_t todo; + unsigned char ch = utf8[i++]; + if (ch <= 0x7F) + { + unicode = ch; + todo = 0; + } + else if (ch <= 0xBF) + { + throw std::logic_error("not a UTF-8 string"); + } + else if (ch <= 0xDF) + { + unicode = ch&0x1F; + todo = 1; + } + else if (ch <= 0xEF) + { + unicode = ch&0x0F; + todo = 2; + } + else if (ch <= 0xF7) + { + unicode = ch&0x07; + todo = 3; + } + else + { + throw std::logic_error("not a UTF-8 string"); + } + for (size_t j = 0; j < todo; ++j) + { + unsigned char ch = utf8[i++]; + if (ch < 0x80 || ch > 0xBF) + throw std::logic_error("not a UTF-8 string"); + unicode <<= 6; + unicode += ch & 0x3F; + } + if (unicode >= 0xD800 && unicode <= 0xDFFF) + throw std::logic_error("not a UTF-8 string"); + if (unicode > 0x10FFFF) + throw std::logic_error("not a UTF-8 string"); + + return unicode; + } +} + +namespace MWGui +{ + + FontLoader::FontLoader(ToUTF8::FromType encoding) + { + if (encoding == ToUTF8::WINDOWS_1252) + mEncoding = ToUTF8::CP437; + else + mEncoding = encoding; + } + + void FontLoader::loadAllFonts() + { + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "*.fnt"); + for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource) + { + loadFont(*resource); + } + } + } + + + typedef struct + { + float x; + float y; + } Point; + + typedef struct + { + float u1; // appears unused, always 0 + Point top_left; + Point top_right; + Point bottom_left; + Point bottom_right; + float width; + float height; + float u2; // appears unused, always 0 + float kerning; + float ascent; + } GlyphInfo; + + void FontLoader::loadFont(const std::string &fileName) + { + Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName); + + float fontSize; + int one; + file->read(&fontSize, sizeof(fontSize)); + + file->read(&one, sizeof(int)); + assert(one == 1); + file->read(&one, sizeof(int)); + assert(one == 1); + + char name_[284]; + file->read(name_, sizeof(name_)); + std::string name(name_); + + GlyphInfo data[256]; + file->read(data, sizeof(data)); + file->close(); + + // Create the font texture + std::string bitmapFilename = "Fonts/" + std::string(name) + ".tex"; + Ogre::DataStreamPtr bitmapFile = Ogre::ResourceGroupManager::getSingleton().openResource(bitmapFilename); + + int width, height; + bitmapFile->read(&width, sizeof(int)); + bitmapFile->read(&height, sizeof(int)); + + std::vector textureData; + textureData.resize(width*height*4); + bitmapFile->read(&textureData[0], width*height*4); + bitmapFile->close(); + + std::string textureName = name; + Ogre::Image image; + image.loadDynamicImage(&textureData[0], width, height, Ogre::PF_BYTE_RGBA); + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(textureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + width, height, 0, Ogre::PF_BYTE_RGBA); + texture->loadImage(image); + + // Register the font with MyGUI + MyGUI::ResourceManualFont* font = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); + // We need to emulate loading from XML because the data members are private as of mygui 3.2.0 + MyGUI::xml::Document xmlDocument; + MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont"); + + if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic")) + root->addAttribute("name", "Magic Cards"); + else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century")) + root->addAttribute("name", "Century Gothic"); + else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric")) + root->addAttribute("name", "Daedric"); + else + return; // no point in loading it, since there is no way of using additional fonts + + MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property"); + defaultHeight->addAttribute("key", "DefaultHeight"); + defaultHeight->addAttribute("value", fontSize); + MyGUI::xml::ElementPtr source = root->createChild("Property"); + source->addAttribute("key", "Source"); + source->addAttribute("value", std::string(textureName)); + MyGUI::xml::ElementPtr codes = root->createChild("Codes"); + + for(int i = 0; i < 256; i++) + { + int x1 = data[i].top_left.x*width; + int y1 = data[i].top_left.y*height; + int w = data[i].top_right.x*width - x1; + int h = data[i].bottom_left.y*height - y1; + + ToUTF8::Utf8Encoder encoder(mEncoding); + unsigned long unicodeVal = utf8ToUnicode(encoder.getUtf8(std::string(1, (unsigned char)(i)))); + + MyGUI::xml::ElementPtr code = codes->createChild("Code"); + code->addAttribute("index", unicodeVal); + code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + code->addAttribute("advance", data[i].width); + code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + + // ASCII vertical bar, use this as text input cursor + if (i == 124) + { + MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); + cursorCode->addAttribute("index", MyGUI::FontCodeType::Cursor); + cursorCode->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + cursorCode->addAttribute("advance", data[i].width); + cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + } + } + + // These are required as well, but the fonts don't provide them + for (int i=0; i<3; ++i) + { + MyGUI::FontCodeType::Enum type; + if(i == 0) + type = MyGUI::FontCodeType::Selected; + else if (i == 1) + type = MyGUI::FontCodeType::SelectedBack; + else if (i == 2) + type = MyGUI::FontCodeType::NotDefined; + + MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); + cursorCode->addAttribute("index", type); + cursorCode->addAttribute("coord", "0 0 0 0"); + cursorCode->addAttribute("advance", "0"); + cursorCode->addAttribute("bearing", "0 0"); + + } + + font->deserialization(root, MyGUI::Version(3,2,0)); + + MyGUI::ResourceManager::getInstance().addResource(font); + } + +} diff --git a/apps/openmw/mwgui/fontloader.hpp b/apps/openmw/mwgui/fontloader.hpp new file mode 100644 index 0000000000..7954b0875e --- /dev/null +++ b/apps/openmw/mwgui/fontloader.hpp @@ -0,0 +1,25 @@ +#ifndef MWGUI_FONTLOADER_H +#define MWGUI_FONTLOADER_H + +#include + +namespace MWGui +{ + + + /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and Ogre + class FontLoader + { + public: + FontLoader (ToUTF8::FromType encoding); + void loadAllFonts (); + + private: + ToUTF8::FromType mEncoding; + + void loadFont (const std::string& fileName); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index aebaf16a23..58d963ce83 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -202,14 +202,14 @@ namespace MWGui float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const { - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); return MyGUI::FontManager::getInstance().getByName(fontName) ->getGlyphInfo(unicodeChar)->width; } float BookTextParser::currentFontHeight() const { - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); } @@ -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/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 0dc2bb62c4..58e89c4fc4 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -13,8 +13,6 @@ namespace MWGui , mType(Type_Normal) , mBase(base) { - assert(base.getContainerStore()); - if (MWWorld::Class::get(base).getEnchantment(base) != "") mFlags |= Flag_Enchanted; } @@ -31,18 +29,42 @@ namespace MWGui { if(mBase == other.mBase) return true; - return mBase.getContainerStore()->stacks(mBase, other.mBase) - && other.mBase.getContainerStore()->stacks(mBase, other.mBase); + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (mBase.getContainerStore() && other.mBase.getContainerStore()) + return mBase.getContainerStore()->stacks(mBase, other.mBase) + && other.mBase.getContainerStore()->stacks(mBase, other.mBase); + + if (mBase.getContainerStore()) + return mBase.getContainerStore()->stacks(mBase, other.mBase); + if (other.mBase.getContainerStore()) + return other.mBase.getContainerStore()->stacks(mBase, other.mBase); + + MWWorld::ContainerStore store; + return store.stacks(mBase, other.mBase); + } bool operator == (const ItemStack& left, const ItemStack& right) { if (left.mType != right.mType) return false; + if(left.mBase == right.mBase) return true; - return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase) - && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (left.mBase.getContainerStore() && right.mBase.getContainerStore()) + return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase) + && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + + if (left.mBase.getContainerStore()) + return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + if (right.mBase.getContainerStore()) + return right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + + MWWorld::ContainerStore store; + return store.stacks(left.mBase, right.mBase); } ItemModel::ItemModel() diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 273985e3e7..dbea10e775 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -189,14 +189,14 @@ book JournalBooks::createEmptyJournalBook () { BookTypesetter::Ptr typesetter = createTypesetter (); - BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); - BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); typesetter->write (header, to_utf8_span ("You have no journal entries!")); typesetter->lineBreak (); typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); - BookTypesetter::Style* big = typesetter->createStyle ("EB Garamond 24", MyGUI::Colour::Black); + BookTypesetter::Style* big = typesetter->createStyle ("", MyGUI::Colour::Black); BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue); typesetter->sectionBreak (20); @@ -231,8 +231,8 @@ book JournalBooks::createJournalBook () { BookTypesetter::Ptr typesetter = createTypesetter (); - BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); - BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true)); @@ -243,8 +243,8 @@ book JournalBooks::createTopicBook (uintptr_t topicId) { BookTypesetter::Ptr typesetter = createTypesetter (); - BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); - BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitTopicName (topicId, AddTopicName (typesetter, header)); @@ -259,8 +259,8 @@ book JournalBooks::createQuestBook (uintptr_t questId) { BookTypesetter::Ptr typesetter = createTypesetter (); - BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); - BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitQuestName (questId, AddQuestName (typesetter, header)); @@ -275,7 +275,7 @@ book JournalBooks::createTopicIndexBook () typesetter->setSectionAlignment (BookTypesetter::AlignCenter); - BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); for (int i = 0; i < 26; ++i) { @@ -300,7 +300,7 @@ book JournalBooks::createTopicIndexBook () book JournalBooks::createTopicIndexBook (char character) { BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); - BookTypesetter::Style* style = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* style = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitTopicNamesStartingWith (character, AddTopicLink (typesetter, style)); @@ -310,7 +310,7 @@ book JournalBooks::createTopicIndexBook (char character) book JournalBooks::createQuestIndexBook (bool activeOnly) { BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); - BookTypesetter::Style* base = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); + BookTypesetter::Style* base = typesetter->createStyle ("", MyGUI::Colour::Black); mModel->visitQuestNames (activeOnly, AddQuestLink (typesetter, base)); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 4a78238ce0..7098853af8 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 18c81a0608..3aefc398cf 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/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 60cc2fb0ee..3cf514dc5c 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -71,7 +71,7 @@ namespace MWGui if (item.mType == ItemStack::Type_Equipped && !mShowEquipped) return false; - int category; + int category = 0; if (base.getTypeName() == typeid(ESM::Armor).name() || base.getTypeName() == typeid(ESM::Clothing).name()) category = Category_Apparel; @@ -106,6 +106,7 @@ namespace MWGui if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted || (base.getTypeName() != typeid(ESM::Armor).name() && base.getTypeName() != typeid(ESM::Clothing).name() + && base.getTypeName() != typeid(ESM::Weapon).name() && base.getTypeName() != typeid(ESM::Book).name()))) return false; if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name() diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 45cf1b0aaa..c4c1be711e 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 5ad306fbea..61b8884917 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/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6a46478afe..01f7e2419f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -88,8 +88,10 @@ namespace MWGui MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); // Important: actor goes last, so that items purchased by the merchant go into his inventory itemSources.push_back(actor); + std::vector worldItems; + MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems); - mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources), mPtr); + mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources, worldItems), mPtr); mSortModel = new SortFilterItemModel(mTradeModel); mItemView->setModel (mSortModel); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 48c5aba6ba..09ceb843d1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -42,6 +42,7 @@ #include "inventorywindow.hpp" #include "bookpage.hpp" #include "itemview.hpp" +#include "fontloader.hpp" namespace MWGui { @@ -49,7 +50,7 @@ namespace MWGui WindowManager::WindowManager( const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage) + Translation::Storage& translationDataStorage, ToUTF8::FromType encoding) : mGuiManager(NULL) , mRendering(ogre) , mHud(NULL) @@ -109,6 +110,10 @@ namespace MWGui mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); mGui = mGuiManager->getGui(); + // Load fonts + FontLoader fontLoader (encoding); + fontLoader.loadAllFonts(); + //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 71bd2c9a75..71686d59bd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -83,7 +83,7 @@ namespace MWGui WindowManager(const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage); + Translation::Storage& translationDataStorage, ToUTF8::FromType encoding); virtual ~WindowManager(); /** diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4db574cb8d..e85aa9b834 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -29,8 +29,11 @@ namespace MWMechanics calculateCreatureStatModifiers (ptr); // AI - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - creatureStats.getAiSequence().execute (ptr); + if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + creatureStats.getAiSequence().execute (ptr); + } } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -309,4 +312,12 @@ namespace MWMechanics if(iter != mActors.end()) iter->second.skipAnim(); } + + bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + return iter->second.isAnimPlaying(groupName); + return false; + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index c01d630930..386840e3a0 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -82,6 +82,7 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); + bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 7c28dd216c..6af87e8bd2 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,6 +1,5 @@ #include "aiescort.hpp" -#include "character.hpp" #include "movement.hpp" #include "../mwworld/class.hpp" @@ -10,16 +9,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include -#include -#include "boost/tuple/tuple.hpp" - namespace { float sgn(float a) { - if(a>0) return 1.; - else return -1.; + if(a > 0) + return 1.0; + return -1.0; } } @@ -29,74 +25,73 @@ 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); - } -} + mMaxDist = 470; -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; - // 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); - std::cout << "AiEscort: " << currentSecond << " time: " << currentSecond - mStartingSecond << std::endl; - if(currentSecond - mStartingSecond >= mDuration) + else { - return true; + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); } } - 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); + 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); + } + } + + + 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. - 200)) + 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; @@ -107,7 +102,8 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) 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)) + 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; @@ -115,68 +111,70 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) } - 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()) + if(!mPathFinder.isPathConstructed() || cellChange) { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + 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); } - ESM::Pathgrid::Point dest; - dest.mX = mX; - dest.mY = mY; - dest.mZ = mZ; + if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; + 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]; - mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + 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; } - if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + int AiEscort::getTypeId() const { - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; + return 2; } - - 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; - } - 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 MWMechanics::AiEscort::getTypeId() const -{ - return 2; } diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 667b88e31a..3ae604035a 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -2,30 +2,9 @@ #define GAME_MWMECHANICS_AIESCORT_H #include "aipackage.hpp" -#include "pathfinding.hpp" #include -/* From CS: -Escort - -Makes the actor escort another actor to a location or for a specified period of time. During this time the actor will also protect the actor it is escorting. - -If you are not doing this package with the player as the target, you’ll want to also put a follow package on the target Actor, since escorting an actor makes the escorter wait for the other actor. If the Target does not know they are supposed to follow, the escorter will most likely just stand there. - -Target: The ActorID to Escort. Remember that since all ActorIDs share the same AI packages, putting this on an Actor with multiple references will cause ALL references of that actor to attempt to escort the same actor. Thus, this type of AI should only be placed on specific or unique sets of Actors. - -Duration: The duration the actor should escort for. Trumped by providing a location. - -Escort to: Check this to use location data for the escort. - -Cell: The Cell to escort to. - -XYZ: Like Travel, specify the XYZ location to escort to. - -View Location: A red X will appear in the render window that you can move around with the standard render window object controls. Place the X on the escort destination. - - -*/ +#include "pathfinding.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index b221856674..90365c16b6 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,54 +1,48 @@ #include "aitravel.hpp" -#include -#include "character.hpp" +#include "movement.hpp" -#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" -#include "movement.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include -#include -#include "boost/tuple/tuple.hpp" - namespace { float sgn(float a) { - if(a>0) return 1.; - else return -1.; + if(a > 0) + return 1.0; + return -1.0; } } namespace MWMechanics { - AiTravel::AiTravel(float x, float y, float z) - : mX(x),mY(y),mZ(z),mPathFinder() + : mX(x),mY(y),mZ(z),mPathFinder() { } - AiTravel * AiTravel::clone() const + AiTravel *MWMechanics::AiTravel::clone() const { return new AiTravel(*this); } 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); - 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(); 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(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; @@ -58,19 +52,21 @@ namespace MWMechanics { 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)) + 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) + 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; @@ -87,16 +83,17 @@ namespace MWMechanics start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); } - if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + + 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],pos.pos[2]); - MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + 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; @@ -106,5 +103,5 @@ namespace MWMechanics { return 1; } - } + diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 52b41850f1..6eb9af8cec 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_AITRAVEL_H #include "aipackage.hpp" + #include "pathfinding.hpp" namespace MWMechanics @@ -25,10 +26,7 @@ namespace MWMechanics int cellX; int cellY; - //bool isPathConstructed; - //std::list mPath; PathFinder mPathFinder; - }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index a730475482..8f79262360 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,23 +1,337 @@ #include "aiwander.hpp" -#include -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) +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include + +namespace { + float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } } -MWMechanics::AiPackage * MWMechanics::AiWander::clone() const +namespace MWMechanics { - return new AiWander(*this); + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) + { + for(unsigned short counter = 0; counter < mIdle.size(); counter++) + { + 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; + } + + AiPackage * MWMechanics::AiWander::clone() const + { + return new AiWander(*this); + } + + bool 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) + { + 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; + } + } + + 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()) + 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; + unsigned short idleRoll = 0; + + 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) + { + if(!checkIdle(actor, mPlayedIdle + 1)) + { + mPlayedIdle = 0; + mIdleNow = false; + mChooseAction = true; + } + } + + if(mMoveNow && mDistance) + { + 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); + } + } + + 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; + + // 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.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } + } + + 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; + } } -bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) -{ - // Return completed - return true; -} - -int MWMechanics::AiWander::getTypeId() const -{ - return 0; -} diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index e06e714bc4..c82ccc2155 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -4,6 +4,10 @@ #include "aipackage.hpp" #include +#include "pathfinding.hpp" + +#include "../mwworld/timestamp.hpp" + namespace MWMechanics { class AiWander : public AiPackage @@ -18,11 +22,42 @@ namespace MWMechanics ///< 0: Wander private: + void stopWalking(const MWWorld::Ptr& actor); + void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); + bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); + int mDistance; int mDuration; int mTimeOfDay; std::vector mIdle; bool mRepeat; + + float mX; + float mY; + float mZ; + + int mCellX; + int mCellY; + float mXCell; + float mYCell; + + bool mStoredAvailableNodes; + bool mChooseAction; + bool mIdleNow; + bool mMoveNow; + bool mWalking; + + float mIdleChanceMultiplier; + unsigned short mPlayedIdle; + + MWWorld::TimeStamp mStartTime; + + std::vector mAllowedNodes; + ESM::Pathgrid::Point mCurrentNode; + + PathFinder mPathFinder; + const ESM::Pathgrid *mPathgrid; + }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 31ba833925..aa20e14146 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -461,15 +461,18 @@ void CharacterController::update(float duration, Movement &movement) Ogre::Vector3 moved = mAnimation->runAnimation(duration); // Ensure we're moving in generally the right direction - if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || - (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) - moved.x = movement.mPosition[0]; - if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) || - (movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f)) - moved.y = movement.mPosition[1]; - if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) || - (movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f)) - moved.z = movement.mPosition[2]; + if (speed > 0.f) + { + if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || + (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) + moved.x = movement.mPosition[0]; + if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) || + (movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f)) + moved.y = movement.mPosition[1]; + if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) || + (movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f)) + moved.z = movement.mPosition[2]; + } movement.mPosition[0] = moved.x; movement.mPosition[1] = moved.y; @@ -509,6 +512,14 @@ void CharacterController::skipAnim() mSkipAnim = true; } +bool CharacterController::isAnimPlaying(const std::string &groupName) +{ + if(mAnimation == NULL) + return false; + else + return mAnimation->isPlaying(groupName); +} + void CharacterController::clearAnimQueue() { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 7ffefab470..7067176e05 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -125,6 +125,7 @@ public: void playGroup(const std::string &groupname, int mode, int count); void skipAnim(); + bool isAnimPlaying(const std::string &groupName); void setState(CharacterState state); CharacterState getState() const diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index ded75f03a9..faa450df7a 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -12,7 +12,7 @@ namespace MWMechanics { Enchanting::Enchanting(): - mEnchantType(0) + mCastStyle(ESM::Enchantment::CastOnce) {} void Enchanting::setOldItem(MWWorld::Ptr oldItem) @@ -41,9 +41,9 @@ namespace MWMechanics mEffectList=effectList; } - int Enchanting::getEnchantType() const + int Enchanting::getCastStyle() const { - return mEnchantType; + return mCastStyle; } void Enchanting::setSoulGem(MWWorld::Ptr soulGem) @@ -74,12 +74,12 @@ namespace MWMechanics MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1); } - if(mEnchantType==3) + if(mCastStyle==ESM::Enchantment::ConstantEffect) { enchantment.mData.mCharge=0; } - enchantment.mData.mType = mEnchantType; - enchantment.mData.mCost = getEnchantCost(); + enchantment.mData.mType = mCastStyle; + enchantment.mData.mCost = getEnchantPoints(); enchantment.mEffects = mEffectList; const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); @@ -98,87 +98,142 @@ namespace MWMechanics return true; } - void Enchanting::nextEnchantType() + void Enchanting::nextCastStyle() { - mEnchantType++; if (itemEmpty()) { - mEnchantType = 0; + mCastStyle = ESM::Enchantment::WhenUsed; return; } - if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name())) - { - int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); - switch(mEnchantType) + + const bool powerfulSoul = getGemCharge() >= \ + MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); + if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) + { // Armor or Clothing + switch(mCastStyle) { - case 1: - mEnchantType = 2; - case 3: - if(getGemCharge() 2 or (2 + 0) == (1 + 2) => 3 + * + * Formula on UESPWiki is not entirely correct. + */ + float Enchanting::getEnchantPoints() const { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float cost = 0; - std::vector mEffects = mEffectList.mList; - int i=mEffects.size(); - if(i<=0) + if (mEffectList.mList.empty()) + // No effects added, cost = 0 return 0; - /* - Formula from http://www.uesp.net/wiki/Morrowind:Enchant - */ + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + std::vector mEffects = mEffectList.mList; + + float enchantmentCost = 0; + int effectsLeftCnt = mEffects.size(); + float baseCost, magnitudeCost, areaCost; + int magMin, magMax, area; for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { - const ESM::MagicEffect* effect = store.get().find(it->mEffectID); + baseCost = (store.get().find(it->mEffectID))->mData.mBaseCost; + // To reflect vanilla behavior + magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin; + magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax; + area = (it->mArea == 0) ? 1 : it->mArea; - float cost1 = ((it->mMagnMin + it->mMagnMax)*it->mDuration*effect->mData.mBaseCost*0.025); - - float cost2 = (std::max(1, it->mArea)*0.125*effect->mData.mBaseCost); - - if(mEnchantType==3) + if (mCastStyle == ESM::Enchantment::ConstantEffect) { - int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantDurationMult")->getFloat(); - cost1 *= constDurationMultipler; - cost2 *= 2; + magnitudeCost = (magMin + magMax) * baseCost * 2.5; + } + else + { + magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025; + if(it->mRange == ESM::RT_Target) + magnitudeCost *= 1.5; } - if(effect->mData.mFlags & ESM::MagicEffect::CastTarget) - cost1 *= 1.5; - float fullcost = cost1+cost2; - fullcost*= i; - i--; + areaCost = area * 0.025 * baseCost; + if (it->mRange == ESM::RT_Target) + areaCost *= 1.5; - cost+=fullcost; + enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt; + --effectsLeftCnt; } - return cost; + + return enchantmentCost; } + + float Enchanting::getCastCost() const + { + if (mCastStyle == ESM::Enchantment::ConstantEffect) + return 0; + + const float enchantCost = getEnchantPoints(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player); + int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + + /* + * Each point of enchant skill above/under 10 subtracts/adds + * one percent of enchantment cost while minimum is 1. + */ + const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); + + return (castCost < 1) ? 1 : castCost; + } + + int Enchanting::getEnchantPrice() const { if(mEnchanter.isEmpty()) return 0; float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); - int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantCost() * priceMultipler), true); + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantPoints() * priceMultipler), true); return price; } @@ -201,16 +256,12 @@ namespace MWMechanics } bool Enchanting::soulEmpty() const { - if (mSoulGemPtr.isEmpty()) - return true; - return false; + return mSoulGemPtr.isEmpty(); } bool Enchanting::itemEmpty() const { - if(mOldItemPtr.isEmpty()) - return true; - return false; + return mOldItemPtr.isEmpty(); } void Enchanting::setSelfEnchanting(bool selfEnchanting) @@ -235,8 +286,8 @@ namespace MWMechanics (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); - float chance2 = 2.5 * getEnchantCost(); - if(mEnchantType==3) + float chance2 = 2.5 * getEnchantPoints(); + if(mCastStyle==ESM::Enchantment::ConstantEffect) { float constantChance = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantChanceMult")->getFloat(); chance2 /= constantChance; diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 2831f9ddb6..a25fd43abc 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -13,7 +13,7 @@ namespace MWMechanics MWWorld::Ptr mSoulGemPtr; MWWorld::Ptr mEnchanter; - int mEnchantType; + int mCastStyle; bool mSelfEnchanting; @@ -33,9 +33,10 @@ namespace MWMechanics void setEffect(ESM::EffectList effectList); void setSoulGem(MWWorld::Ptr soulGem); bool create(); //Return true if created, false if failed. - void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) - int getEnchantType() const; - float getEnchantCost() const; + void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) + int getCastStyle() const; + float getEnchantPoints() const; + float getCastCost() const; int getEnchantPrice() const; float getMaxEnchantValue() const; int getGemCharge() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b83cfb365a..29880291d2 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -668,5 +668,12 @@ namespace MWMechanics else mObjects.skipAnimation(ptr); } + bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName) + { + if(MWWorld::Class::get(ptr).isActor()) + return mActors.checkAnimationPlaying(ptr, groupName); + else + return false; + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index f3a38bf368..95f760d113 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -100,6 +100,7 @@ namespace MWMechanics virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void skipAnimation(const MWWorld::Ptr& ptr); + virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); }; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 62c825be75..986595a9af 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 b1bbab37ab..1727c650f3 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 9061f84027..34a7924358 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -64,13 +64,16 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b { // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); + bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); // glsl is only supported in opengl mode and hlsl only in direct3d mode. - if (Settings::Manager::getString("shader mode", "General") == "" - || (openGL && Settings::Manager::getString("shader mode", "General") == "hlsl") - || (!openGL && Settings::Manager::getString("shader mode", "General") == "glsl")) + std::string currentMode = Settings::Manager::getString("shader mode", "General"); + if (currentMode == "" + || (openGL && currentMode == "hlsl") + || (!openGL && currentMode == "glsl") + || (glES && currentMode != "glsles")) { - Settings::Manager::setString("shader mode", "General", openGL ? "glsl" : "hlsl"); + Settings::Manager::setString("shader mode", "General", openGL ? (glES ? "glsles" : "glsl") : "hlsl"); } mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); @@ -93,6 +96,8 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b std::string l = Settings::Manager::getString("shader mode", "General"); if (l == "glsl") lang = sh::Language_GLSL; + else if (l == "glsles") + lang = sh::Language_GLSLES; else if (l == "hlsl") lang = sh::Language_HLSL; else @@ -396,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 @@ -433,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 87ae8175de..1b5eddc21b 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/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 32b2797ed7..a5e5b5083d 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -1,8 +1,6 @@ #ifndef GAME_SOUND_FFMPEG_DECODER_H #define GAME_SOUND_FFMPEG_DECODER_H -#include - // FIXME: This can't be right? The headers refuse to build without UINT64_C, // which only gets defined in stdint.h in either C99 mode or with this macro // defined... @@ -14,6 +12,8 @@ extern "C" #include } +#include + #include "sound_decoder.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8b76efdaeb..16cba2ea8e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1724,4 +1724,31 @@ namespace MWWorld } } } + + struct ListHandlesFunctor + { + std::vector mHandles; + + bool operator() (ESM::CellRef& ref, RefData& data) + { + Ogre::SceneNode* handle = data.getBaseNode(); + if (handle) + mHandles.push_back(handle->getName()); + return true; + } + }; + + void World::getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + { + const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) + { + ListHandlesFunctor functor; + (*cellIt)->forEach(functor); + + for (std::vector::iterator it = functor.mHandles.begin(); it != functor.mHandles.end(); ++it) + if (Misc::StringUtils::ciEqual(searchPtrViaHandle(*it).mCellRef->mOwner, npc.getCellRef().mRefID)) + out.push_back(searchPtrViaHandle(*it)); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1baf3d4ba2..12438efd42 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -391,6 +391,8 @@ namespace MWWorld virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out); ///< get all containers in active cells owned by this Npc + virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + ///< get all items in active cells owned by this Npc virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 9056e792d0..765f1cebf8 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -3,6 +3,8 @@ #ifdef _WIN32 #include +#elif defined HAVE_UNORDERED_MAP +#include #else #include #endif @@ -48,7 +50,11 @@ struct ConfigurationManager typedef Files::FixedPath<> FixedPathType; typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; - typedef std::tr1::unordered_map TokensMappingContainer; + #if defined HAVE_UNORDERED_MAP + typedef std::unordered_map TokensMappingContainer; + #else + typedef std::tr1::unordered_map TokensMappingContainer; + #endif void loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index dea68c1fa2..8198b305dd 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -46,7 +46,7 @@ void writeMissing(bool last) int write_table(const std::string &charset, const std::string &tableName) { // Write table header - cout << "static char " << tableName << "[] =\n{\n"; + cout << "static signed char " << tableName << "[] =\n{\n"; // Open conversion system iconv_t cd = iconv_open ("UTF-8", charset.c_str()); @@ -106,6 +106,8 @@ int main() "\n"; write_table("WINDOWS-1252", "windows_1252"); + write_table("CP437", "cp437"); + // Close namespace cout << "\n}\n\n"; diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index a1d4b6d80d..14e66eac17 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -790,6 +790,265 @@ static signed char windows_1252[] = 2, -61, -66, 0, 0, 0, 2, -61, -65, 0, 0, 0 }; +static signed char cp437[] = +{ + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -61, -96, 0, 0, 0, + 2, -61, -91, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -61, -86, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -61, -88, 0, 0, 0, + 2, -61, -81, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -61, -84, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -61, -123, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -61, -90, 0, 0, 0, + 2, -61, -122, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -78, 0, 0, 0, + 2, -61, -69, 0, 0, 0, + 2, -61, -71, 0, 0, 0, + 2, -61, -65, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -62, -94, 0, 0, 0, + 2, -62, -93, 0, 0, 0, + 2, -62, -91, 0, 0, 0, + 3, -30, -126, -89, 0, 0, + 2, -58, -110, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -61, -79, 0, 0, 0, + 2, -61, -111, 0, 0, 0, + 2, -62, -86, 0, 0, 0, + 2, -62, -70, 0, 0, 0, + 2, -62, -65, 0, 0, 0, + 3, -30, -116, -112, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -67, 0, 0, 0, + 2, -62, -68, 0, 0, 0, + 2, -62, -95, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 3, -30, -106, -111, 0, 0, + 3, -30, -106, -110, 0, 0, + 3, -30, -106, -109, 0, 0, + 3, -30, -108, -126, 0, 0, + 3, -30, -108, -92, 0, 0, + 3, -30, -107, -95, 0, 0, + 3, -30, -107, -94, 0, 0, + 3, -30, -107, -106, 0, 0, + 3, -30, -107, -107, 0, 0, + 3, -30, -107, -93, 0, 0, + 3, -30, -107, -111, 0, 0, + 3, -30, -107, -105, 0, 0, + 3, -30, -107, -99, 0, 0, + 3, -30, -107, -100, 0, 0, + 3, -30, -107, -101, 0, 0, + 3, -30, -108, -112, 0, 0, + 3, -30, -108, -108, 0, 0, + 3, -30, -108, -76, 0, 0, + 3, -30, -108, -84, 0, 0, + 3, -30, -108, -100, 0, 0, + 3, -30, -108, -128, 0, 0, + 3, -30, -108, -68, 0, 0, + 3, -30, -107, -98, 0, 0, + 3, -30, -107, -97, 0, 0, + 3, -30, -107, -102, 0, 0, + 3, -30, -107, -108, 0, 0, + 3, -30, -107, -87, 0, 0, + 3, -30, -107, -90, 0, 0, + 3, -30, -107, -96, 0, 0, + 3, -30, -107, -112, 0, 0, + 3, -30, -107, -84, 0, 0, + 3, -30, -107, -89, 0, 0, + 3, -30, -107, -88, 0, 0, + 3, -30, -107, -92, 0, 0, + 3, -30, -107, -91, 0, 0, + 3, -30, -107, -103, 0, 0, + 3, -30, -107, -104, 0, 0, + 3, -30, -107, -110, 0, 0, + 3, -30, -107, -109, 0, 0, + 3, -30, -107, -85, 0, 0, + 3, -30, -107, -86, 0, 0, + 3, -30, -108, -104, 0, 0, + 3, -30, -108, -116, 0, 0, + 3, -30, -106, -120, 0, 0, + 3, -30, -106, -124, 0, 0, + 3, -30, -106, -116, 0, 0, + 3, -30, -106, -112, 0, 0, + 3, -30, -106, -128, 0, 0, + 2, -50, -79, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -50, -109, 0, 0, 0, + 2, -49, -128, 0, 0, 0, + 2, -50, -93, 0, 0, 0, + 2, -49, -125, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -49, -124, 0, 0, 0, + 2, -50, -90, 0, 0, 0, + 2, -50, -104, 0, 0, 0, + 2, -50, -87, 0, 0, 0, + 2, -50, -76, 0, 0, 0, + 3, -30, -120, -98, 0, 0, + 2, -49, -122, 0, 0, 0, + 2, -50, -75, 0, 0, 0, + 3, -30, -120, -87, 0, 0, + 3, -30, -119, -95, 0, 0, + 2, -62, -79, 0, 0, 0, + 3, -30, -119, -91, 0, 0, + 3, -30, -119, -92, 0, 0, + 3, -30, -116, -96, 0, 0, + 3, -30, -116, -95, 0, 0, + 2, -61, -73, 0, 0, 0, + 3, -30, -119, -120, 0, 0, + 2, -62, -80, 0, 0, 0, + 3, -30, -120, -103, 0, 0, + 2, -62, -73, 0, 0, 0, + 3, -30, -120, -102, 0, 0, + 3, -30, -127, -65, 0, 0, + 2, -62, -78, 0, 0, 0, + 3, -30, -106, -96, 0, 0, + 2, -62, -96, 0, 0, 0 +}; } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 1de15d79c4..59a9aff80f 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -63,6 +63,12 @@ Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): translationArray = ToUTF8::windows_1251; break; } + case ToUTF8::CP437: + { + translationArray = ToUTF8::cp437; + break; + } + default: { assert(0); diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 839aa36aa3..3f20a51f86 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -12,8 +12,9 @@ namespace ToUTF8 { WINDOWS_1250, // Central ane Eastern European languages WINDOWS_1251, // Cyrillic languages - WINDOWS_1252 // Used by English version of Morrowind (and + WINDOWS_1252, // Used by English version of Morrowind (and // probably others) + CP437 // Used for fonts (*.fnt) if data files encoding is 1252. Otherwise, uses the same encoding as the data files. }; FromType calculateEncoding(const std::string& encodingName); diff --git a/credits.txt b/credits.txt index 390a2d721d..630f6ff390 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/extern/shiny/Docs/Materials.dox b/extern/shiny/Docs/Materials.dox index 91e9be4b30..d08599a04a 100644 --- a/extern/shiny/Docs/Materials.dox +++ b/extern/shiny/Docs/Materials.dox @@ -87,6 +87,8 @@ Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'. Make sure to also copy the 'core.h' file to the same location. It is included in shiny's source tree under 'Extra/'. + Important: a newline at the end of the file is required. Many editors do this automatically or can be configured to do so. If there is no newline at the end of the file, and the last line is '#endif', you will get the rather cryptic error message " ill formed preprocessor directive: #endif" from boost::wave. + \code #include "core.h" diff --git a/extern/shiny/Editor/Actions.hpp b/extern/shiny/Editor/Actions.hpp index e5cb6df6a5..1bbdbe5a98 100644 --- a/extern/shiny/Editor/Actions.hpp +++ b/extern/shiny/Editor/Actions.hpp @@ -10,7 +10,7 @@ namespace sh { public: virtual void execute() = 0; - virtual ~Action(); + virtual ~Action() {} }; class ActionDeleteMaterial : public Action diff --git a/extern/shiny/Editor/Query.hpp b/extern/shiny/Editor/Query.hpp index 7aec684880..d98c8c9b27 100644 --- a/extern/shiny/Editor/Query.hpp +++ b/extern/shiny/Editor/Query.hpp @@ -15,7 +15,7 @@ class Query public: Query() : mDone(false) {} - virtual ~Query(); + virtual ~Query() {} void execute(); diff --git a/files/materials/core.h b/files/materials/core.h index 3385e5face..6f8179c08d 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -58,11 +58,20 @@ #endif -#if SH_GLSL == 1 +#if SH_GLSL == 1 || SH_GLSLES == 1 #define shFract(val) fract(val) +#if SH_GLSLES == 1 + @version 100 +#else @version 120 +#endif + +#if SH_GLSLES == 1 && SH_FRAGMENT_SHADER +precision mediump int; +precision mediump float; +#endif #define float2 vec2 #define float3 vec3 diff --git a/files/mygui/core.skin b/files/mygui/core.skin index e52080fe0f..ea3e2debce 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -2,7 +2,6 @@ - diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index ef43003264..726bfb281c 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -23,56 +23,21 @@ - - - - + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_settings.xml b/files/mygui/openmw_settings.xml index c63f962fb7..37d2359683 100644 --- a/files/mygui/openmw_settings.xml +++ b/files/mygui/openmw_settings.xml @@ -1,7 +1,7 @@ - + diff --git a/files/opencs/added.png b/files/opencs/added.png index 091e3c61a6..aff7e25d46 100644 Binary files a/files/opencs/added.png and b/files/opencs/added.png differ diff --git a/files/opencs/modified.png b/files/opencs/modified.png index 0ac732ad91..d15ad827c3 100644 Binary files a/files/opencs/modified.png and b/files/opencs/modified.png differ diff --git a/files/opencs/removed.png b/files/opencs/removed.png index d4b0a4897e..2ca9e094be 100644 Binary files a/files/opencs/removed.png and b/files/opencs/removed.png differ diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui index c0320de1eb..bf883b96ef 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/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 7be7137969..e699fbbcc6 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -206,6 +206,7 @@ void OgreRenderer::configure(const std::string &logPath, pluginDir = absPluginPath.string(); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); diff --git a/readme.txt b/readme.txt index f8361780c4..67c02cc61e 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