Fixed conflicts

Merge branch 'cs-windows' of https://github.com/Glorf/openmw

Conflicts:
	apps/opencs/model/settings/usersettings.cpp
	apps/opencs/model/settings/usersettings.hpp
	apps/opencs/view/settings/usersettingsdialog.cpp
actorid
graffy76 12 years ago
commit f87eb67968

@ -4,6 +4,10 @@ if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks")
set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks")
set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks")
endif (APPLE) endif (APPLE)
# Macros # Macros
@ -15,7 +19,7 @@ include (OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 23) set (OPENMW_VERSION_MINOR 24)
set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
@ -181,6 +185,12 @@ if (UNIX AND NOT APPLE)
find_package (Threads) find_package (Threads)
endif() 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) set(BOOST_COMPONENTS system filesystem program_options thread date_time wave)

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

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

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

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

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

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

@ -7,8 +7,11 @@
#include <QMap> #include <QMap>
#include <QMessageBox> #include <QMessageBox>
#include <QTextCodec> #include <QTextCodec>
#include <QFile> #include <QFile>
#include <QDebug>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include "settingcontainer.hpp" #include "settingcontainer.hpp"
@ -32,8 +35,11 @@ namespace boost
} /* namespace boost */ } /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */ #endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings() CSMSettings::UserSettings::UserSettings()
{ {
assert(!mUserSettingsInstance);
mUserSettingsInstance = this; mUserSettingsInstance = this;
mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \ mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \
@ -45,9 +51,18 @@ CSMSettings::UserSettings::UserSettings()
CSMSettings::UserSettings::~UserSettings() CSMSettings::UserSettings::~UserSettings()
{ {
mUserSettingsInstance = 0;
}
//QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly)
CSMSettings::SectionMap CSMSettings::UserSettings::getSettingsMap() const
{
return mSectionMap;
} }
QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) QFile *CSMSettings::UserSettings::openFile (const QString &filename) const
{ {
QFile *file = new QFile(filePath); QFile *file = new QFile(filePath);
@ -91,6 +106,9 @@ QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath,
} }
bool CSMSettings::UserSettings::writeFile(QMap<QString, CSMSettings::SettingList *> &settings) bool CSMSettings::UserSettings::writeFile(QMap<QString, CSMSettings::SettingList *> &settings)
//bool CSMSettings::UserSettings::writeFile(QFile *file, QMap<QString, CSMSettings::SettingList *> &settings) const
{ {
QTextStream *stream = openFileStream(mPaths.back()); QTextStream *stream = openFileStream(mPaths.back());
@ -111,7 +129,11 @@ bool CSMSettings::UserSettings::writeFile(QMap<QString, CSMSettings::SettingList
return true; return true;
} }
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSettings() const CSMSettings::SectionMap &CSMSettings::UserSettings::getSettings()
//void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap &sections) const
{ {
return mSectionSettings; return mSectionSettings;
} }
@ -218,3 +240,70 @@ void CSMSettings::UserSettings::updateSettings (const QString &sectionName, cons
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue()); emit signalUpdateEditorSetting (setting->objectName(), 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("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
getSettings(stream, mSectionMap);
}
file.close();
}
}
void CSMSettings::UserSettings::setSettingsFiles(QStringList files)
{
mSettingsFiles = files;
}
QStringList CSMSettings::UserSettings::getSettingsFiles () const
{
return mSettingsFiles;
}
QString CSMSettings::UserSettings::getSettingValue(QString section, QString setting) const
{
if(mSectionMap.find(section) == mSectionMap.end())
return QString();
CSMSettings::SettingMap *settings = mSectionMap.value(section);
if(settings->find(setting) == settings->end())
return QString();
CSMSettings::SettingContainer *settingContainer = settings->value(setting);
return settingContainer->getValue();
}
const CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
{
assert(mUserSettingsInstance);
return *mUserSettingsInstance;
}

@ -36,13 +36,15 @@ namespace CSMSettings {
public: public:
static UserSettings &instance() UserSettings();
{ ~UserSettings();
static UserSettings instance;
return instance; static const UserSettings& instance();
}
void readSettings();
void setSettingsFiles(QStringList files);
<<<<<<< HEAD
bool writeFile(QMap<QString, SettingList *> &sections); bool writeFile(QMap<QString, SettingList *> &sections);
const SectionMap &getSettings (); const SectionMap &getSettings ();
void updateSettings (const QString &sectionName, const QString &settingName = ""); void updateSettings (const QString &sectionName, const QString &settingName = "");
@ -52,6 +54,21 @@ namespace CSMSettings {
UserSettings(); UserSettings();
~UserSettings(); ~UserSettings();
=======
QFile *openFile (const QString &) const;
bool writeFile(QFile *file, QMap<QString, SettingList *> &sections) const;
void getSettings (QTextStream &stream, SectionMap &settings) const;
QStringList getSettingsFiles () const;
CSMSettings::SectionMap getSettingsMap() const;
QString getSettingValue(QString section, QString setting) const;
private:
static UserSettings *mUserSettingsInstance;
CSMSettings::SectionMap mSectionMap;
QStringList mSettingsFiles;
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
UserSettings (UserSettings const &); //not implemented UserSettings (UserSettings const &); //not implemented
void operator= (UserSettings const &); //not implemented void operator= (UserSettings const &); //not implemented

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

@ -9,10 +9,19 @@
#include <QFile> #include <QFile>
#include <QPushButton> #include <QPushButton>
#include <QDockWidget> #include <QDockWidget>
<<<<<<< HEAD
#include <QGridLayout> #include <QGridLayout>
#include "blankpage.hpp" #include "blankpage.hpp"
#include "samplepage.hpp" #include "samplepage.hpp"
=======
#include <QDebug>
#include "blankpage.hpp"
#include "editorpage.hpp"
#include "windowpage.hpp"
#include "../../model/settings/support.hpp"
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
#include "../../model/settings/support.hpp" #include "../../model/settings/support.hpp"
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
@ -23,7 +32,11 @@ CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) :
{ {
setWindowTitle(QString::fromUtf8 ("User Settings")); setWindowTitle(QString::fromUtf8 ("User Settings"));
buildPages(); buildPages();
<<<<<<< HEAD
setWidgetStates (); setWidgetStates ();
=======
setWidgetStates (CSMSettings::UserSettings::instance().getSettingsMap());
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
positionWindow (); positionWindow ();
connect (mListWidget, connect (mListWidget,
@ -85,11 +98,17 @@ void CSVSettings::UserSettingsDialog::buildPages()
setDockOptions (QMainWindow::AllowNestedDocks); setDockOptions (QMainWindow::AllowNestedDocks);
//uncomment to test with sample editor page. //uncomment to test with sample editor page.
<<<<<<< HEAD
// TODO: Reimplement sample page using createPage function // TODO: Reimplement sample page using createPage function
//createPage<SamplePage>("Sample"); //createPage<SamplePage>("Sample");
createPage<EditorPage>("Editor"); createPage<EditorPage>("Editor");
=======
//createSamplePage();
/*createPage<BlankPage>("Page1");
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
createPage<BlankPage>("Page2"); createPage<BlankPage>("Page2");
createPage<BlankPage>("Page3"); createPage<BlankPage>("Page3");*/
createWindowPage();
} }
void CSVSettings::UserSettingsDialog::createSamplePage() void CSVSettings::UserSettingsDialog::createSamplePage()
@ -108,6 +127,22 @@ void CSVSettings::UserSettingsDialog::createSamplePage()
new QListWidgetItem (page->objectName(), mListWidget); 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 () void CSVSettings::UserSettingsDialog::positionWindow ()
{ {
QRect scr = QApplication::desktop()->screenGeometry(); QRect scr = QApplication::desktop()->screenGeometry();
@ -116,6 +151,8 @@ void CSVSettings::UserSettingsDialog::positionWindow ()
} }
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
void CSVSettings::UserSettingsDialog::writeSettings() void CSVSettings::UserSettingsDialog::writeSettings()
{ {
QMap<QString, CSMSettings::SettingList *> settings; QMap<QString, CSMSettings::SettingList *> settings;
@ -126,7 +163,13 @@ void CSVSettings::UserSettingsDialog::writeSettings()
settings [page->objectName()] = page->getSettings(); settings [page->objectName()] = page->getSettings();
} }
<<<<<<< HEAD
CSMSettings::UserSettings::instance().writeFile(settings); CSMSettings::UserSettings::instance().writeFile(settings);
=======
QStringList paths = CSMSettings::UserSettings::instance().getSettingsFiles();
CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(paths.back()), settings);
>>>>>>> df1f1bd5c81d94a1ea2693000ec5dc589b069826
} }

@ -24,7 +24,6 @@ namespace CSVSettings {
{ {
Q_OBJECT Q_OBJECT
QStringList mPaths;
QListWidget *mListWidget; QListWidget *mListWidget;
QStackedWidget *mStackedWidget; QStackedWidget *mStackedWidget;
@ -42,6 +41,9 @@ namespace CSVSettings {
void writeSettings(); void writeSettings();
void createSamplePage(); void createSamplePage();
//Pages
void createWindowPage();
template <typename T> template <typename T>
void createPage (const QString &title) void createPage (const QString &title)
{ {

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

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

@ -33,7 +33,7 @@ add_openmw_dir (mwgui
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel tradeitemmodel companionitemmodel pickpocketitemmodel fontloader
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue

@ -380,7 +380,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setWindowManager (new MWGui::WindowManager( mEnvironment.setWindowManager (new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage)); mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding));
if (mNewGame) if (mNewGame)
mEnvironment.getWindowManager()->setNewGame(true); mEnvironment.getWindowManager()->setNewGame(true);

@ -112,6 +112,8 @@ namespace MWBase
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for ///< 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. /// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
}; };
} }

@ -342,6 +342,8 @@ namespace MWBase
virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0; virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
///< get all containers in active cells owned by this Npc ///< get all containers in active cells owned by this Npc
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
///< get all items in active cells owned by this Npc
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0;

@ -5,6 +5,7 @@
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -29,6 +30,7 @@ namespace
{ {
MWMechanics::CreatureStats mCreatureStats; MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore mContainerStore; MWWorld::ContainerStore mContainerStore;
MWMechanics::Movement mMovement;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
@ -47,6 +49,18 @@ namespace MWClass
{ {
std::auto_ptr<CustomData> data (new CustomData); std::auto_ptr<CustomData> data (new CustomData);
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature");
inited = true;
}
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
// creature stats // creature stats
@ -181,6 +195,42 @@ namespace MWClass
return true; 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<CustomData&> (*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 MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
@ -249,4 +299,7 @@ namespace MWClass
return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell);
} }
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
} }

@ -12,6 +12,9 @@ namespace MWClass
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature;
public: public:
virtual std::string getId (const MWWorld::Ptr& ptr) const; virtual std::string getId (const MWWorld::Ptr& ptr) const;
@ -66,6 +69,18 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const; 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(); static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;

@ -223,6 +223,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
Style * createStyle (char const * fontName, Colour fontColour) 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) for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i)
if (i->match (fontName, fontColour, fontColour, fontColour, 0)) if (i->match (fontName, fontColour, fontColour, fontColour, 0))
return &*i; return &*i;
@ -405,7 +408,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ()))
{ {
MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ());
space_width += gi->advance; if (gi)
space_width += gi->advance + gi->bearingX;
stream.consume (); stream.consume ();
} }
@ -414,7 +418,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ()))
{ {
MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (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; word_height = line_height;
++character_count; ++character_count;
stream.consume (); stream.consume ();
@ -628,6 +633,9 @@ namespace
{ {
MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch);
if (!gi)
return;
MyGUI::FloatRect vr; MyGUI::FloatRect vr;
vr.left = mCursor.left + gi->bearingX; vr.left = mCursor.left + gi->bearingX;
@ -647,7 +655,8 @@ namespace
{ {
MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch);
mCursor.left += gi->bearingX + gi->advance; if (gi)
mCursor.left += gi->bearingX + gi->advance;
} }
private: private:

@ -3,11 +3,39 @@
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwworld/class.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 namespace MWGui
{ {
ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources) ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)
: mItemSources(itemSources) : mItemSources(itemSources)
, mWorldItems(worldItems)
{ {
assert (mItemSources.size()); 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) 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 (stacks(*it, item.mBase))
if (*it == item.mBase || (store.stacks(*it, item.mBase) && item.mBase.getContainerStore()->stacks(*it, item.mBase)))
{ {
int refCount = it->getRefData().getCount(); int refCount = it->getRefData().getCount();
it->getRefData().setCount(std::max(0, refCount - toRemove)); it->getRefData().setCount(std::max(0, refCount - toRemove));
@ -76,6 +103,21 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
} }
} }
} }
for (std::vector<MWWorld::Ptr>::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"); throw std::runtime_error("Not enough items to remove could be found");
} }
@ -91,8 +133,7 @@ void ContainerItemModel::update()
std::vector<ItemStack>::iterator itemStack = mItems.begin(); std::vector<ItemStack>::iterator itemStack = mItems.begin();
for (; itemStack != mItems.end(); ++itemStack) 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 (stacks(*it, itemStack->mBase))
if (store.stacks(itemStack->mBase, *it) && it->getContainerStore()->stacks(itemStack->mBase, *it))
{ {
// we already have an item stack of this kind, add to it // we already have an item stack of this kind, add to it
itemStack->mCount += it->getRefData().getCount(); itemStack->mCount += it->getRefData().getCount();
@ -108,6 +149,26 @@ void ContainerItemModel::update()
} }
} }
} }
for (std::vector<MWWorld::Ptr>::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source)
{
std::vector<ItemStack>::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);
}
}
} }
} }

@ -11,7 +11,7 @@ namespace MWGui
class ContainerItemModel : public ItemModel class ContainerItemModel : public ItemModel
{ {
public: public:
ContainerItemModel (const std::vector<MWWorld::Ptr>& itemSources); ContainerItemModel (const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems);
///< @note The order of elements \a itemSources matters here. The first element has the highest priority for removal, ///< @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. /// while the last element will be used to add new items to.
@ -28,6 +28,7 @@ namespace MWGui
private: private:
std::vector<MWWorld::Ptr> mItemSources; std::vector<MWWorld::Ptr> mItemSources;
std::vector<MWWorld::Ptr> mWorldItems;
std::vector<ItemStack> mItems; std::vector<ItemStack> mItems;
}; };

@ -115,7 +115,7 @@ namespace MWGui
void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& topicLinks) const void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& 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->sectionBreak(9);
if (mTitle != "") if (mTitle != "")
typesetter->write(title, to_utf8_span(mTitle.c_str())); typesetter->write(title, to_utf8_span(mTitle.c_str()));
@ -159,7 +159,7 @@ namespace MWGui
if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) 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 size_t formatted = 0; // points to the first character that is not laid out yet
for (std::map<Range, intptr_t>::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) for (std::map<Range, intptr_t>::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 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 linkHot (143/255.f, 155/255.f, 218/255.f);
const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/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<std::string, Link*>& topicLinks) const void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<std::string, Link*>& 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->sectionBreak(9);
typesetter->write(title, to_utf8_span(mText.c_str())); typesetter->write(title, to_utf8_span(mText.c_str()));
} }
@ -465,7 +465,7 @@ namespace MWGui
(*it)->write(typesetter, &mKeywordSearch, mTopicLinks); (*it)->write(typesetter, &mKeywordSearch, mTopicLinks);
BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White); BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White);
// choices // choices
const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f); const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f);

@ -61,30 +61,32 @@ namespace MWGui
void EnchantingDialog::updateLabels() void EnchantingDialog::updateLabels()
{ {
std::stringstream enchantCost; 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<std::string>(mEnchanting.getMaxEnchantValue())); mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue()));
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge())); mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));
mCastCost->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantCost())); std::stringstream castCost;
castCost << std::setprecision(1) << std::fixed << mEnchanting.getCastCost();
mCastCost->setCaption(boost::lexical_cast<std::string>(castCost.str()));
mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice())); mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice()));
switch(mEnchanting.getEnchantType()) switch(mEnchanting.getCastStyle())
{ {
case 0: case ESM::Enchantment::CastOnce:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 1: case ESM::Enchantment::WhenStrikes:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 2: case ESM::Enchantment::WhenUsed:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 3: case ESM::Enchantment::ConstantEffect:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant"));
mAddEffectDialog.constantEffect=true; mAddEffectDialog.constantEffect=true;
break; break;
@ -169,7 +171,7 @@ namespace MWGui
image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem); image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem);
mEnchanting.setOldItem(item); mEnchanting.setOldItem(item);
mEnchanting.nextEnchantType(); mEnchanting.nextCastStyle();
updateLabels(); updateLabels();
} }
@ -248,7 +250,7 @@ namespace MWGui
void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender)
{ {
mEnchanting.nextEnchantType(); mEnchanting.nextCastStyle();
updateLabels(); updateLabels();
} }
@ -278,7 +280,7 @@ namespace MWGui
return; return;
} }
if (mEnchanting.getEnchantCost() > mEnchanting.getMaxEnchantValue()) if (mEnchanting.getEnchantPoints() > mEnchanting.getMaxEnchantValue())
{ {
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}");
return; return;

@ -0,0 +1,238 @@
#include "fontloader.hpp"
#include <OgreResourceGroupManager.h>
#include <OgreTextureManager.h>
#include <MyGUI_ResourceManager.h>
#include <MyGUI_FontManager.h>
#include <MyGUI_ResourceManualFont.h>
#include <MyGUI_XmlDocument.h>
#include <MyGUI_FactoryManager.h>
#include <components/misc/stringops.hpp>
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<Ogre::uchar> 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::ResourceManualFont*>(
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);
}
}

@ -0,0 +1,25 @@
#ifndef MWGUI_FONTLOADER_H
#define MWGUI_FONTLOADER_H
#include <components/to_utf8/to_utf8.hpp>
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

@ -202,14 +202,14 @@ namespace MWGui
float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const 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) return MyGUI::FontManager::getInstance().getByName(fontName)
->getGlyphInfo(unicodeChar)->width; ->getGlyphInfo(unicodeChar)->width;
} }
float BookTextParser::currentFontHeight() const 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(); return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
} }
@ -251,10 +251,8 @@ namespace MWGui
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
} }
boost::algorithm::replace_all(text, "\n", "\n"); boost::algorithm::replace_all(text, "<BR>", "\n");
boost::algorithm::replace_all(text, "\r", "\r"); boost::algorithm::replace_all(text, "<P>", "\n\n");
boost::algorithm::replace_all(text, "<BR>", "\n\n");
boost::algorithm::replace_all(text, "<P>", "\n\n"); // tweaking by adding another newline to see if that spaces out better
boost::algorithm::trim_left(text); boost::algorithm::trim_left(text);
// remove trailing " // remove trailing "

@ -13,8 +13,6 @@ namespace MWGui
, mType(Type_Normal) , mType(Type_Normal)
, mBase(base) , mBase(base)
{ {
assert(base.getContainerStore());
if (MWWorld::Class::get(base).getEnchantment(base) != "") if (MWWorld::Class::get(base).getEnchantment(base) != "")
mFlags |= Flag_Enchanted; mFlags |= Flag_Enchanted;
} }
@ -31,18 +29,42 @@ namespace MWGui
{ {
if(mBase == other.mBase) if(mBase == other.mBase)
return true; 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) bool operator == (const ItemStack& left, const ItemStack& right)
{ {
if (left.mType != right.mType) if (left.mType != right.mType)
return false; return false;
if(left.mBase == right.mBase) if(left.mBase == right.mBase)
return true; 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() ItemModel::ItemModel()

@ -189,14 +189,14 @@ book JournalBooks::createEmptyJournalBook ()
{ {
BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black);
typesetter->write (header, to_utf8_span ("You have no journal entries!")); typesetter->write (header, to_utf8_span ("You have no journal entries!"));
typesetter->lineBreak (); typesetter->lineBreak ();
typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); 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); BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue);
typesetter->sectionBreak (20); typesetter->sectionBreak (20);
@ -231,8 +231,8 @@ book JournalBooks::createJournalBook ()
{ {
BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black);
mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true)); mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true));
@ -243,8 +243,8 @@ book JournalBooks::createTopicBook (uintptr_t topicId)
{ {
BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black);
mModel->visitTopicName (topicId, AddTopicName (typesetter, header)); mModel->visitTopicName (topicId, AddTopicName (typesetter, header));
@ -259,8 +259,8 @@ book JournalBooks::createQuestBook (uintptr_t questId)
{ {
BookTypesetter::Ptr typesetter = createTypesetter (); BookTypesetter::Ptr typesetter = createTypesetter ();
BookTypesetter::Style* header = typesetter->createStyle ("EB Garamond", MyGUI::Colour (0.60f, 0.00f, 0.00f)); BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f));
BookTypesetter::Style* body = typesetter->createStyle ("EB Garamond", MyGUI::Colour::Black); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black);
mModel->visitQuestName (questId, AddQuestName (typesetter, header)); mModel->visitQuestName (questId, AddQuestName (typesetter, header));
@ -275,7 +275,7 @@ book JournalBooks::createTopicIndexBook ()
typesetter->setSectionAlignment (BookTypesetter::AlignCenter); 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) for (int i = 0; i < 26; ++i)
{ {
@ -300,7 +300,7 @@ book JournalBooks::createTopicIndexBook ()
book JournalBooks::createTopicIndexBook (char character) book JournalBooks::createTopicIndexBook (char character)
{ {
BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); 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)); mModel->visitTopicNamesStartingWith (character, AddTopicLink (typesetter, style));
@ -310,7 +310,7 @@ book JournalBooks::createTopicIndexBook (char character)
book JournalBooks::createQuestIndexBook (bool activeOnly) book JournalBooks::createQuestIndexBook (bool activeOnly)
{ {
BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); 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)); mModel->visitQuestNames (activeOnly, AddQuestLink (typesetter, base));

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

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

@ -71,7 +71,7 @@ namespace MWGui
if (item.mType == ItemStack::Type_Equipped && !mShowEquipped) if (item.mType == ItemStack::Type_Equipped && !mShowEquipped)
return false; return false;
int category; int category = 0;
if (base.getTypeName() == typeid(ESM::Armor).name() if (base.getTypeName() == typeid(ESM::Armor).name()
|| base.getTypeName() == typeid(ESM::Clothing).name()) || base.getTypeName() == typeid(ESM::Clothing).name())
category = Category_Apparel; category = Category_Apparel;
@ -106,6 +106,7 @@ namespace MWGui
if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted
|| (base.getTypeName() != typeid(ESM::Armor).name() || (base.getTypeName() != typeid(ESM::Armor).name()
&& base.getTypeName() != typeid(ESM::Clothing).name() && base.getTypeName() != typeid(ESM::Clothing).name()
&& base.getTypeName() != typeid(ESM::Weapon).name()
&& base.getTypeName() != typeid(ESM::Book).name()))) && base.getTypeName() != typeid(ESM::Book).name())))
return false; return false;
if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name() if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name()

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

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

@ -88,8 +88,10 @@ namespace MWGui
MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
// Important: actor goes last, so that items purchased by the merchant go into his inventory // Important: actor goes last, so that items purchased by the merchant go into his inventory
itemSources.push_back(actor); itemSources.push_back(actor);
std::vector<MWWorld::Ptr> 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); mSortModel = new SortFilterItemModel(mTradeModel);
mItemView->setModel (mSortModel); mItemView->setModel (mSortModel);

@ -42,6 +42,7 @@
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "bookpage.hpp" #include "bookpage.hpp"
#include "itemview.hpp" #include "itemview.hpp"
#include "fontloader.hpp"
namespace MWGui namespace MWGui
{ {
@ -49,7 +50,7 @@ namespace MWGui
WindowManager::WindowManager( WindowManager::WindowManager(
const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre,
const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts,
Translation::Storage& translationDataStorage) Translation::Storage& translationDataStorage, ToUTF8::FromType encoding)
: mGuiManager(NULL) : mGuiManager(NULL)
, mRendering(ogre) , mRendering(ogre)
, mHud(NULL) , mHud(NULL)
@ -109,6 +110,10 @@ namespace MWGui
mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath);
mGui = mGuiManager->getGui(); mGui = mGuiManager->getGui();
// Load fonts
FontLoader fontLoader (encoding);
fontLoader.loadAllFonts();
//Register own widgets with MyGUI //Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget");

@ -83,7 +83,7 @@ namespace MWGui
WindowManager(const Compiler::Extensions& extensions, int fpsLevel, WindowManager(const Compiler::Extensions& extensions, int fpsLevel,
OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath,
const std::string& cacheDir, bool consoleOnlyScripts, const std::string& cacheDir, bool consoleOnlyScripts,
Translation::Storage& translationDataStorage); Translation::Storage& translationDataStorage, ToUTF8::FromType encoding);
virtual ~WindowManager(); virtual ~WindowManager();
/** /**

@ -29,8 +29,11 @@ namespace MWMechanics
calculateCreatureStatModifiers (ptr); calculateCreatureStatModifiers (ptr);
// AI // AI
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
creatureStats.getAiSequence().execute (ptr); {
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
creatureStats.getAiSequence().execute (ptr);
}
} }
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
@ -309,4 +312,12 @@ namespace MWMechanics
if(iter != mActors.end()) if(iter != mActors.end())
iter->second.skipAnim(); 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;
}
} }

@ -82,6 +82,7 @@ namespace MWMechanics
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr); void skipAnimation(const MWWorld::Ptr& ptr);
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName);
}; };
} }

@ -1,6 +1,5 @@
#include "aiescort.hpp" #include "aiescort.hpp"
#include "character.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -10,16 +9,13 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include <boost/graph/astar_search.hpp>
#include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a>0) return 1.; if(a > 0)
else return -1.; return 1.0;
return -1.0;
} }
} }
@ -29,74 +25,73 @@ namespace
TODO: Take account for actors being in different cells. TODO: Take account for actors being in different cells.
*/ */
MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) namespace MWMechanics
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
mMaxDist = 470; AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
{
mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long // 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. // 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) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mDuration = 0;
else else
{ {
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
} }
}
MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) 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) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
mMaxDist = 470; mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long // 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. // 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) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mDuration = 0;
else else
{ {
MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100);
}
} }
}
MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const AiEscort *MWMechanics::AiEscort::clone() const
{ {
return new AiEscort(*this); return new AiEscort(*this);
} }
bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) 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(); // If AiEscort has ran for as long or longer then the duration specified
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); // and the duration is not infinite, the package is complete.
std::cout << "AiEscort: " << currentSecond << " time: " << currentSecond - mStartingSecond << std::endl; if(mDuration != 0)
if(currentSecond - mStartingSecond >= mDuration)
{ {
return true; MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp();
unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100);
if(currentSecond - mStartingSecond >= mDuration)
return true;
} }
}
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{ {
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort. // 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. // 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; 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); 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. // 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. // 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -115,68 +111,70 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
} }
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; cellX = actor.getCell()->mCell->mData.mX;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; 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; ESM::Pathgrid::Point dest;
dest.mX = mX; dest.mX = mX;
dest.mY = mY; dest.mY = mY;
dest.mZ = mZ; dest.mZ = mZ;
ESM::Pathgrid::Point start; ESM::Pathgrid::Point start;
start.mX = pos.pos[0]; start.mX = pos.pos[0];
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
} }
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const leaderPos = actor.getRefData().getPosition().pos;
const float* const followerPos = follower.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos;
double differenceBetween[3]; double differenceBetween[3];
for (short i = 0; i < 3; ++i) for (short counter = 0; counter < 3; counter++)
differenceBetween[i] = (leaderPos[i] - followerPos[i]); differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]);
float distanceBetweenResult = float distanceBetweenResult =
(differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] *
differenceBetween[2]);
if(distanceBetweenResult <= mMaxDist * mMaxDist) if(distanceBetweenResult <= mMaxDist * mMaxDist)
{ {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470; 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;
} }
else
int AiEscort::getTypeId() const
{ {
// Stop moving if the player is to far away return 2;
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;
} }

@ -2,30 +2,9 @@
#define GAME_MWMECHANICS_AIESCORT_H #define GAME_MWMECHANICS_AIESCORT_H
#include "aipackage.hpp" #include "aipackage.hpp"
#include "pathfinding.hpp"
#include <string> #include <string>
/* From CS: #include "pathfinding.hpp"
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, youll 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.
*/
namespace MWMechanics namespace MWMechanics
{ {

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

@ -2,6 +2,7 @@
#define GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H
#include "aipackage.hpp" #include "aipackage.hpp"
#include "pathfinding.hpp" #include "pathfinding.hpp"
namespace MWMechanics namespace MWMechanics
@ -25,10 +26,7 @@ namespace MWMechanics
int cellX; int cellX;
int cellY; int cellY;
//bool isPathConstructed;
//std::list<ESM::Pathgrid::Point> mPath;
PathFinder mPathFinder; PathFinder mPathFinder;
}; };
} }

@ -1,23 +1,337 @@
#include "aiwander.hpp" #include "aiwander.hpp"
#include <iostream>
MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): #include "movement.hpp"
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{
}
MWMechanics::AiPackage * MWMechanics::AiWander::clone() const #include "../mwworld/class.hpp"
{ #include "../mwworld/player.hpp"
return new AiWander(*this); #include "../mwbase/world.hpp"
} #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include <OgreVector3.h>
bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) namespace
{ {
// Return completed float sgn(float a)
return true; {
if(a > 0)
return 1.0;
return -1.0;
}
} }
int MWMechanics::AiWander::getTypeId() const namespace MWMechanics
{ {
return 0; AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{
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<ESM::GameSetting>().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<ESM::Pathgrid>().search(*actor.getCell()->mCell);
mCellX = actor.getCell()->mCell->mData.mX;
mCellY = actor.getCell()->mCell->mData.mY;
if(!mPathgrid)
mDistance = 0;
else if(mPathgrid->mPoints.empty())
mDistance = 0;
if(mDistance)
{
mXCell = 0;
mYCell = 0;
if(actor.getCell()->mCell->isExterior())
{
mXCell = mCellX * ESM::Land::REAL_SIZE;
mYCell = mCellY * ESM::Land::REAL_SIZE;
}
Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos);
npcPos[0] = npcPos[0] - mXCell;
npcPos[1] = npcPos[1] - mYCell;
for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++)
{
Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY,
mPathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(mPathgrid->mPoints[counter]);
}
if(!mAllowedNodes.empty())
{
Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
float closestNode = npcPos.squaredDistance(firstNodePos);
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
}
if(mAllowedNodes.empty())
mDistance = 0;
}
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY;
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
stopWalking(actor);
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiWander.
// FIXME: This *should* pause the AiWander package instead of terminating it.
if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200))
{
stopWalking(actor);
return true;
}
}
// Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY)))
mDistance = 0;
if(mChooseAction)
{
mPlayedIdle = 0;
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;
}
} }

@ -4,6 +4,10 @@
#include "aipackage.hpp" #include "aipackage.hpp"
#include <vector> #include <vector>
#include "pathfinding.hpp"
#include "../mwworld/timestamp.hpp"
namespace MWMechanics namespace MWMechanics
{ {
class AiWander : public AiPackage class AiWander : public AiPackage
@ -18,11 +22,42 @@ namespace MWMechanics
///< 0: Wander ///< 0: Wander
private: 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 mDistance;
int mDuration; int mDuration;
int mTimeOfDay; int mTimeOfDay;
std::vector<int> mIdle; std::vector<int> mIdle;
bool mRepeat; 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<ESM::Pathgrid::Point> mAllowedNodes;
ESM::Pathgrid::Point mCurrentNode;
PathFinder mPathFinder;
const ESM::Pathgrid *mPathgrid;
}; };
} }

@ -461,15 +461,18 @@ void CharacterController::update(float duration, Movement &movement)
Ogre::Vector3 moved = mAnimation->runAnimation(duration); Ogre::Vector3 moved = mAnimation->runAnimation(duration);
// Ensure we're moving in generally the right direction // Ensure we're moving in generally the right direction
if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || if (speed > 0.f)
(movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) {
moved.x = movement.mPosition[0]; if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) ||
if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) || (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f))
(movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f)) moved.x = movement.mPosition[0];
moved.y = movement.mPosition[1]; if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) ||
if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) || (movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f))
(movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f)) moved.y = movement.mPosition[1];
moved.z = movement.mPosition[2]; 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[0] = moved.x;
movement.mPosition[1] = moved.y; movement.mPosition[1] = moved.y;
@ -509,6 +512,14 @@ void CharacterController::skipAnim()
mSkipAnim = true; mSkipAnim = true;
} }
bool CharacterController::isAnimPlaying(const std::string &groupName)
{
if(mAnimation == NULL)
return false;
else
return mAnimation->isPlaying(groupName);
}
void CharacterController::clearAnimQueue() void CharacterController::clearAnimQueue()
{ {

@ -125,6 +125,7 @@ public:
void playGroup(const std::string &groupname, int mode, int count); void playGroup(const std::string &groupname, int mode, int count);
void skipAnim(); void skipAnim();
bool isAnimPlaying(const std::string &groupName);
void setState(CharacterState state); void setState(CharacterState state);
CharacterState getState() const CharacterState getState() const

@ -12,7 +12,7 @@
namespace MWMechanics namespace MWMechanics
{ {
Enchanting::Enchanting(): Enchanting::Enchanting():
mEnchantType(0) mCastStyle(ESM::Enchantment::CastOnce)
{} {}
void Enchanting::setOldItem(MWWorld::Ptr oldItem) void Enchanting::setOldItem(MWWorld::Ptr oldItem)
@ -41,9 +41,9 @@ namespace MWMechanics
mEffectList=effectList; mEffectList=effectList;
} }
int Enchanting::getEnchantType() const int Enchanting::getCastStyle() const
{ {
return mEnchantType; return mCastStyle;
} }
void Enchanting::setSoulGem(MWWorld::Ptr soulGem) void Enchanting::setSoulGem(MWWorld::Ptr soulGem)
@ -74,12 +74,12 @@ namespace MWMechanics
MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1); MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1);
} }
if(mEnchantType==3) if(mCastStyle==ESM::Enchantment::ConstantEffect)
{ {
enchantment.mData.mCharge=0; enchantment.mData.mCharge=0;
} }
enchantment.mData.mType = mEnchantType; enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getEnchantCost(); enchantment.mData.mCost = getEnchantPoints();
enchantment.mEffects = mEffectList; enchantment.mEffects = mEffectList;
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
@ -98,87 +98,142 @@ namespace MWMechanics
return true; return true;
} }
void Enchanting::nextEnchantType() void Enchanting::nextCastStyle()
{ {
mEnchantType++;
if (itemEmpty()) if (itemEmpty())
{ {
mEnchantType = 0; mCastStyle = ESM::Enchantment::WhenUsed;
return; return;
} }
if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name()))
{ const bool powerfulSoul = getGemCharge() >= \
int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt();
switch(mEnchantType) if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
{ // Armor or Clothing
switch(mCastStyle)
{ {
case 1: case ESM::Enchantment::WhenUsed:
mEnchantType = 2; if (powerfulSoul)
case 3: mCastStyle = ESM::Enchantment::ConstantEffect;
if(getGemCharge()<soulConstAmount) return;
mEnchantType=2; default: // takes care of Constant effect too
case 4: mCastStyle = ESM::Enchantment::WhenUsed;
mEnchantType = 2; return;
} }
} }
else if(mObjectType == typeid(ESM::Weapon).name()) else if(mObjectType == typeid(ESM::Weapon).name())
{ { // Weapon
switch(mEnchantType) switch(mCastStyle)
{ {
case 3: case ESM::Enchantment::WhenStrikes:
mEnchantType = 1; mCastStyle = ESM::Enchantment::WhenUsed;
return;
case ESM::Enchantment::WhenUsed:
if (powerfulSoul)
mCastStyle = ESM::Enchantment::ConstantEffect;
else
mCastStyle = ESM::Enchantment::WhenStrikes;
return;
default: // takes care of Constant effect too
mCastStyle = ESM::Enchantment::WhenStrikes;
return;
} }
} }
else if(mObjectType == typeid(ESM::Book).name()) else if(mObjectType == typeid(ESM::Book).name())
{ { // Scroll or Book
mEnchantType=0; mCastStyle = ESM::Enchantment::CastOnce;
return;
} }
// Fail case
mCastStyle = ESM::Enchantment::CastOnce;
} }
float Enchanting::getEnchantCost() const /*
* Vanilla enchant cost formula:
*
* Touch/Self: (min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025
* Target: 1.5 * ((min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025)
* Constant eff: (min + max) * baseCost * 2.5 + area * baseCost * 0.025
*
* For multiple effects - cost of each effect is multiplied by number of effects that follows +1.
*
* Note: Minimal value inside formula for 'min' and 'max' is 1. So in vanilla:
* (0 + 0) == (1 + 0) == (1 + 1) => 2 or (2 + 0) == (1 + 2) => 3
*
* Formula on UESPWiki is not entirely correct.
*/
float Enchanting::getEnchantPoints() const
{ {
if (mEffectList.mList.empty())
// No effects added, cost = 0
return 0;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float cost = 0;
std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList; std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList;
int i=mEffects.size();
if(i<=0)
return 0;
/* float enchantmentCost = 0;
Formula from http://www.uesp.net/wiki/Morrowind:Enchant int effectsLeftCnt = mEffects.size();
*/ float baseCost, magnitudeCost, areaCost;
int magMin, magMax, area;
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{ {
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(it->mEffectID); baseCost = (store.get<ESM::MagicEffect>().find(it->mEffectID))->mData.mBaseCost;
// To reflect vanilla behavior
float cost1 = ((it->mMagnMin + it->mMagnMax)*it->mDuration*effect->mData.mBaseCost*0.025); magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin;
magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax;
float cost2 = (std::max(1, it->mArea)*0.125*effect->mData.mBaseCost); area = (it->mArea == 0) ? 1 : it->mArea;
if(mEnchantType==3) if (mCastStyle == ESM::Enchantment::ConstantEffect)
{ {
int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantDurationMult")->getFloat(); magnitudeCost = (magMin + magMax) * baseCost * 2.5;
cost1 *= constDurationMultipler; }
cost2 *= 2; 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; areaCost = area * 0.025 * baseCost;
fullcost*= i; if (it->mRange == ESM::RT_Target)
i--; 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 int Enchanting::getEnchantPrice() const
{ {
if(mEnchanter.isEmpty()) if(mEnchanter.isEmpty())
return 0; return 0;
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->getFloat(); float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().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; return price;
} }
@ -201,16 +256,12 @@ namespace MWMechanics
} }
bool Enchanting::soulEmpty() const bool Enchanting::soulEmpty() const
{ {
if (mSoulGemPtr.isEmpty()) return mSoulGemPtr.isEmpty();
return true;
return false;
} }
bool Enchanting::itemEmpty() const bool Enchanting::itemEmpty() const
{ {
if(mOldItemPtr.isEmpty()) return mOldItemPtr.isEmpty();
return true;
return false;
} }
void Enchanting::setSelfEnchanting(bool selfEnchanting) void Enchanting::setSelfEnchanting(bool selfEnchanting)
@ -235,8 +286,8 @@ namespace MWMechanics
(0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified())
+ (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()));
float chance2 = 2.5 * getEnchantCost(); float chance2 = 2.5 * getEnchantPoints();
if(mEnchantType==3) if(mCastStyle==ESM::Enchantment::ConstantEffect)
{ {
float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat(); float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat();
chance2 /= constantChance; chance2 /= constantChance;

@ -13,7 +13,7 @@ namespace MWMechanics
MWWorld::Ptr mSoulGemPtr; MWWorld::Ptr mSoulGemPtr;
MWWorld::Ptr mEnchanter; MWWorld::Ptr mEnchanter;
int mEnchantType; int mCastStyle;
bool mSelfEnchanting; bool mSelfEnchanting;
@ -33,9 +33,10 @@ namespace MWMechanics
void setEffect(ESM::EffectList effectList); void setEffect(ESM::EffectList effectList);
void setSoulGem(MWWorld::Ptr soulGem); void setSoulGem(MWWorld::Ptr soulGem);
bool create(); //Return true if created, false if failed. bool create(); //Return true if created, false if failed.
void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object)
int getEnchantType() const; int getCastStyle() const;
float getEnchantCost() const; float getEnchantPoints() const;
float getCastCost() const;
int getEnchantPrice() const; int getEnchantPrice() const;
float getMaxEnchantValue() const; float getMaxEnchantValue() const;
int getGemCharge() const; int getGemCharge() const;

@ -668,5 +668,12 @@ namespace MWMechanics
else else
mObjects.skipAnimation(ptr); 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;
}
} }

@ -100,6 +100,7 @@ namespace MWMechanics
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual void skipAnimation(const MWWorld::Ptr& ptr);
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
}; };
} }

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

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

@ -64,13 +64,16 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
{ {
// select best shader mode // select best shader mode
bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); 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. // glsl is only supported in opengl mode and hlsl only in direct3d mode.
if (Settings::Manager::getString("shader mode", "General") == "" std::string currentMode = Settings::Manager::getString("shader mode", "General");
|| (openGL && Settings::Manager::getString("shader mode", "General") == "hlsl") if (currentMode == ""
|| (!openGL && Settings::Manager::getString("shader mode", "General") == "glsl")) || (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); 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"); std::string l = Settings::Manager::getString("shader mode", "General");
if (l == "glsl") if (l == "glsl")
lang = sh::Language_GLSL; lang = sh::Language_GLSL;
else if (l == "glsles")
lang = sh::Language_GLSLES;
else if (l == "hlsl") else if (l == "hlsl")
lang = sh::Language_HLSL; lang = sh::Language_HLSL;
else else
@ -396,29 +401,24 @@ void RenderingManager::setWaterHeight(const float height)
void RenderingManager::skyEnable () void RenderingManager::skyEnable ()
{ {
if(mSkyManager)
mSkyManager->enable(); mSkyManager->enable();
mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); mOcclusionQuery->setSunNode(mSkyManager->getSunNode());
} }
void RenderingManager::skyDisable () void RenderingManager::skyDisable ()
{ {
if(mSkyManager) mSkyManager->disable();
mSkyManager->disable();
} }
void RenderingManager::skySetHour (double hour) void RenderingManager::skySetHour (double hour)
{ {
if(mSkyManager) mSkyManager->setHour(hour);
mSkyManager->setHour(hour);
} }
void RenderingManager::skySetDate (int day, int month) void RenderingManager::skySetDate (int day, int month)
{ {
if(mSkyManager) mSkyManager->setDate(day, month);
mSkyManager->setDate(day, month);
} }
int RenderingManager::skyGetMasserPhase() const int RenderingManager::skyGetMasserPhase() const
@ -433,8 +433,7 @@ int RenderingManager::skyGetSecundaPhase() const
} }
void RenderingManager::skySetMoonColour (bool red){ void RenderingManager::skySetMoonColour (bool red){
if(mSkyManager) mSkyManager->setMoonColour(red);
mSkyManager->setMoonColour(red);
} }
bool RenderingManager::toggleRenderMode(int mode) bool RenderingManager::toggleRenderMode(int mode)

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

@ -1,8 +1,6 @@
#ifndef GAME_SOUND_FFMPEG_DECODER_H #ifndef GAME_SOUND_FFMPEG_DECODER_H
#define GAME_SOUND_FFMPEG_DECODER_H #define GAME_SOUND_FFMPEG_DECODER_H
#include <string>
// FIXME: This can't be right? The headers refuse to build without UINT64_C, // 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 // which only gets defined in stdint.h in either C99 mode or with this macro
// defined... // defined...
@ -14,6 +12,8 @@ extern "C"
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
} }
#include <string>
#include "sound_decoder.hpp" #include "sound_decoder.hpp"

@ -1724,4 +1724,31 @@ namespace MWWorld
} }
} }
} }
struct ListHandlesFunctor
{
std::vector<std::string> 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<MWWorld::Ptr>& out)
{
const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells();
for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt)
{
ListHandlesFunctor functor;
(*cellIt)->forEach<ListHandlesFunctor>(functor);
for (std::vector<std::string>::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));
}
}
} }

@ -391,6 +391,8 @@ namespace MWWorld
virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out); virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);
///< get all containers in active cells owned by this Npc ///< get all containers in active cells owned by this Npc
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);
///< get all items in active cells owned by this Npc
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering);

@ -3,6 +3,8 @@
#ifdef _WIN32 #ifdef _WIN32
#include <boost/tr1/tr1/unordered_map> #include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP
#include <unordered_map>
#else #else
#include <tr1/unordered_map> #include <tr1/unordered_map>
#endif #endif
@ -48,7 +50,11 @@ struct ConfigurationManager
typedef Files::FixedPath<> FixedPathType; typedef Files::FixedPath<> FixedPathType;
typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const;
typedef std::tr1::unordered_map<std::string, path_type_f> TokensMappingContainer; #if defined HAVE_UNORDERED_MAP
typedef std::unordered_map<std::string, path_type_f> TokensMappingContainer;
#else
typedef std::tr1::unordered_map<std::string, path_type_f> TokensMappingContainer;
#endif
void loadConfig(const boost::filesystem::path& path, void loadConfig(const boost::filesystem::path& path,
boost::program_options::variables_map& variables, boost::program_options::variables_map& variables,

@ -46,7 +46,7 @@ void writeMissing(bool last)
int write_table(const std::string &charset, const std::string &tableName) int write_table(const std::string &charset, const std::string &tableName)
{ {
// Write table header // Write table header
cout << "static char " << tableName << "[] =\n{\n"; cout << "static signed char " << tableName << "[] =\n{\n";
// Open conversion system // Open conversion system
iconv_t cd = iconv_open ("UTF-8", charset.c_str()); iconv_t cd = iconv_open ("UTF-8", charset.c_str());
@ -106,6 +106,8 @@ int main()
"\n"; "\n";
write_table("WINDOWS-1252", "windows_1252"); write_table("WINDOWS-1252", "windows_1252");
write_table("CP437", "cp437");
// Close namespace // Close namespace
cout << "\n}\n\n"; cout << "\n}\n\n";

@ -790,6 +790,265 @@ static signed char windows_1252[] =
2, -61, -66, 0, 0, 0, 2, -61, -66, 0, 0, 0,
2, -61, -65, 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
};
} }

@ -63,6 +63,12 @@ Utf8Encoder::Utf8Encoder(const FromType sourceEncoding):
translationArray = ToUTF8::windows_1251; translationArray = ToUTF8::windows_1251;
break; break;
} }
case ToUTF8::CP437:
{
translationArray = ToUTF8::cp437;
break;
}
default: default:
{ {
assert(0); assert(0);

@ -12,8 +12,9 @@ namespace ToUTF8
{ {
WINDOWS_1250, // Central ane Eastern European languages WINDOWS_1250, // Central ane Eastern European languages
WINDOWS_1251, // Cyrillic 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) // 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); FromType calculateEncoding(const std::string& encodingName);

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

@ -87,6 +87,8 @@
Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'. 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/'. 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 <b>required</b>. 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 \code
#include "core.h" #include "core.h"

@ -10,7 +10,7 @@ namespace sh
{ {
public: public:
virtual void execute() = 0; virtual void execute() = 0;
virtual ~Action(); virtual ~Action() {}
}; };
class ActionDeleteMaterial : public Action class ActionDeleteMaterial : public Action

@ -15,7 +15,7 @@ class Query
public: public:
Query() Query()
: mDone(false) {} : mDone(false) {}
virtual ~Query(); virtual ~Query() {}
void execute(); void execute();

@ -58,11 +58,20 @@
#endif #endif
#if SH_GLSL == 1 #if SH_GLSL == 1 || SH_GLSLES == 1
#define shFract(val) fract(val) #define shFract(val) fract(val)
#if SH_GLSLES == 1
@version 100
#else
@version 120 @version 120
#endif
#if SH_GLSLES == 1 && SH_FRAGMENT_SHADER
precision mediump int;
precision mediump float;
#endif
#define float2 vec2 #define float2 vec2
#define float3 vec3 #define float3 vec3

@ -2,7 +2,6 @@
<MyGUI type="Skin"> <MyGUI type="Skin">
<Skin name = "TextBox" size = "16 16"> <Skin name = "TextBox" size = "16 16">
<Property key="FontName" value = "EB Garamond" />
<Property key="FontHeight" value = "16" /> <Property key="FontHeight" value = "16" />
<Property key="TextAlign" value = "ALIGN_DEFAULT" /> <Property key="TextAlign" value = "ALIGN_DEFAULT" />
<Property key="TextColour" value = "0.7 0.7 0.7" /> <Property key="TextColour" value = "0.7 0.7 0.7" />

@ -23,56 +23,21 @@
</Codes> </Codes>
</Resource> </Resource>
<Resource type="ResourceTrueTypeFont" name="EB Garamond 24"> <Resource type="ResourceTrueTypeFont" name="MonoFont">
<Property key="Source" value="EBGaramond-Regular.ttf"/> <Property key="Source" value="DejaVuLGCSansMono.ttf"/>
<Property key="Size" value="24"/> <Property key="Size" value="18"/>
<Property key="Resolution" value="72"/> <Property key="Resolution" value="50"/>
<Property key="Antialias" value="false"/> <Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/> <Property key="TabWidth" value="8"/>
<Property key="OffsetHeight" value="0"/> <Property key="OffsetHeight" value="0"/>
<Codes> <Codes>
<Code range="33 126"/> <Code range="33 126"/>
<Code range="160"/> <!-- Non-breaking space --> <Code range="192 382"/>
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/> <Code range="1025 1105"/>
<Code range="2026"/> <!-- Ellipsis -->
<Code range="8470"/> <Code range="8470"/>
<Code range="8211"/> <!-- Minus -->
<Code range="8216 8217"/> <!-- Single quotes -->
<Code range="8220 8221"/> <!-- Right and Left Double Quotation mark -->
<Code hide="128"/> <Code hide="128"/>
<Code hide="1026 1039"/> <Code hide="1026 1039"/>
<Code hide="1104"/> <Code hide="1104"/>
</Codes> </Codes>
</Resource> </Resource>
<Resource type="ResourceTrueTypeFont" name="Daedric">
<!--<Property key="Source" value="Oblivion/Oblivion Worn.ttf"/>-->
<Property key="Source" value="Oblivion/Oblivion.ttf"/>
<Property key="Size" value="24"/>
<Property key="Resolution" value="72"/>
<Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="65 122"/>
</Codes>
</Resource>
<Resource type="ResourceTrueTypeFont" name="MonoFont">
<Property key="Source" value="DejaVuLGCSansMono.ttf"/>
<Property key="Size" value="18"/>
<Property key="Resolution" value="50"/>
<Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="33 126"/>
<Code range="192 382"/>
<Code range="1025 1105"/>
<Code range="8470"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Codes>
</Resource>
</MyGUI> </MyGUI>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<MyGUI> <MyGUI>
<MyGUI type="Font"> <MyGUI type="Font">
<Property key="Default" value="EB Garamond"/> <Property key="Default" value="Magic Cards"/>
</MyGUI> </MyGUI>
<MyGUI type="Pointer"> <MyGUI type="Pointer">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

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

@ -206,6 +206,7 @@ void OgreRenderer::configure(const std::string &logPath,
pluginDir = absPluginPath.string(); pluginDir = absPluginPath.string();
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot);
Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot);

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

Loading…
Cancel
Save