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

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

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

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

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

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

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

@ -7,8 +7,11 @@
#include <QMap>
#include <QMessageBox>
#include <QTextCodec>
#include <QFile>
#include <QDebug>
#include <components/files/configurationmanager.hpp>
#include "settingcontainer.hpp"
@ -32,8 +35,11 @@ namespace boost
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
CSMSettings::UserSettings::UserSettings()
{
assert(!mUserSettingsInstance);
mUserSettingsInstance = this;
mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \
@ -45,9 +51,18 @@ 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);
@ -91,6 +106,9 @@ QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath,
}
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());
@ -111,7 +129,11 @@ bool CSMSettings::UserSettings::writeFile(QMap<QString, CSMSettings::SettingList
return true;
}
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSettings()
//void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap &sections) const
{
return mSectionSettings;
}
@ -218,3 +240,70 @@ void CSMSettings::UserSettings::updateSettings (const QString &sectionName, cons
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:
static UserSettings &instance()
{
static UserSettings instance;
UserSettings();
~UserSettings();
return instance;
}
static const UserSettings& instance();
void readSettings();
void setSettingsFiles(QStringList files);
<<<<<<< HEAD
bool writeFile(QMap<QString, SettingList *> &sections);
const SectionMap &getSettings ();
void updateSettings (const QString &sectionName, const QString &settingName = "");
@ -52,6 +54,21 @@ namespace CSMSettings {
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
void operator= (UserSettings const &); //not implemented

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

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

@ -24,7 +24,6 @@ namespace CSVSettings {
{
Q_OBJECT
QStringList mPaths;
QListWidget *mListWidget;
QStackedWidget *mStackedWidget;
@ -42,6 +41,9 @@ namespace CSVSettings {
void writeSettings();
void createSamplePage();
//Pages
void createWindowPage();
template <typename T>
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
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel
tradeitemmodel companionitemmodel pickpocketitemmodel fontloader
)
add_openmw_dir (mwdialogue

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

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

@ -342,6 +342,8 @@ namespace MWBase
virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
///< 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;

@ -5,6 +5,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -29,6 +30,7 @@ namespace
{
MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore mContainerStore;
MWMechanics::Movement mMovement;
virtual MWWorld::CustomData *clone() const;
};
@ -47,6 +49,18 @@ namespace MWClass
{
std::auto_ptr<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>();
// creature stats
@ -181,6 +195,42 @@ namespace MWClass
return true;
}
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
/// \todo what about the rest?
return walkSpeed;
}
MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const
{
ensureCustomData (ptr);
return dynamic_cast<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
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
@ -249,4 +299,7 @@ namespace MWClass
return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
}

@ -12,6 +12,9 @@ namespace MWClass
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature;
public:
virtual std::string getId (const MWWorld::Ptr& ptr) const;
@ -66,6 +69,18 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement.
virtual Ogre::Vector3 getMovementVector (const MWWorld::Ptr& ptr) const;
///< Return desired movement vector (determined based on movement settings,
/// stance and stats).
virtual Ogre::Vector3 getRotationVector (const MWWorld::Ptr& ptr) const;
///< Return desired rotations, as euler angles.
float getSpeed (const MWWorld::Ptr& ptr) const;
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;

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

@ -3,11 +3,39 @@
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace
{
bool stacks (const MWWorld::Ptr& left, const MWWorld::Ptr& right)
{
if (left == right)
return true;
// If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure
if (left.getContainerStore() && right.getContainerStore())
return left.getContainerStore()->stacks(left, right)
&& right.getContainerStore()->stacks(left, right);
if (left.getContainerStore())
return left.getContainerStore()->stacks(left, right);
if (right.getContainerStore())
return right.getContainerStore()->stacks(left, right);
MWWorld::ContainerStore store;
return store.stacks(left, right);
}
}
namespace MWGui
{
ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources)
ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)
: mItemSources(itemSources)
, mWorldItems(worldItems)
{
assert (mItemSources.size());
}
@ -65,8 +93,7 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
// If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure
if (*it == item.mBase || (store.stacks(*it, item.mBase) && item.mBase.getContainerStore()->stacks(*it, item.mBase)))
if (stacks(*it, item.mBase))
{
int refCount = it->getRefData().getCount();
it->getRefData().setCount(std::max(0, refCount - toRemove));
@ -76,6 +103,21 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
}
}
}
for (std::vector<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");
}
@ -91,8 +133,7 @@ void ContainerItemModel::update()
std::vector<ItemStack>::iterator itemStack = mItems.begin();
for (; itemStack != mItems.end(); ++itemStack)
{
// If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure
if (store.stacks(itemStack->mBase, *it) && it->getContainerStore()->stacks(itemStack->mBase, *it))
if (stacks(*it, itemStack->mBase))
{
// we already have an item stack of this kind, add to it
itemStack->mCount += it->getRefData().getCount();
@ -108,6 +149,26 @@ void ContainerItemModel::update()
}
}
}
for (std::vector<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
{
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,
/// while the last element will be used to add new items to.
@ -28,6 +28,7 @@ namespace MWGui
private:
std::vector<MWWorld::Ptr> mItemSources;
std::vector<MWWorld::Ptr> mWorldItems;
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
{
BookTypesetter::Style* title = typesetter->createStyle("EB Garamond", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f));
BookTypesetter::Style* title = typesetter->createStyle("", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f));
typesetter->sectionBreak(9);
if (mTitle != "")
typesetter->write(title, to_utf8_span(mTitle.c_str()));
@ -159,7 +159,7 @@ namespace MWGui
if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation())
{
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
size_t formatted = 0; // points to the first character that is not laid out yet
for (std::map<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
{
BookTypesetter::Style* style = typesetter->createStyle("EB Garamond", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f));
const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f);
const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f);
@ -215,7 +215,7 @@ namespace MWGui
void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map<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->write(title, to_utf8_span(mText.c_str()));
}
@ -465,7 +465,7 @@ namespace MWGui
(*it)->write(typesetter, &mKeywordSearch, mTopicLinks);
BookTypesetter::Style* body = typesetter->createStyle("EB Garamond", MyGUI::Colour::White);
BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White);
// choices
const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f);

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

@ -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
{
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont);
return MyGUI::FontManager::getInstance().getByName(fontName)
->getGlyphInfo(unicodeChar)->width;
}
float BookTextParser::currentFontHeight() const
{
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont);
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
}
@ -251,10 +251,8 @@ namespace MWGui
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
}
boost::algorithm::replace_all(text, "\n", "\n");
boost::algorithm::replace_all(text, "\r", "\r");
boost::algorithm::replace_all(text, "<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::replace_all(text, "<BR>", "\n");
boost::algorithm::replace_all(text, "<P>", "\n\n");
boost::algorithm::trim_left(text);
// remove trailing "

@ -13,8 +13,6 @@ namespace MWGui
, mType(Type_Normal)
, mBase(base)
{
assert(base.getContainerStore());
if (MWWorld::Class::get(base).getEnchantment(base) != "")
mFlags |= Flag_Enchanted;
}
@ -31,18 +29,42 @@ namespace MWGui
{
if(mBase == other.mBase)
return true;
return mBase.getContainerStore()->stacks(mBase, other.mBase)
&& other.mBase.getContainerStore()->stacks(mBase, other.mBase);
// If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure
if (mBase.getContainerStore() && other.mBase.getContainerStore())
return mBase.getContainerStore()->stacks(mBase, other.mBase)
&& other.mBase.getContainerStore()->stacks(mBase, other.mBase);
if (mBase.getContainerStore())
return mBase.getContainerStore()->stacks(mBase, other.mBase);
if (other.mBase.getContainerStore())
return other.mBase.getContainerStore()->stacks(mBase, other.mBase);
MWWorld::ContainerStore store;
return store.stacks(mBase, other.mBase);
}
bool operator == (const ItemStack& left, const ItemStack& right)
{
if (left.mType != right.mType)
return false;
if(left.mBase == right.mBase)
return true;
return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase)
&& right.mBase.getContainerStore()->stacks(left.mBase, right.mBase);
// If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure
if (left.mBase.getContainerStore() && right.mBase.getContainerStore())
return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase)
&& right.mBase.getContainerStore()->stacks(left.mBase, right.mBase);
if (left.mBase.getContainerStore())
return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase);
if (right.mBase.getContainerStore())
return right.mBase.getContainerStore()->stacks(left.mBase, right.mBase);
MWWorld::ContainerStore store;
return store.stacks(left.mBase, right.mBase);
}
ItemModel::ItemModel()

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

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

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

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

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

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

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

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

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

@ -29,8 +29,11 @@ namespace MWMechanics
calculateCreatureStatModifiers (ptr);
// AI
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
creatureStats.getAiSequence().execute (ptr);
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
{
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
creatureStats.getAiSequence().execute (ptr);
}
}
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
@ -309,4 +312,12 @@ namespace MWMechanics
if(iter != mActors.end())
iter->second.skipAnim();
}
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
return iter->second.isAnimPlaying(groupName);
return false;
}
}

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

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

@ -2,30 +2,9 @@
#define GAME_MWMECHANICS_AIESCORT_H
#include "aipackage.hpp"
#include "pathfinding.hpp"
#include <string>
/* From CS:
Escort
Makes the actor escort another actor to a location or for a specified period of time. During this time the actor will also protect the actor it is escorting.
If you are not doing this package with the player as the target, 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.
*/
#include "pathfinding.hpp"
namespace MWMechanics
{

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

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

@ -1,23 +1,337 @@
#include "aiwander.hpp"
#include <iostream>
MWMechanics::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)
{
}
#include "movement.hpp"
MWMechanics::AiPackage * MWMechanics::AiWander::clone() const
{
return new AiWander(*this);
}
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#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
return true;
float sgn(float a)
{
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 <vector>
#include "pathfinding.hpp"
#include "../mwworld/timestamp.hpp"
namespace MWMechanics
{
class AiWander : public AiPackage
@ -18,11 +22,42 @@ namespace MWMechanics
///< 0: Wander
private:
void stopWalking(const MWWorld::Ptr& actor);
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
int mDistance;
int mDuration;
int mTimeOfDay;
std::vector<int> mIdle;
bool mRepeat;
float mX;
float mY;
float mZ;
int mCellX;
int mCellY;
float mXCell;
float mYCell;
bool mStoredAvailableNodes;
bool mChooseAction;
bool mIdleNow;
bool mMoveNow;
bool mWalking;
float mIdleChanceMultiplier;
unsigned short mPlayedIdle;
MWWorld::TimeStamp mStartTime;
std::vector<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);
// Ensure we're moving in generally the right direction
if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) ||
(movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f))
moved.x = movement.mPosition[0];
if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) ||
(movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f))
moved.y = movement.mPosition[1];
if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) ||
(movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f))
moved.z = movement.mPosition[2];
if (speed > 0.f)
{
if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) ||
(movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f))
moved.x = movement.mPosition[0];
if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) ||
(movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f))
moved.y = movement.mPosition[1];
if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) ||
(movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f))
moved.z = movement.mPosition[2];
}
movement.mPosition[0] = moved.x;
movement.mPosition[1] = moved.y;
@ -509,6 +512,14 @@ void CharacterController::skipAnim()
mSkipAnim = true;
}
bool CharacterController::isAnimPlaying(const std::string &groupName)
{
if(mAnimation == NULL)
return false;
else
return mAnimation->isPlaying(groupName);
}
void CharacterController::clearAnimQueue()
{

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

@ -12,7 +12,7 @@
namespace MWMechanics
{
Enchanting::Enchanting():
mEnchantType(0)
mCastStyle(ESM::Enchantment::CastOnce)
{}
void Enchanting::setOldItem(MWWorld::Ptr oldItem)
@ -41,9 +41,9 @@ namespace MWMechanics
mEffectList=effectList;
}
int Enchanting::getEnchantType() const
int Enchanting::getCastStyle() const
{
return mEnchantType;
return mCastStyle;
}
void Enchanting::setSoulGem(MWWorld::Ptr soulGem)
@ -74,12 +74,12 @@ namespace MWMechanics
MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1);
}
if(mEnchantType==3)
if(mCastStyle==ESM::Enchantment::ConstantEffect)
{
enchantment.mData.mCharge=0;
}
enchantment.mData.mType = mEnchantType;
enchantment.mData.mCost = getEnchantCost();
enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getEnchantPoints();
enchantment.mEffects = mEffectList;
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
@ -98,87 +98,142 @@ namespace MWMechanics
return true;
}
void Enchanting::nextEnchantType()
void Enchanting::nextCastStyle()
{
mEnchantType++;
if (itemEmpty())
{
mEnchantType = 0;
mCastStyle = ESM::Enchantment::WhenUsed;
return;
}
if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name()))
{
int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt();
switch(mEnchantType)
const bool powerfulSoul = getGemCharge() >= \
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt();
if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
{ // Armor or Clothing
switch(mCastStyle)
{
case 1:
mEnchantType = 2;
case 3:
if(getGemCharge()<soulConstAmount)
mEnchantType=2;
case 4:
mEnchantType = 2;
case ESM::Enchantment::WhenUsed:
if (powerfulSoul)
mCastStyle = ESM::Enchantment::ConstantEffect;
return;
default: // takes care of Constant effect too
mCastStyle = ESM::Enchantment::WhenUsed;
return;
}
}
else if(mObjectType == typeid(ESM::Weapon).name())
{
switch(mEnchantType)
{ // Weapon
switch(mCastStyle)
{
case 3:
mEnchantType = 1;
case ESM::Enchantment::WhenStrikes:
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())
{
mEnchantType=0;
{ // Scroll or Book
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();
float cost = 0;
std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList;
int i=mEffects.size();
if(i<=0)
return 0;
/*
Formula from http://www.uesp.net/wiki/Morrowind:Enchant
*/
float enchantmentCost = 0;
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)
{
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(it->mEffectID);
float cost1 = ((it->mMagnMin + it->mMagnMax)*it->mDuration*effect->mData.mBaseCost*0.025);
float cost2 = (std::max(1, it->mArea)*0.125*effect->mData.mBaseCost);
baseCost = (store.get<ESM::MagicEffect>().find(it->mEffectID))->mData.mBaseCost;
// To reflect vanilla behavior
magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin;
magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax;
area = (it->mArea == 0) ? 1 : it->mArea;
if(mEnchantType==3)
if (mCastStyle == ESM::Enchantment::ConstantEffect)
{
int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantDurationMult")->getFloat();
cost1 *= constDurationMultipler;
cost2 *= 2;
magnitudeCost = (magMin + magMax) * baseCost * 2.5;
}
else
{
magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025;
if(it->mRange == ESM::RT_Target)
magnitudeCost *= 1.5;
}
if(effect->mData.mFlags & ESM::MagicEffect::CastTarget)
cost1 *= 1.5;
float fullcost = cost1+cost2;
fullcost*= i;
i--;
areaCost = area * 0.025 * baseCost;
if (it->mRange == ESM::RT_Target)
areaCost *= 1.5;
cost+=fullcost;
enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt;
--effectsLeftCnt;
}
return cost;
return enchantmentCost;
}
float Enchanting::getCastCost() const
{
if (mCastStyle == ESM::Enchantment::ConstantEffect)
return 0;
const float enchantCost = getEnchantPoints();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player);
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified();
/*
* Each point of enchant skill above/under 10 subtracts/adds
* one percent of enchantment cost while minimum is 1.
*/
const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10);
return (castCost < 1) ? 1 : castCost;
}
int Enchanting::getEnchantPrice() const
{
if(mEnchanter.isEmpty())
return 0;
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<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;
}
@ -201,16 +256,12 @@ namespace MWMechanics
}
bool Enchanting::soulEmpty() const
{
if (mSoulGemPtr.isEmpty())
return true;
return false;
return mSoulGemPtr.isEmpty();
}
bool Enchanting::itemEmpty() const
{
if(mOldItemPtr.isEmpty())
return true;
return false;
return mOldItemPtr.isEmpty();
}
void Enchanting::setSelfEnchanting(bool selfEnchanting)
@ -235,8 +286,8 @@ namespace MWMechanics
(0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified())
+ (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()));
float chance2 = 2.5 * getEnchantCost();
if(mEnchantType==3)
float chance2 = 2.5 * getEnchantPoints();
if(mCastStyle==ESM::Enchantment::ConstantEffect)
{
float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat();
chance2 /= constantChance;

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

@ -668,5 +668,12 @@ namespace MWMechanics
else
mObjects.skipAnimation(ptr);
}
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName)
{
if(MWWorld::Class::get(ptr).isActor())
return mActors.checkAnimationPlaying(ptr, groupName);
else
return false;
}
}

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

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

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

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

@ -32,9 +32,16 @@ namespace MWRender
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.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)

@ -1,8 +1,6 @@
#ifndef 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,
// which only gets defined in stdint.h in either C99 mode or with this macro
// defined...
@ -14,6 +12,8 @@ extern "C"
#include <libavformat/avformat.h>
}
#include <string>
#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);
///< 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);

@ -3,6 +3,8 @@
#ifdef _WIN32
#include <boost/tr1/tr1/unordered_map>
#elif defined HAVE_UNORDERED_MAP
#include <unordered_map>
#else
#include <tr1/unordered_map>
#endif
@ -48,7 +50,11 @@ struct ConfigurationManager
typedef Files::FixedPath<> FixedPathType;
typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const;
typedef std::tr1::unordered_map<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,
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)
{
// Write table header
cout << "static char " << tableName << "[] =\n{\n";
cout << "static signed char " << tableName << "[] =\n{\n";
// Open conversion system
iconv_t cd = iconv_open ("UTF-8", charset.c_str());
@ -106,6 +106,8 @@ int main()
"\n";
write_table("WINDOWS-1252", "windows_1252");
write_table("CP437", "cp437");
// Close namespace
cout << "\n}\n\n";

@ -790,6 +790,265 @@ static signed char windows_1252[] =
2, -61, -66, 0, 0, 0,
2, -61, -65, 0, 0, 0
};
static signed char cp437[] =
{
1, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0,
1, 2, 0, 0, 0, 0,
1, 3, 0, 0, 0, 0,
1, 4, 0, 0, 0, 0,
1, 5, 0, 0, 0, 0,
1, 6, 0, 0, 0, 0,
1, 7, 0, 0, 0, 0,
1, 8, 0, 0, 0, 0,
1, 9, 0, 0, 0, 0,
1, 10, 0, 0, 0, 0,
1, 11, 0, 0, 0, 0,
1, 12, 0, 0, 0, 0,
1, 13, 0, 0, 0, 0,
1, 14, 0, 0, 0, 0,
1, 15, 0, 0, 0, 0,
1, 16, 0, 0, 0, 0,
1, 17, 0, 0, 0, 0,
1, 18, 0, 0, 0, 0,
1, 19, 0, 0, 0, 0,
1, 20, 0, 0, 0, 0,
1, 21, 0, 0, 0, 0,
1, 22, 0, 0, 0, 0,
1, 23, 0, 0, 0, 0,
1, 24, 0, 0, 0, 0,
1, 25, 0, 0, 0, 0,
1, 26, 0, 0, 0, 0,
1, 27, 0, 0, 0, 0,
1, 28, 0, 0, 0, 0,
1, 29, 0, 0, 0, 0,
1, 30, 0, 0, 0, 0,
1, 31, 0, 0, 0, 0,
1, 32, 0, 0, 0, 0,
1, 33, 0, 0, 0, 0,
1, 34, 0, 0, 0, 0,
1, 35, 0, 0, 0, 0,
1, 36, 0, 0, 0, 0,
1, 37, 0, 0, 0, 0,
1, 38, 0, 0, 0, 0,
1, 39, 0, 0, 0, 0,
1, 40, 0, 0, 0, 0,
1, 41, 0, 0, 0, 0,
1, 42, 0, 0, 0, 0,
1, 43, 0, 0, 0, 0,
1, 44, 0, 0, 0, 0,
1, 45, 0, 0, 0, 0,
1, 46, 0, 0, 0, 0,
1, 47, 0, 0, 0, 0,
1, 48, 0, 0, 0, 0,
1, 49, 0, 0, 0, 0,
1, 50, 0, 0, 0, 0,
1, 51, 0, 0, 0, 0,
1, 52, 0, 0, 0, 0,
1, 53, 0, 0, 0, 0,
1, 54, 0, 0, 0, 0,
1, 55, 0, 0, 0, 0,
1, 56, 0, 0, 0, 0,
1, 57, 0, 0, 0, 0,
1, 58, 0, 0, 0, 0,
1, 59, 0, 0, 0, 0,
1, 60, 0, 0, 0, 0,
1, 61, 0, 0, 0, 0,
1, 62, 0, 0, 0, 0,
1, 63, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
1, 65, 0, 0, 0, 0,
1, 66, 0, 0, 0, 0,
1, 67, 0, 0, 0, 0,
1, 68, 0, 0, 0, 0,
1, 69, 0, 0, 0, 0,
1, 70, 0, 0, 0, 0,
1, 71, 0, 0, 0, 0,
1, 72, 0, 0, 0, 0,
1, 73, 0, 0, 0, 0,
1, 74, 0, 0, 0, 0,
1, 75, 0, 0, 0, 0,
1, 76, 0, 0, 0, 0,
1, 77, 0, 0, 0, 0,
1, 78, 0, 0, 0, 0,
1, 79, 0, 0, 0, 0,
1, 80, 0, 0, 0, 0,
1, 81, 0, 0, 0, 0,
1, 82, 0, 0, 0, 0,
1, 83, 0, 0, 0, 0,
1, 84, 0, 0, 0, 0,
1, 85, 0, 0, 0, 0,
1, 86, 0, 0, 0, 0,
1, 87, 0, 0, 0, 0,
1, 88, 0, 0, 0, 0,
1, 89, 0, 0, 0, 0,
1, 90, 0, 0, 0, 0,
1, 91, 0, 0, 0, 0,
1, 92, 0, 0, 0, 0,
1, 93, 0, 0, 0, 0,
1, 94, 0, 0, 0, 0,
1, 95, 0, 0, 0, 0,
1, 96, 0, 0, 0, 0,
1, 97, 0, 0, 0, 0,
1, 98, 0, 0, 0, 0,
1, 99, 0, 0, 0, 0,
1, 100, 0, 0, 0, 0,
1, 101, 0, 0, 0, 0,
1, 102, 0, 0, 0, 0,
1, 103, 0, 0, 0, 0,
1, 104, 0, 0, 0, 0,
1, 105, 0, 0, 0, 0,
1, 106, 0, 0, 0, 0,
1, 107, 0, 0, 0, 0,
1, 108, 0, 0, 0, 0,
1, 109, 0, 0, 0, 0,
1, 110, 0, 0, 0, 0,
1, 111, 0, 0, 0, 0,
1, 112, 0, 0, 0, 0,
1, 113, 0, 0, 0, 0,
1, 114, 0, 0, 0, 0,
1, 115, 0, 0, 0, 0,
1, 116, 0, 0, 0, 0,
1, 117, 0, 0, 0, 0,
1, 118, 0, 0, 0, 0,
1, 119, 0, 0, 0, 0,
1, 120, 0, 0, 0, 0,
1, 121, 0, 0, 0, 0,
1, 122, 0, 0, 0, 0,
1, 123, 0, 0, 0, 0,
1, 124, 0, 0, 0, 0,
1, 125, 0, 0, 0, 0,
1, 126, 0, 0, 0, 0,
1, 127, 0, 0, 0, 0,
2, -61, -121, 0, 0, 0,
2, -61, -68, 0, 0, 0,
2, -61, -87, 0, 0, 0,
2, -61, -94, 0, 0, 0,
2, -61, -92, 0, 0, 0,
2, -61, -96, 0, 0, 0,
2, -61, -91, 0, 0, 0,
2, -61, -89, 0, 0, 0,
2, -61, -86, 0, 0, 0,
2, -61, -85, 0, 0, 0,
2, -61, -88, 0, 0, 0,
2, -61, -81, 0, 0, 0,
2, -61, -82, 0, 0, 0,
2, -61, -84, 0, 0, 0,
2, -61, -124, 0, 0, 0,
2, -61, -123, 0, 0, 0,
2, -61, -119, 0, 0, 0,
2, -61, -90, 0, 0, 0,
2, -61, -122, 0, 0, 0,
2, -61, -76, 0, 0, 0,
2, -61, -74, 0, 0, 0,
2, -61, -78, 0, 0, 0,
2, -61, -69, 0, 0, 0,
2, -61, -71, 0, 0, 0,
2, -61, -65, 0, 0, 0,
2, -61, -106, 0, 0, 0,
2, -61, -100, 0, 0, 0,
2, -62, -94, 0, 0, 0,
2, -62, -93, 0, 0, 0,
2, -62, -91, 0, 0, 0,
3, -30, -126, -89, 0, 0,
2, -58, -110, 0, 0, 0,
2, -61, -95, 0, 0, 0,
2, -61, -83, 0, 0, 0,
2, -61, -77, 0, 0, 0,
2, -61, -70, 0, 0, 0,
2, -61, -79, 0, 0, 0,
2, -61, -111, 0, 0, 0,
2, -62, -86, 0, 0, 0,
2, -62, -70, 0, 0, 0,
2, -62, -65, 0, 0, 0,
3, -30, -116, -112, 0, 0,
2, -62, -84, 0, 0, 0,
2, -62, -67, 0, 0, 0,
2, -62, -68, 0, 0, 0,
2, -62, -95, 0, 0, 0,
2, -62, -85, 0, 0, 0,
2, -62, -69, 0, 0, 0,
3, -30, -106, -111, 0, 0,
3, -30, -106, -110, 0, 0,
3, -30, -106, -109, 0, 0,
3, -30, -108, -126, 0, 0,
3, -30, -108, -92, 0, 0,
3, -30, -107, -95, 0, 0,
3, -30, -107, -94, 0, 0,
3, -30, -107, -106, 0, 0,
3, -30, -107, -107, 0, 0,
3, -30, -107, -93, 0, 0,
3, -30, -107, -111, 0, 0,
3, -30, -107, -105, 0, 0,
3, -30, -107, -99, 0, 0,
3, -30, -107, -100, 0, 0,
3, -30, -107, -101, 0, 0,
3, -30, -108, -112, 0, 0,
3, -30, -108, -108, 0, 0,
3, -30, -108, -76, 0, 0,
3, -30, -108, -84, 0, 0,
3, -30, -108, -100, 0, 0,
3, -30, -108, -128, 0, 0,
3, -30, -108, -68, 0, 0,
3, -30, -107, -98, 0, 0,
3, -30, -107, -97, 0, 0,
3, -30, -107, -102, 0, 0,
3, -30, -107, -108, 0, 0,
3, -30, -107, -87, 0, 0,
3, -30, -107, -90, 0, 0,
3, -30, -107, -96, 0, 0,
3, -30, -107, -112, 0, 0,
3, -30, -107, -84, 0, 0,
3, -30, -107, -89, 0, 0,
3, -30, -107, -88, 0, 0,
3, -30, -107, -92, 0, 0,
3, -30, -107, -91, 0, 0,
3, -30, -107, -103, 0, 0,
3, -30, -107, -104, 0, 0,
3, -30, -107, -110, 0, 0,
3, -30, -107, -109, 0, 0,
3, -30, -107, -85, 0, 0,
3, -30, -107, -86, 0, 0,
3, -30, -108, -104, 0, 0,
3, -30, -108, -116, 0, 0,
3, -30, -106, -120, 0, 0,
3, -30, -106, -124, 0, 0,
3, -30, -106, -116, 0, 0,
3, -30, -106, -112, 0, 0,
3, -30, -106, -128, 0, 0,
2, -50, -79, 0, 0, 0,
2, -61, -97, 0, 0, 0,
2, -50, -109, 0, 0, 0,
2, -49, -128, 0, 0, 0,
2, -50, -93, 0, 0, 0,
2, -49, -125, 0, 0, 0,
2, -62, -75, 0, 0, 0,
2, -49, -124, 0, 0, 0,
2, -50, -90, 0, 0, 0,
2, -50, -104, 0, 0, 0,
2, -50, -87, 0, 0, 0,
2, -50, -76, 0, 0, 0,
3, -30, -120, -98, 0, 0,
2, -49, -122, 0, 0, 0,
2, -50, -75, 0, 0, 0,
3, -30, -120, -87, 0, 0,
3, -30, -119, -95, 0, 0,
2, -62, -79, 0, 0, 0,
3, -30, -119, -91, 0, 0,
3, -30, -119, -92, 0, 0,
3, -30, -116, -96, 0, 0,
3, -30, -116, -95, 0, 0,
2, -61, -73, 0, 0, 0,
3, -30, -119, -120, 0, 0,
2, -62, -80, 0, 0, 0,
3, -30, -120, -103, 0, 0,
2, -62, -73, 0, 0, 0,
3, -30, -120, -102, 0, 0,
3, -30, -127, -65, 0, 0,
2, -62, -78, 0, 0, 0,
3, -30, -106, -96, 0, 0,
2, -62, -96, 0, 0, 0
};
}

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

@ -12,8 +12,9 @@ namespace ToUTF8
{
WINDOWS_1250, // Central ane Eastern European languages
WINDOWS_1251, // Cyrillic languages
WINDOWS_1252 // Used by English version of Morrowind (and
WINDOWS_1252, // Used by English version of Morrowind (and
// probably others)
CP437 // Used for fonts (*.fnt) if data files encoding is 1252. Otherwise, uses the same encoding as the data files.
};
FromType calculateEncoding(const std::string& encodingName);

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

@ -87,6 +87,8 @@
Now, let's get into writing our shader! As you can guess from above, the filename should be 'example.shader'.
Make sure to also copy the 'core.h' file to the same location. It is included in shiny's source tree under 'Extra/'.
Important: a newline at the end of the file is <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
#include "core.h"

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

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

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

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

@ -23,56 +23,21 @@
</Codes>
</Resource>
<Resource type="ResourceTrueTypeFont" name="EB Garamond 24">
<Property key="Source" value="EBGaramond-Regular.ttf"/>
<Property key="Size" value="24"/>
<Property key="Resolution" value="72"/>
<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="160"/> <!-- Non-breaking space -->
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="192 382"/>
<Code range="1025 1105"/>
<Code range="2026"/> <!-- Ellipsis -->
<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="1026 1039"/>
<Code hide="1104"/>
</Codes>
</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>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI>
<MyGUI type="Font">
<Property key="Default" value="EB Garamond"/>
<Property key="Default" value="Magic Cards"/>
</MyGUI>
<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">
<class>PlayPage</class>
<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">
<item>
<widget class="QFrame" name="Scroll">
<widget class="QWidget" name="Scroll" native="true">
<property name="styleSheet">
<string notr="true">#Scroll {
background-image: url(&quot;:/images/playpage-background.png&quot;);
@ -13,15 +21,6 @@
}
</string>
</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">
<property name="leftMargin">
<number>30</number>

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

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

Loading…
Cancel
Save