diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f5e21c61..0d380aea0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,6 +54,10 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros
include(OpenMWMacros)
+if (ANDROID)
+ set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
+endif (ANDROID)
+
# doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp")
@@ -102,7 +106,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp
- ${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/lights.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp
@@ -233,7 +236,12 @@ if(OGRE_STATIC)
list(APPEND OGRE_STATIC_PLUGINS ${Cg_LIBRARIES})
endif(Cg_FOUND)
+if (ANDROID)
+ add_static_ogre_plugin(RenderSystem_GLES2)
+else ()
add_static_ogre_plugin(RenderSystem_GL)
+endif ()
+
if(WIN32)
add_static_ogre_plugin(RenderSystem_Direct3D9)
endif(WIN32)
diff --git a/apps/opencs/model/world/resourcetable.cpp b/apps/opencs/model/world/resourcetable.cpp
index 86de0a6a6..58e03ff6d 100644
--- a/apps/opencs/model/world/resourcetable.cpp
+++ b/apps/opencs/model/world/resourcetable.cpp
@@ -50,7 +50,7 @@ QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orien
return QVariant();
if (role==ColumnBase::Role_Flags)
- return ColumnBase::Flag_Table;
+ return section==0 ? ColumnBase::Flag_Table : 0;
switch (section)
{
diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp
index 1d53d2393..307ea1a75 100644
--- a/apps/opencs/view/widget/pushbutton.cpp
+++ b/apps/opencs/view/widget/pushbutton.cpp
@@ -26,6 +26,8 @@ void CSVWidget::PushButton::setExtendedToolTip()
"
(left click to activate,"
"
shift-left click to activate and keep panel open)";
+ break;
+
case Type_Toggle:
tooltip += "
(left click to ";
diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp
index 23bc76000..89c27970f 100644
--- a/apps/opencs/view/world/scriptedit.cpp
+++ b/apps/opencs/view/world/scriptedit.cpp
@@ -6,14 +6,35 @@
#include
#include
+#include "../../model/doc/document.hpp"
+
#include "../../model/world/universalid.hpp"
#include "../../model/world/tablemimedata.hpp"
-CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& document) :
+#include "scripthighlighter.hpp"
+
+CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit)
+{
+ ++mEdit.mChangeLocked;
+}
+
+CSVWorld::ScriptEdit::ChangeLock::~ChangeLock()
+{
+ --mEdit.mChangeLocked;
+}
+
+
+CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, QWidget* parent) :
QTextEdit (parent),
mDocument (document),
- mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive)
+ mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive),
+ mChangeLocked (0)
{
+ setAcceptRichText (false);
+ setLineWrapMode (QTextEdit::NoWrap);
+ setTabStopWidth (4);
+ setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
+
mAllowedTypes <invalidateIds();
+
+ if (!mUpdateTimer.isActive())
+ mUpdateTimer.start (0);
+}
+
+void CSVWorld::ScriptEdit::updateHighlighting()
+{
+ if (isChangeLocked())
+ return;
+
+ ChangeLock lock (*this);
+
+ mHighlighter->rehighlight();
+}
\ No newline at end of file
diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp
index b4627c2fe..5713f2426 100644
--- a/apps/opencs/view/world/scriptedit.hpp
+++ b/apps/opencs/view/world/scriptedit.hpp
@@ -1,8 +1,9 @@
#ifndef SCRIPTEDIT_H
#define SCRIPTEDIT_H
-#include
+#include
#include
+#include
#include "../../model/world/universalid.hpp"
@@ -16,11 +17,43 @@ namespace CSMDoc
namespace CSVWorld
{
+ class ScriptHighlighter;
+
class ScriptEdit : public QTextEdit
{
Q_OBJECT
+
public:
- ScriptEdit (QWidget* parent, const CSMDoc::Document& document);
+
+ class ChangeLock
+ {
+ ScriptEdit& mEdit;
+
+ ChangeLock (const ChangeLock&);
+ ChangeLock& operator= (const ChangeLock&);
+
+ public:
+
+ ChangeLock (ScriptEdit& edit);
+ ~ChangeLock();
+ };
+
+ friend class ChangeLock;
+
+ private:
+
+ int mChangeLocked;
+ ScriptHighlighter *mHighlighter;
+ QTimer mUpdateTimer;
+
+ public:
+
+ ScriptEdit (const CSMDoc::Document& document, QWidget* parent);
+
+ /// Should changes to the data be ignored (i.e. not cause updated)?
+ ///
+ /// \note This mechanism is used to avoid infinite update recursions
+ bool isChangeLocked() const;
private:
QVector mAllowedTypes;
@@ -34,6 +67,12 @@ namespace CSVWorld
void dragMoveEvent (QDragMoveEvent* event);
bool stringNeedsQuote(const std::string& id) const;
+
+ private slots:
+
+ void idListChanged();
+
+ void updateHighlighting();
};
}
#endif // SCRIPTEDIT_H
\ No newline at end of file
diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp
index fa41151ca..d2cde1db2 100644
--- a/apps/opencs/view/world/scriptsubview.cpp
+++ b/apps/opencs/view/world/scriptsubview.cpp
@@ -3,8 +3,6 @@
#include
-#include
-
#include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/data.hpp"
@@ -12,28 +10,12 @@
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
-#include "scripthighlighter.hpp"
#include "scriptedit.hpp"
-CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view)
-{
- ++mView.mChangeLocked;
-}
-
-CSVWorld::ScriptSubView::ChangeLock::~ChangeLock()
-{
- --mView.mChangeLocked;
-}
-
CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
-: SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0)
+: SubView (id), mDocument (document), mColumn (-1)
{
- setWidget (mEditor = new ScriptEdit (this, mDocument));
-
- mEditor->setAcceptRichText (false);
- mEditor->setLineWrapMode (QTextEdit::NoWrap);
- mEditor->setTabStopWidth (4);
- mEditor->setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
+ setWidget (mEditor = new ScriptEdit (mDocument, this));
mModel = &dynamic_cast (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));
@@ -58,14 +40,6 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc:
connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int)));
-
- connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged()));
-
- mHighlighter = new ScriptHighlighter (document.getData(), mEditor->document());
-
- connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting()));
-
- mUpdateTimer.setSingleShot (true);
}
void CSVWorld::ScriptSubView::setEditLock (bool locked)
@@ -73,20 +47,12 @@ void CSVWorld::ScriptSubView::setEditLock (bool locked)
mEditor->setReadOnly (locked);
}
-void CSVWorld::ScriptSubView::idListChanged()
-{
- mHighlighter->invalidateIds();
-
- if (!mUpdateTimer.isActive())
- mUpdateTimer.start (0);
-}
-
void CSVWorld::ScriptSubView::textChanged()
{
- if (mChangeLocked)
+ if (mEditor->isChangeLocked())
return;
- ChangeLock lock (*this);
+ ScriptEdit::ChangeLock lock (*mEditor);
mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel,
mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText()));
@@ -94,10 +60,10 @@ void CSVWorld::ScriptSubView::textChanged()
void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
- if (mChangeLocked)
+ if (mEditor->isChangeLocked())
return;
- ChangeLock lock (*this);
+ ScriptEdit::ChangeLock lock (*mEditor);
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
@@ -118,12 +84,3 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
deleteLater();
}
-void CSVWorld::ScriptSubView::updateHighlighting()
-{
- if (mChangeLocked)
- return;
-
- ChangeLock lock (*this);
-
- mHighlighter->rehighlight();
-}
\ No newline at end of file
diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp
index 7ceab70ba..77127d9be 100644
--- a/apps/opencs/view/world/scriptsubview.hpp
+++ b/apps/opencs/view/world/scriptsubview.hpp
@@ -3,9 +3,6 @@
#include "../doc/subview.hpp"
-#include
-
-class QTextEdit;
class QModelIndex;
namespace CSMDoc
@@ -20,34 +17,16 @@ namespace CSMWorld
namespace CSVWorld
{
- class ScriptHighlighter;
+ class ScriptEdit;
class ScriptSubView : public CSVDoc::SubView
{
Q_OBJECT
- QTextEdit *mEditor;
+ ScriptEdit *mEditor;
CSMDoc::Document& mDocument;
CSMWorld::IdTable *mModel;
int mColumn;
- int mChangeLocked;
- ScriptHighlighter *mHighlighter;
- QTimer mUpdateTimer;
-
- class ChangeLock
- {
- ScriptSubView& mView;
-
- ChangeLock (const ChangeLock&);
- ChangeLock& operator= (const ChangeLock&);
-
- public:
-
- ChangeLock (ScriptSubView& view);
- ~ChangeLock();
- };
-
- friend class ChangeLock;
public:
@@ -57,17 +36,11 @@ namespace CSVWorld
public slots:
- void idListChanged();
-
void textChanged();
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
-
- private slots:
-
- void updateHighlighting();
};
}
diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt
index d8966bdcf..6df6210ee 100644
--- a/apps/openmw/CMakeLists.txt
+++ b/apps/openmw/CMakeLists.txt
@@ -1,9 +1,17 @@
# local files
-set(GAME
- main.cpp
- engine.cpp
-)
-if(NOT WIN32)
+if (NOT ANDROID)
+ set(GAME
+ main.cpp
+ engine.cpp
+ )
+else()
+ set(GAME
+ main.cpp
+ android_main.c
+ engine.cpp
+ )
+endif()
+if(NOT WIN32 AND NOT ANDROID)
set(GAME ${GAME} crashcatcher.cpp)
endif()
set(GAME_HEADER
@@ -15,7 +23,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction
- terrainstorage renderconst effectmanager weaponanimation terraingrid
+ terrainstorage renderconst effectmanager weaponanimation
)
add_openmw_dir (mwinput
@@ -33,7 +41,7 @@ add_openmw_dir (mwgui
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog
- recharge mode videowidget backgroundimage itemwidget
+ recharge mode videowidget backgroundimage itemwidget screenfader
)
add_openmw_dir (mwdialogue
@@ -82,19 +90,33 @@ add_openmw_dir (mwbase
)
# Main executable
-set(BOOST_COMPONENTS system filesystem program_options thread wave)
+if (ANDROID)
+ set(BOOST_COMPONENTS system filesystem program_options thread wave atomic)
+else ()
+ set(BOOST_COMPONENTS system filesystem program_options thread wave)
+endif ()
+
if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
-add_executable(openmw
- ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
- ${OPENMW_FILES}
- ${GAME} ${GAME_HEADER}
- ${APPLE_BUNDLE_RESOURCES}
-)
+if (NOT ANDROID)
+ add_executable(openmw
+ ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
+ ${OPENMW_FILES}
+ ${GAME} ${GAME_HEADER}
+ ${APPLE_BUNDLE_RESOURCES}
+ )
+else ()
+ add_library(openmw
+ SHARED
+ ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
+ ${OPENMW_FILES}
+ ${GAME} ${GAME_HEADER}
+ )
+endif ()
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
# when we change the backend.
@@ -116,6 +138,23 @@ target_link_libraries(openmw
components
)
+if (ANDROID)
+ target_link_libraries(openmw
+ ${OGRE_STATIC_PLUGINS}
+ EGL
+ android
+ log
+ dl
+ MyGUI.OgrePlatform
+ MyGUIEngineStatic
+ Plugin_StrangeButtonStatic
+ cpufeatures
+ BulletCollision
+ BulletDynamics
+ LinearMath
+ )
+endif (ANDROID)
+
if (USE_SYSTEM_TINYXML)
target_link_libraries(openmw ${TINYXML_LIBRARIES})
endif()
diff --git a/apps/openmw/android_main.c b/apps/openmw/android_main.c
new file mode 100644
index 000000000..76da91c4f
--- /dev/null
+++ b/apps/openmw/android_main.c
@@ -0,0 +1,43 @@
+
+#include "../../SDL_internal.h"
+
+#ifdef __ANDROID__
+#include "SDL_main.h"
+
+
+/*******************************************************************************
+ Functions called by JNI
+*******************************************************************************/
+#include
+
+/* Called before to initialize JNI bindings */
+
+
+
+extern void SDL_Android_Init(JNIEnv* env, jclass cls);
+
+
+int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj)
+{
+
+ SDL_Android_Init(env, cls);
+
+ SDL_SetMainReady();
+
+
+/* Run the application code! */
+
+ int status;
+ char *argv[2];
+ argv[0] = SDL_strdup("openmw");
+ argv[1] = NULL;
+ status = main(1, argv);
+
+ /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
+ /* exit(status); */
+
+ return status;
+}
+
+#endif /* __ANDROID__ */
+
diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp
index 66eae5f5d..2e5689fa8 100644
--- a/apps/openmw/engine.cpp
+++ b/apps/openmw/engine.cpp
@@ -181,7 +181,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mActivationDistanceOverride(-1)
, mGrab(true)
, mScriptBlacklistUse (true)
-
+ , mExportFonts(false)
{
std::srand ( std::time(NULL) );
MWClass::registerClasses();
@@ -372,7 +372,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
MWGui::WindowManager* window = new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
- mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding);
+ mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts);
mEnvironment.setWindowManager (window);
// Create sound system
@@ -576,4 +576,9 @@ void OMW::Engine::setScriptBlacklist (const std::vector& list)
void OMW::Engine::setScriptBlacklistUse (bool use)
{
mScriptBlacklistUse = use;
-}
\ No newline at end of file
+}
+
+void OMW::Engine::enableFontExport(bool exportFonts)
+{
+ mExportFonts = exportFonts;
+}
diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp
index 203379a93..10c1f6ff2 100644
--- a/apps/openmw/engine.hpp
+++ b/apps/openmw/engine.hpp
@@ -83,6 +83,8 @@ namespace OMW
// Grab mouse?
bool mGrab;
+ bool mExportFonts;
+
Compiler::Extensions mExtensions;
Compiler::Context *mScriptContext;
@@ -187,6 +189,8 @@ namespace OMW
void setScriptBlacklistUse (bool use);
+ void enableFontExport(bool exportFonts);
+
private:
Files::ConfigurationManager& mCfgMgr;
};
diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp
index 6c699bed1..57b3e045e 100644
--- a/apps/openmw/main.cpp
+++ b/apps/openmw/main.cpp
@@ -168,6 +168,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("no-grab", "Don't grab mouse cursor")
+ ("export-fonts", bpo::value()->implicit_value(true)
+ ->default_value(false), "Export Morrowind .fnt fonts to PNG image and XML file in current directory")
+
("activate-dist", bpo::value ()->default_value (-1), "activation distance override");
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
@@ -268,6 +271,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setSoundUsage(!variables["no-sound"].as());
engine.setFallbackValues(variables["fallback"].as().mMap);
engine.setActivationDistanceOverride (variables["activate-dist"].as());
+ engine.enableFontExport(variables["export-fonts"].as());
return true;
}
diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp
index f718972b9..532c1c3fa 100644
--- a/apps/openmw/mwbase/mechanicsmanager.hpp
+++ b/apps/openmw/mwbase/mechanicsmanager.hpp
@@ -128,6 +128,7 @@ namespace MWBase
OffenseType type, int arg=0) = 0;
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0) = 0;
+ virtual void actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
/// Utility to check if taking this item is illegal and calling commitCrime if so
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0;
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp
index 8b407c9ba..f1c69e622 100644
--- a/apps/openmw/mwbase/windowmanager.hpp
+++ b/apps/openmw/mwbase/windowmanager.hpp
@@ -331,6 +331,15 @@ namespace MWBase
virtual void removeCurrentModal(MWGui::WindowModal* input) = 0;
virtual void pinWindow (MWGui::GuiWindow window) = 0;
+
+ /// Fade the screen in, over \a time seconds
+ virtual void fadeScreenIn(const float time) = 0;
+ /// Fade the screen out to black, over \a time seconds
+ virtual void fadeScreenOut(const float time) = 0;
+ /// Fade the screen to a specified percentage of black, over \a time seconds
+ virtual void fadeScreenTo(const int percent, const float time) = 0;
+ /// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading.
+ virtual void setScreenFactor (float factor) = 0;
};
}
diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 62da2682d..a3a127d63 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -17,11 +17,6 @@ namespace Ogre
namespace OEngine
{
- namespace Render
- {
- class Fader;
- }
-
namespace Physic
{
class PhysicEngine;
@@ -113,9 +108,6 @@ namespace MWBase
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map& contentFileMap) = 0;
- virtual OEngine::Render::Fader* getFader() = 0;
- ///< \todo remove this function. Rendering details should not be exposed.
-
virtual MWWorld::CellStore *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;
@@ -271,8 +263,9 @@ namespace MWBase
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance) = 0;
- virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
+ virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0;
///< Adjust position after load to be on ground. Must be called after model load.
+ /// @param force do this even if the ptr is flying
virtual void fixPosition (const MWWorld::Ptr& actor) = 0;
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp
index b3fc5890b..f842528d3 100644
--- a/apps/openmw/mwclass/armor.cpp
+++ b/apps/openmw/mwclass/armor.cpp
@@ -157,15 +157,19 @@ namespace MWClass
float iWeight = gmst.find (typeGmst)->getInt();
- if (iWeight * gmst.find ("fLightMaxMod")->getFloat()>=
- ref->mBase->mData.mWeight)
+ float epsilon = 5e-4;
+
+ if (ref->mBase->mData.mWeight == 0)
+ return ESM::Skill::Unarmored;
+
+ if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon)
return ESM::Skill::LightArmor;
- if (iWeight * gmst.find ("fMedMaxMod")->getFloat()>=
- ref->mBase->mData.mWeight)
+ if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon)
return ESM::Skill::MediumArmor;
- return ESM::Skill::HeavyArmor;
+ else
+ return ESM::Skill::HeavyArmor;
}
int Armor::getValue (const MWWorld::Ptr& ptr) const
diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp
index c175ce3eb..36937ac7e 100644
--- a/apps/openmw/mwclass/creature.cpp
+++ b/apps/openmw/mwclass/creature.cpp
@@ -148,9 +148,9 @@ namespace MWClass
return ref->mBase->mId;
}
- void Creature::adjustPosition(const MWWorld::Ptr& ptr) const
+ void Creature::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{
- MWBase::Environment::get().getWorld()->adjustPosition(ptr);
+ MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);
}
void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@@ -255,23 +255,7 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
-
- // Weapon health is reduced by 1 even if the attack misses
- const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
- if(weaphashealth)
- {
- int weaphealth = weapon.getClass().getItemHealth(weapon);
-
- if (!MWBase::Environment::get().getWorld()->getGodModeState())
- {
- weaphealth -= std::min(1, weaphealth);
- weapon.getCellRef().setCharge(weaphealth);
- }
-
- // Weapon broken? unequip it
- if (weapon.getCellRef().getCharge() == 0)
- weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
- }
+ MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
return;
}
@@ -298,7 +282,6 @@ namespace MWClass
if (!weapon.isEmpty())
{
- const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop)
attack = weapon.get()->mBase->mData.mChop;
@@ -309,26 +292,10 @@ namespace MWClass
if(attack)
{
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
- damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
- if(weaphashealth)
- {
- int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
- int weaphealth = weapon.getClass().getItemHealth(weapon);
- damage *= float(weaphealth) / weapmaxhealth;
-
- if (!MWBase::Environment::get().getWorld()->getGodModeState())
- {
- // Reduce weapon charge by at least one, but cap at 0
- weaphealth -= std::min(std::max(1,
- (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
-
- weapon.getCellRef().setCharge(weaphealth);
- }
-
- // Weapon broken? unequip it
- if (weapon.getCellRef().getCharge() == 0)
- weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
- }
+ damage *= gmst.find("fDamageStrengthBase")->getFloat() +
+ (stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.find("fDamageStrengthMult")->getFloat() * 0.1);
+ MWMechanics::adjustWeaponDamage(damage, weapon);
+ MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
}
// Apply "On hit" enchanted weapons
@@ -366,16 +333,11 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true);
// Self defense
- if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr))
- || attacker == MWBase::Environment::get().getWorld()->getPlayerPtr())
- && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
- && (canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
+ if ((canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
// (they have no movement or attacks anyway)
- )
+ && !attacker.isEmpty())
{
- // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
- // Note: accidental or collateral damage attacks are ignored.
- MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
+ MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
}
if(!successful)
@@ -406,18 +368,21 @@ namespace MWClass
if (damage > 0.f)
{
- // Check for knockdown
- float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
- float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
- * getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
- int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99]
- if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
+ if (!attacker.isEmpty())
{
- getCreatureStats(ptr).setKnockedDown(true);
+ // Check for knockdown
+ float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
+ float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
+ * getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
+ int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99]
+ if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
+ {
+ getCreatureStats(ptr).setKnockedDown(true);
+ }
+ else
+ getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
}
- else
- getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
damage = std::max(1.f, damage);
diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp
index 6920a4b1d..1820d4ea4 100644
--- a/apps/openmw/mwclass/creature.hpp
+++ b/apps/openmw/mwclass/creature.hpp
@@ -49,7 +49,9 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const;
- virtual void adjustPosition(const MWWorld::Ptr& ptr) const;
+ virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
+ ///< Adjust position to stand on ground. Must be called post model load
+ /// @param force do this even if the ptr is flying
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp
index 464c78e9f..8c2decb72 100644
--- a/apps/openmw/mwclass/light.cpp
+++ b/apps/openmw/mwclass/light.cpp
@@ -56,8 +56,11 @@ namespace MWClass
{
const std::string model = getModel(ptr);
+ MWWorld::LiveCellRef *ref =
+ ptr.get();
+
// Insert even if model is empty, so that the light is added
- renderingInterface.getObjects().insertModel(ptr, model);
+ renderingInterface.getObjects().insertModel(ptr, model, false, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault));
}
void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index d087febd0..662672204 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -403,9 +403,9 @@ namespace MWClass
return ref->mBase->mId;
}
- void Npc::adjustPosition(const MWWorld::Ptr& ptr) const
+ void Npc::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{
- MWBase::Environment::get().getWorld()->adjustPosition(ptr);
+ MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);
}
void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@@ -529,24 +529,7 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
-
- // Weapon health is reduced by 1 even if the attack misses
- const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
- if(weaphashealth)
- {
- int weaphealth = weapon.getClass().getItemHealth(weapon);
-
- if (!MWBase::Environment::get().getWorld()->getGodModeState())
- {
- weaphealth -= std::min(1, weaphealth);
- weapon.getCellRef().setCharge(weaphealth);
- }
-
- // Weapon broken? unequip it
- if (weaphealth == 0)
- weapon = *inv.unequipItem(weapon, ptr);
- }
-
+ MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
return;
}
@@ -555,7 +538,6 @@ namespace MWClass
MWMechanics::NpcStats &stats = getNpcStats(ptr);
if(!weapon.isEmpty())
{
- const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop)
attack = weapon.get()->mBase->mData.mChop;
@@ -568,27 +550,9 @@ namespace MWClass
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= gmst.fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
- if(weaphashealth)
- {
- int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
- int weaphealth = weapon.getClass().getItemHealth(weapon);
-
- damage *= float(weaphealth) / weapmaxhealth;
-
- if (!MWBase::Environment::get().getWorld()->getGodModeState())
- {
- // Reduce weapon charge by at least one, but cap at 0
- weaphealth -= std::min(std::max(1,
- (int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
-
- weapon.getCellRef().setCharge(weaphealth);
- }
-
- // Weapon broken? unequip it
- if (weaphealth == 0)
- weapon = *inv.unequipItem(weapon, ptr);
- }
}
+ MWMechanics::adjustWeaponDamage(damage, weapon);
+ MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
healthdmg = true;
}
else
@@ -675,26 +639,13 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty.
- if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
- {
- // Attacking peaceful NPCs is a crime
- if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
- && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
- MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
-
- if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)
- && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker))
- {
- // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
- // Note: accidental or collateral damage attacks are ignored.
- MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
- }
- }
-
bool wasDead = getCreatureStats(ptr).isDead();
getCreatureStats(ptr).setAttacked(true);
+ if (!attacker.isEmpty())
+ MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
+
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
@@ -721,7 +672,7 @@ namespace MWClass
if (damage < 0.001f)
damage = 0;
- if(damage > 0.0f)
+ if(damage > 0.0f && !attacker.isEmpty())
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc.
@@ -749,7 +700,7 @@ namespace MWClass
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
- if(damage > 0 && ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage
+ if(damage > 0 && ishealth)
{
// Hit percentages:
// cuirass = 30%
@@ -967,8 +918,6 @@ namespace MWClass
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
- if(npcdata->mNpcStats.isWerewolf())
- runSpeed *= gmst.fWereWolfRunMult->getFloat();
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
@@ -1000,6 +949,9 @@ namespace MWClass
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
+ if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
+ moveSpeed *= gmst.fWereWolfRunMult->getFloat();
+
return moveSpeed;
}
diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp
index b0ca0a658..71e34e498 100644
--- a/apps/openmw/mwclass/npc.hpp
+++ b/apps/openmw/mwclass/npc.hpp
@@ -55,7 +55,9 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const;
- virtual void adjustPosition(const MWWorld::Ptr& ptr) const;
+ virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
+ ///< Adjust position to stand on ground. Must be called post model load
+ /// @param force do this even if the ptr is flying
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp
index e5d0d279e..2da213c8d 100644
--- a/apps/openmw/mwclass/potion.cpp
+++ b/apps/openmw/mwclass/potion.cpp
@@ -145,11 +145,8 @@ namespace MWClass
MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat();
for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it)
{
- it->mKnown = ( (i == 0 && alchemySkill >= fWortChanceValue)
- || (i == 1 && alchemySkill >= fWortChanceValue*2)
- || (i == 2 && alchemySkill >= fWortChanceValue*3)
- || (i == 3 && alchemySkill >= fWortChanceValue*4));
-
+ it->mKnown = (i <= 1 && alchemySkill >= fWortChanceValue)
+ || (i <= 3 && alchemySkill >= fWortChanceValue*2);
++i;
}
diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp
index f0212031e..0bdabb222 100644
--- a/apps/openmw/mwgui/container.cpp
+++ b/apps/openmw/mwgui/container.cpp
@@ -28,6 +28,16 @@
namespace MWGui
{
+ DragAndDrop::DragAndDrop()
+ : mDraggedWidget(NULL)
+ , mDraggedCount(0)
+ , mSourceModel(NULL)
+ , mSourceView(NULL)
+ , mSourceSortModel(NULL)
+ , mIsOnDragAndDrop(NULL)
+ {
+ }
+
void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count)
{
mItem = sourceModel->getItem(index);
@@ -36,7 +46,6 @@ namespace MWGui
mSourceView = sourceView;
mSourceSortModel = sortModel;
mIsOnDragAndDrop = true;
- mDragAndDropWidget->setVisible(true);
// If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend
// immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive,
@@ -73,8 +82,13 @@ namespace MWGui
mSourceSortModel->addDragItem(mItem.mBase, count);
}
- ItemWidget* baseWidget = mDragAndDropWidget->createWidget
- ("MW_ItemIcon", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);
+ ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop");
+
+ Controllers::ControllerFollowMouse* controller =
+ MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName())
+ ->castType();
+ MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller);
+
mDraggedWidget = baseWidget;
baseWidget->setItem(mItem.mBase);
baseWidget->setNeedMouseFocus(false);
@@ -99,8 +113,6 @@ namespace MWGui
std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase);
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
- mDragAndDropWidget->setVisible(false);
-
// If item is dropped where it was taken from, we don't need to do anything -
// otherwise, do the transfer
if (targetModel != mSourceModel)
diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp
index 79951f70e..e32c6efaf 100644
--- a/apps/openmw/mwgui/container.hpp
+++ b/apps/openmw/mwgui/container.hpp
@@ -33,13 +33,14 @@ namespace MWGui
public:
bool mIsOnDragAndDrop;
MyGUI::Widget* mDraggedWidget;
- MyGUI::Widget* mDragAndDropWidget;
ItemModel* mSourceModel;
ItemView* mSourceView;
SortFilterItemModel* mSourceSortModel;
ItemStack mItem;
int mDraggedCount;
+ DragAndDrop();
+
void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
void drop (ItemModel* targetModel, ItemView* targetView);
diff --git a/apps/openmw/mwgui/controllers.cpp b/apps/openmw/mwgui/controllers.cpp
index e62fb3fce..a50e6496c 100644
--- a/apps/openmw/mwgui/controllers.cpp
+++ b/apps/openmw/mwgui/controllers.cpp
@@ -1,5 +1,7 @@
#include "controllers.hpp"
+#include
+
namespace MWGui
{
namespace Controllers
@@ -50,5 +52,17 @@ namespace MWGui
{
}
+ // -------------------------------------------------------------
+
+ void ControllerFollowMouse::prepareItem(MyGUI::Widget *_widget)
+ {
+ }
+
+ bool ControllerFollowMouse::addTime(MyGUI::Widget *_widget, float _time)
+ {
+ _widget->setPosition(MyGUI::InputManager::getInstance().getMousePosition());
+ return true;
+ }
+
}
}
diff --git a/apps/openmw/mwgui/controllers.hpp b/apps/openmw/mwgui/controllers.hpp
index 798acde62..937855606 100644
--- a/apps/openmw/mwgui/controllers.hpp
+++ b/apps/openmw/mwgui/controllers.hpp
@@ -40,6 +40,17 @@ namespace MWGui
bool mEnabled;
float mTimeLeft;
};
+
+ /// Automatically positions a widget below the mouse cursor.
+ class ControllerFollowMouse :
+ public MyGUI::ControllerItem
+ {
+ MYGUI_RTTI_DERIVED( ControllerFollowMouse )
+
+ private:
+ bool addTime(MyGUI::Widget* _widget, float _time);
+ void prepareItem(MyGUI::Widget* _widget);
+ };
}
}
diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp
index e89777bee..8612617c1 100644
--- a/apps/openmw/mwgui/enchantingdialog.cpp
+++ b/apps/openmw/mwgui/enchantingdialog.cpp
@@ -338,7 +338,7 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item));
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
- MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
+ MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;
}
}
diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp
index 92d9a25b6..b1cb7fccf 100644
--- a/apps/openmw/mwgui/fontloader.cpp
+++ b/apps/openmw/mwgui/fontloader.cpp
@@ -134,7 +134,7 @@ namespace MWGui
mEncoding = encoding;
}
- void FontLoader::loadAllFonts()
+ void FontLoader::loadAllFonts(bool exportToFile)
{
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
@@ -142,7 +142,7 @@ namespace MWGui
Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "*.fnt");
for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource)
{
- loadFont(*resource);
+ loadFont(*resource, exportToFile);
}
}
}
@@ -168,7 +168,7 @@ namespace MWGui
float ascent;
} GlyphInfo;
- void FontLoader::loadFont(const std::string &fileName)
+ void FontLoader::loadFont(const std::string &fileName, bool exportToFile)
{
Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName);
@@ -221,6 +221,9 @@ namespace MWGui
width, height, 0, Ogre::PF_BYTE_RGBA);
texture->loadImage(image);
+ if (exportToFile)
+ image.save(resourceName + ".png");
+
// Register the font with MyGUI
MyGUI::ResourceManualFont* font = static_cast(
MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont"));
@@ -240,10 +243,10 @@ namespace MWGui
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;
+ float x1 = data[i].top_left.x*width;
+ float y1 = data[i].top_left.y*height;
+ float w = data[i].top_right.x*width - x1;
+ float h = data[i].bottom_left.y*height - y1;
ToUTF8::Utf8Encoder encoder(mEncoding);
unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding));
@@ -257,16 +260,38 @@ namespace MWGui
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
+ code->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
// More hacks! The french game uses several win1252 characters that are not included
// in the cp437 encoding of the font. Fall back to similar available characters.
if (mEncoding == ToUTF8::CP437)
{
- std::multimap additional;
+ std::multimap additional; //
additional.insert(std::make_pair(39, 0x2019)); // apostrophe
additional.insert(std::make_pair(45, 0x2013)); // dash
+ additional.insert(std::make_pair(45, 0x2014)); // dash
additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark
additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark
+ additional.insert(std::make_pair(44, 0x201A));
+ additional.insert(std::make_pair(44, 0x201E));
+ additional.insert(std::make_pair(43, 0x2020));
+ additional.insert(std::make_pair(94, 0x02C6));
+ additional.insert(std::make_pair(37, 0x2030));
+ additional.insert(std::make_pair(83, 0x0160));
+ additional.insert(std::make_pair(60, 0x2039));
+ additional.insert(std::make_pair(79, 0x0152));
+ additional.insert(std::make_pair(90, 0x017D));
+ additional.insert(std::make_pair(39, 0x2019));
+ additional.insert(std::make_pair(126, 0x02DC));
+ additional.insert(std::make_pair(84, 0x2122));
+ additional.insert(std::make_pair(83, 0x0161));
+ additional.insert(std::make_pair(62, 0x203A));
+ additional.insert(std::make_pair(111, 0x0153));
+ additional.insert(std::make_pair(122, 0x017E));
+ additional.insert(std::make_pair(89, 0x0178));
+ additional.insert(std::make_pair(156, 0x00A2));
+ additional.insert(std::make_pair(46, 0x2026));
+
for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it)
{
if (it->first != i)
@@ -281,6 +306,7 @@ namespace MWGui
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
+ code->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
}
}
@@ -296,6 +322,7 @@ namespace MWGui
cursorCode->addAttribute("advance", data[i].width);
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
+ cursorCode->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
}
// Question mark, use for NotDefined marker (used for glyphs not existing in the font)
@@ -310,6 +337,7 @@ namespace MWGui
cursorCode->addAttribute("advance", data[i].width);
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
+ cursorCode->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
}
}
@@ -327,7 +355,13 @@ namespace MWGui
cursorCode->addAttribute("coord", "0 0 0 0");
cursorCode->addAttribute("advance", "0");
cursorCode->addAttribute("bearing", "0 0");
+ cursorCode->addAttribute("size", "0 0");
+ }
+ if (exportToFile)
+ {
+ xmlDocument.createDeclaration();
+ xmlDocument.save(resourceName + ".xml");
}
font->deserialization(root, MyGUI::Version(3,2,0));
diff --git a/apps/openmw/mwgui/fontloader.hpp b/apps/openmw/mwgui/fontloader.hpp
index 7954b0875..619717840 100644
--- a/apps/openmw/mwgui/fontloader.hpp
+++ b/apps/openmw/mwgui/fontloader.hpp
@@ -12,12 +12,15 @@ namespace MWGui
{
public:
FontLoader (ToUTF8::FromType encoding);
- void loadAllFonts ();
+
+ /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files?
+ void loadAllFonts (bool exportToFile);
private:
ToUTF8::FromType mEncoding;
- void loadFont (const std::string& fileName);
+ /// @param exportToFile export the converted font (Image and XML with glyph metrics) to files?
+ void loadFont (const std::string& fileName, bool exportToFile);
};
}
diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp
index 4d3d04ced..aac5ccbbf 100644
--- a/apps/openmw/mwgui/formatting.cpp
+++ b/apps/openmw/mwgui/formatting.cpp
@@ -1,6 +1,7 @@
#include "formatting.hpp"
#include
+#include
#include "../mwscript/interpretercontext.hpp"
@@ -269,15 +270,6 @@ namespace MWGui
void BookTextParser::parseImage(std::string tag, bool createWidget)
{
int src_start = tag.find("SRC=")+5;
- std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start);
-
- // fix texture extension to .dds
- if (image.size() > 4)
- {
- image[image.size()-3] = 'd';
- image[image.size()-2] = 'd';
- image[image.size()-1] = 's';
- }
int width_start = tag.find("WIDTH=")+7;
int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start));
@@ -291,16 +283,8 @@ namespace MWGui
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast(mParent->getChildCount()));
- // Apparently a bug with some morrowind versions, they reference the image without the size suffix.
- // So if the image isn't found, try appending the size.
- if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image))
- {
- std::stringstream str;
- str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind("."));
- image = str.str();
- }
-
- box->setImageTexture("bookart\\" + image);
+ std::string image = Misc::ResourceHelpers::correctBookartPath(tag.substr(src_start, tag.find('"', src_start)-src_start), width, height);
+ box->setImageTexture(image);
box->setProperty("NeedMouse", "false");
}
@@ -406,7 +390,7 @@ namespace MWGui
box->setTextAlign(mTextStyle.mTextAlign);
box->setTextColour(mTextStyle.mColour);
box->setFontName(mTextStyle.mFont);
- box->setCaption(realText);
+ box->setCaption(MyGUI::TextIterator::toTagsString(realText));
box->setSize(box->getSize().width, box->getTextSize().height);
mHeight += box->getTextSize().height;
diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp
index 3e753b7f9..3a9aa7dcd 100644
--- a/apps/openmw/mwgui/hud.cpp
+++ b/apps/openmw/mwgui/hud.cpp
@@ -59,7 +59,7 @@ namespace MWGui
};
- HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop)
+ HUD::HUD(int fpsLevel, DragAndDrop* dragAndDrop)
: Layout("openmw_hud.layout")
, mHealth(NULL)
, mMagicka(NULL)
@@ -97,7 +97,7 @@ namespace MWGui
, mWeaponSpellTimer(0.f)
, mDrowningFlashTheta(0.f)
{
- setCoord(0,0, width, height);
+ mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
// Energy bars
getWidget(mHealthFrame, "HealthFrame");
@@ -405,11 +405,6 @@ namespace MWGui
mDrowningFlashTheta += dt * Ogre::Math::TWO_PI;
}
- void HUD::onResChange(int width, int height)
- {
- setCoord(0, 0, width, height);
- }
-
void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent)
{
const ESM::Spell* spell =
diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp
index 56b0ef7b5..bf0419aae 100644
--- a/apps/openmw/mwgui/hud.hpp
+++ b/apps/openmw/mwgui/hud.hpp
@@ -15,7 +15,7 @@ namespace MWGui
class HUD : public OEngine::GUI::Layout, public LocalMapBase
{
public:
- HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop);
+ HUD(int fpsLevel, DragAndDrop* dragAndDrop);
virtual ~HUD();
void setValue (const std::string& id, const MWMechanics::DynamicStat& value);
void setFPS(float fps);
@@ -47,7 +47,6 @@ namespace MWGui
void setCrosshairVisible(bool visible);
void onFrame(float dt);
- void onResChange(int width, int height);
void setCellName(const std::string& cellName);
diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp
index abea68185..e2048a34b 100644
--- a/apps/openmw/mwgui/inventorywindow.cpp
+++ b/apps/openmw/mwgui/inventorywindow.cpp
@@ -37,6 +37,7 @@ namespace MWGui
, mLastYSize(0)
, mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()))
, mPreviewDirty(true)
+ , mPreviewResize(true)
, mDragAndDrop(dragAndDrop)
, mSelectedItem(-1)
, mGuiMode(GM_Inventory)
@@ -91,8 +92,18 @@ namespace MWGui
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
mSortModel = new SortFilterItemModel(mTradeModel);
mItemView->setModel(mSortModel);
+
+ mPreview.reset(NULL);
+ mAvatarImage->setImageTexture("");
+ MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture("CharacterPreview");
+ if (tex)
+ MyGUI::RenderManager::getInstance().destroyTexture(tex);
+
mPreview.reset(new MWRender::InventoryPreview(mPtr));
mPreview->setup();
+
+ mPreviewDirty = true;
+ mPreviewResize = true;
}
void InventoryWindow::setGuiMode(GuiMode mode)
@@ -125,7 +136,7 @@ namespace MWGui
Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height);
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
- mPreviewDirty = true;
+ mPreviewResize = true;
mMainWidget->setPosition(pos);
mMainWidget->setSize(size);
@@ -333,7 +344,7 @@ namespace MWGui
{
mLastXSize = mMainWidget->getSize().width;
mLastYSize = mMainWidget->getSize().height;
- mPreviewDirty = true;
+ mPreviewResize = true;
}
}
@@ -366,6 +377,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned);
}
+ void InventoryWindow::onTitleDoubleClicked()
+ {
+ if (!mPinned)
+ MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
+ }
+
void InventoryWindow::useItem(const MWWorld::Ptr &ptr)
{
const std::string& script = ptr.getClass().getScript(ptr);
@@ -403,9 +420,13 @@ namespace MWGui
else
mSkippedToEquip = ptr;
- mItemView->update();
+ if (isVisible())
+ {
+ mItemView->update();
- notifyContentChanged();
+ notifyContentChanged();
+ }
+ // else: will be updated in open()
}
void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)
@@ -491,16 +512,23 @@ namespace MWGui
void InventoryWindow::doRenderUpdate ()
{
- if (mPreviewDirty)
+ mPreview->onFrame();
+ if (mPreviewResize)
{
- mPreviewDirty = false;
+ mPreviewResize = false;
MyGUI::IntSize size = mAvatarImage->getSize();
-
- mPreview->update (size.width, size.height);
+ mPreview->resize(size.width, size.height);
mAvatarImage->setImageTexture("CharacterPreview");
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height)));
mAvatarImage->setImageTile(MyGUI::IntSize(std::min(512, size.width), std::min(1024, size.height)));
+ }
+ if (mPreviewDirty)
+ {
+ mPreviewDirty = false;
+ mPreview->update ();
+
+ mAvatarImage->setImageTexture("CharacterPreview");
mArmorRating->setCaptionWithReplacing ("#{sArmor}: "
+ boost::lexical_cast(static_cast(mPtr.getClass().getArmorRating(mPtr))));
diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp
index ae7af5719..6fd6ece4a 100644
--- a/apps/openmw/mwgui/inventorywindow.hpp
+++ b/apps/openmw/mwgui/inventorywindow.hpp
@@ -53,6 +53,7 @@ namespace MWGui
DragAndDrop* mDragAndDrop;
bool mPreviewDirty;
+ bool mPreviewResize;
int mSelectedItem;
MWWorld::Ptr mPtr;
@@ -98,6 +99,7 @@ namespace MWGui
void onFilterChanged(MyGUI::Widget* _sender);
void onAvatarClicked(MyGUI::Widget* _sender);
void onPinToggled();
+ void onTitleDoubleClicked();
void updateEncumbranceBar();
void notifyContentChanged();
diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp
index 7c79f8027..3e109384b 100644
--- a/apps/openmw/mwgui/itemwidget.cpp
+++ b/apps/openmw/mwgui/itemwidget.cpp
@@ -3,6 +3,8 @@
#include
#include
+#include
+
#include "../mwworld/class.hpp"
namespace MWGui
@@ -63,15 +65,7 @@ namespace MWGui
void ItemWidget::setIcon(const MWWorld::Ptr &ptr)
{
- // image
- std::string path = std::string("icons\\");
- path += ptr.getClass().getInventoryIcon(ptr);
-
- std::string::size_type pos = path.rfind(".");
- if(pos != std::string::npos)
- path.erase(pos);
- path.append(".dds");
- setIcon(path);
+ setIcon(Misc::ResourceHelpers::correctIconPath(ptr.getClass().getInventoryIcon(ptr)));
}
diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp
index b588ba3e1..3e2006e57 100644
--- a/apps/openmw/mwgui/journalwindow.cpp
+++ b/apps/openmw/mwgui/journalwindow.cpp
@@ -123,6 +123,9 @@ namespace
getPage (LeftBookPage)->adviseLinkClicked (callback);
getPage (RightBookPage)->adviseLinkClicked (callback);
+
+ getPage (LeftBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
+ getPage (RightBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
}
{
@@ -488,6 +491,14 @@ namespace
MWBase::Environment::get().getWindowManager ()->popGuiMode ();
}
+ void notifyMouseWheel(MyGUI::Widget* sender, int rel)
+ {
+ if (rel < 0)
+ notifyNextPage(sender);
+ else
+ notifyPrevPage(sender);
+ }
+
void notifyNextPage(MyGUI::Widget* _sender)
{
if (!mStates.empty ())
@@ -509,7 +520,7 @@ namespace
{
unsigned int & page = mStates.top ().mPage;
- if(page > 0)
+ if(page >= 2)
{
page -= 2;
updateShowingPages ();
diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp
index f6d6b8135..034163469 100644
--- a/apps/openmw/mwgui/loadingscreen.cpp
+++ b/apps/openmw/mwgui/loadingscreen.cpp
@@ -30,6 +30,8 @@ namespace MWGui
, mProgress(0)
, mVSyncWasEnabled(false)
{
+ mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
+
getWidget(mLoadingText, "LoadingText");
getWidget(mProgressBar, "ProgressBar");
@@ -56,13 +58,6 @@ namespace MWGui
mBackgroundImage->setVisible(visible);
}
- void LoadingScreen::onResChange(int w, int h)
- {
- setCoord(0,0,w,h);
-
- mBackgroundImage->setCoord(MyGUI::IntCoord(0,0,w,h));
- }
-
void LoadingScreen::loadingOn()
{
// Early-out if already on
diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp
index f198d625d..310a6df3c 100644
--- a/apps/openmw/mwgui/loadingscreen.hpp
+++ b/apps/openmw/mwgui/loadingscreen.hpp
@@ -35,8 +35,6 @@ namespace MWGui
void setLoadingProgress (const std::string& stage, int depth, int current, int total);
void loadingDone();
- void onResChange(int w, int h);
-
void updateWindow(Ogre::RenderWindow* rw) { mWindow = rw; }
private:
diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp
index af26456f2..39df4d03e 100644
--- a/apps/openmw/mwgui/mapwindow.cpp
+++ b/apps/openmw/mwgui/mapwindow.cpp
@@ -536,6 +536,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);
}
+ void MapWindow::onTitleDoubleClicked()
+ {
+ if (!mPinned)
+ MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);
+ }
+
void MapWindow::open()
{
// force markers to foreground
diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp
index c73e5d7a1..7021a5d62 100644
--- a/apps/openmw/mwgui/mapwindow.hpp
+++ b/apps/openmw/mwgui/mapwindow.hpp
@@ -150,6 +150,7 @@ namespace MWGui
protected:
virtual void onPinToggled();
+ virtual void onTitleDoubleClicked();
virtual void notifyPlayerUpdate();
virtual void notifyMapChanged();
diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp
index 84150c322..12defc631 100644
--- a/apps/openmw/mwgui/quickkeysmenu.cpp
+++ b/apps/openmw/mwgui/quickkeysmenu.cpp
@@ -3,6 +3,7 @@
#include
#include
+#include
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
@@ -226,12 +227,9 @@ namespace MWGui
esmStore.get().find(spell->mEffects.mList.front().mEffectID);
std::string path = effect->mIcon;
- int slashPos = path.find("\\");
+ int slashPos = path.rfind('\\');
path.insert(slashPos+1, "b_");
- path = std::string("icons\\") + path;
- int pos = path.rfind(".");
- path.erase(pos);
- path.append(".dds");
+ path = Misc::ResourceHelpers::correctIconPath(path);
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(path);
diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp
index 1b994d584..c45c2566e 100644
--- a/apps/openmw/mwgui/recharge.cpp
+++ b/apps/openmw/mwgui/recharge.cpp
@@ -181,6 +181,10 @@ void Recharge::onItemClicked(MyGUI::Widget *sender)
std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->getString();
message = boost::str(boost::format(message) % gem.getClass().getName(gem));
MWBase::Environment::get().getWindowManager()->messageBox(message);
+
+ // special case: readd Azura's Star
+ if (Misc::StringUtils::ciEqual(gem.get()->mBase->mId, "Misc_SoulGem_Azura"))
+ player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
}
updateView();
diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp
new file mode 100644
index 000000000..c27737285
--- /dev/null
+++ b/apps/openmw/mwgui/screenfader.cpp
@@ -0,0 +1,111 @@
+#include "screenfader.hpp"
+
+namespace MWGui
+{
+
+ ScreenFader::ScreenFader()
+ : WindowBase("openmw_screen_fader.layout")
+ , mMode(FadingMode_In)
+ , mRemainingTime(0.f)
+ , mTargetTime(0.f)
+ , mTargetAlpha(0.f)
+ , mCurrentAlpha(0.f)
+ , mStartAlpha(0.f)
+ , mFactor(1.f)
+ {
+ mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
+
+ setVisible(false);
+ }
+
+ void ScreenFader::update(float dt)
+ {
+ if (mRemainingTime > 0)
+ {
+ if (mMode == FadingMode_In)
+ {
+ mCurrentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha);
+ if (mCurrentAlpha < mTargetAlpha) mCurrentAlpha = mTargetAlpha;
+ }
+ else if (mMode == FadingMode_Out)
+ {
+ mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);
+ if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
+ }
+
+ mRemainingTime -= dt;
+ }
+
+ if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
+ mMainWidget->setVisible(false);
+ else
+ applyAlpha();
+ }
+
+ void ScreenFader::applyAlpha()
+ {
+ setVisible(true);
+ mMainWidget->setAlpha(1.f-((1.f-mCurrentAlpha) * mFactor));
+ }
+
+ void ScreenFader::fadeIn(float time)
+ {
+ if (time<0.f) return;
+ if (time==0.f)
+ {
+ mCurrentAlpha = 0.f;
+ applyAlpha();
+ return;
+ }
+
+ mStartAlpha = mCurrentAlpha;
+ mTargetAlpha = 0.f;
+ mMode = FadingMode_In;
+ mTargetTime = time;
+ mRemainingTime = time;
+ }
+
+ void ScreenFader::fadeOut(const float time)
+ {
+ if (time<0.f) return;
+ if (time==0.f)
+ {
+ mCurrentAlpha = 1.f;
+ applyAlpha();
+ return;
+ }
+
+ mStartAlpha = mCurrentAlpha;
+ mTargetAlpha = 1.f;
+ mMode = FadingMode_Out;
+ mTargetTime = time;
+ mRemainingTime = time;
+ }
+
+ void ScreenFader::fadeTo(const int percent, const float time)
+ {
+ if (time<0.f) return;
+ if (time==0.f)
+ {
+ mCurrentAlpha = percent/100.f;
+ applyAlpha();
+ return;
+ }
+
+ mStartAlpha = mCurrentAlpha;
+ mTargetAlpha = percent/100.f;
+
+ if (mTargetAlpha == mStartAlpha) return;
+ else if (mTargetAlpha > mStartAlpha) mMode = FadingMode_Out;
+ else mMode = FadingMode_In;
+
+ mTargetTime = time;
+ mRemainingTime = time;
+ }
+
+ void ScreenFader::setFactor(float factor)
+ {
+ mFactor = factor;
+ }
+
+}
diff --git a/apps/openmw/mwgui/screenfader.hpp b/apps/openmw/mwgui/screenfader.hpp
new file mode 100644
index 000000000..975006a85
--- /dev/null
+++ b/apps/openmw/mwgui/screenfader.hpp
@@ -0,0 +1,44 @@
+#ifndef OPENMW_MWGUI_SCREENFADER_H
+#define OPENMW_MWGUI_SCREENFADER_H
+
+#include "windowbase.hpp"
+
+namespace MWGui
+{
+
+ class ScreenFader : public WindowBase
+ {
+ public:
+ ScreenFader();
+
+ void update(float dt);
+
+ void fadeIn(const float time);
+ void fadeOut(const float time);
+ void fadeTo(const int percent, const float time);
+
+ void setFactor (float factor);
+
+ private:
+ enum FadingMode
+ {
+ FadingMode_In,
+ FadingMode_Out
+ };
+
+ void applyAlpha();
+
+ FadingMode mMode;
+
+ float mRemainingTime;
+ float mTargetTime;
+ float mTargetAlpha;
+ float mCurrentAlpha;
+ float mStartAlpha;
+
+ float mFactor;
+ };
+
+}
+
+#endif
diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp
index 63cbdb567..b72962125 100644
--- a/apps/openmw/mwgui/spellcreationdialog.cpp
+++ b/apps/openmw/mwgui/spellcreationdialog.cpp
@@ -2,6 +2,8 @@
#include
+#include
+
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@@ -143,13 +145,7 @@ namespace MWGui
void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect)
{
- std::string icon = effect->mIcon;
- icon[icon.size()-3] = 'd';
- icon[icon.size()-2] = 'd';
- icon[icon.size()-1] = 's';
- icon = "icons\\" + icon;
-
- mEffectImage->setImageTexture (icon);
+ mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (effect->mIndex)+"}");
@@ -391,6 +387,14 @@ namespace MWGui
void SpellCreationDialog::notifyEffectsChanged ()
{
+ if (mEffects.empty())
+ {
+ mMagickaCost->setCaption("0");
+ mPriceLabel->setCaption("0");
+ mSuccessChance->setCaption("0");
+ return;
+ }
+
float y = 0;
const MWWorld::ESMStore &store =
@@ -398,7 +402,7 @@ namespace MWGui
for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
- float x = 0.5 * it->mMagnMin + it->mMagnMax;
+ float x = 0.5 * (it->mMagnMin + it->mMagnMax);
const ESM::MagicEffect* effect =
store.get().find(it->mEffectID);
@@ -413,7 +417,7 @@ namespace MWGui
y += x * fEffectCostMult;
y = std::max(1.f,y);
- if (effect->mData.mFlags & ESM::MagicEffect::CastTarget)
+ if (it->mRange == ESM::RT_Target)
y *= 1.5;
}
@@ -520,16 +524,24 @@ namespace MWGui
void EffectEditorBase::onSelectAttribute ()
{
- mAddEffectDialog.setVisible(true);
+ const ESM::MagicEffect* effect =
+ MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId);
+
+ mAddEffectDialog.newEffect(effect);
mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId());
+ mAddEffectDialog.setVisible(true);
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);
mSelectAttributeDialog = 0;
}
void EffectEditorBase::onSelectSkill ()
{
+ const ESM::MagicEffect* effect =
+ MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId);
+
+ mAddEffectDialog.newEffect(effect);
+ mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId());
mAddEffectDialog.setVisible(true);
- mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId ());
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);
mSelectSkillDialog = 0;
}
@@ -554,11 +566,10 @@ namespace MWGui
}
int buttonId = *sender->getUserData();
- short effectId = mButtonMapping[buttonId];
-
+ mSelectedKnownEffectId = mButtonMapping[buttonId];
for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
- if (it->mEffectID == effectId)
+ if (it->mEffectID == mSelectedKnownEffectId)
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}");
return;
@@ -566,9 +577,7 @@ namespace MWGui
}
const ESM::MagicEffect* effect =
- MWBase::Environment::get().getWorld()->getStore().get().find(effectId);
-
- mAddEffectDialog.newEffect (effect);
+ MWBase::Environment::get().getWorld()->getStore().get().find(mSelectedKnownEffectId);
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
{
@@ -588,6 +597,7 @@ namespace MWGui
}
else
{
+ mAddEffectDialog.newEffect(effect);
mAddEffectDialog.setVisible(true);
}
}
diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp
index 25c615678..f5d6f0616 100644
--- a/apps/openmw/mwgui/spellcreationdialog.hpp
+++ b/apps/openmw/mwgui/spellcreationdialog.hpp
@@ -99,6 +99,7 @@ namespace MWGui
SelectSkillDialog* mSelectSkillDialog;
int mSelectedEffect;
+ short mSelectedKnownEffectId;
std::vector mEffects;
diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp
index 1a9e418de..dbd91ab75 100644
--- a/apps/openmw/mwgui/spellicons.cpp
+++ b/apps/openmw/mwgui/spellicons.cpp
@@ -5,6 +5,8 @@
#include
#include
+#include
+
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@@ -133,13 +135,7 @@ namespace MWGui
("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);
mWidgetMap[it->first] = image;
- std::string icon = effect->mIcon;
- icon[icon.size()-3] = 'd';
- icon[icon.size()-2] = 'd';
- icon[icon.size()-1] = 's';
- icon = "icons\\" + icon;
-
- image->setImageTexture(icon);
+ image->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
std::string name = ESM::MagicEffect::effectIdToString (it->first);
diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp
index 15b5bf280..b558025d1 100644
--- a/apps/openmw/mwgui/spellwindow.cpp
+++ b/apps/openmw/mwgui/spellwindow.cpp
@@ -45,6 +45,7 @@ namespace MWGui
, NoDrop(drag, mMainWidget)
, mHeight(0)
, mWidth(0)
+ , mWindowSize(mMainWidget->getSize())
{
mSpellIcons = new SpellIcons();
@@ -66,6 +67,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned);
}
+ void SpellWindow::onTitleDoubleClicked()
+ {
+ if (!mPinned)
+ MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);
+ }
+
void SpellWindow::open()
{
updateSpells();
@@ -302,7 +309,11 @@ namespace MWGui
void SpellWindow::onWindowResize(MyGUI::Window* _sender)
{
- updateSpells();
+ if (mMainWidget->getSize() != mWindowSize)
+ {
+ mWindowSize = mMainWidget->getSize();
+ updateSpells();
+ }
}
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)
diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp
index 53eed1ba1..a74847b90 100644
--- a/apps/openmw/mwgui/spellwindow.hpp
+++ b/apps/openmw/mwgui/spellwindow.hpp
@@ -29,6 +29,8 @@ namespace MWGui
int mHeight;
int mWidth;
+ MyGUI::IntSize mWindowSize;
+
std::string mSpellToDelete;
void addGroup(const std::string& label, const std::string& label2);
@@ -42,6 +44,7 @@ namespace MWGui
void onDeleteSpellAccept();
virtual void onPinToggled();
+ virtual void onTitleDoubleClicked();
virtual void open();
SpellIcons* mSpellIcons;
diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp
index 7f3b66d83..2a07a1624 100644
--- a/apps/openmw/mwgui/statswindow.cpp
+++ b/apps/openmw/mwgui/statswindow.cpp
@@ -591,4 +591,10 @@ namespace MWGui
{
MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned);
}
+
+ void StatsWindow::onTitleDoubleClicked()
+ {
+ if (!mPinned)
+ MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);
+ }
}
diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp
index d90c16be9..1a62b6d6b 100644
--- a/apps/openmw/mwgui/statswindow.hpp
+++ b/apps/openmw/mwgui/statswindow.hpp
@@ -74,6 +74,7 @@ namespace MWGui
protected:
virtual void onPinToggled();
+ virtual void onTitleDoubleClicked();
};
}
#endif
diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp
index e09ff2487..2cd86c7b7 100644
--- a/apps/openmw/mwgui/tooltips.cpp
+++ b/apps/openmw/mwgui/tooltips.cpp
@@ -4,6 +4,8 @@
#include
+#include
+
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@@ -319,20 +321,6 @@ namespace MWGui
return tooltipSize;
}
- void ToolTips::findImageExtension(std::string& image)
- {
- int len = image.size();
- if (len < 4) return;
-
- if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image))
- {
- // Change texture extension to .dds
- image[len-3] = 'd';
- image[len-2] = 'd';
- image[len-1] = 's';
- }
- }
-
MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
{
mDynamicToolTipBox->setVisible(true);
@@ -371,8 +359,7 @@ namespace MWGui
const int imageCaptionHPadding = (caption != "" ? 8 : 0);
const int imageCaptionVPadding = (caption != "" ? 4 : 0);
- std::string realImage = "icons\\" + image;
- findImageExtension(realImage);
+ std::string realImage = Misc::ResourceHelpers::correctIconPath(image);
MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
captionWidget->setProperty("Static", "true");
@@ -644,9 +631,7 @@ namespace MWGui
widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
- std::string image = sign->mTexture;
- image.replace(image.size()-3, 3, "dds");
- widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image);
+ widget->setUserString("ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture));
std::string text;
text += sign->mName;
@@ -739,15 +724,9 @@ namespace MWGui
const std::string &name = ESM::MagicEffect::effectIdToString (id);
std::string icon = effect->mIcon;
-
- int slashPos = icon.find("\\");
+ int slashPos = icon.rfind('\\');
icon.insert(slashPos+1, "b_");
-
- icon[icon.size()-3] = 'd';
- icon[icon.size()-2] = 'd';
- icon[icon.size()-1] = 's';
-
- icon = "icons\\" + icon;
+ icon = Misc::ResourceHelpers::correctIconPath(icon);
std::vector schools;
schools.push_back ("#{sSchoolAlteration}");
diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp
index 8b6174b87..62ac69343 100644
--- a/apps/openmw/mwgui/tooltips.hpp
+++ b/apps/openmw/mwgui/tooltips.hpp
@@ -84,8 +84,6 @@ namespace MWGui
MWWorld::Ptr mFocusObject;
- void findImageExtension(std::string& image);
-
MyGUI::IntSize getToolTipViaPtr (bool image=true);
///< @return requested tooltip size
diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp
index f94e39b2f..ccfe6b1eb 100644
--- a/apps/openmw/mwgui/tradewindow.cpp
+++ b/apps/openmw/mwgui/tradewindow.cpp
@@ -308,7 +308,7 @@ namespace MWGui
it->mBase.getClass().getValue(it->mBase)
* it->mCount);
onCancelButtonClicked(mCancelButton);
- MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
+ MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;
}
}
diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp
index babfe099f..f67376c93 100644
--- a/apps/openmw/mwgui/trainingwindow.cpp
+++ b/apps/openmw/mwgui/trainingwindow.cpp
@@ -2,12 +2,11 @@
#include
-#include
-
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
+#include "../mwbase/dialoguemanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@@ -164,14 +163,14 @@ namespace MWGui
// go back to game mode
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training);
- MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
+ MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
// advance time
MWBase::Environment::get().getWorld ()->advanceTime (2);
MWBase::Environment::get().getMechanicsManager()->rest(false);
MWBase::Environment::get().getMechanicsManager()->rest(false);
- MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.25);
mFadeTimeRemaining = 0.5;
}
@@ -183,6 +182,6 @@ namespace MWGui
mFadeTimeRemaining -= dt;
if (mFadeTimeRemaining <= 0)
- MWBase::Environment::get().getWorld ()->getFader()->fadeIn(0.25);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25);
}
}
diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp
index ae191e764..9f3cc949d 100644
--- a/apps/openmw/mwgui/travelwindow.cpp
+++ b/apps/openmw/mwgui/travelwindow.cpp
@@ -4,13 +4,12 @@
#include
-#include
-
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
+#include "../mwbase/dialoguemanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
@@ -156,7 +155,7 @@ namespace MWGui
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
npcStats.setGoldPool(npcStats.getGoldPool() + price);
- MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
ESM::Position pos = *_sender->getUserData();
std::string cellname = _sender->getUserString("Destination");
bool interior = _sender->getUserString("interior") == "y";
@@ -173,14 +172,15 @@ namespace MWGui
MWBase::Environment::get().getWorld()->advanceTime(hours);
}
+ MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
+ MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
+
// Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos);
action.execute(player);
- MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
- MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
- MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0);
- MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);
}
void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp
index 9c7757af9..434c6926e 100644
--- a/apps/openmw/mwgui/waitdialog.cpp
+++ b/apps/openmw/mwgui/waitdialog.cpp
@@ -2,8 +2,6 @@
#include
-#include
-
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
@@ -128,7 +126,7 @@ namespace MWGui
MWBase::Environment::get().getStateManager()->quickSave("Autosave");
MWBase::World* world = MWBase::Environment::get().getWorld();
- world->getFader ()->fadeOut(0.2);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2);
setVisible(false);
mProgressBar.setVisible (true);
@@ -179,8 +177,7 @@ namespace MWGui
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
- bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified())
- && (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
+ bool full = (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
&& (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());
MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player);
bool werewolf = npcstats.isWerewolf();
@@ -243,7 +240,7 @@ namespace MWGui
void WaitDialog::stopWaiting ()
{
- MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2);
mProgressBar.setVisible (false);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed);
diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp
index 5cd8899d8..2cd044cf4 100644
--- a/apps/openmw/mwgui/windowmanagerimp.cpp
+++ b/apps/openmw/mwgui/windowmanagerimp.cpp
@@ -65,6 +65,7 @@
#include "videowidget.hpp"
#include "backgroundimage.hpp"
#include "itemwidget.hpp"
+#include "screenfader.hpp"
namespace MWGui
{
@@ -72,7 +73,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, ToUTF8::FromType encoding)
+ Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts)
: mConsoleOnlyScripts(consoleOnlyScripts)
, mGuiManager(NULL)
, mRendering(ogre)
@@ -112,6 +113,7 @@ namespace MWGui
, mCompanionWindow(NULL)
, mVideoBackground(NULL)
, mVideoWidget(NULL)
+ , mScreenFader(NULL)
, mTranslationDataStorage (translationDataStorage)
, mCharGen(NULL)
, mInputBlocker(NULL)
@@ -146,7 +148,7 @@ namespace MWGui
// Load fonts
FontLoader fontLoader (encoding);
- fontLoader.loadAllFonts();
+ fontLoader.loadAllFonts(exportFonts);
//Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory("Widget");
@@ -171,18 +173,14 @@ namespace MWGui
ItemWidget::registerComponents();
MyGUI::FactoryManager::getInstance().registerFactory("Controller");
+ MyGUI::FactoryManager::getInstance().registerFactory("Controller");
MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer");
MyGUI::ResourceManager::getInstance().load("core.xml");
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
- // Get size info from the Gui object
- int w = MyGUI::RenderManager::getInstance().getViewSize().width;
- int h = MyGUI::RenderManager::getInstance().getViewSize().height;
-
mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ());
- mLoadingScreen->onResChange (w,h);
//set up the hardware cursor manager
mCursorManager = new SFO::SDLCursorManager();
@@ -192,7 +190,6 @@ namespace MWGui
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
- //SDL_ShowCursor(false);
mCursorManager->setEnabled(true);
@@ -217,13 +214,7 @@ namespace MWGui
int w = MyGUI::RenderManager::getInstance().getViewSize().width;
int h = MyGUI::RenderManager::getInstance().getViewSize().height;
- MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget");
- dragAndDropWidget->setVisible(false);
-
mDragAndDrop = new DragAndDrop();
- mDragAndDrop->mIsOnDragAndDrop = false;
- mDragAndDrop->mDraggedWidget = 0;
- mDragAndDrop->mDragAndDropWidget = dragAndDropWidget;
mRecharge = new Recharge();
mMenu = new MainMenu(w,h);
@@ -245,7 +236,7 @@ namespace MWGui
trackWindow(mDialogueWindow, "dialogue");
mContainerWindow = new ContainerWindow(mDragAndDrop);
trackWindow(mContainerWindow, "container");
- mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop);
+ mHud = new HUD(mShowFPSLevel, mDragAndDrop);
mToolTips = new ToolTips();
mScrollWindow = new ScrollWindow();
mBookWindow = new BookWindow();
@@ -267,8 +258,9 @@ namespace MWGui
mSoulgemDialog = new SoulgemDialog(mMessageBoxManager);
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
trackWindow(mCompanionWindow, "companion");
+ mScreenFader = new ScreenFader();
- mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Overlay");
+ mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Stretch,"Overlay");
mHud->setVisible(mHudEnabled);
@@ -357,6 +349,7 @@ namespace MWGui
delete mCursorManager;
delete mRecharge;
delete mCompanionWindow;
+ delete mScreenFader;
cleanupGarbage();
@@ -858,6 +851,8 @@ namespace MWGui
mCompanionWindow->checkReferenceAvailable();
mConsole->checkReferenceAvailable();
mCompanionWindow->onFrame();
+
+ mScreenFader->update(frameDuration);
}
void WindowManager::changeCell(MWWorld::CellStore* cell)
@@ -1019,7 +1014,6 @@ namespace MWGui
{
sizeVideo(x, y);
mGuiManager->windowResized();
- mLoadingScreen->onResChange (x,y);
if (!mHud)
return; // UI not initialized yet
@@ -1033,7 +1027,6 @@ namespace MWGui
it->first->setSize(size);
}
- mHud->onResChange(x, y);
mConsole->onResChange(x, y);
mMenu->onResChange(x, y);
mSettingsWindow->center();
@@ -1042,8 +1035,6 @@ namespace MWGui
mBookWindow->center();
mQuickKeysMenu->center();
mSpellBuyingWindow->center();
- mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
- mInputBlocker->setSize(MyGUI::IntSize(x,y));
}
void WindowManager::pushGuiMode(GuiMode mode)
@@ -1688,4 +1679,24 @@ namespace MWGui
updateVisible();
}
+
+ void WindowManager::fadeScreenIn(const float time)
+ {
+ mScreenFader->fadeIn(time);
+ }
+
+ void WindowManager::fadeScreenOut(const float time)
+ {
+ mScreenFader->fadeOut(time);
+ }
+
+ void WindowManager::fadeScreenTo(const int percent, const float time)
+ {
+ mScreenFader->fadeTo(percent, time);
+ }
+
+ void WindowManager::setScreenFactor(float factor)
+ {
+ mScreenFader->setFactor(factor);
+ }
}
diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp
index 8093d637e..598ecd1ee 100644
--- a/apps/openmw/mwgui/windowmanagerimp.hpp
+++ b/apps/openmw/mwgui/windowmanagerimp.hpp
@@ -86,6 +86,7 @@ namespace MWGui
class CompanionWindow;
class VideoWidget;
class WindowModal;
+ class ScreenFader;
class WindowManager : public MWBase::WindowManager
{
@@ -96,7 +97,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, ToUTF8::FromType encoding);
+ Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts);
virtual ~WindowManager();
void initUI();
@@ -324,6 +325,15 @@ namespace MWGui
virtual void pinWindow (MWGui::GuiWindow window);
+ /// Fade the screen in, over \a time seconds
+ virtual void fadeScreenIn(const float time);
+ /// Fade the screen out to black, over \a time seconds
+ virtual void fadeScreenOut(const float time);
+ /// Fade the screen to a specified percentage of black, over \a time seconds
+ virtual void fadeScreenTo(const int percent, const float time);
+ /// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading.
+ virtual void setScreenFactor (float factor);
+
private:
bool mConsoleOnlyScripts;
@@ -373,6 +383,7 @@ namespace MWGui
CompanionWindow* mCompanionWindow;
MyGUI::ImageBox* mVideoBackground;
VideoWidget* mVideoWidget;
+ ScreenFader* mScreenFader;
Translation::Storage& mTranslationDataStorage;
Cursor* mSoftwareCursor;
diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp
index 919d315f2..92ba5876d 100644
--- a/apps/openmw/mwgui/windowpinnablebase.cpp
+++ b/apps/openmw/mwgui/windowpinnablebase.cpp
@@ -11,6 +11,17 @@ namespace MWGui
mPinButton = window->getSkinWidget ("Button");
mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked);
+
+ MyGUI::Button* button = NULL;
+ MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action");
+ for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it)
+ {
+ if ((*it)->isUserString("HideWindowOnDoubleClick"))
+ button = (*it)->castType();
+ }
+
+ if (button)
+ button->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WindowPinnableBase::onDoubleClick);
}
void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
@@ -25,6 +36,11 @@ namespace MWGui
onPinToggled();
}
+ void WindowPinnableBase::onDoubleClick(MyGUI::Widget *_sender)
+ {
+ onTitleDoubleClicked();
+ }
+
void WindowPinnableBase::setPinned(bool pinned)
{
if (pinned != mPinned)
diff --git a/apps/openmw/mwgui/windowpinnablebase.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp
index 3aad60988..8b7bbefaf 100644
--- a/apps/openmw/mwgui/windowpinnablebase.hpp
+++ b/apps/openmw/mwgui/windowpinnablebase.hpp
@@ -17,9 +17,11 @@ namespace MWGui
private:
void onPinButtonClicked(MyGUI::Widget* _sender);
+ void onDoubleClick(MyGUI::Widget* _sender);
protected:
virtual void onPinToggled() = 0;
+ virtual void onTitleDoubleClicked() = 0;
MyGUI::Widget* mPinButton;
bool mPinned;
diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp
index 067ad72bb..f60c89681 100644
--- a/apps/openmw/mwinput/inputmanagerimp.cpp
+++ b/apps/openmw/mwinput/inputmanagerimp.cpp
@@ -805,16 +805,16 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
- if((!MWBase::Environment::get().getWindowManager()->isGuiMode()
- || MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue)
- && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
+ if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
+ && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()
+ && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console)
{
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);
}
- else if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Journal)
+ else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal))
{
- MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
+ MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal);
}
}
diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index bd87f8b4b..92879fe37 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -89,6 +89,58 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
return false;
}
+class CheckActorCommanded : public MWMechanics::EffectSourceVisitor
+{
+ MWWorld::Ptr mActor;
+public:
+ bool mCommanded;
+ CheckActorCommanded(MWWorld::Ptr actor)
+ : mActor(actor)
+ , mCommanded(false){}
+
+ virtual void visit (MWMechanics::EffectKey key,
+ const std::string& sourceName, int casterActorId,
+ float magnitude, float remainingTime = -1)
+ {
+ MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
+ if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc())
+ || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name()))
+ && casterActorId == player.getClass().getCreatureStats(player).getActorId()
+ && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel())
+ mCommanded = true;
+ }
+};
+
+void adjustCommandedActor (const MWWorld::Ptr& actor)
+{
+ CheckActorCommanded check(actor);
+ MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
+ stats.getActiveSpells().visitEffectSources(check);
+
+ bool hasCommandPackage = false;
+
+ std::list::const_iterator it;
+ for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
+ {
+ if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
+ dynamic_cast(*it)->isCommanded())
+ {
+ hasCommandPackage = true;
+ break;
+ }
+ }
+
+ if (check.mCommanded && !hasCommandPackage)
+ {
+ MWMechanics::AiFollow package("player", true);
+ stats.getAiSequence().stack(package, actor);
+ }
+ else if (!check.mCommanded && hasCommandPackage)
+ {
+ stats.getAiSequence().erase(it);
+ }
+}
+
void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka)
{
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
@@ -107,6 +159,30 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
}
}
+void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)
+{
+ MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
+ if (!ptr.isEmpty())
+ {
+ // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
+ // plays though, which is a rather lame exploit in vanilla.
+ MWBase::Environment::get().getWorld()->deleteObject(ptr);
+
+ const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get()
+ .search("VFX_Summon_End");
+ if (fx)
+ MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
+ "", Ogre::Vector3(ptr.getRefData().getPosition().pos));
+ }
+ else
+ {
+ // We didn't find the creature. It's probably in an inactive cell.
+ // Add to graveyard so we can delete it when the cell becomes active.
+ std::vector& graveyard = casterStats.getSummonedCreatureGraveyard();
+ graveyard.push_back(creatureActorId);
+ }
+}
+
}
namespace MWMechanics
@@ -268,7 +344,8 @@ namespace MWMechanics
void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)
{
CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature);
-
+ if (creatureStats.isDead())
+ return;
MagicEffects now = creatureStats.getSpells().getMagicEffects();
if (creature.getTypeName()==typeid (ESM::NPC).name())
@@ -290,17 +367,17 @@ namespace MWMechanics
{
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
- int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
- int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
- int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getBase();
- int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getBase();
- int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
+ int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getModified();
+ int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
+ int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getModified();
+ int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
+ int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getModified();
double magickaFactor =
- creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5;
+ creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 1;
DynamicStat magicka = creatureStats.getMagicka();
- float diff = (static_cast(intelligence + magickaFactor*intelligence)) - magicka.getBase();
+ float diff = (static_cast(magickaFactor*intelligence)) - magicka.getBase();
magicka.modify(diff);
creatureStats.setMagicka(magicka);
@@ -612,9 +689,9 @@ namespace MWMechanics
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
}
+ std::map& creatureMap = creatureStats.getSummonedCreatureMap();
for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it)
{
- std::map& creatureMap = creatureStats.getSummonedCreatureMap();
bool found = creatureMap.find(it->first) != creatureMap.end();
int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude;
if (found != (magnitude > 0))
@@ -665,34 +742,31 @@ namespace MWMechanics
}
else
{
- // Summon lifetime has expired. Try to delete the creature.
- int actorId = creatureMap[it->first];
- creatureMap.erase(it->first);
-
- MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
- if (!ptr.isEmpty())
- {
- // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
- // plays though, which is a rather lame exploit in vanilla.
- MWBase::Environment::get().getWorld()->deleteObject(ptr);
-
- const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get()
- .search("VFX_Summon_End");
- if (fx)
- MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
- "", Ogre::Vector3(ptr.getRefData().getPosition().pos));
- }
- else
- {
- // We didn't find the creature. It's probably in an inactive cell.
- // Add to graveyard so we can delete it when the cell becomes active.
- std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard();
- graveyard.push_back(actorId);
- }
+ // Effect has ended
+ std::map::iterator foundCreature = creatureMap.find(it->first);
+ cleanupSummonedCreature(creatureStats, foundCreature->second);
+ creatureMap.erase(foundCreature);
}
}
}
+ for (std::map::iterator it = creatureMap.begin(); it != creatureMap.end(); )
+ {
+ MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
+ if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead())
+ {
+ // Purge the magic effect so a new creature can be summoned if desired
+ creatureStats.getActiveSpells().purgeEffect(it->first);
+ if (ptr.getClass().hasInventoryStore(ptr))
+ ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first);
+
+ cleanupSummonedCreature(creatureStats, it->second);
+ creatureMap.erase(it++);
+ }
+ else
+ ++it;
+ }
+
std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard();
for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); )
{
@@ -1005,7 +1079,10 @@ namespace MWMechanics
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
{
if (timerUpdateAITargets == 0)
- {
+ {
+ if (iter->first != player)
+ adjustCommandedActor(iter->first);
+
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
if (it->first == iter->first || iter->first == player) // player is not AI-controlled
@@ -1053,10 +1130,12 @@ namespace MWMechanics
//KnockedOutOneFrameLogic
//Used for "OnKnockedOut" command
//Put here to ensure that it's run for PRECISELY one frame.
- if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Start it for one frame if nessesary
+ if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
+ { //Start it for one frame if nessesary
stats.setKnockedDownOneFrame(true);
}
- else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Turn off KnockedOutOneframe
+ else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
+ { //Turn off KnockedOutOneframe
stats.setKnockedDownOneFrame(false);
stats.setKnockedDownOverOneFrame(true);
}
diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp
index bab8bca28..eea1b4850 100644
--- a/apps/openmw/mwmechanics/aiavoiddoor.cpp
+++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp
@@ -51,14 +51,17 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
float x = pos.pos[0] - tPos.pos[0];
float y = pos.pos[1] - tPos.pos[1];
- float dirToDoor = std::atan2(x,y) + pos.rot[2] + mAdjAngle; //Calculates the direction to the door, relative to the direction of the NPC
- // For example, if the NPC is directly facing the door this will be pi/2
- // Make actor move away from the door
- actor.getClass().getMovementSettings(actor).mPosition[1] = -1 * std::sin(dirToDoor); //I knew I'd use trig someday
- actor.getClass().getMovementSettings(actor).mPosition[0] = -1 * std::cos(dirToDoor);
+ actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
- //Make all nearby actors also avoid the door
+ // Turn away from the door and move when turn completed
+ if (zTurn(actor, Ogre::Radian(std::atan2(x,y) + mAdjAngle), Ogre::Degree(5)))
+ actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
+ else
+ actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
+ actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
+
+ // Make all nearby actors also avoid the door
std::vector actors;
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(Ogre::Vector3(pos.pos[0],pos.pos[1],pos.pos[2]),100,actors);
for(std::vector::iterator it = actors.begin(); it != actors.end(); it++) {
diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp
index cc8dead8a..a0f8bffbd 100644
--- a/apps/openmw/mwmechanics/aiescort.cpp
+++ b/apps/openmw/mwmechanics/aiescort.cpp
@@ -75,6 +75,7 @@ namespace MWMechanics
}
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
+ actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
const float* const leaderPos = actor.getRefData().getPosition().pos;
diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp
index 27d944657..623306411 100644
--- a/apps/openmw/mwmechanics/aifollow.cpp
+++ b/apps/openmw/mwmechanics/aifollow.cpp
@@ -16,16 +16,16 @@
#include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
-: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
+: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
-: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
+: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
{
}
-MWMechanics::AiFollow::AiFollow(const std::string &actorId)
-: mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
+MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded)
+: mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
{
}
@@ -99,6 +99,11 @@ int MWMechanics::AiFollow::getTypeId() const
return TypeIdFollow;
}
+bool MWMechanics::AiFollow::isCommanded() const
+{
+ return mCommanded;
+}
+
void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr follow(new ESM::AiSequence::AiFollow());
@@ -109,6 +114,7 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co
follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow;
+ follow->mCommanded = mCommanded;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Follow;
@@ -120,6 +126,7 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mActorId(follow->mTargetId), mCellId(follow->mCellId)
+ , mCommanded(follow->mCommanded)
{
}
diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp
index 744a9c505..c0eef8008 100644
--- a/apps/openmw/mwmechanics/aifollow.hpp
+++ b/apps/openmw/mwmechanics/aifollow.hpp
@@ -27,7 +27,7 @@ namespace MWMechanics
/// Follow Actor for duration or until you arrive at a position in a cell
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
/// Follow Actor indefinitively
- AiFollow(const std::string &ActorId);
+ AiFollow(const std::string &ActorId, bool commanded=false);
AiFollow(const ESM::AiSequence::AiFollow* follow);
@@ -44,10 +44,13 @@ namespace MWMechanics
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
+ bool isCommanded() const;
+
private:
/// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow;
+ bool mCommanded;
float mRemainingDuration; // Seconds
float mX;
float mY;
diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp
index 1d0065679..f15b357e5 100644
--- a/apps/openmw/mwmechanics/aisequence.cpp
+++ b/apps/openmw/mwmechanics/aisequence.cpp
@@ -82,6 +82,20 @@ std::list::const_iterator AiSequence::end() const
return mPackages.end();
}
+void AiSequence::erase(std::list::const_iterator package)
+{
+ // Not sure if manually terminated packages should trigger mDone, probably not?
+ for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
+ {
+ if (package == it)
+ {
+ mPackages.erase(it);
+ return;
+ }
+ }
+ throw std::runtime_error("can't find package to erase");
+}
+
bool AiSequence::isInCombat() const
{
for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
@@ -245,6 +259,9 @@ void AiSequence::clear()
void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
{
+ if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())
+ throw std::runtime_error("Can't add AI packages to player");
+
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
{
// Notify AiWander of our current position so we can return to it after combat finished
diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp
index af14cce59..c7d176661 100644
--- a/apps/openmw/mwmechanics/aisequence.hpp
+++ b/apps/openmw/mwmechanics/aisequence.hpp
@@ -54,6 +54,8 @@ namespace MWMechanics
std::list::const_iterator begin() const;
std::list::const_iterator end() const;
+ void erase (std::list::const_iterator package);
+
/// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/
int getTypeId() const;
diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp
index 0809cd640..e0e8057f2 100644
--- a/apps/openmw/mwmechanics/character.cpp
+++ b/apps/openmw/mwmechanics/character.cpp
@@ -395,22 +395,35 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
CharacterState walkState = runStateToWalkState(mMovementState);
const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState));
anim = stateinfo->groupname;
- }
- if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
- {
- speedmult = mMovementSpeed / vel;
+ if (mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
+ speedmult = mMovementSpeed / vel;
+ else
+ // Another bug: when using a fallback animation (e.g. RunForward as fallback to SwimRunForward),
+ // then the equivalent Walk animation will not use a fallback, and if that animation doesn't exist
+ // we will play without any scaling.
+ // Makes the speed attribute of most water creatures totally useless.
+ // And again, this can not be fixed without patching game data.
+ speedmult = 1.f;
}
- else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
- speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
- else if (mMovementSpeed > 0.0f)
+ else
{
- // The first person anims don't have any velocity to calculate a speed multiplier from.
- // We use the third person velocities instead.
- // FIXME: should be pulled from the actual animation, but it is not presently loaded.
- speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
- mMovementAnimationControlled = false;
+ if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
+ {
+ speedmult = mMovementSpeed / vel;
+ }
+ else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
+ speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
+ else if (mMovementSpeed > 0.0f)
+ {
+ // The first person anims don't have any velocity to calculate a speed multiplier from.
+ // We use the third person velocities instead.
+ // FIXME: should be pulled from the actual animation, but it is not presently loaded.
+ speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
+ mMovementAnimationControlled = false;
+ }
}
+
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
}
@@ -1174,6 +1187,36 @@ void CharacterController::update(float duration)
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool flying = world->isFlying(mPtr);
+ CreatureStats &stats = cls.getCreatureStats(mPtr);
+
+ //Force Jump Logic
+
+ bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
+ if(!inwater && !flying)
+ {
+ //Force Jump
+ if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))
+ {
+ if(onground)
+ {
+ cls.getMovementSettings(mPtr).mPosition[2] = 1;
+ }
+ else
+ cls.getMovementSettings(mPtr).mPosition[2] = 0;
+ }
+ //Force Move Jump, only jump if they're otherwise moving
+ if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)
+ {
+
+ if(onground)
+ {
+ cls.getMovementSettings(mPtr).mPosition[2] = 1;
+ }
+ else
+ cls.getMovementSettings(mPtr).mPosition[2] = 0;
+ }
+ }
+
//Ogre::Vector3 vec = cls.getMovementVector(mPtr);
Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition);
if(vec.z > 0.0f) // to avoid slow-down when jumping
@@ -1183,7 +1226,7 @@ void CharacterController::update(float duration)
vec.x = vecXY.x;
vec.y = vecXY.y;
}
- else
+ else
vec.normalise();
if(mHitState != CharState_None && mJumpState == JumpState_None)
@@ -1352,8 +1395,7 @@ void CharacterController::update(float duration)
}
else
{
- if(!(vec.z > 0.0f))
- mJumpState = JumpState_None;
+ mJumpState = JumpState_None;
vec.z = 0.0f;
inJump = false;
@@ -1482,6 +1524,8 @@ void CharacterController::update(float duration)
else if (mAnimation)
mAnimation->updateEffects(duration);
mSkipAnim = false;
+
+ mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
}
diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp
index c13eac98c..c04e2c62d 100644
--- a/apps/openmw/mwmechanics/combat.cpp
+++ b/apps/openmw/mwmechanics/combat.cpp
@@ -169,12 +169,12 @@ namespace MWMechanics
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
- const MWWorld::Class &othercls = victim.getClass();
- if(!othercls.isActor()) // Can't hit non-actors
- return;
- MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
- if(otherstats.isDead()) // Can't hit dead actors
+ if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
+ // Can't hit non-actors or dead actors
+ {
+ reduceWeaponCondition(0.f, false, weapon, attacker);
return;
+ }
if(attacker.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
@@ -189,6 +189,7 @@ namespace MWMechanics
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
+ MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
return;
}
@@ -209,6 +210,8 @@ namespace MWMechanics
damage *= fDamageStrengthBase +
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
+ adjustWeaponDamage(damage, weapon);
+ reduceWeaponCondition(damage, true, weapon, attacker);
if(attacker.getRefData().getHandle() == "player")
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
@@ -295,4 +298,42 @@ namespace MWMechanics
}
}
+ void reduceWeaponCondition(float damage, bool hit, MWWorld::Ptr &weapon, const MWWorld::Ptr &attacker)
+ {
+ if (weapon.isEmpty())
+ return;
+
+ if (!hit)
+ damage = 0.f;
+
+ const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
+ if(weaphashealth)
+ {
+ int weaphealth = weapon.getClass().getItemHealth(weapon);
+
+ const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->getFloat();
+ float x = std::max(1.f, fWeaponDamageMult * damage);
+
+ weaphealth -= std::min(int(x), weaphealth);
+ weapon.getCellRef().setCharge(weaphealth);
+
+ // Weapon broken? unequip it
+ if (weaphealth == 0)
+ weapon = *attacker.getClass().getInventoryStore(attacker).unequipItem(weapon, attacker);
+ }
+ }
+
+ void adjustWeaponDamage(float &damage, const MWWorld::Ptr &weapon)
+ {
+ if (weapon.isEmpty())
+ return;
+
+ const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
+ if(weaphashealth)
+ {
+ int weaphealth = weapon.getClass().getItemHealth(weapon);
+ int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
+ damage *= (float(weaphealth) / weapmaxhealth);
+ }
+ }
}
diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp
index eb89cb820..0d3009510 100644
--- a/apps/openmw/mwmechanics/combat.hpp
+++ b/apps/openmw/mwmechanics/combat.hpp
@@ -13,6 +13,7 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
+/// @note \a victim may be empty (e.g. for a hit on terrain), a non-actor (environment objects) or an actor
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
const Ogre::Vector3& hitPosition);
@@ -22,6 +23,15 @@ float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, in
/// Applies damage to attacker based on the victim's elemental shields.
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
+/// @param damage Unmitigated weapon damage of the attack
+/// @param hit Was the attack successful?
+/// @param weapon The weapon used.
+/// @note if the weapon is unequipped as result of condition damage, a new Ptr will be assigned to \a weapon.
+void reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker);
+
+/// Adjust weapon damage based on its condition. A used weapon will be less effective.
+void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon);
+
}
#endif
diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp
index 4ee994de5..a36d28fd3 100644
--- a/apps/openmw/mwmechanics/creaturestats.hpp
+++ b/apps/openmw/mwmechanics/creaturestats.hpp
@@ -227,7 +227,9 @@ namespace MWMechanics
Flag_ForceRun = 1,
Flag_ForceSneak = 2,
Flag_Run = 4,
- Flag_Sneak = 8
+ Flag_Sneak = 8,
+ Flag_ForceJump = 16,
+ Flag_ForceMoveJump = 32
};
enum Stance
{
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 199dc488d..ed21fa8a8 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -973,6 +973,9 @@ namespace MWMechanics
if (!it->getClass().isNpc())
continue;
+ if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
+ continue;
+
// Will the witness report the crime?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
{
@@ -1071,6 +1074,9 @@ namespace MWMechanics
if (*it != victim && type == OT_Assault)
aggression = iFightAttacking;
+ if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
+ continue;
+
if (it->getClass().isClass(*it, "guard"))
{
// Mark as Alarmed for dialogue
@@ -1100,6 +1106,35 @@ namespace MWMechanics
}
}
+ void MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker)
+ {
+ if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
+ return;
+
+ std::list followers = getActorsFollowing(attacker);
+ if (std::find(followers.begin(), followers.end(), ptr) != followers.end())
+ {
+ ptr.getClass().getCreatureStats(ptr).friendlyHit();
+
+ if (ptr.getClass().getCreatureStats(ptr).getFriendlyHits() < 4)
+ return;
+ }
+
+ // Attacking peaceful NPCs is a crime
+ if (ptr.getClass().isNpc() && !attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
+ && !isAggressive(ptr, attacker))
+ commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
+
+ if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)
+ || attacker == MWBase::Environment::get().getWorld()->getPlayerPtr())
+ && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker))
+ {
+ // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
+ // Note: accidental or collateral damage attacks are ignored.
+ startCombat(ptr, attacker);
+ }
+ }
+
bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
{
if (observer.getClass().getCreatureStats(observer).isDead())
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
index 365e24067..7dbc6da74 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
@@ -119,6 +119,7 @@ namespace MWMechanics
OffenseType type, int arg=0);
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0);
+ virtual void actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
/// Utility to check if taking this item is illegal and calling commitCrime if so
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count);
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp
index 579969f9d..87fb835a7 100644
--- a/apps/openmw/mwmechanics/npcstats.cpp
+++ b/apps/openmw/mwmechanics/npcstats.cpp
@@ -34,7 +34,6 @@ MWMechanics::NpcStats::NpcStats()
, mProfit(0)
, mTimeToStartDrowning(20.0)
, mLastDrowningHit(0)
-, mLevelHealthBonus(0)
{
mSkillIncreases.resize (ESM::Attribute::Length, 0);
}
@@ -262,8 +261,7 @@ void MWMechanics::NpcStats::levelUp()
// "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance"
- mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat();
- updateHealth();
+ setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->getFloat());
setLevel(getLevel()+1);
}
@@ -273,7 +271,7 @@ void MWMechanics::NpcStats::updateHealth()
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
- setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus);
+ setHealth(static_cast (0.5 * (strength + endurance)));
}
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
@@ -497,7 +495,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mTimeToStartDrowning = mTimeToStartDrowning;
state.mLastDrowningHit = mLastDrowningHit;
- state.mLevelHealthBonus = mLevelHealthBonus;
}
void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
@@ -549,5 +546,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
mTimeToStartDrowning = state.mTimeToStartDrowning;
mLastDrowningHit = state.mLastDrowningHit;
- mLevelHealthBonus = state.mLevelHealthBonus;
}
diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp
index a066760d0..c78900287 100644
--- a/apps/openmw/mwmechanics/npcstats.hpp
+++ b/apps/openmw/mwmechanics/npcstats.hpp
@@ -52,8 +52,6 @@ namespace MWMechanics
/// time since last hit from drowning
float mLastDrowningHit;
- float mLevelHealthBonus;
-
public:
NpcStats();
@@ -104,7 +102,7 @@ namespace MWMechanics
void updateHealth();
///< Calculate health based on endurance and strength.
- /// Called at character creation and at level up.
+ /// Called at character creation.
void flagAsUsed (const std::string& id);
diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp
index 94b479007..f53aceaef 100644
--- a/apps/openmw/mwmechanics/spellcasting.cpp
+++ b/apps/openmw/mwmechanics/spellcasting.cpp
@@ -72,7 +72,7 @@ namespace MWMechanics
return schoolSkillMap[school];
}
- float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)
+ float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
{
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
@@ -123,14 +123,17 @@ namespace MWMechanics
if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
castChance = 100;
- return std::max(0.f, std::min(100.f, castChance));
+ if (!cap)
+ return std::max(0.f, castChance);
+ else
+ return std::max(0.f, std::min(100.f, castChance));
}
- float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool)
+ float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
{
const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get().find(spellId);
- return getSpellSuccessChance(spell, actor, effectiveSchool);
+ return getSpellSuccessChance(spell, actor, effectiveSchool, cap);
}
@@ -184,6 +187,10 @@ namespace MWMechanics
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
+ // Effects with no resistance attribute belonging to them can not be resisted
+ if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
+ return 0.f;
+
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
@@ -191,12 +198,13 @@ namespace MWMechanics
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa
+ float castChance = 100.f;
if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
{
- float castChance = getSpellSuccessChance(spell, caster);
- if (castChance > 0)
- x *= 50 / castChance;
+ castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
}
+ if (castChance > 0)
+ x *= 50 / castChance;
float roll = static_cast(std::rand()) / RAND_MAX * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
@@ -230,6 +238,44 @@ namespace MWMechanics
return -(resistance-100) / 100.f;
}
+ /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
+ bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer)
+ {
+ switch (effectId)
+ {
+ case ESM::MagicEffect::Levitate:
+ if (!MWBase::Environment::get().getWorld()->isLevitationEnabled())
+ {
+ if (castByPlayer)
+ MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
+ return false;
+ }
+ break;
+ case ESM::MagicEffect::Soultrap:
+ if (!target.getClass().isNpc() // no messagebox for NPCs
+ && (target.getTypeName() == typeid(ESM::Creature).name() && target.get()->mBase->mData.mSoul == 0))
+ {
+ if (castByPlayer)
+ MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}");
+ return true; // must still apply to get visual effect and have target regard it as attack
+ }
+ break;
+ case ESM::MagicEffect::AlmsiviIntervention:
+ case ESM::MagicEffect::DivineIntervention:
+ case ESM::MagicEffect::Mark:
+ case ESM::MagicEffect::Recall:
+ if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled())
+ {
+ if (castByPlayer)
+ MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
+ return false;
+ }
+ break;
+ }
+
+ return true;
+ }
+
CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target)
: mCaster(caster)
, mTarget(target)
@@ -318,23 +364,8 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getStore().get().find (
effectIt->mEffectID);
- if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate)
- {
- if (castByPlayer)
- MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
- continue;
- }
-
- if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled() &&
- (effectIt->mEffectID == ESM::MagicEffect::AlmsiviIntervention ||
- effectIt->mEffectID == ESM::MagicEffect::DivineIntervention ||
- effectIt->mEffectID == ESM::MagicEffect::Mark ||
- effectIt->mEffectID == ESM::MagicEffect::Recall))
- {
- if (castByPlayer)
- MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
+ if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer))
continue;
- }
// If player is healing someone, show the target's HP bar
if (castByPlayer && target != caster
diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp
index 2de187ae4..9381b605e 100644
--- a/apps/openmw/mwmechanics/spellcasting.hpp
+++ b/apps/openmw/mwmechanics/spellcasting.hpp
@@ -26,11 +26,12 @@ namespace MWMechanics
* @param spell spell to cast
* @param actor calculate spell success chance for this actor (depends on actor's skills)
* @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here
- * @attention actor has to be an NPC and not a creature!
- * @return success chance from 0 to 100 (in percent)
+ * @param cap cap the result to 100%?
+ * @note actor can be an NPC or a creature
+ * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned.
*/
- float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL);
- float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL);
+ float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true);
+ float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true);
int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp
index 872740d74..7a5a45832 100644
--- a/apps/openmw/mwrender/animation.cpp
+++ b/apps/openmw/mwrender/animation.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
@@ -467,7 +468,13 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
const std::string stop = groupname+": stop";
float starttime = std::numeric_limits::max();
float stoptime = 0.0f;
- // Have to find keys in reverse (see reset method)
+
+ // Pick the last Loop Stop key and the last Loop Start key.
+ // This is required because of broken text keys in AshVampire.nif.
+ // It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback
+ // but the animation velocity calculation uses the second one.
+ // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated,
+ // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough.
NifOgre::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin());
while(keyiter != keys.rend())
{
@@ -476,8 +483,18 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
starttime = keyiter->first;
break;
}
- else if(keyiter->second == loopstop || keyiter->second == stop)
+ ++keyiter;
+ }
+ keyiter = keys.rbegin();
+ while(keyiter != keys.rend())
+ {
+ if (keyiter->second == stop)
+ stoptime = keyiter->first;
+ else if (keyiter->second == loopstop)
+ {
stoptime = keyiter->first;
+ break;
+ }
++keyiter;
}
@@ -1191,13 +1208,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename)
return;
- // fix texture extension to .dds
- if (texture.size() > 4)
- {
- texture[texture.size()-3] = 'd';
- texture[texture.size()-2] = 'd';
- texture[texture.size()-1] = 's';
- }
+ std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture);
EffectParams params;
params.mModelName = model;
@@ -1255,7 +1266,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for (int tex=0; texgetNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
- tus->setTextureName("textures\\" + texture);
+ tus->setTextureName(correctedTexture);
}
}
}
@@ -1285,7 +1296,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for (int tex=0; texgetNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
- tus->setTextureName("textures\\" + texture);
+ tus->setTextureName(correctedTexture);
}
}
}
@@ -1415,6 +1426,15 @@ void ObjectAnimation::addLight(const ESM::Light *light)
addExtraLight(mInsert->getCreator(), mObjectRoot, light);
}
+void ObjectAnimation::removeParticles()
+{
+ for (unsigned int i=0; imParticles.size(); ++i)
+ {
+ mObjectRoot->mSceneMgr->destroyParticleSystem(mObjectRoot->mParticles[i]);
+ }
+ mObjectRoot->mParticles.clear();
+}
+
class FindEntityTransparency {
public:
diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp
index e15bd6ecb..dc4244c39 100644
--- a/apps/openmw/mwrender/animation.hpp
+++ b/apps/openmw/mwrender/animation.hpp
@@ -306,6 +306,7 @@ public:
virtual void attachArrow() {}
virtual void releaseArrow() {}
void enableLights(bool enable);
+ virtual void enableHeadAnimation(bool enable) {}
Ogre::AxisAlignedBox getWorldBounds();
@@ -323,6 +324,7 @@ public:
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model);
void addLight(const ESM::Light *light);
+ void removeParticles();
bool canBatch() const;
void fillBatch(Ogre::StaticGeometry *sg);
diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp
index 5e88b2250..fd07f1a19 100644
--- a/apps/openmw/mwrender/characterpreview.cpp
+++ b/apps/openmw/mwrender/characterpreview.cpp
@@ -37,6 +37,7 @@ namespace MWRender
, mViewport(NULL)
, mCamera(NULL)
, mNode(NULL)
+ , mRecover(false)
{
mCharacter.mCell = NULL;
}
@@ -46,6 +47,16 @@ namespace MWRender
}
+ void CharacterPreview::onFrame()
+ {
+ if (mRecover)
+ {
+ setupRenderTarget();
+ mRenderTarget->update();
+ mRecover = false;
+ }
+ }
+
void CharacterPreview::setup ()
{
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
@@ -83,19 +94,10 @@ namespace MWRender
mCamera->setNearClipDistance (0.01);
mCamera->setFarClipDistance (1000);
- mTexture = Ogre::TextureManager::getSingleton().getByName (mName);
- if (mTexture.isNull ())
- mTexture = Ogre::TextureManager::getSingleton().createManual(mName,
- Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET);
+ mTexture = Ogre::TextureManager::getSingleton().createManual(mName,
+ Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET, this);
- mRenderTarget = mTexture->getBuffer()->getRenderTarget();
- mRenderTarget->removeAllViewports ();
- mViewport = mRenderTarget->addViewport(mCamera);
- mViewport->setOverlaysEnabled(false);
- mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
- mViewport->setShadowsEnabled(false);
- mRenderTarget->setActive(true);
- mRenderTarget->setAutoUpdated (false);
+ setupRenderTarget();
onSetup ();
}
@@ -107,6 +109,7 @@ namespace MWRender
mSceneMgr->destroyAllCameras();
delete mAnimation;
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
+ Ogre::TextureManager::getSingleton().remove(mName);
}
}
@@ -127,12 +130,39 @@ namespace MWRender
onSetup();
}
+ void CharacterPreview::loadResource(Ogre::Resource *resource)
+ {
+ Ogre::Texture* tex = dynamic_cast(resource);
+ if (!tex)
+ return;
+
+ tex->createInternalResources();
+
+ mRenderTarget = NULL;
+ mViewport = NULL;
+ mRecover = true;
+ }
+
+ void CharacterPreview::setupRenderTarget()
+ {
+ mRenderTarget = mTexture->getBuffer()->getRenderTarget();
+ mRenderTarget->removeAllViewports ();
+ mViewport = mRenderTarget->addViewport(mCamera);
+ mViewport->setOverlaysEnabled(false);
+ mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
+ mViewport->setShadowsEnabled(false);
+ mRenderTarget->setActive(true);
+ mRenderTarget->setAutoUpdated (false);
+ }
+
// --------------------------------------------------------------------------------------------------
InventoryPreview::InventoryPreview(MWWorld::Ptr character)
: CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0))
, mSelectionBuffer(NULL)
+ , mSizeX(0)
+ , mSizeY(0)
{
}
@@ -141,7 +171,21 @@ namespace MWRender
delete mSelectionBuffer;
}
- void InventoryPreview::update(int sizeX, int sizeY)
+ void InventoryPreview::resize(int sizeX, int sizeY)
+ {
+ mSizeX = sizeX;
+ mSizeY = sizeY;
+
+ mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
+ mTexture->load();
+
+ if (!mRenderTarget)
+ setupRenderTarget();
+
+ mRenderTarget->update();
+ }
+
+ void InventoryPreview::update()
{
mAnimation->updateParts();
@@ -197,15 +241,25 @@ namespace MWRender
mAnimation->runAnimation(0.0f);
- mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024)));
-
mNode->setOrientation (Ogre::Quaternion::IDENTITY);
+ mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
+ mTexture->load();
+
+ if (!mRenderTarget)
+ setupRenderTarget();
+
mRenderTarget->update();
mSelectionBuffer->update();
}
+ void InventoryPreview::setupRenderTarget()
+ {
+ CharacterPreview::setupRenderTarget();
+ mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
+ }
+
int InventoryPreview::getSlotSelected (int posX, int posY)
{
return mSelectionBuffer->getSelected (posX, posY);
@@ -243,6 +297,7 @@ namespace MWRender
void RaceSelectionPreview::render()
{
+ mTexture->load();
mRenderTarget->update();
}
diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp
index 60312455f..711de0d15 100644
--- a/apps/openmw/mwrender/characterpreview.hpp
+++ b/apps/openmw/mwrender/characterpreview.hpp
@@ -22,7 +22,7 @@ namespace MWRender
class NpcAnimation;
- class CharacterPreview
+ class CharacterPreview : public Ogre::ManualResourceLoader
{
public:
CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name,
@@ -34,6 +34,13 @@ namespace MWRender
virtual void rebuild();
+ void onFrame();
+
+ void loadResource(Ogre::Resource *resource);
+
+ private:
+ bool mRecover; // Texture content was lost and needs to be re-rendered
+
private:
CharacterPreview(const CharacterPreview&);
CharacterPreview& operator=(const CharacterPreview&);
@@ -41,6 +48,8 @@ namespace MWRender
protected:
virtual bool renderHeadOnly() { return false; }
+ virtual void setupRenderTarget();
+
Ogre::TexturePtr mTexture;
Ogre::RenderTarget* mRenderTarget;
Ogre::Viewport* mViewport;
@@ -72,11 +81,17 @@ namespace MWRender
virtual ~InventoryPreview();
virtual void onSetup();
- void update(int sizeX, int sizeY);
+ void update(); // Render preview again, e.g. after changed equipment
+ void resize(int sizeX, int sizeY);
int getSlotSelected(int posX, int posY);
+ protected:
+ virtual void setupRenderTarget();
+
private:
+ int mSizeX;
+ int mSizeY;
OEngine::Render::SelectionBuffer* mSelectionBuffer;
};
diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp
index 968be0f9e..a48dea8d5 100644
--- a/apps/openmw/mwrender/effectmanager.cpp
+++ b/apps/openmw/mwrender/effectmanager.cpp
@@ -1,5 +1,7 @@
#include "effectmanager.hpp"
+#include
+
#include
#include
#include
@@ -21,15 +23,6 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition);
sceneNode->setScale(scale,scale,scale);
- // fix texture extension to .dds
- if (textureOverride.size() > 4)
- {
- textureOverride[textureOverride.size()-3] = 'd';
- textureOverride[textureOverride.size()-2] = 'd';
- textureOverride[textureOverride.size()-1] = 's';
- }
-
-
NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
// TODO: turn off shadow casting
@@ -44,6 +37,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
if (!textureOverride.empty())
{
+ std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(textureOverride);
for(size_t i = 0;i < scene->mParticles.size(); ++i)
{
Ogre::ParticleSystem* partSys = scene->mParticles[i];
@@ -59,7 +53,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
for (int tex=0; texgetNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
- tus->setTextureName("textures\\" + textureOverride);
+ tus->setTextureName(correctedTexture);
}
}
}
diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp
index 271cbbfe1..50dec8950 100644
--- a/apps/openmw/mwrender/localmap.cpp
+++ b/apps/openmw/mwrender/localmap.cpp
@@ -98,6 +98,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
return;
Ogre::Image image;
+ tex->load();
tex->convertToImage(image);
Ogre::DataStreamPtr encoded = image.encode("tga");
@@ -137,6 +138,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
return;
Ogre::Image image;
+ tex->load();
tex->convertToImage(image);
fog->mFogTextures.push_back(ESM::FogTexture());
@@ -581,6 +583,8 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
}
}
+ tex->load();
+
// copy to the texture
// NOTE: Could be optimized later. We actually only need to update the region that changed.
// Not a big deal at the moment, the FoW is only 32x32 anyway.
diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp
index 0a9b56b33..23b761646 100644
--- a/apps/openmw/mwrender/npcanimation.cpp
+++ b/apps/openmw/mwrender/npcanimation.cpp
@@ -67,11 +67,16 @@ namespace MWRender
{
HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference)
- : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0)
+ : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0), mEnabled(true)
{
resetBlinkTimer();
}
+void HeadAnimationTime::setEnabled(bool enabled)
+{
+ mEnabled = enabled;
+}
+
void HeadAnimationTime::resetBlinkTimer()
{
mBlinkTimer = -(2 + (std::rand() / double(RAND_MAX*1.0)) * 6);
@@ -79,6 +84,9 @@ void HeadAnimationTime::resetBlinkTimer()
void HeadAnimationTime::update(float dt)
{
+ if (!mEnabled)
+ return;
+
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference))
{
mBlinkTimer += dt;
@@ -864,6 +872,11 @@ void NpcAnimation::setAlpha(float alpha)
}
}
+void NpcAnimation::enableHeadAnimation(bool enable)
+{
+ mHeadAnimationTime->setEnabled(enable);
+}
+
void NpcAnimation::preRender(Ogre::Camera *camera)
{
Animation::preRender(camera);
diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp
index 95cd35ddb..979210591 100644
--- a/apps/openmw/mwrender/npcanimation.hpp
+++ b/apps/openmw/mwrender/npcanimation.hpp
@@ -26,6 +26,8 @@ private:
float mBlinkTimer;
+ bool mEnabled;
+
float mValue;
private:
void resetBlinkTimer();
@@ -34,6 +36,8 @@ public:
void update(float dt);
+ void setEnabled(bool enabled);
+
void setTalkStart(float value);
void setTalkStop(float value);
void setBlinkStart(float value);
@@ -125,6 +129,8 @@ public:
ViewMode viewMode=VM_Normal);
virtual ~NpcAnimation();
+ virtual void enableHeadAnimation(bool enable);
+
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
virtual Ogre::Vector3 runAnimation(float timepassed);
diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp
index d9e20e1f8..96decbb36 100644
--- a/apps/openmw/mwrender/objects.cpp
+++ b/apps/openmw/mwrender/objects.cpp
@@ -73,14 +73,19 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
ptr.getRefData().setBaseNode(insert);
}
-void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch)
+void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch, bool addLight)
{
insertBegin(ptr);
std::auto_ptr anim(new ObjectAnimation(ptr, mesh));
if(ptr.getTypeName() == typeid(ESM::Light).name())
- anim->addLight(ptr.get()->mBase);
+ {
+ if (addLight)
+ anim->addLight(ptr.get()->mBase);
+ else
+ anim->removeParticles();
+ }
if (!mesh.empty())
{
diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp
index 02e974e2d..7f740dbab 100644
--- a/apps/openmw/mwrender/objects.hpp
+++ b/apps/openmw/mwrender/objects.hpp
@@ -41,7 +41,7 @@ public:
, mRootNode(NULL)
{}
~Objects(){}
- void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false);
+ void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false, bool addLight=false);
ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr);
diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp
index 23edb3a7f..ed6b56f37 100644
--- a/apps/openmw/mwrender/renderingmanager.cpp
+++ b/apps/openmw/mwrender/renderingmanager.cpp
@@ -23,6 +23,7 @@
#include
#include
+#include
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
@@ -45,7 +46,6 @@
#include "globalmap.hpp"
#include "terrainstorage.hpp"
#include "effectmanager.hpp"
-#include "terraingrid.hpp"
using namespace MWRender;
using namespace Ogre;
@@ -212,11 +212,6 @@ MWRender::Actors& RenderingManager::getActors(){
return *mActors;
}
-OEngine::Render::Fader* RenderingManager::getFader()
-{
- return mRendering.getFader();
-}
-
MWRender::Camera* RenderingManager::getCamera() const
{
return mCamera;
@@ -345,7 +340,7 @@ void RenderingManager::update (float duration, bool paused)
MWWorld::Ptr player = world->getPlayerPtr();
int blind = player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude;
- mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f)));
+ MWBase::Environment::get().getWindowManager()->setScreenFactor(std::max(0.f, 1.f-(blind / 100.f)));
setAmbientMode();
// player position
@@ -1050,7 +1045,7 @@ void RenderingManager::enableTerrain(bool enable)
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
else
- mTerrain = new MWRender::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
+ mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
Settings::Manager::getBool("split", "Shadows"));
diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp
index ea7905cf5..ef436931d 100644
--- a/apps/openmw/mwrender/renderingmanager.hpp
+++ b/apps/openmw/mwrender/renderingmanager.hpp
@@ -4,8 +4,6 @@
#include "sky.hpp"
#include "debugging.hpp"
-#include
-
#include
#include
@@ -98,8 +96,6 @@ public:
void toggleLight();
bool toggleRenderMode(int mode);
- OEngine::Render::Fader* getFader();
-
void removeCell (MWWorld::CellStore *store);
/// \todo this function should be removed later. Instead the rendering subsystems should track
diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp
index 8354cca5d..229bf92e7 100644
--- a/apps/openmw/mwrender/sky.cpp
+++ b/apps/openmw/mwrender/sky.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
@@ -294,7 +295,12 @@ void SkyManager::create()
// Stars
mAtmosphereNight = mRootNode->createChildSceneNode();
- NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif");
+ NifOgre::ObjectScenePtr objects;
+ if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("meshes\\sky_night_02.nif"))
+ objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_02.nif");
+ else
+ objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif");
+
for(size_t i = 0, matidx = 0;i < objects->mEntities.size();i++)
{
Entity* night1_ent = objects->mEntities[i];
@@ -584,13 +590,13 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
if (mClouds != weather.mCloudTexture)
{
- sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture);
+ sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", Misc::ResourceHelpers::correctTexturePath(weather.mCloudTexture));
mClouds = weather.mCloudTexture;
}
if (mNextClouds != weather.mNextCloudTexture)
{
- sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", "textures\\"+weather.mNextCloudTexture);
+ sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", Misc::ResourceHelpers::correctTexturePath(weather.mNextCloudTexture));
mNextClouds = weather.mNextCloudTexture;
}
diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp
index 2558c95c5..cbd9e2444 100644
--- a/apps/openmw/mwrender/terrainstorage.cpp
+++ b/apps/openmw/mwrender/terrainstorage.cpp
@@ -1,21 +1,11 @@
#include "terrainstorage.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
#include
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp"
-#include
-
namespace MWRender
{
@@ -59,515 +49,4 @@ namespace MWRender
return esmStore.get().find(index, plugin);
}
- bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max)
- {
- assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell");
-
- /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead
-
- Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
-
- assert(origin.x == (int) origin.x);
- assert(origin.y == (int) origin.y);
-
- int cellX = origin.x;
- int cellY = origin.y;
-
- const ESM::Land* land = getLand(cellX, cellY);
- if (!land)
- return false;
-
- min = std::numeric_limits().max();
- max = -std::numeric_limits().max();
- for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
- if (h > max)
- max = h;
- if (h < min)
- min = h;
- }
- }
- return true;
- }
-
- void TerrainStorage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row)
- {
- while (col >= ESM::Land::LAND_SIZE-1)
- {
- ++cellY;
- col -= ESM::Land::LAND_SIZE-1;
- }
- while (row >= ESM::Land::LAND_SIZE-1)
- {
- ++cellX;
- row -= ESM::Land::LAND_SIZE-1;
- }
- while (col < 0)
- {
- --cellY;
- col += ESM::Land::LAND_SIZE-1;
- }
- while (row < 0)
- {
- --cellX;
- row += ESM::Land::LAND_SIZE-1;
- }
- ESM::Land* land = getLand(cellX, cellY);
- if (land && land->mHasData)
- {
- normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
- normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
- normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
- normal.normalise();
- }
- else
- normal = Ogre::Vector3(0,0,1);
- }
-
- void TerrainStorage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row)
- {
- Ogre::Vector3 n1,n2,n3,n4;
- fixNormal(n1, cellX, cellY, col+1, row);
- fixNormal(n2, cellX, cellY, col-1, row);
- fixNormal(n3, cellX, cellY, col, row+1);
- fixNormal(n4, cellX, cellY, col, row-1);
- normal = (n1+n2+n3+n4);
- normal.normalise();
- }
-
- void TerrainStorage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row)
- {
- if (col == ESM::Land::LAND_SIZE-1)
- {
- ++cellY;
- col = 0;
- }
- if (row == ESM::Land::LAND_SIZE-1)
- {
- ++cellX;
- row = 0;
- }
- ESM::Land* land = getLand(cellX, cellY);
- if (land && land->mLandData->mUsingColours)
- {
- color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
- color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
- color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
- }
- else
- {
- color.r = 1;
- color.g = 1;
- color.b = 1;
- }
-
- }
-
- void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
- std::vector& positions,
- std::vector& normals,
- std::vector& colours)
- {
- // LOD level n means every 2^n-th vertex is kept
- size_t increment = 1 << lodLevel;
-
- Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
- assert(origin.x == (int) origin.x);
- assert(origin.y == (int) origin.y);
-
- int startX = origin.x;
- int startY = origin.y;
-
- size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1;
-
- colours.resize(numVerts*numVerts*4);
- positions.resize(numVerts*numVerts*3);
- normals.resize(numVerts*numVerts*3);
-
- Ogre::Vector3 normal;
- Ogre::ColourValue color;
-
- float vertY;
- float vertX;
-
- float vertY_ = 0; // of current cell corner
- for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY)
- {
- float vertX_ = 0; // of current cell corner
- for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
- {
- ESM::Land* land = getLand(cellX, cellY);
- if (land && !land->mHasData)
- land = NULL;
- bool hasColors = land && land->mLandData->mUsingColours;
-
- int rowStart = 0;
- int colStart = 0;
- // Skip the first row / column unless we're at a chunk edge,
- // since this row / column is already contained in a previous cell
- if (colStart == 0 && vertY_ != 0)
- colStart += increment;
- if (rowStart == 0 && vertX_ != 0)
- rowStart += increment;
-
- vertY = vertY_;
- for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
- else
- positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
-
- if (land)
- {
- normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
- normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
- normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
- normal.normalise();
- }
- else
- normal = Ogre::Vector3(0,0,1);
-
- // Normals apparently don't connect seamlessly between cells
- if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
- fixNormal(normal, cellX, cellY, col, row);
-
- // some corner normals appear to be complete garbage (z < 0)
- if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1))
- averageNormal(normal, cellX, cellY, col, row);
-
- assert(normal.z > 0);
-
- normals[vertX*numVerts*3 + vertY*3] = normal.x;
- normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
- normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
-
- if (hasColors)
- {
- color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
- color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
- color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
- }
- else
- {
- color.r = 1;
- color.g = 1;
- color.b = 1;
- }
-
- // Unlike normals, colors mostly connect seamlessly between cells, but not always...
- if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
- fixColour(color, cellX, cellY, col, row);
-
- color.a = 1;
- Ogre::uint32 rsColor;
- Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
- memcpy(&colours[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32));
-
- ++vertX;
- }
- ++vertY;
- }
- vertX_ = vertX;
- }
- vertY_ = vertY;
-
- assert(vertX_ == numVerts); // Ensure we covered whole area
- }
- assert(vertY_ == numVerts); // Ensure we covered whole area
- }
-
- TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY,
- int x, int y)
- {
- // For the first/last row/column, we need to get the texture from the neighbour cell
- // to get consistent blending at the borders
- --x;
- if (x < 0)
- {
- --cellX;
- x += ESM::Land::LAND_TEXTURE_SIZE;
- }
- if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
- {
- ++cellY;
- y -= ESM::Land::LAND_TEXTURE_SIZE;
- }
-
- assert(xmLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
- if (tex == 0)
- return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin
- return std::make_pair(tex, land->mPlugin);
- }
- else
- return std::make_pair(0,0);
- }
-
- std::string TerrainStorage::getTextureName(UniqueTextureId id)
- {
- if (id.first == 0)
- return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded?
-
- // NB: All vtex ids are +1 compared to the ltex ids
- const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);
-
- std::string texture = ltex->mTexture;
- //TODO this is needed due to MWs messed up texture handling
- texture = texture.substr(0, texture.rfind(".")) + ".dds";
-
- return texture;
- }
-
- void TerrainStorage::getBlendmaps (const std::vector& nodes, std::vector& out, bool pack)
- {
- for (std::vector::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
- {
- out.push_back(Terrain::LayerCollection());
- out.back().mTarget = *it;
- getBlendmapsImpl((*it)->getSize(), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers);
- }
- }
-
- void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
- bool pack, std::vector &blendmaps, std::vector &layerList)
- {
- getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList);
- }
-
- void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter,
- bool pack, std::vector &blendmaps, std::vector &layerList)
- {
- // TODO - blending isn't completely right yet; the blending radius appears to be
- // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap
- // and interpolate the rest of the cell by hand? :/
-
- Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f);
- int cellX = origin.x;
- int cellY = origin.y;
-
- // Save the used texture indices so we know the total number of textures
- // and number of required blend maps
- std::set textureIndices;
- // Due to the way the blending works, the base layer will always shine through in between
- // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible).
- // To get a consistent look, we need to make sure to use the same base layer in all cells.
- // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
- textureIndices.insert(std::make_pair(0,0));
-
- for (int y=0; y textureIndicesMap;
- for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it)
- {
- int size = textureIndicesMap.size();
- textureIndicesMap[*it] = size;
- layerList.push_back(getLayerInfo(getTextureName(*it)));
- }
-
- int numTextures = textureIndices.size();
- // numTextures-1 since the base layer doesn't need blending
- int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1);
-
- int channels = pack ? 4 : 1;
-
- // Second iteration - create and fill in the blend maps
- const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
-
- for (int i=0; isecond;
- int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1);
- int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
-
- if (blendIndex == i)
- pData[y*blendmapSize*channels + x*channels + channel] = 255;
- else
- pData[y*blendmapSize*channels + x*channels + channel] = 0;
- }
- }
- blendmaps.push_back(Ogre::PixelBox(blendmapSize, blendmapSize, 1, format, pData));
- }
- }
-
- float TerrainStorage::getHeightAt(const Ogre::Vector3 &worldPos)
- {
- int cellX = std::floor(worldPos.x / 8192.f);
- int cellY = std::floor(worldPos.y / 8192.f);
-
- ESM::Land* land = getLand(cellX, cellY);
- if (!land)
- return -2048;
-
- // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
-
- // Normalized position in the cell
- float nX = (worldPos.x - (cellX * 8192))/8192.f;
- float nY = (worldPos.y - (cellY * 8192))/8192.f;
-
- // get left / bottom points (rounded down)
- float factor = ESM::Land::LAND_SIZE - 1.0f;
- float invFactor = 1.0f / factor;
-
- int startX = static_cast(nX * factor);
- int startY = static_cast(nY * factor);
- int endX = startX + 1;
- int endY = startY + 1;
-
- assert(endX < ESM::Land::LAND_SIZE);
- assert(endY < ESM::Land::LAND_SIZE);
-
- // now get points in terrain space (effectively rounding them to boundaries)
- float startXTS = startX * invFactor;
- float startYTS = startY * invFactor;
- float endXTS = endX * invFactor;
- float endYTS = endY * invFactor;
-
- // get parametric from start coord to next point
- float xParam = (nX - startXTS) * factor;
- float yParam = (nY - startYTS) * factor;
-
- /* For even / odd tri strip rows, triangles are this shape:
- even odd
- 3---2 3---2
- | / | | \ |
- 0---1 0---1
- */
-
- // Build all 4 positions in normalized cell space, using point-sampled height
- Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f);
- Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f);
- Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f);
- Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f);
- // define this plane in terrain space
- Ogre::Plane plane;
- // (At the moment, all rows have the same triangle alignment)
- if (true)
- {
- // odd row
- bool secondTri = ((1.0 - yParam) > xParam);
- if (secondTri)
- plane.redefine(v0, v1, v3);
- else
- plane.redefine(v1, v2, v3);
- }
- else
- {
- // even row
- bool secondTri = (yParam > xParam);
- if (secondTri)
- plane.redefine(v0, v2, v3);
- else
- plane.redefine(v0, v1, v2);
- }
-
- // Solve plane equation for z
- return (-plane.normal.x * nX
- -plane.normal.y * nY
- - plane.d) / plane.normal.z * 8192;
-
- }
-
- float TerrainStorage::getVertexHeight(const ESM::Land *land, int x, int y)
- {
- assert(x < ESM::Land::LAND_SIZE);
- assert(y < ESM::Land::LAND_SIZE);
- return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x];
- }
-
- Terrain::LayerInfo TerrainStorage::getLayerInfo(const std::string& texture)
- {
- // Already have this cached?
- if (mLayerInfoMap.find(texture) != mLayerInfoMap.end())
- return mLayerInfoMap[texture];
-
- Terrain::LayerInfo info;
- info.mParallax = false;
- info.mSpecular = false;
- info.mDiffuseMap = "textures\\" + texture;
- std::string texture_ = texture;
- boost::replace_last(texture_, ".", "_nh.");
- if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
- {
- info.mNormalMap = "textures\\" + texture_;
- info.mParallax = true;
- }
- else
- {
- texture_ = texture;
- boost::replace_last(texture_, ".", "_n.");
- if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
- info.mNormalMap = "textures\\" + texture_;
- }
-
- texture_ = texture;
- boost::replace_last(texture_, ".", "_diffusespec.");
- if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
- {
- info.mDiffuseMap = "textures\\" + texture_;
- info.mSpecular = true;
- }
-
- // This wasn't cached, so the textures are probably not loaded either.
- // Background load them so they are hopefully already loaded once we need them!
- Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mDiffuseMap, "General");
- if (!info.mNormalMap.empty())
- Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mNormalMap, "General");
-
- mLayerInfoMap[texture] = info;
-
- return info;
- }
-
- Terrain::LayerInfo TerrainStorage::getDefaultLayer()
- {
- Terrain::LayerInfo info;
- info.mDiffuseMap = "textures\\_land_default.dds";
- info.mParallax = false;
- info.mSpecular = false;
- return info;
- }
-
- float TerrainStorage::getCellWorldSize()
- {
- return ESM::Land::REAL_SIZE;
- }
-
- int TerrainStorage::getCellVertices()
- {
- return ESM::Land::LAND_SIZE;
- }
-
}
diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp
index c1fb74445..30a2a61ac 100644
--- a/apps/openmw/mwrender/terrainstorage.hpp
+++ b/apps/openmw/mwrender/terrainstorage.hpp
@@ -1,15 +1,13 @@
#ifndef MWRENDER_TERRAINSTORAGE_H
#define MWRENDER_TERRAINSTORAGE_H
-#include
-#include
-
-#include
+#include
namespace MWRender
{
- class TerrainStorage : public Terrain::Storage
+ /// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage.
+ class TerrainStorage : public ESMTerrain::Storage
{
private:
virtual ESM::Land* getLand (int cellX, int cellY);
@@ -18,92 +16,6 @@ namespace MWRender
/// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
-
- /// Get the minimum and maximum heights of a terrain region.
- /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
- /// Larger chunks can simply merge AABB of children.
- /// @param size size of the chunk in cell units
- /// @param center center of the chunk in cell units
- /// @param min min height will be stored here
- /// @param max max height will be stored here
- /// @return true if there was data available for this terrain chunk
- virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max);
-
- /// Fill vertex buffers for a terrain chunk.
- /// @note May be called from background threads. Make sure to only call thread-safe functions from here!
- /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue.
- /// @param lodLevel LOD level, 0 = most detailed
- /// @param size size of the terrain chunk in cell units
- /// @param center center of the chunk in cell units
- /// @param positions buffer to write vertices
- /// @param normals buffer to write vertex normals
- /// @param colours buffer to write vertex colours
- virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
- std::vector& positions,
- std::vector& normals,
- std::vector& colours);
-
- /// Create textures holding layer blend values for a terrain chunk.
- /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
- /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
- /// @note May be called from background threads.
- /// @param chunkSize size of the terrain chunk in cell units
- /// @param chunkCenter center of the chunk in cell units
- /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
- /// otherwise, each texture contains blend values for one layer only. Shader-based rendering
- /// can utilize packing, FFP can't.
- /// @param blendmaps created blendmaps will be written here
- /// @param layerList names of the layer textures used will be written here
- virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
- std::vector& blendmaps,
- std::vector& layerList);
-
- /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information.
- /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once.
- /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might
- /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
- /// @note May be called from background threads.
- /// @param nodes A collection of nodes for which to retrieve the aforementioned data
- /// @param out Output vector
- /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
- /// otherwise, each texture contains blend values for one layer only. Shader-based rendering
- /// can utilize packing, FFP can't.
- virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack);
-
- virtual float getHeightAt (const Ogre::Vector3& worldPos);
-
- virtual Terrain::LayerInfo getDefaultLayer();
-
- /// Get the transformation factor for mapping cell units to world units.
- virtual float getCellWorldSize();
-
- /// Get the number of vertices on one side for each cell. Should be (power of two)+1
- virtual int getCellVertices();
-
- private:
- void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
- void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row);
- void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
-
- float getVertexHeight (const ESM::Land* land, int x, int y);
-
- // Since plugins can define new texture palettes, we need to know the plugin index too
- // in order to retrieve the correct texture name.
- // pair
- typedef std::pair UniqueTextureId;
-
- UniqueTextureId getVtexIndexAt(int cellX, int cellY,
- int x, int y);
- std::string getTextureName (UniqueTextureId id);
-
- std::map mLayerInfoMap;
-
- Terrain::LayerInfo getLayerInfo(const std::string& texture);
-
- // Non-virtual
- void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
- std::vector& blendmaps,
- std::vector& layerList);
};
}
diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp
index d2e774859..fb6b73be6 100644
--- a/apps/openmw/mwscript/controlextensions.cpp
+++ b/apps/openmw/mwscript/controlextensions.cpp
@@ -121,6 +121,34 @@ namespace MWScript
}
};
+ template
+ class OpGetForceJump : public Interpreter::Opcode0
+ {
+ public:
+
+ virtual void execute (Interpreter::Runtime& runtime)
+ {
+ MWWorld::Ptr ptr = R()(runtime);
+
+ MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
+ runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));
+ }
+ };
+
+ template
+ class OpGetForceMoveJump : public Interpreter::Opcode0
+ {
+ public:
+
+ virtual void execute (Interpreter::Runtime& runtime)
+ {
+ MWWorld::Ptr ptr = R()(runtime);
+
+ MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
+ runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));
+ }
+ };
+
template
class OpGetForceSneak : public Interpreter::Opcode0
{
@@ -169,27 +197,54 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision);
+ //Force Run
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun,
new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
- interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
- new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
- interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,
- new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
- interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
- new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
-
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit,
new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit,
new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
+
+ //Force Jump
+ interpreter.installSegment5 (Compiler::Control::opcodeClearForceJump,
+ new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeClearForceJumpExplicit,
+ new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceJump,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceJumpExplicit,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceJump));
+
+ //Force MoveJump
+ interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJump,
+ new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJumpExplicit,
+ new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJump,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJumpExplicit,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceMoveJump));
+
+ //Force Sneak
+ interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,
+ new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit,
new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
+ interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
+ new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit,
new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
+
interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning);
interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun);
+ interpreter.installSegment5 (Compiler::Control::opcodeGetForceJump, new OpGetForceJump);
+ interpreter.installSegment5 (Compiler::Control::opcodeGetForceJumpExplicit, new OpGetForceJump);
+ interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJump, new OpGetForceMoveJump);
+ interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJumpExplicit, new OpGetForceMoveJump);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak);
}
diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt
index 4b501b20f..f600117a9 100644
--- a/apps/openmw/mwscript/docs/vmformat.txt
+++ b/apps/openmw/mwscript/docs/vmformat.txt
@@ -413,5 +413,13 @@ op 0x2000254: HurtStandingActor
op 0x2000255: HurtStandingActor, explicit
op 0x2000256: HurtCollidingActor
op 0x2000257: HurtCollidingActor, explicit
+op 0x2000258: ClearForceJump
+op 0x2000259: ClearForceJump, explicit reference
+op 0x200025a: ForceJump
+op 0x200025b: ForceJump, explicit reference
+op 0x200025c: ClearForceMoveJump
+op 0x200025d: ClearForceMoveJump, explicit reference
+op 0x200025e: ForceMoveJump
+op 0x200025f: ForceMoveJump, explicit reference
-opcodes 0x2000258-0x3ffffff unused
+opcodes 0x2000260-0x3ffffff unused
diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp
index 36848d44c..b487ac70f 100644
--- a/apps/openmw/mwscript/miscextensions.cpp
+++ b/apps/openmw/mwscript/miscextensions.cpp
@@ -3,8 +3,6 @@
#include
-#include
-
#include
#include
#include
@@ -248,7 +246,7 @@ namespace MWScript
Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop();
- MWBase::Environment::get().getWorld()->getFader()->fadeIn(time);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(time);
}
};
@@ -261,7 +259,7 @@ namespace MWScript
Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop();
- MWBase::Environment::get().getWorld()->getFader()->fadeOut(time);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(time);
}
};
@@ -277,7 +275,7 @@ namespace MWScript
Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop();
- MWBase::Environment::get().getWorld()->getFader()->fadeTo(alpha, time);
+ MWBase::Environment::get().getWindowManager()->fadeScreenTo(alpha, time);
}
};
diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp
index 07d137ce2..7150704ca 100644
--- a/apps/openmw/mwscript/statsextensions.cpp
+++ b/apps/openmw/mwscript/statsextensions.cpp
@@ -637,8 +637,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
- MWWorld::Ptr ptr = R()(runtime);
-
std::string factionID = "";
if(arg0 >0)
{
@@ -647,6 +645,8 @@ namespace MWScript
}
else
{
+ MWWorld::Ptr ptr = R()(runtime);
+
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
@@ -913,8 +913,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
- MWWorld::Ptr ptr = R()(runtime);
-
std::string factionID = "";
if(arg0 >0 )
{
@@ -923,6 +921,8 @@ namespace MWScript
}
else
{
+ MWWorld::Ptr ptr = R()(runtime);
+
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp
index a041049ca..3355e705f 100644
--- a/apps/openmw/mwscript/transformationextensions.cpp
+++ b/apps/openmw/mwscript/transformationextensions.cpp
@@ -327,7 +327,7 @@ namespace MWScript
}
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
- ptr.getClass().adjustPosition(ptr);
+ ptr.getClass().adjustPosition(ptr, false);
}
else
{
@@ -363,8 +363,19 @@ namespace MWScript
runtime.pop();
int cx,cy;
MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);
- MWBase::Environment::get().getWorld()->moveObject(ptr,
- MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
+
+ // another morrowind oddity: player will be moved to the exterior cell at this location,
+ // non-player actors will move within the cell they are in.
+ if (ptr.getRefData().getHandle() == "player")
+ {
+ MWBase::Environment::get().getWorld()->moveObject(ptr,
+ MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
+ }
+ else
+ {
+ MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z);
+ }
+
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
@@ -374,7 +385,7 @@ namespace MWScript
zRot = zRot/60.;
}
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
- ptr.getClass().adjustPosition(ptr);
+ ptr.getClass().adjustPosition(ptr, false);
}
};
diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp
index 446519b87..d241278d1 100644
--- a/apps/openmw/mwworld/class.cpp
+++ b/apps/openmw/mwworld/class.cpp
@@ -322,7 +322,7 @@ namespace MWWorld
return std::make_pair (1, "");
}
- void Class::adjustPosition(const MWWorld::Ptr& ptr) const
+ void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{
}
diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp
index e880030f9..64e83179d 100644
--- a/apps/openmw/mwworld/class.hpp
+++ b/apps/openmw/mwworld/class.hpp
@@ -90,8 +90,9 @@ namespace MWWorld
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.
- virtual void adjustPosition(const MWWorld::Ptr& ptr) const;
+ virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
///< Adjust position to stand on ground. Must be called post model load
+ /// @param force do this even if the ptr is flying
virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const;
///< Return creature stats or throw an exception, if class does not have creature stats
diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp
index 3188c1cd5..085b079d9 100644
--- a/apps/openmw/mwworld/containerstore.cpp
+++ b/apps/openmw/mwworld/containerstore.cpp
@@ -701,7 +701,8 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state)
for (std::vector >::const_iterator iter (state.mLights.begin());
iter!=state.mLights.end(); ++iter)
{
- getState (lights, iter->first);
+ int slot = iter->second;
+ setSlot (getState (lights, iter->first), slot);
}
mLevelledItemMap = state.mLevelledItemMap;
diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp
index 02a198f3d..3c0e67729 100644
--- a/apps/openmw/mwworld/physicssystem.cpp
+++ b/apps/openmw/mwworld/physicssystem.cpp
@@ -612,9 +612,9 @@ namespace MWWorld
}
}
- std::vector PhysicsSystem::getCollisions(const Ptr &ptr)
+ std::vector PhysicsSystem::getCollisions(const Ptr &ptr, int collisionGroup, int collisionMask)
{
- return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName());
+ return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName(), collisionGroup, collisionMask);
}
Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight)
diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp
index ac60a80b5..f0522426d 100644
--- a/apps/openmw/mwworld/physicssystem.hpp
+++ b/apps/openmw/mwworld/physicssystem.hpp
@@ -55,7 +55,7 @@ namespace MWWorld
void stepSimulation(float dt);
- std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with
+ std::vector getCollisions(const MWWorld::Ptr &ptr, int collisionGroup, int collisionMask); ///< get handles this object collides with
Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, float maxHeight);
std::pair getFacedHandle(float queryDistance);
diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp
index 280761215..b3996f756 100644
--- a/apps/openmw/mwworld/player.cpp
+++ b/apps/openmw/mwworld/player.cpp
@@ -244,6 +244,8 @@ namespace MWWorld
mPlayer.load (player.mObject);
+ getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear();
+
MWBase::World& world = *MWBase::Environment::get().getWorld();
try
diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp
index fb376bb93..ff1743c64 100644
--- a/apps/openmw/mwworld/projectilemanager.cpp
+++ b/apps/openmw/mwworld/projectilemanager.cpp
@@ -247,29 +247,23 @@ namespace MWWorld
if (obstacle == caster)
continue;
- if (obstacle.isEmpty())
+ MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
+
+ // Try to get a Ptr to the bow that was used. It might no longer exist.
+ MWWorld::Ptr bow = projectileRef.getPtr();
+ if (!caster.isEmpty())
{
- // Terrain
- }
- else if (obstacle.getClass().isActor())
- {
- MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
-
- // Try to get a Ptr to the bow that was used. It might no longer exist.
- MWWorld::Ptr bow = projectileRef.getPtr();
- if (!caster.isEmpty())
- {
- MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
- MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
- if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId))
- bow = *invIt;
- }
-
- if (caster.isEmpty())
- caster = obstacle;
-
- MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
+ MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
+ MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
+ if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId))
+ bow = *invIt;
}
+
+ if (caster.isEmpty())
+ caster = obstacle;
+
+ MWMechanics::projectileHit(caster, obstacle, bow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
+
hit = true;
}
if (hit)
diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp
index b2faa1a01..ee107e1f2 100644
--- a/apps/openmw/mwworld/scene.cpp
+++ b/apps/openmw/mwworld/scene.cpp
@@ -4,8 +4,6 @@
#include
-#include
-
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" /// FIXME
#include "../mwbase/soundmanager.hpp"
@@ -86,7 +84,7 @@ namespace
updateObjectLocalRotation(ptr, mPhysics, mRendering);
MWBase::Environment::get().getWorld()->scaleObject (ptr, ptr.getCellRef().getScale());
- ptr.getClass().adjustPosition (ptr);
+ ptr.getClass().adjustPosition (ptr, false);
}
catch (const std::exception& e)
{
@@ -232,7 +230,7 @@ namespace MWWorld
float z = Ogre::Radian(pos.rot[2]).valueDegrees();
world->rotateObject(player, x, y, z);
- player.getClass().adjustPosition(player);
+ player.getClass().adjustPosition(player, true);
}
MWBase::MechanicsManager *mechMgr =
@@ -405,21 +403,20 @@ namespace MWWorld
void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
{
+ CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);
+ bool loadcell = (mCurrentCell == NULL);
+ if(!loadcell)
+ loadcell = *mCurrentCell != *cell;
+
Nif::NIFFile::CacheLock lock;
- MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5);
+ MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
- Loading::ScopedLoad load(loadingListener);
-
- mRendering.enableTerrain(false);
-
std::string loadingInteriorText = "#{sLoadingMessage2}";
loadingListener->setLabel(loadingInteriorText);
+ Loading::ScopedLoad load(loadingListener);
- CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);
- bool loadcell = (mCurrentCell == NULL);
- if(!loadcell)
- loadcell = *mCurrentCell != *cell;
+ mRendering.enableTerrain(false);
if(!loadcell)
{
@@ -431,8 +428,8 @@ namespace MWWorld
float z = Ogre::Radian(position.rot[2]).valueDegrees();
world->rotateObject(world->getPlayerPtr(), x, y, z);
- world->getPlayerPtr().getClass().adjustPosition(world->getPlayerPtr());
- world->getFader()->fadeIn(0.5f);
+ world->getPlayerPtr().getClass().adjustPosition(world->getPlayerPtr(), true);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
return;
}
@@ -480,7 +477,7 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->adjustSky();
mCellChanged = true;
- MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.5);
+ MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
}
void Scene::changeToExteriorCell (const ESM::Position& position)
diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp
index fb45cb034..675a3e86e 100644
--- a/apps/openmw/mwworld/weather.cpp
+++ b/apps/openmw/mwworld/weather.cpp
@@ -73,11 +73,6 @@ Rain Height Max=700 ?
Rain Threshold=0.6 ?
Max Raindrops=650 ?
*/
-
- size_t offset = weather.mCloudTexture.find(".tga");
- if (offset != std::string::npos)
- weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds");
-
weather.mIsStorm = (name == "ashstorm" || name == "blight");
mWeatherSettings[name] = weather;
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index f6aa6fec7..a84e4781d 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -1151,7 +1151,7 @@ namespace MWWorld
}
}
- void World::adjustPosition(const Ptr &ptr)
+ void World::adjustPosition(const Ptr &ptr, bool force)
{
ESM::Position pos (ptr.getRefData().getPosition());
@@ -1170,7 +1170,7 @@ namespace MWWorld
ptr.getRefData().setPosition(pos);
- if (!isFlying(ptr))
+ if (force || !isFlying(ptr))
{
Ogre::Vector3 traced = mPhysics->traceDown(ptr, 200);
if (traced.z < pos.pos[2])
@@ -1284,7 +1284,8 @@ namespace MWWorld
bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f;
/// \todo should use convexSweepTest here
- std::vector collisions = mPhysics->getCollisions(it->first);
+ std::vector collisions = mPhysics->getCollisions(it->first, OEngine::Physic::CollisionType_Actor
+ , OEngine::Physic::CollisionType_Actor);
for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
{
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
@@ -1300,7 +1301,6 @@ namespace MWWorld
// we need to undo the rotation
localRotateObject(it->first, 0, 0, oldRot);
reached = false;
- //break; //Removed in case multiple actors are touching
}
}
@@ -1555,11 +1555,6 @@ namespace MWWorld
mWeatherManager->modRegion(regionid, chances);
}
- OEngine::Render::Fader* World::getFader()
- {
- return mRendering->getFader();
- }
-
Ogre::Vector2 World::getNorthVector (CellStore* cell)
{
MWWorld::CellRefList& statics = cell->get();
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index c4c8b588e..fad3dfd97 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -169,9 +169,6 @@ namespace MWWorld
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map& contentFileMap);
- virtual OEngine::Render::Fader* getFader();
- ///< \todo remove this function. Rendering details should not be exposed.
-
virtual CellStore *getExterior (int x, int y);
virtual CellStore *getInterior (const std::string& name);
@@ -260,8 +257,9 @@ namespace MWWorld
virtual Ptr searchPtrViaActorId (int actorId);
///< Search is limited to the active cells.
- virtual void adjustPosition (const Ptr& ptr);
+ virtual void adjustPosition (const Ptr& ptr, bool force);
///< Adjust position after load to be on ground. Must be called after model load.
+ /// @param force do this even if the ptr is flying
virtual void fixPosition (const Ptr& actor);
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake
index 81b52b1b7..5608c528c 100644
--- a/cmake/FindOGRE.cmake
+++ b/cmake/FindOGRE.cmake
@@ -127,7 +127,7 @@ endif ()
set(OGRE_COMPONENTS Paging Terrain
Plugin_BSPSceneManager Plugin_CgProgramManager Plugin_OctreeSceneManager
Plugin_OctreeZone Plugin_PCZSceneManager Plugin_ParticleFX
- RenderSystem_Direct3D10 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES)
+ RenderSystem_Direct3D10 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES2)
set(OGRE_RESET_VARS
OGRE_CONFIG_INCLUDE_DIR OGRE_INCLUDE_DIR
OGRE_LIBRARY_FWK OGRE_LIBRARY_REL OGRE_LIBRARY_DBG
@@ -234,10 +234,10 @@ if (OGRE_STATIC)
find_package(FreeImage QUIET)
find_package(Freetype QUIET)
find_package(OpenGL QUIET)
- find_package(OpenGLES QUIET)
+ find_package(OpenGLES2 QUIET)
find_package(ZLIB QUIET)
find_package(ZZip QUIET)
- if (UNIX AND NOT APPLE)
+ if (UNIX AND (NOT APPLE AND NOT ANDROID))
find_package(X11 QUIET)
find_library(XAW_LIBRARY NAMES Xaw Xaw7 PATHS ${DEP_LIB_SEARCH_DIR} ${X11_LIB_SEARCH_PATH})
if (NOT XAW_LIBRARY OR NOT X11_Xt_FOUND)
@@ -258,10 +258,16 @@ if (OGRE_STATIC)
endif ()
endif ()
- set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${OGRE_LIBRARY_FWK} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES}
+if (ANDROID)
+ set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${OGRE_LIBRARY_FWK} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES}
${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES}
- ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB}
${Cocoa_LIBRARIES} ${Carbon_LIBRARIES})
+else ()
+ set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${OGRE_LIBRARY_FWK} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES}
+ ${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES}
+ ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB}
+ ${Cocoa_LIBRARIES} ${Carbon_LIBRARIES})
+endif()
if (NOT ZLIB_FOUND OR NOT ZZip_FOUND)
set(OGRE_DEPS_FOUND FALSE)
@@ -272,7 +278,7 @@ if (OGRE_STATIC)
if (NOT FREETYPE_FOUND)
set(OGRE_DEPS_FOUND FALSE)
endif ()
- if (UNIX AND NOT APPLE)
+ if (UNIX AND NOT APPLE AND NOT ANDROID)
if (NOT X11_FOUND)
set(OGRE_DEPS_FOUND FALSE)
endif ()
@@ -486,7 +492,7 @@ ogre_find_plugin(Plugin_CgProgramManager OgreCgProgram.h PlugIns/CgProgramManage
ogre_find_plugin(Plugin_OctreeSceneManager OgreOctreeSceneManager.h PlugIns/OctreeSceneManager/include)
ogre_find_plugin(Plugin_ParticleFX OgreParticleFXPrerequisites.h PlugIns/ParticleFX/include)
ogre_find_plugin(RenderSystem_GL OgreGLRenderSystem.h RenderSystems/GL/include)
-ogre_find_plugin(RenderSystem_GLES OgreGLESRenderSystem.h RenderSystems/GLES/include)
+ogre_find_plugin(RenderSystem_GLES2 OgreGLES2RenderSystem.h RenderSystems/GLES2/include)
ogre_find_plugin(RenderSystem_Direct3D9 OgreD3D9RenderSystem.h RenderSystems/Direct3D9/include)
ogre_find_plugin(RenderSystem_Direct3D10 OgreD3D10RenderSystem.h RenderSystems/Direct3D10/include)
ogre_find_plugin(RenderSystem_Direct3D11 OgreD3D11RenderSystem.h RenderSystems/Direct3D11/include)
@@ -528,8 +534,8 @@ if (OGRE_STATIC)
set(OGRE_RenderSystem_GL_LIBRARIES ${OGRE_RenderSystem_GL_LIBRARIES}
${OPENGL_LIBRARIES}
)
- set(OGRE_RenderSystem_GLES_LIBRARIES ${OGRE_RenderSystem_GLES_LIBRARIES}
- ${OPENGLES_LIBRARIES}
+ set(OGRE_RenderSystem_GLES2_LIBRARIES ${OGRE_RenderSystem_GLES2_LIBRARIES}
+ ${OPENGLES2_LIBRARIES}
)
set(OGRE_Plugin_CgProgramManager_LIBRARIES ${OGRE_Plugin_CgProgramManager_LIBRARIES}
${Cg_LIBRARIES}
diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt
index c8758ca9e..8cd99b6af 100644
--- a/components/CMakeLists.txt
+++ b/components/CMakeLists.txt
@@ -45,12 +45,16 @@ add_component_dir (esm
aisequence
)
+add_component_dir (esmterrain
+ storage
+ )
+
add_component_dir (misc
- utf8stream stringops
+ utf8stream stringops resourcehelpers
)
add_component_dir (files
- linuxpath windowspath macospath fixedpath multidircollection collections configurationmanager
+ linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager
constrainedfiledatastream lowlevelfile
)
@@ -72,7 +76,7 @@ add_component_dir (translation
add_definitions(-DTERRAIN_USE_SHADER=1)
add_component_dir (terrain
- quadtreenode chunk world defaultworld storage material buffercache defs
+ quadtreenode chunk world defaultworld terraingrid storage material buffercache defs
)
add_component_dir (loadinglistener
diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp
index bb377a0dc..73877f459 100644
--- a/components/compiler/extensions0.cpp
+++ b/components/compiler/extensions0.cpp
@@ -113,7 +113,7 @@ namespace Compiler
{
void registerExtensions (Extensions& extensions)
{
- extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit);
+ extensions.registerInstruction ("additem", "clX", opcodeAddItem, opcodeAddItemExplicit);
extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount,
opcodeGetItemCountExplicit);
extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem,
@@ -148,6 +148,16 @@ namespace Compiler
extensions.registerInstruction ("forcerun", "", opcodeForceRun,
opcodeForceRunExplicit);
+ extensions.registerInstruction ("clearforcejump", "", opcodeClearForceJump,
+ opcodeClearForceJumpExplicit);
+ extensions.registerInstruction ("forcejump", "", opcodeForceJump,
+ opcodeForceJumpExplicit);
+
+ extensions.registerInstruction ("clearforcemovejump", "", opcodeClearForceMoveJump,
+ opcodeClearForceMoveJumpExplicit);
+ extensions.registerInstruction ("forcemovejump", "", opcodeForceMoveJump,
+ opcodeForceMoveJumpExplicit);
+
extensions.registerInstruction ("clearforcesneak", "", opcodeClearForceSneak,
opcodeClearForceSneakExplicit);
extensions.registerInstruction ("forcesneak", "", opcodeForceSneak,
@@ -155,6 +165,8 @@ namespace Compiler
extensions.registerFunction ("getpcrunning", 'l', "", opcodeGetPcRunning);
extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking);
extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit);
+ extensions.registerFunction ("getforcejump", 'l', "", opcodeGetForceJump, opcodeGetForceJumpExplicit);
+ extensions.registerFunction ("getforcemovejump", 'l', "", opcodeGetForceMoveJump, opcodeGetForceMoveJumpExplicit);
extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit);
}
}
diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp
index 828d736c4..c09ec9f94 100644
--- a/components/compiler/opcodes.hpp
+++ b/components/compiler/opcodes.hpp
@@ -123,6 +123,14 @@ namespace Compiler
const int opcodeClearForceRunExplicit = 0x2000155;
const int opcodeForceRun = 0x2000156;
const int opcodeForceRunExplicit = 0x2000157;
+ const int opcodeClearForceJump = 0x2000258;
+ const int opcodeClearForceJumpExplicit = 0x2000259;
+ const int opcodeForceJump = 0x200025a;
+ const int opcodeForceJumpExplicit = 0x200025b;
+ const int opcodeClearForceMoveJump = 0x200025c;
+ const int opcodeClearForceMoveJumpExplicit = 0x200025d;
+ const int opcodeForceMoveJump = 0x200025e;
+ const int opcodeForceMoveJumpExplicit = 0x200025f;
const int opcodeClearForceSneak = 0x2000158;
const int opcodeClearForceSneakExplicit = 0x2000159;
const int opcodeForceSneak = 0x200015a;
@@ -134,6 +142,10 @@ namespace Compiler
const int opcodeGetForceSneak = 0x20001cc;
const int opcodeGetForceRunExplicit = 0x20001cd;
const int opcodeGetForceSneakExplicit = 0x20001ce;
+ const int opcodeGetForceJump = 0x2000260;
+ const int opcodeGetForceMoveJump = 0x2000262;
+ const int opcodeGetForceJumpExplicit = 0x2000261;
+ const int opcodeGetForceMoveJumpExplicit = 0x2000263;
}
namespace Dialogue
diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp
index 80440bdd3..0e3e54102 100644
--- a/components/esm/aisequence.cpp
+++ b/components/esm/aisequence.cpp
@@ -58,6 +58,8 @@ namespace AiSequence
esm.getHNT (mRemainingDuration, "DURA");
mCellId = esm.getHNOString ("CELL");
esm.getHNT (mAlwaysFollow, "ALWY");
+ mCommanded = false;
+ esm.getHNOT (mCommanded, "CMND");
}
void AiFollow::save(ESMWriter &esm) const
@@ -68,6 +70,7 @@ namespace AiSequence
if (!mCellId.empty())
esm.writeHNString ("CELL", mCellId);
esm.writeHNT ("ALWY", mAlwaysFollow);
+ esm.writeHNT ("CMND", mCommanded);
}
void AiActivate::load(ESMReader &esm)
diff --git a/components/esm/aisequence.hpp b/components/esm/aisequence.hpp
index bf0e17fa0..da16bf867 100644
--- a/components/esm/aisequence.hpp
+++ b/components/esm/aisequence.hpp
@@ -96,6 +96,7 @@ namespace ESM
float mRemainingDuration;
bool mAlwaysFollow;
+ bool mCommanded;
void load(ESMReader &esm);
void save(ESMWriter &esm) const;
diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp
index c9860dcef..7c98cc35b 100644
--- a/components/esm/loadinfo.cpp
+++ b/components/esm/loadinfo.cpp
@@ -10,6 +10,9 @@ namespace ESM
void DialInfo::load(ESMReader &esm)
{
+ mQuestStatus = QS_None;
+ mFactionLess = false;
+
mPrev = esm.getHNString("PNAM");
mNext = esm.getHNString("NNAM");
@@ -49,7 +52,6 @@ void DialInfo::load(ESMReader &esm)
return;
}
- mFactionLess = false;
if (subName.val == REC_FNAM)
{
mFaction = esm.getHString();
@@ -104,8 +106,6 @@ void DialInfo::load(ESMReader &esm)
return;
}
- mQuestStatus = QS_None;
-
if (subName.val == REC_QSTN)
mQuestStatus = QS_Name;
else if (subName.val == REC_QSTF)
diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp
index 74eb37197..ffc291609 100644
--- a/components/esm/loadligh.hpp
+++ b/components/esm/loadligh.hpp
@@ -25,7 +25,7 @@ struct Light
Negative = 0x004, // Negative light - i.e. darkness
Flicker = 0x008,
Fire = 0x010,
- OffDefault = 0x020, // Off by default
+ OffDefault = 0x020, // Off by default - does not burn while placed in a cell, but can burn when equipped by an NPC
FlickerSlow = 0x040,
Pulse = 0x080,
PulseSlow = 0x100
diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp
index aee8628bd..2e8f31cd2 100644
--- a/components/esm/loadscpt.cpp
+++ b/components/esm/loadscpt.cpp
@@ -38,13 +38,28 @@ void Script::load(ESMReader &esm)
char* str = &tmp[0];
for (size_t i = 0; i < mVarNames.size(); i++)
{
+ // Support '\r' terminated strings like vanilla. See Bug #1324.
char *termsym = strchr(str, '\r');
if(termsym) *termsym = '\0';
mVarNames[i] = std::string(str);
str += mVarNames[i].size() + 1;
if (str - &tmp[0] > s)
- esm.fail("String table overflow");
+ {
+ // Apparently SCVR subrecord is not used and variable names are
+ // determined on the fly from the script text. Therefore don't throw
+ // an exeption, just log an error and continue.
+ std::stringstream ss;
+
+ ss << "ESM Error: " << "String table overflow";
+ ss << "\n File: " << esm.getName();
+ ss << "\n Record: " << esm.getContext().recName.toString();
+ ss << "\n Subrecord: " << "SCVR";
+ ss << "\n Offset: 0x" << std::hex << esm.getFileOffset();
+ std::cerr << ss.str() << std::endl;
+ break;
+ }
+
}
}
diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp
index 26d8f9596..3e6aed99d 100644
--- a/components/esm/npcstats.cpp
+++ b/components/esm/npcstats.cpp
@@ -78,8 +78,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mLastDrowningHit = 0;
esm.getHNOT (mLastDrowningHit, "DRLH");
- mLevelHealthBonus = 0;
- esm.getHNOT (mLevelHealthBonus, "LVLH");
+ // No longer used
+ float levelHealthBonus = 0;
+ esm.getHNOT (levelHealthBonus, "LVLH");
mCrimeId = -1;
esm.getHNOT (mCrimeId, "CRID");
@@ -148,9 +149,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mLastDrowningHit)
esm.writeHNT ("DRLH", mLastDrowningHit);
- if (mLevelHealthBonus)
- esm.writeHNT ("LVLH", mLevelHealthBonus);
-
if (mCrimeId != -1)
esm.writeHNT ("CRID", mCrimeId);
}
diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp
index ce7c75d2a..0061fc05f 100644
--- a/components/esm/npcstats.hpp
+++ b/components/esm/npcstats.hpp
@@ -46,7 +46,6 @@ namespace ESM
std::vector mUsedIds;
float mTimeToStartDrowning;
float mLastDrowningHit;
- float mLevelHealthBonus;
int mCrimeId;
void load (ESMReader &esm);
diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp
new file mode 100644
index 000000000..585c1495d
--- /dev/null
+++ b/components/esmterrain/storage.cpp
@@ -0,0 +1,530 @@
+#include "storage.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include