Merge branch 'scriptedit' into run

This commit is contained in:
Marc Zinnschlag 2014-08-22 13:20:02 +02:00
commit 232c6c9ad3
160 changed files with 2574 additions and 1619 deletions

View file

@ -54,6 +54,10 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
# Macros # Macros
include(OpenMWMacros) include(OpenMWMacros)
if (ANDROID)
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
endif (ANDROID)
# doxygen main page # doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp") 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 set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/lights.cpp ${LIBDIR}/openengine/ogre/lights.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp
@ -233,7 +236,12 @@ if(OGRE_STATIC)
list(APPEND OGRE_STATIC_PLUGINS ${Cg_LIBRARIES}) list(APPEND OGRE_STATIC_PLUGINS ${Cg_LIBRARIES})
endif(Cg_FOUND) endif(Cg_FOUND)
if (ANDROID)
add_static_ogre_plugin(RenderSystem_GLES2)
else ()
add_static_ogre_plugin(RenderSystem_GL) add_static_ogre_plugin(RenderSystem_GL)
endif ()
if(WIN32) if(WIN32)
add_static_ogre_plugin(RenderSystem_Direct3D9) add_static_ogre_plugin(RenderSystem_Direct3D9)
endif(WIN32) endif(WIN32)

View file

@ -50,7 +50,7 @@ QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orien
return QVariant(); return QVariant();
if (role==ColumnBase::Role_Flags) if (role==ColumnBase::Role_Flags)
return ColumnBase::Flag_Table; return section==0 ? ColumnBase::Flag_Table : 0;
switch (section) switch (section)
{ {

View file

@ -26,6 +26,8 @@ void CSVWidget::PushButton::setExtendedToolTip()
"<p>(left click to activate," "<p>(left click to activate,"
"<br>shift-left click to activate and keep panel open)"; "<br>shift-left click to activate and keep panel open)";
break;
case Type_Toggle: case Type_Toggle:
tooltip += "<p>(left click to "; tooltip += "<p>(left click to ";

View file

@ -6,14 +6,35 @@
#include <QRegExp> #include <QRegExp>
#include <QString> #include <QString>
#include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/tablemimedata.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), QTextEdit (parent),
mDocument (document), 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 <<CSMWorld::UniversalId::Type_Journal mAllowedTypes <<CSMWorld::UniversalId::Type_Journal
<<CSMWorld::UniversalId::Type_Global <<CSMWorld::UniversalId::Type_Global
<<CSMWorld::UniversalId::Type_Topic <<CSMWorld::UniversalId::Type_Topic
@ -40,7 +61,22 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& docum
<<CSMWorld::UniversalId::Type_Probe <<CSMWorld::UniversalId::Type_Probe
<<CSMWorld::UniversalId::Type_Repair <<CSMWorld::UniversalId::Type_Repair
<<CSMWorld::UniversalId::Type_Static <<CSMWorld::UniversalId::Type_Static
<<CSMWorld::UniversalId::Type_Weapon; <<CSMWorld::UniversalId::Type_Weapon
<<CSMWorld::UniversalId::Type_Script
<<CSMWorld::UniversalId::Type_Region;
mHighlighter = new ScriptHighlighter (document.getData(), ScriptEdit::document());
connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged()));
connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting()));
mUpdateTimer.setSingleShot (true);
}
bool CSVWorld::ScriptEdit::isChangeLocked() const
{
return mChangeLocked!=0;
} }
void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event) void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event)
@ -103,3 +139,21 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const
//I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than…
return !(string.contains(mWhiteListQoutes)); return !(string.contains(mWhiteListQoutes));
} }
void CSVWorld::ScriptEdit::idListChanged()
{
mHighlighter->invalidateIds();
if (!mUpdateTimer.isActive())
mUpdateTimer.start (0);
}
void CSVWorld::ScriptEdit::updateHighlighting()
{
if (isChangeLocked())
return;
ChangeLock lock (*this);
mHighlighter->rehighlight();
}

View file

@ -1,8 +1,9 @@
#ifndef SCRIPTEDIT_H #ifndef SCRIPTEDIT_H
#define SCRIPTEDIT_H #define SCRIPTEDIT_H
#include <qtextedit.h> #include <QTextEdit>
#include <QVector> #include <QVector>
#include <QTimer>
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
@ -16,11 +17,43 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class ScriptHighlighter;
class ScriptEdit : public QTextEdit class ScriptEdit : public QTextEdit
{ {
Q_OBJECT Q_OBJECT
public: 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: private:
QVector<CSMWorld::UniversalId::Type> mAllowedTypes; QVector<CSMWorld::UniversalId::Type> mAllowedTypes;
@ -34,6 +67,12 @@ namespace CSVWorld
void dragMoveEvent (QDragMoveEvent* event); void dragMoveEvent (QDragMoveEvent* event);
bool stringNeedsQuote(const std::string& id) const; bool stringNeedsQuote(const std::string& id) const;
private slots:
void idListChanged();
void updateHighlighting();
}; };
} }
#endif // SCRIPTEDIT_H #endif // SCRIPTEDIT_H

View file

@ -3,8 +3,6 @@
#include <stdexcept> #include <stdexcept>
#include <QTextEdit>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
@ -12,28 +10,12 @@
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "scripthighlighter.hpp"
#include "scriptedit.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) 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)); setWidget (mEditor = new ScriptEdit (mDocument, this));
mEditor->setAcceptRichText (false);
mEditor->setLineWrapMode (QTextEdit::NoWrap);
mEditor->setTabStopWidth (4);
mEditor->setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
mModel = &dynamic_cast<CSMWorld::IdTable&> ( mModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); *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)), connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (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) void CSVWorld::ScriptSubView::setEditLock (bool locked)
@ -73,20 +47,12 @@ void CSVWorld::ScriptSubView::setEditLock (bool locked)
mEditor->setReadOnly (locked); mEditor->setReadOnly (locked);
} }
void CSVWorld::ScriptSubView::idListChanged()
{
mHighlighter->invalidateIds();
if (!mUpdateTimer.isActive())
mUpdateTimer.start (0);
}
void CSVWorld::ScriptSubView::textChanged() void CSVWorld::ScriptSubView::textChanged()
{ {
if (mChangeLocked) if (mEditor->isChangeLocked())
return; return;
ChangeLock lock (*this); ScriptEdit::ChangeLock lock (*mEditor);
mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel,
mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText())); 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) void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{ {
if (mChangeLocked) if (mEditor->isChangeLocked())
return; return;
ChangeLock lock (*this); ScriptEdit::ChangeLock lock (*mEditor);
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
@ -118,12 +84,3 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
deleteLater(); deleteLater();
} }
void CSVWorld::ScriptSubView::updateHighlighting()
{
if (mChangeLocked)
return;
ChangeLock lock (*this);
mHighlighter->rehighlight();
}

View file

@ -3,9 +3,6 @@
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
#include <QTimer>
class QTextEdit;
class QModelIndex; class QModelIndex;
namespace CSMDoc namespace CSMDoc
@ -20,34 +17,16 @@ namespace CSMWorld
namespace CSVWorld namespace CSVWorld
{ {
class ScriptHighlighter; class ScriptEdit;
class ScriptSubView : public CSVDoc::SubView class ScriptSubView : public CSVDoc::SubView
{ {
Q_OBJECT Q_OBJECT
QTextEdit *mEditor; ScriptEdit *mEditor;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mColumn; 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: public:
@ -57,17 +36,11 @@ namespace CSVWorld
public slots: public slots:
void idListChanged();
void textChanged(); void textChanged();
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
private slots:
void updateHighlighting();
}; };
} }

View file

@ -1,9 +1,17 @@
# local files # local files
set(GAME if (NOT ANDROID)
main.cpp set(GAME
engine.cpp main.cpp
) engine.cpp
if(NOT WIN32) )
else()
set(GAME
main.cpp
android_main.c
engine.cpp
)
endif()
if(NOT WIN32 AND NOT ANDROID)
set(GAME ${GAME} crashcatcher.cpp) set(GAME ${GAME} crashcatcher.cpp)
endif() endif()
set(GAME_HEADER set(GAME_HEADER
@ -15,7 +23,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst effectmanager weaponanimation terraingrid terrainstorage renderconst effectmanager weaponanimation
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -33,7 +41,7 @@ add_openmw_dir (mwgui
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget recharge mode videowidget backgroundimage itemwidget screenfader
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue
@ -82,19 +90,33 @@ add_openmw_dir (mwbase
) )
# Main executable # 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) if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
endif(WIN32) endif(WIN32)
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
add_executable(openmw if (NOT ANDROID)
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} add_executable(openmw
${OPENMW_FILES} ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
${GAME} ${GAME_HEADER} ${OPENMW_FILES}
${APPLE_BUNDLE_RESOURCES} ${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 # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
# when we change the backend. # when we change the backend.
@ -116,6 +138,23 @@ target_link_libraries(openmw
components 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) if (USE_SYSTEM_TINYXML)
target_link_libraries(openmw ${TINYXML_LIBRARIES}) target_link_libraries(openmw ${TINYXML_LIBRARIES})
endif() endif()

View file

@ -0,0 +1,43 @@
#include "../../SDL_internal.h"
#ifdef __ANDROID__
#include "SDL_main.h"
/*******************************************************************************
Functions called by JNI
*******************************************************************************/
#include <jni.h>
/* 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__ */

View file

@ -181,7 +181,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mActivationDistanceOverride(-1) , mActivationDistanceOverride(-1)
, mGrab(true) , mGrab(true)
, mScriptBlacklistUse (true) , mScriptBlacklistUse (true)
, mExportFonts(false)
{ {
std::srand ( std::time(NULL) ); std::srand ( std::time(NULL) );
MWClass::registerClasses(); MWClass::registerClasses();
@ -372,7 +372,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
MWGui::WindowManager* window = new MWGui::WindowManager( MWGui::WindowManager* window = new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding); mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts);
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
// Create sound system // Create sound system
@ -576,4 +576,9 @@ void OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)
void OMW::Engine::setScriptBlacklistUse (bool use) void OMW::Engine::setScriptBlacklistUse (bool use)
{ {
mScriptBlacklistUse = use; mScriptBlacklistUse = use;
} }
void OMW::Engine::enableFontExport(bool exportFonts)
{
mExportFonts = exportFonts;
}

View file

@ -83,6 +83,8 @@ namespace OMW
// Grab mouse? // Grab mouse?
bool mGrab; bool mGrab;
bool mExportFonts;
Compiler::Extensions mExtensions; Compiler::Extensions mExtensions;
Compiler::Context *mScriptContext; Compiler::Context *mScriptContext;
@ -187,6 +189,8 @@ namespace OMW
void setScriptBlacklistUse (bool use); void setScriptBlacklistUse (bool use);
void enableFontExport(bool exportFonts);
private: private:
Files::ConfigurationManager& mCfgMgr; Files::ConfigurationManager& mCfgMgr;
}; };

View file

@ -168,6 +168,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("no-grab", "Don't grab mouse cursor") ("no-grab", "Don't grab mouse cursor")
("export-fonts", bpo::value<bool>()->implicit_value(true)
->default_value(false), "Export Morrowind .fnt fonts to PNG image and XML file in current directory")
("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override"); ("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override");
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) 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<bool>()); engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap); engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>()); engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
engine.enableFontExport(variables["export-fonts"].as<bool>());
return true; return true;
} }

View file

@ -128,6 +128,7 @@ namespace MWBase
OffenseType type, int arg=0) = 0; OffenseType type, int arg=0) = 0;
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0) = 0; 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 /// 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; 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 /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so

View file

@ -331,6 +331,15 @@ namespace MWBase
virtual void removeCurrentModal(MWGui::WindowModal* input) = 0; virtual void removeCurrentModal(MWGui::WindowModal* input) = 0;
virtual void pinWindow (MWGui::GuiWindow window) = 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;
}; };
} }

View file

@ -17,11 +17,6 @@ namespace Ogre
namespace OEngine namespace OEngine
{ {
namespace Render
{
class Fader;
}
namespace Physic namespace Physic
{ {
class PhysicEngine; class PhysicEngine;
@ -113,9 +108,6 @@ namespace MWBase
virtual void readRecord (ESM::ESMReader& reader, int32_t type, virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 0; const std::map<int, int>& 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 *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 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. /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
virtual std::pair<MWWorld::Ptr,Ogre::Vector3> getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; virtual std::pair<MWWorld::Ptr,Ogre::Vector3> 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. ///< 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; virtual void fixPosition (const MWWorld::Ptr& actor) = 0;
///< Attempt to fix position so that the Ptr is no longer inside collision geometry. ///< Attempt to fix position so that the Ptr is no longer inside collision geometry.

View file

@ -157,15 +157,19 @@ namespace MWClass
float iWeight = gmst.find (typeGmst)->getInt(); float iWeight = gmst.find (typeGmst)->getInt();
if (iWeight * gmst.find ("fLightMaxMod")->getFloat()>= float epsilon = 5e-4;
ref->mBase->mData.mWeight)
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; return ESM::Skill::LightArmor;
if (iWeight * gmst.find ("fMedMaxMod")->getFloat()>= if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon)
ref->mBase->mData.mWeight)
return ESM::Skill::MediumArmor; return ESM::Skill::MediumArmor;
return ESM::Skill::HeavyArmor; else
return ESM::Skill::HeavyArmor;
} }
int Armor::getValue (const MWWorld::Ptr& ptr) const int Armor::getValue (const MWWorld::Ptr& ptr) const

View file

@ -148,9 +148,9 @@ namespace MWClass
return ref->mBase->mId; 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 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) if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{ {
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
// 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);
}
return; return;
} }
@ -298,7 +282,6 @@ namespace MWClass
if (!weapon.isEmpty()) if (!weapon.isEmpty())
{ {
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
const unsigned char *attack = NULL; const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop) if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop; attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
@ -309,26 +292,10 @@ namespace MWClass
if(attack) if(attack)
{ {
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); damage *= gmst.find("fDamageStrengthBase")->getFloat() +
if(weaphashealth) (stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.find("fDamageStrengthMult")->getFloat() * 0.1);
{ MWMechanics::adjustWeaponDamage(damage, weapon);
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
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);
}
} }
// Apply "On hit" enchanted weapons // Apply "On hit" enchanted weapons
@ -366,16 +333,11 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);
// Self defense // Self defense
if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)) if ((canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
|| 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
// (they have no movement or attacks anyway) // (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. MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
// Note: accidental or collateral damage attacks are ignored.
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
} }
if(!successful) if(!successful)
@ -406,18 +368,21 @@ namespace MWClass
if (damage > 0.f) if (damage > 0.f)
{ {
// Check for knockdown if (!attacker.isEmpty())
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<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{ {
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<double> (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); damage = std::max(1.f, damage);

View file

@ -49,7 +49,9 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; 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; 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); ///< \return name (the one that is to be presented to the user; not the internal one);

View file

@ -56,8 +56,11 @@ namespace MWClass
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>();
// Insert even if model is empty, so that the light is added // 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 void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const

View file

@ -403,9 +403,9 @@ namespace MWClass
return ref->mBase->mId; 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 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) if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{ {
othercls.onHit(victim, 0.0f, false, weapon, ptr, false); othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
// 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);
}
return; return;
} }
@ -555,7 +538,6 @@ namespace MWClass
MWMechanics::NpcStats &stats = getNpcStats(ptr); MWMechanics::NpcStats &stats = getNpcStats(ptr);
if(!weapon.isEmpty()) if(!weapon.isEmpty())
{ {
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
const unsigned char *attack = NULL; const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop) if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop; attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
@ -568,27 +550,9 @@ namespace MWClass
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= gmst.fDamageStrengthBase->getFloat() + damage *= gmst.fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1); (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; healthdmg = true;
} }
else else
@ -675,26 +639,13 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty. // 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(); bool wasDead = getCreatureStats(ptr).isDead();
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);
if (!attacker.isEmpty())
MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
if(!successful) if(!successful)
{ {
// TODO: Handle HitAttemptOnMe script function // TODO: Handle HitAttemptOnMe script function
@ -721,7 +672,7 @@ namespace MWClass
if (damage < 0.001f) if (damage < 0.001f)
damage = 0; 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 // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc. // something, alert the character controller, scripts, etc.
@ -749,7 +700,7 @@ namespace MWClass
else else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? 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: // Hit percentages:
// cuirass = 30% // cuirass = 30%
@ -967,8 +918,6 @@ namespace MWClass
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
if(npcdata->mNpcStats.isWerewolf())
runSpeed *= gmst.fWereWolfRunMult->getFloat();
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
@ -1000,6 +949,9 @@ namespace MWClass
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
moveSpeed *= gmst.fWereWolfRunMult->getFloat();
return moveSpeed; return moveSpeed;
} }

View file

@ -55,7 +55,9 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; 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; 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); ///< \return name (the one that is to be presented to the user; not the internal one);

View file

@ -145,11 +145,8 @@ namespace MWClass
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat();
for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it) for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it)
{ {
it->mKnown = ( (i == 0 && alchemySkill >= fWortChanceValue) it->mKnown = (i <= 1 && alchemySkill >= fWortChanceValue)
|| (i == 1 && alchemySkill >= fWortChanceValue*2) || (i <= 3 && alchemySkill >= fWortChanceValue*2);
|| (i == 2 && alchemySkill >= fWortChanceValue*3)
|| (i == 3 && alchemySkill >= fWortChanceValue*4));
++i; ++i;
} }

View file

@ -28,6 +28,16 @@
namespace MWGui 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) void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count)
{ {
mItem = sourceModel->getItem(index); mItem = sourceModel->getItem(index);
@ -36,7 +46,6 @@ namespace MWGui
mSourceView = sourceView; mSourceView = sourceView;
mSourceSortModel = sortModel; mSourceSortModel = sortModel;
mIsOnDragAndDrop = true; 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 // 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, // 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); mSourceSortModel->addDragItem(mItem.mBase, count);
} }
ItemWidget* baseWidget = mDragAndDropWidget->createWidget<ItemWidget> ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget<ItemWidget>("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop");
("MW_ItemIcon", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);
Controllers::ControllerFollowMouse* controller =
MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName())
->castType<Controllers::ControllerFollowMouse>();
MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller);
mDraggedWidget = baseWidget; mDraggedWidget = baseWidget;
baseWidget->setItem(mItem.mBase); baseWidget->setItem(mItem.mBase);
baseWidget->setNeedMouseFocus(false); baseWidget->setNeedMouseFocus(false);
@ -99,8 +113,6 @@ namespace MWGui
std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase); std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase);
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); 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 - // If item is dropped where it was taken from, we don't need to do anything -
// otherwise, do the transfer // otherwise, do the transfer
if (targetModel != mSourceModel) if (targetModel != mSourceModel)

View file

@ -33,13 +33,14 @@ namespace MWGui
public: public:
bool mIsOnDragAndDrop; bool mIsOnDragAndDrop;
MyGUI::Widget* mDraggedWidget; MyGUI::Widget* mDraggedWidget;
MyGUI::Widget* mDragAndDropWidget;
ItemModel* mSourceModel; ItemModel* mSourceModel;
ItemView* mSourceView; ItemView* mSourceView;
SortFilterItemModel* mSourceSortModel; SortFilterItemModel* mSourceSortModel;
ItemStack mItem; ItemStack mItem;
int mDraggedCount; int mDraggedCount;
DragAndDrop();
void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
void drop (ItemModel* targetModel, ItemView* targetView); void drop (ItemModel* targetModel, ItemView* targetView);

View file

@ -1,5 +1,7 @@
#include "controllers.hpp" #include "controllers.hpp"
#include <MyGUI_InputManager.h>
namespace MWGui namespace MWGui
{ {
namespace Controllers 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;
}
} }
} }

View file

@ -40,6 +40,17 @@ namespace MWGui
bool mEnabled; bool mEnabled;
float mTimeLeft; 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);
};
} }
} }

View file

@ -338,7 +338,7 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item)); item.getClass().getValue(item));
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return; return;
} }
} }

View file

@ -134,7 +134,7 @@ namespace MWGui
mEncoding = encoding; mEncoding = encoding;
} }
void FontLoader::loadAllFonts() void FontLoader::loadAllFonts(bool exportToFile)
{ {
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) 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"); Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "*.fnt");
for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource) for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource)
{ {
loadFont(*resource); loadFont(*resource, exportToFile);
} }
} }
} }
@ -168,7 +168,7 @@ namespace MWGui
float ascent; float ascent;
} GlyphInfo; } 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); Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName);
@ -221,6 +221,9 @@ namespace MWGui
width, height, 0, Ogre::PF_BYTE_RGBA); width, height, 0, Ogre::PF_BYTE_RGBA);
texture->loadImage(image); texture->loadImage(image);
if (exportToFile)
image.save(resourceName + ".png");
// Register the font with MyGUI // Register the font with MyGUI
MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>( MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>(
MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont"));
@ -240,10 +243,10 @@ namespace MWGui
for(int i = 0; i < 256; i++) for(int i = 0; i < 256; i++)
{ {
int x1 = data[i].top_left.x*width; float x1 = data[i].top_left.x*width;
int y1 = data[i].top_left.y*height; float y1 = data[i].top_left.y*height;
int w = data[i].top_right.x*width - x1; float w = data[i].top_right.x*width - x1;
int h = data[i].bottom_left.y*height - y1; float h = data[i].bottom_left.y*height - y1;
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding)); unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding));
@ -257,16 +260,38 @@ namespace MWGui
code->addAttribute("advance", data[i].width); code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + 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 // 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. // in the cp437 encoding of the font. Fall back to similar available characters.
if (mEncoding == ToUTF8::CP437) if (mEncoding == ToUTF8::CP437)
{ {
std::multimap<int, int> additional; std::multimap<int, int> additional; // <cp437, unicode>
additional.insert(std::make_pair(39, 0x2019)); // apostrophe additional.insert(std::make_pair(39, 0x2019)); // apostrophe
additional.insert(std::make_pair(45, 0x2013)); // dash 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, 0x201D)); // right double quotation mark
additional.insert(std::make_pair(34, 0x201C)); // left 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<int, int>::iterator it = additional.begin(); it != additional.end(); ++it) for (std::multimap<int, int>::iterator it = additional.begin(); it != additional.end(); ++it)
{ {
if (it->first != i) if (it->first != i)
@ -281,6 +306,7 @@ namespace MWGui
code->addAttribute("advance", data[i].width); code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + 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("advance", data[i].width);
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + 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) // 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("advance", data[i].width);
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + 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("coord", "0 0 0 0");
cursorCode->addAttribute("advance", "0"); cursorCode->addAttribute("advance", "0");
cursorCode->addAttribute("bearing", "0 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)); font->deserialization(root, MyGUI::Version(3,2,0));

View file

@ -12,12 +12,15 @@ namespace MWGui
{ {
public: public:
FontLoader (ToUTF8::FromType encoding); 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: private:
ToUTF8::FromType mEncoding; 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);
}; };
} }

View file

@ -1,6 +1,7 @@
#include "formatting.hpp" #include "formatting.hpp"
#include <components/interpreter/defines.hpp> #include <components/interpreter/defines.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwscript/interpretercontext.hpp" #include "../mwscript/interpretercontext.hpp"
@ -269,15 +270,6 @@ namespace MWGui
void BookTextParser::parseImage(std::string tag, bool createWidget) void BookTextParser::parseImage(std::string tag, bool createWidget)
{ {
int src_start = tag.find("SRC=")+5; 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_start = tag.find("WIDTH=")+7;
int width = boost::lexical_cast<int>(tag.substr(width_start, tag.find('"', width_start)-width_start)); int width = boost::lexical_cast<int>(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, MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount())); mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
// Apparently a bug with some morrowind versions, they reference the image without the size suffix. std::string image = Misc::ResourceHelpers::correctBookartPath(tag.substr(src_start, tag.find('"', src_start)-src_start), width, height);
// So if the image isn't found, try appending the size. box->setImageTexture(image);
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);
box->setProperty("NeedMouse", "false"); box->setProperty("NeedMouse", "false");
} }
@ -406,7 +390,7 @@ namespace MWGui
box->setTextAlign(mTextStyle.mTextAlign); box->setTextAlign(mTextStyle.mTextAlign);
box->setTextColour(mTextStyle.mColour); box->setTextColour(mTextStyle.mColour);
box->setFontName(mTextStyle.mFont); box->setFontName(mTextStyle.mFont);
box->setCaption(realText); box->setCaption(MyGUI::TextIterator::toTagsString(realText));
box->setSize(box->getSize().width, box->getTextSize().height); box->setSize(box->getSize().width, box->getTextSize().height);
mHeight += box->getTextSize().height; mHeight += box->getTextSize().height;

View file

@ -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") : Layout("openmw_hud.layout")
, mHealth(NULL) , mHealth(NULL)
, mMagicka(NULL) , mMagicka(NULL)
@ -97,7 +97,7 @@ namespace MWGui
, mWeaponSpellTimer(0.f) , mWeaponSpellTimer(0.f)
, mDrowningFlashTheta(0.f) , mDrowningFlashTheta(0.f)
{ {
setCoord(0,0, width, height); mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
// Energy bars // Energy bars
getWidget(mHealthFrame, "HealthFrame"); getWidget(mHealthFrame, "HealthFrame");
@ -405,11 +405,6 @@ namespace MWGui
mDrowningFlashTheta += dt * Ogre::Math::TWO_PI; 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) void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent)
{ {
const ESM::Spell* spell = const ESM::Spell* spell =

View file

@ -15,7 +15,7 @@ namespace MWGui
class HUD : public OEngine::GUI::Layout, public LocalMapBase class HUD : public OEngine::GUI::Layout, public LocalMapBase
{ {
public: public:
HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop); HUD(int fpsLevel, DragAndDrop* dragAndDrop);
virtual ~HUD(); virtual ~HUD();
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value); void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
void setFPS(float fps); void setFPS(float fps);
@ -47,7 +47,6 @@ namespace MWGui
void setCrosshairVisible(bool visible); void setCrosshairVisible(bool visible);
void onFrame(float dt); void onFrame(float dt);
void onResChange(int width, int height);
void setCellName(const std::string& cellName); void setCellName(const std::string& cellName);

View file

@ -37,6 +37,7 @@ namespace MWGui
, mLastYSize(0) , mLastYSize(0)
, mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr())) , mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()))
, mPreviewDirty(true) , mPreviewDirty(true)
, mPreviewResize(true)
, mDragAndDrop(dragAndDrop) , mDragAndDrop(dragAndDrop)
, mSelectedItem(-1) , mSelectedItem(-1)
, mGuiMode(GM_Inventory) , mGuiMode(GM_Inventory)
@ -91,8 +92,18 @@ namespace MWGui
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
mSortModel = new SortFilterItemModel(mTradeModel); mSortModel = new SortFilterItemModel(mTradeModel);
mItemView->setModel(mSortModel); 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.reset(new MWRender::InventoryPreview(mPtr));
mPreview->setup(); mPreview->setup();
mPreviewDirty = true;
mPreviewResize = true;
} }
void InventoryWindow::setGuiMode(GuiMode mode) void InventoryWindow::setGuiMode(GuiMode mode)
@ -125,7 +136,7 @@ namespace MWGui
Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height); Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height);
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()) if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
mPreviewDirty = true; mPreviewResize = true;
mMainWidget->setPosition(pos); mMainWidget->setPosition(pos);
mMainWidget->setSize(size); mMainWidget->setSize(size);
@ -333,7 +344,7 @@ namespace MWGui
{ {
mLastXSize = mMainWidget->getSize().width; mLastXSize = mMainWidget->getSize().width;
mLastYSize = mMainWidget->getSize().height; mLastYSize = mMainWidget->getSize().height;
mPreviewDirty = true; mPreviewResize = true;
} }
} }
@ -366,6 +377,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); 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) void InventoryWindow::useItem(const MWWorld::Ptr &ptr)
{ {
const std::string& script = ptr.getClass().getScript(ptr); const std::string& script = ptr.getClass().getScript(ptr);
@ -403,9 +420,13 @@ namespace MWGui
else else
mSkippedToEquip = ptr; mSkippedToEquip = ptr;
mItemView->update(); if (isVisible())
{
mItemView->update();
notifyContentChanged(); notifyContentChanged();
}
// else: will be updated in open()
} }
void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)
@ -491,16 +512,23 @@ namespace MWGui
void InventoryWindow::doRenderUpdate () void InventoryWindow::doRenderUpdate ()
{ {
if (mPreviewDirty) mPreview->onFrame();
if (mPreviewResize)
{ {
mPreviewDirty = false; mPreviewResize = false;
MyGUI::IntSize size = mAvatarImage->getSize(); MyGUI::IntSize size = mAvatarImage->getSize();
mPreview->resize(size.width, size.height);
mPreview->update (size.width, size.height);
mAvatarImage->setImageTexture("CharacterPreview"); mAvatarImage->setImageTexture("CharacterPreview");
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height))); 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))); 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}: " mArmorRating->setCaptionWithReplacing ("#{sArmor}: "
+ boost::lexical_cast<std::string>(static_cast<int>(mPtr.getClass().getArmorRating(mPtr)))); + boost::lexical_cast<std::string>(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));

View file

@ -53,6 +53,7 @@ namespace MWGui
DragAndDrop* mDragAndDrop; DragAndDrop* mDragAndDrop;
bool mPreviewDirty; bool mPreviewDirty;
bool mPreviewResize;
int mSelectedItem; int mSelectedItem;
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
@ -98,6 +99,7 @@ namespace MWGui
void onFilterChanged(MyGUI::Widget* _sender); void onFilterChanged(MyGUI::Widget* _sender);
void onAvatarClicked(MyGUI::Widget* _sender); void onAvatarClicked(MyGUI::Widget* _sender);
void onPinToggled(); void onPinToggled();
void onTitleDoubleClicked();
void updateEncumbranceBar(); void updateEncumbranceBar();
void notifyContentChanged(); void notifyContentChanged();

View file

@ -3,6 +3,8 @@
#include <MyGUI_FactoryManager.h> #include <MyGUI_FactoryManager.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <components/misc/resourcehelpers.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
namespace MWGui namespace MWGui
@ -63,15 +65,7 @@ namespace MWGui
void ItemWidget::setIcon(const MWWorld::Ptr &ptr) void ItemWidget::setIcon(const MWWorld::Ptr &ptr)
{ {
// image setIcon(Misc::ResourceHelpers::correctIconPath(ptr.getClass().getInventoryIcon(ptr)));
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);
} }

View file

@ -123,6 +123,9 @@ namespace
getPage (LeftBookPage)->adviseLinkClicked (callback); getPage (LeftBookPage)->adviseLinkClicked (callback);
getPage (RightBookPage)->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 (); 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) void notifyNextPage(MyGUI::Widget* _sender)
{ {
if (!mStates.empty ()) if (!mStates.empty ())
@ -509,7 +520,7 @@ namespace
{ {
unsigned int & page = mStates.top ().mPage; unsigned int & page = mStates.top ().mPage;
if(page > 0) if(page >= 2)
{ {
page -= 2; page -= 2;
updateShowingPages (); updateShowingPages ();

View file

@ -30,6 +30,8 @@ namespace MWGui
, mProgress(0) , mProgress(0)
, mVSyncWasEnabled(false) , mVSyncWasEnabled(false)
{ {
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
getWidget(mLoadingText, "LoadingText"); getWidget(mLoadingText, "LoadingText");
getWidget(mProgressBar, "ProgressBar"); getWidget(mProgressBar, "ProgressBar");
@ -56,13 +58,6 @@ namespace MWGui
mBackgroundImage->setVisible(visible); 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() void LoadingScreen::loadingOn()
{ {
// Early-out if already on // Early-out if already on

View file

@ -35,8 +35,6 @@ namespace MWGui
void setLoadingProgress (const std::string& stage, int depth, int current, int total); void setLoadingProgress (const std::string& stage, int depth, int current, int total);
void loadingDone(); void loadingDone();
void onResChange(int w, int h);
void updateWindow(Ogre::RenderWindow* rw) { mWindow = rw; } void updateWindow(Ogre::RenderWindow* rw) { mWindow = rw; }
private: private:

View file

@ -536,6 +536,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);
} }
void MapWindow::onTitleDoubleClicked()
{
if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);
}
void MapWindow::open() void MapWindow::open()
{ {
// force markers to foreground // force markers to foreground

View file

@ -150,6 +150,7 @@ namespace MWGui
protected: protected:
virtual void onPinToggled(); virtual void onPinToggled();
virtual void onTitleDoubleClicked();
virtual void notifyPlayerUpdate(); virtual void notifyPlayerUpdate();
virtual void notifyMapChanged(); virtual void notifyMapChanged();

View file

@ -3,6 +3,7 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/esm/quickkeys.hpp> #include <components/esm/quickkeys.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -226,12 +227,9 @@ namespace MWGui
esmStore.get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID); esmStore.get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID);
std::string path = effect->mIcon; std::string path = effect->mIcon;
int slashPos = path.find("\\"); int slashPos = path.rfind('\\');
path.insert(slashPos+1, "b_"); path.insert(slashPos+1, "b_");
path = std::string("icons\\") + path; path = Misc::ResourceHelpers::correctIconPath(path);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(path); button->setIcon(path);

View file

@ -181,6 +181,10 @@ void Recharge::onItemClicked(MyGUI::Widget *sender)
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->getString(); std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->getString();
message = boost::str(boost::format(message) % gem.getClass().getName(gem)); message = boost::str(boost::format(message) % gem.getClass().getName(gem));
MWBase::Environment::get().getWindowManager()->messageBox(message); MWBase::Environment::get().getWindowManager()->messageBox(message);
// special case: readd Azura's Star
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
} }
updateView(); updateView();

View file

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

View file

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

View file

@ -2,6 +2,8 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -143,13 +145,7 @@ namespace MWGui
void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect) void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect)
{ {
std::string icon = effect->mIcon; mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
icon[icon.size()-3] = 'd';
icon[icon.size()-2] = 'd';
icon[icon.size()-1] = 's';
icon = "icons\\" + icon;
mEffectImage->setImageTexture (icon);
mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (effect->mIndex)+"}"); mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (effect->mIndex)+"}");
@ -391,6 +387,14 @@ namespace MWGui
void SpellCreationDialog::notifyEffectsChanged () void SpellCreationDialog::notifyEffectsChanged ()
{ {
if (mEffects.empty())
{
mMagickaCost->setCaption("0");
mPriceLabel->setCaption("0");
mSuccessChance->setCaption("0");
return;
}
float y = 0; float y = 0;
const MWWorld::ESMStore &store = const MWWorld::ESMStore &store =
@ -398,7 +402,7 @@ namespace MWGui
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{ {
float x = 0.5 * it->mMagnMin + it->mMagnMax; float x = 0.5 * (it->mMagnMin + it->mMagnMax);
const ESM::MagicEffect* effect = const ESM::MagicEffect* effect =
store.get<ESM::MagicEffect>().find(it->mEffectID); store.get<ESM::MagicEffect>().find(it->mEffectID);
@ -413,7 +417,7 @@ namespace MWGui
y += x * fEffectCostMult; y += x * fEffectCostMult;
y = std::max(1.f,y); y = std::max(1.f,y);
if (effect->mData.mFlags & ESM::MagicEffect::CastTarget) if (it->mRange == ESM::RT_Target)
y *= 1.5; y *= 1.5;
} }
@ -520,16 +524,24 @@ namespace MWGui
void EffectEditorBase::onSelectAttribute () void EffectEditorBase::onSelectAttribute ()
{ {
mAddEffectDialog.setVisible(true); const ESM::MagicEffect* effect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
mAddEffectDialog.newEffect(effect);
mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId()); mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId());
mAddEffectDialog.setVisible(true);
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);
mSelectAttributeDialog = 0; mSelectAttributeDialog = 0;
} }
void EffectEditorBase::onSelectSkill () void EffectEditorBase::onSelectSkill ()
{ {
const ESM::MagicEffect* effect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
mAddEffectDialog.newEffect(effect);
mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId());
mAddEffectDialog.setVisible(true); mAddEffectDialog.setVisible(true);
mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId ());
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog); MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);
mSelectSkillDialog = 0; mSelectSkillDialog = 0;
} }
@ -554,11 +566,10 @@ namespace MWGui
} }
int buttonId = *sender->getUserData<int>(); int buttonId = *sender->getUserData<int>();
short effectId = mButtonMapping[buttonId]; mSelectedKnownEffectId = mButtonMapping[buttonId];
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{ {
if (it->mEffectID == effectId) if (it->mEffectID == mSelectedKnownEffectId)
{ {
MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}");
return; return;
@ -566,9 +577,7 @@ namespace MWGui
} }
const ESM::MagicEffect* effect = const ESM::MagicEffect* effect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId); MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
mAddEffectDialog.newEffect (effect);
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
{ {
@ -588,6 +597,7 @@ namespace MWGui
} }
else else
{ {
mAddEffectDialog.newEffect(effect);
mAddEffectDialog.setVisible(true); mAddEffectDialog.setVisible(true);
} }
} }

View file

@ -99,6 +99,7 @@ namespace MWGui
SelectSkillDialog* mSelectSkillDialog; SelectSkillDialog* mSelectSkillDialog;
int mSelectedEffect; int mSelectedEffect;
short mSelectedKnownEffectId;
std::vector<ESM::ENAMstruct> mEffects; std::vector<ESM::ENAMstruct> mEffects;

View file

@ -5,6 +5,8 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -133,13 +135,7 @@ namespace MWGui
("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default); ("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);
mWidgetMap[it->first] = image; mWidgetMap[it->first] = image;
std::string icon = effect->mIcon; image->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
icon[icon.size()-3] = 'd';
icon[icon.size()-2] = 'd';
icon[icon.size()-1] = 's';
icon = "icons\\" + icon;
image->setImageTexture(icon);
std::string name = ESM::MagicEffect::effectIdToString (it->first); std::string name = ESM::MagicEffect::effectIdToString (it->first);

View file

@ -45,6 +45,7 @@ namespace MWGui
, NoDrop(drag, mMainWidget) , NoDrop(drag, mMainWidget)
, mHeight(0) , mHeight(0)
, mWidth(0) , mWidth(0)
, mWindowSize(mMainWidget->getSize())
{ {
mSpellIcons = new SpellIcons(); mSpellIcons = new SpellIcons();
@ -66,6 +67,12 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned);
} }
void SpellWindow::onTitleDoubleClicked()
{
if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);
}
void SpellWindow::open() void SpellWindow::open()
{ {
updateSpells(); updateSpells();
@ -302,7 +309,11 @@ namespace MWGui
void SpellWindow::onWindowResize(MyGUI::Window* _sender) void SpellWindow::onWindowResize(MyGUI::Window* _sender)
{ {
updateSpells(); if (mMainWidget->getSize() != mWindowSize)
{
mWindowSize = mMainWidget->getSize();
updateSpells();
}
} }
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)

View file

@ -29,6 +29,8 @@ namespace MWGui
int mHeight; int mHeight;
int mWidth; int mWidth;
MyGUI::IntSize mWindowSize;
std::string mSpellToDelete; std::string mSpellToDelete;
void addGroup(const std::string& label, const std::string& label2); void addGroup(const std::string& label, const std::string& label2);
@ -42,6 +44,7 @@ namespace MWGui
void onDeleteSpellAccept(); void onDeleteSpellAccept();
virtual void onPinToggled(); virtual void onPinToggled();
virtual void onTitleDoubleClicked();
virtual void open(); virtual void open();
SpellIcons* mSpellIcons; SpellIcons* mSpellIcons;

View file

@ -591,4 +591,10 @@ namespace MWGui
{ {
MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned);
} }
void StatsWindow::onTitleDoubleClicked()
{
if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);
}
} }

View file

@ -74,6 +74,7 @@ namespace MWGui
protected: protected:
virtual void onPinToggled(); virtual void onPinToggled();
virtual void onTitleDoubleClicked();
}; };
} }
#endif #endif

View file

@ -4,6 +4,8 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -319,20 +321,6 @@ namespace MWGui
return tooltipSize; 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) MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
{ {
mDynamicToolTipBox->setVisible(true); mDynamicToolTipBox->setVisible(true);
@ -371,8 +359,7 @@ namespace MWGui
const int imageCaptionHPadding = (caption != "" ? 8 : 0); const int imageCaptionHPadding = (caption != "" ? 8 : 0);
const int imageCaptionVPadding = (caption != "" ? 4 : 0); const int imageCaptionVPadding = (caption != "" ? 4 : 0);
std::string realImage = "icons\\" + image; std::string realImage = Misc::ResourceHelpers::correctIconPath(image);
findImageExtension(realImage);
MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
captionWidget->setProperty("Static", "true"); captionWidget->setProperty("Static", "true");
@ -644,9 +631,7 @@ namespace MWGui
widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "BirthSignToolTip"); widget->setUserString("ToolTipLayout", "BirthSignToolTip");
std::string image = sign->mTexture; widget->setUserString("ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture));
image.replace(image.size()-3, 3, "dds");
widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image);
std::string text; std::string text;
text += sign->mName; text += sign->mName;
@ -739,15 +724,9 @@ namespace MWGui
const std::string &name = ESM::MagicEffect::effectIdToString (id); const std::string &name = ESM::MagicEffect::effectIdToString (id);
std::string icon = effect->mIcon; std::string icon = effect->mIcon;
int slashPos = icon.rfind('\\');
int slashPos = icon.find("\\");
icon.insert(slashPos+1, "b_"); icon.insert(slashPos+1, "b_");
icon = Misc::ResourceHelpers::correctIconPath(icon);
icon[icon.size()-3] = 'd';
icon[icon.size()-2] = 'd';
icon[icon.size()-1] = 's';
icon = "icons\\" + icon;
std::vector<std::string> schools; std::vector<std::string> schools;
schools.push_back ("#{sSchoolAlteration}"); schools.push_back ("#{sSchoolAlteration}");

View file

@ -84,8 +84,6 @@ namespace MWGui
MWWorld::Ptr mFocusObject; MWWorld::Ptr mFocusObject;
void findImageExtension(std::string& image);
MyGUI::IntSize getToolTipViaPtr (bool image=true); MyGUI::IntSize getToolTipViaPtr (bool image=true);
///< @return requested tooltip size ///< @return requested tooltip size

View file

@ -308,7 +308,7 @@ namespace MWGui
it->mBase.getClass().getValue(it->mBase) it->mBase.getClass().getValue(it->mBase)
* it->mCount); * it->mCount);
onCancelButtonClicked(mCancelButton); onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return; return;
} }
} }

View file

@ -2,12 +2,11 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <openengine/ogre/fader.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
@ -164,14 +163,14 @@ namespace MWGui
// go back to game mode // go back to game mode
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
// advance time // advance time
MWBase::Environment::get().getWorld ()->advanceTime (2); MWBase::Environment::get().getWorld ()->advanceTime (2);
MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getMechanicsManager()->rest(false);
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; mFadeTimeRemaining = 0.5;
} }
@ -183,6 +182,6 @@ namespace MWGui
mFadeTimeRemaining -= dt; mFadeTimeRemaining -= dt;
if (mFadeTimeRemaining <= 0) if (mFadeTimeRemaining <= 0)
MWBase::Environment::get().getWorld ()->getFader()->fadeIn(0.25); MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25);
} }
} }

View file

@ -4,13 +4,12 @@
#include <OgreVector3.h> #include <OgreVector3.h>
#include <libs/openengine/ogre/fader.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
@ -156,7 +155,7 @@ namespace MWGui
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
npcStats.setGoldPool(npcStats.getGoldPool() + price); npcStats.setGoldPool(npcStats.getGoldPool() + price);
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
ESM::Position pos = *_sender->getUserData<ESM::Position>(); ESM::Position pos = *_sender->getUserData<ESM::Position>();
std::string cellname = _sender->getUserString("Destination"); std::string cellname = _sender->getUserString("Destination");
bool interior = _sender->getUserString("interior") == "y"; bool interior = _sender->getUserString("interior") == "y";
@ -173,14 +172,15 @@ namespace MWGui
MWBase::Environment::get().getWorld()->advanceTime(hours); MWBase::Environment::get().getWorld()->advanceTime(hours);
} }
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
// Teleports any followers, too. // Teleports any followers, too.
MWWorld::ActionTeleport action(interior ? cellname : "", pos); MWWorld::ActionTeleport action(interior ? cellname : "", pos);
action.execute(player); action.execute(player);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0);
MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1);
} }
void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender)

View file

@ -2,8 +2,6 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <libs/openengine/ogre/fader.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -128,7 +126,7 @@ namespace MWGui
MWBase::Environment::get().getStateManager()->quickSave("Autosave"); MWBase::Environment::get().getStateManager()->quickSave("Autosave");
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
world->getFader ()->fadeOut(0.2); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2);
setVisible(false); setVisible(false);
mProgressBar.setVisible (true); mProgressBar.setVisible (true);
@ -179,8 +177,7 @@ namespace MWGui
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified()) bool full = (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
&& (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
&& (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified()); && (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());
MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player); MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player);
bool werewolf = npcstats.isWerewolf(); bool werewolf = npcstats.isWerewolf();
@ -243,7 +240,7 @@ namespace MWGui
void WaitDialog::stopWaiting () void WaitDialog::stopWaiting ()
{ {
MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2);
mProgressBar.setVisible (false); mProgressBar.setVisible (false);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed);

View file

@ -65,6 +65,7 @@
#include "videowidget.hpp" #include "videowidget.hpp"
#include "backgroundimage.hpp" #include "backgroundimage.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "screenfader.hpp"
namespace MWGui namespace MWGui
{ {
@ -72,7 +73,7 @@ namespace MWGui
WindowManager::WindowManager( WindowManager::WindowManager(
const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre,
const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts,
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding) Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts)
: mConsoleOnlyScripts(consoleOnlyScripts) : mConsoleOnlyScripts(consoleOnlyScripts)
, mGuiManager(NULL) , mGuiManager(NULL)
, mRendering(ogre) , mRendering(ogre)
@ -112,6 +113,7 @@ namespace MWGui
, mCompanionWindow(NULL) , mCompanionWindow(NULL)
, mVideoBackground(NULL) , mVideoBackground(NULL)
, mVideoWidget(NULL) , mVideoWidget(NULL)
, mScreenFader(NULL)
, mTranslationDataStorage (translationDataStorage) , mTranslationDataStorage (translationDataStorage)
, mCharGen(NULL) , mCharGen(NULL)
, mInputBlocker(NULL) , mInputBlocker(NULL)
@ -146,7 +148,7 @@ namespace MWGui
// Load fonts // Load fonts
FontLoader fontLoader (encoding); FontLoader fontLoader (encoding);
fontLoader.loadAllFonts(); fontLoader.loadAllFonts(exportFonts);
//Register own widgets with MyGUI //Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
@ -171,18 +173,14 @@ namespace MWGui
ItemWidget::registerComponents(); ItemWidget::registerComponents();
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerRepeatClick>("Controller"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerRepeatClick>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer"); MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::ResourceManager::getInstance().load("core.xml"); MyGUI::ResourceManager::getInstance().load("core.xml");
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); 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 = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ());
mLoadingScreen->onResChange (w,h);
//set up the hardware cursor manager //set up the hardware cursor manager
mCursorManager = new SFO::SDLCursorManager(); mCursorManager = new SFO::SDLCursorManager();
@ -192,7 +190,6 @@ namespace MWGui
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
//SDL_ShowCursor(false);
mCursorManager->setEnabled(true); mCursorManager->setEnabled(true);
@ -217,13 +214,7 @@ namespace MWGui
int w = MyGUI::RenderManager::getInstance().getViewSize().width; int w = MyGUI::RenderManager::getInstance().getViewSize().width;
int h = MyGUI::RenderManager::getInstance().getViewSize().height; 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 = new DragAndDrop();
mDragAndDrop->mIsOnDragAndDrop = false;
mDragAndDrop->mDraggedWidget = 0;
mDragAndDrop->mDragAndDropWidget = dragAndDropWidget;
mRecharge = new Recharge(); mRecharge = new Recharge();
mMenu = new MainMenu(w,h); mMenu = new MainMenu(w,h);
@ -245,7 +236,7 @@ namespace MWGui
trackWindow(mDialogueWindow, "dialogue"); trackWindow(mDialogueWindow, "dialogue");
mContainerWindow = new ContainerWindow(mDragAndDrop); mContainerWindow = new ContainerWindow(mDragAndDrop);
trackWindow(mContainerWindow, "container"); trackWindow(mContainerWindow, "container");
mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); mHud = new HUD(mShowFPSLevel, mDragAndDrop);
mToolTips = new ToolTips(); mToolTips = new ToolTips();
mScrollWindow = new ScrollWindow(); mScrollWindow = new ScrollWindow();
mBookWindow = new BookWindow(); mBookWindow = new BookWindow();
@ -267,8 +258,9 @@ namespace MWGui
mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); mSoulgemDialog = new SoulgemDialog(mMessageBoxManager);
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
trackWindow(mCompanionWindow, "companion"); trackWindow(mCompanionWindow, "companion");
mScreenFader = new ScreenFader();
mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Overlay"); mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Stretch,"Overlay");
mHud->setVisible(mHudEnabled); mHud->setVisible(mHudEnabled);
@ -357,6 +349,7 @@ namespace MWGui
delete mCursorManager; delete mCursorManager;
delete mRecharge; delete mRecharge;
delete mCompanionWindow; delete mCompanionWindow;
delete mScreenFader;
cleanupGarbage(); cleanupGarbage();
@ -858,6 +851,8 @@ namespace MWGui
mCompanionWindow->checkReferenceAvailable(); mCompanionWindow->checkReferenceAvailable();
mConsole->checkReferenceAvailable(); mConsole->checkReferenceAvailable();
mCompanionWindow->onFrame(); mCompanionWindow->onFrame();
mScreenFader->update(frameDuration);
} }
void WindowManager::changeCell(MWWorld::CellStore* cell) void WindowManager::changeCell(MWWorld::CellStore* cell)
@ -1019,7 +1014,6 @@ namespace MWGui
{ {
sizeVideo(x, y); sizeVideo(x, y);
mGuiManager->windowResized(); mGuiManager->windowResized();
mLoadingScreen->onResChange (x,y);
if (!mHud) if (!mHud)
return; // UI not initialized yet return; // UI not initialized yet
@ -1033,7 +1027,6 @@ namespace MWGui
it->first->setSize(size); it->first->setSize(size);
} }
mHud->onResChange(x, y);
mConsole->onResChange(x, y); mConsole->onResChange(x, y);
mMenu->onResChange(x, y); mMenu->onResChange(x, y);
mSettingsWindow->center(); mSettingsWindow->center();
@ -1042,8 +1035,6 @@ namespace MWGui
mBookWindow->center(); mBookWindow->center();
mQuickKeysMenu->center(); mQuickKeysMenu->center();
mSpellBuyingWindow->center(); mSpellBuyingWindow->center();
mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
mInputBlocker->setSize(MyGUI::IntSize(x,y));
} }
void WindowManager::pushGuiMode(GuiMode mode) void WindowManager::pushGuiMode(GuiMode mode)
@ -1688,4 +1679,24 @@ namespace MWGui
updateVisible(); 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);
}
} }

View file

@ -86,6 +86,7 @@ namespace MWGui
class CompanionWindow; class CompanionWindow;
class VideoWidget; class VideoWidget;
class WindowModal; class WindowModal;
class ScreenFader;
class WindowManager : public MWBase::WindowManager class WindowManager : public MWBase::WindowManager
{ {
@ -96,7 +97,7 @@ namespace MWGui
WindowManager(const Compiler::Extensions& extensions, int fpsLevel, WindowManager(const Compiler::Extensions& extensions, int fpsLevel,
OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath,
const std::string& cacheDir, bool consoleOnlyScripts, const std::string& cacheDir, bool consoleOnlyScripts,
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding); Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts);
virtual ~WindowManager(); virtual ~WindowManager();
void initUI(); void initUI();
@ -324,6 +325,15 @@ namespace MWGui
virtual void pinWindow (MWGui::GuiWindow window); 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: private:
bool mConsoleOnlyScripts; bool mConsoleOnlyScripts;
@ -373,6 +383,7 @@ namespace MWGui
CompanionWindow* mCompanionWindow; CompanionWindow* mCompanionWindow;
MyGUI::ImageBox* mVideoBackground; MyGUI::ImageBox* mVideoBackground;
VideoWidget* mVideoWidget; VideoWidget* mVideoWidget;
ScreenFader* mScreenFader;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
Cursor* mSoftwareCursor; Cursor* mSoftwareCursor;

View file

@ -11,6 +11,17 @@ namespace MWGui
mPinButton = window->getSkinWidget ("Button"); mPinButton = window->getSkinWidget ("Button");
mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); 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<MyGUI::Button>();
}
if (button)
button->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WindowPinnableBase::onDoubleClick);
} }
void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
@ -25,6 +36,11 @@ namespace MWGui
onPinToggled(); onPinToggled();
} }
void WindowPinnableBase::onDoubleClick(MyGUI::Widget *_sender)
{
onTitleDoubleClicked();
}
void WindowPinnableBase::setPinned(bool pinned) void WindowPinnableBase::setPinned(bool pinned)
{ {
if (pinned != mPinned) if (pinned != mPinned)

View file

@ -17,9 +17,11 @@ namespace MWGui
private: private:
void onPinButtonClicked(MyGUI::Widget* _sender); void onPinButtonClicked(MyGUI::Widget* _sender);
void onDoubleClick(MyGUI::Widget* _sender);
protected: protected:
virtual void onPinToggled() = 0; virtual void onPinToggled() = 0;
virtual void onTitleDoubleClicked() = 0;
MyGUI::Widget* mPinButton; MyGUI::Widget* mPinButton;
bool mPinned; bool mPinned;

View file

@ -805,16 +805,16 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
if((!MWBase::Environment::get().getWindowManager()->isGuiMode() if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
|| MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue) && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()
&& 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().getSoundManager()->playSound ("book open", 1.0, 1.0);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); 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);
} }
} }

View file

@ -89,6 +89,58 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
return false; 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<MWMechanics::AiPackage*>::const_iterator it;
for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
{
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
dynamic_cast<MWMechanics::AiFollow*>(*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) void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka)
{ {
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); 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<ESM::Static>()
.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<int>& graveyard = casterStats.getSummonedCreatureGraveyard();
graveyard.push_back(creatureActorId);
}
}
} }
namespace MWMechanics namespace MWMechanics
@ -268,7 +344,8 @@ namespace MWMechanics
void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)
{ {
CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature);
if (creatureStats.isDead())
return;
MagicEffects now = creatureStats.getSpells().getMagicEffects(); MagicEffects now = creatureStats.getSpells().getMagicEffects();
if (creature.getTypeName()==typeid (ESM::NPC).name()) if (creature.getTypeName()==typeid (ESM::NPC).name())
@ -290,17 +367,17 @@ namespace MWMechanics
{ {
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getModified();
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getBase(); int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getModified();
int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getBase(); int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getModified();
double magickaFactor = 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<float> magicka = creatureStats.getMagicka(); DynamicStat<float> magicka = creatureStats.getMagicka();
float diff = (static_cast<int>(intelligence + magickaFactor*intelligence)) - magicka.getBase(); float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();
magicka.modify(diff); magicka.modify(diff);
creatureStats.setMagicka(magicka); creatureStats.setMagicka(magicka);
@ -612,9 +689,9 @@ namespace MWMechanics
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
} }
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
for (std::map<int, std::string>::iterator it = summonMap.begin(); it != summonMap.end(); ++it) for (std::map<int, std::string>::iterator it = summonMap.begin(); it != summonMap.end(); ++it)
{ {
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
bool found = creatureMap.find(it->first) != creatureMap.end(); bool found = creatureMap.find(it->first) != creatureMap.end();
int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude;
if (found != (magnitude > 0)) if (found != (magnitude > 0))
@ -665,34 +742,31 @@ namespace MWMechanics
} }
else else
{ {
// Summon lifetime has expired. Try to delete the creature. // Effect has ended
int actorId = creatureMap[it->first]; std::map<int, int>::iterator foundCreature = creatureMap.find(it->first);
creatureMap.erase(it->first); cleanupSummonedCreature(creatureStats, foundCreature->second);
creatureMap.erase(foundCreature);
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<ESM::Static>()
.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<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
graveyard.push_back(actorId);
}
} }
} }
} }
for (std::map<int, int>::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<int>& graveyard = creatureStats.getSummonedCreatureGraveyard(); std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); ) for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
{ {
@ -1005,7 +1079,10 @@ namespace MWMechanics
if (MWBase::Environment::get().getMechanicsManager()->isAIActive()) if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
{ {
if (timerUpdateAITargets == 0) if (timerUpdateAITargets == 0)
{ {
if (iter->first != player)
adjustCommandedActor(iter->first);
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{ {
if (it->first == iter->first || iter->first == player) // player is not AI-controlled if (it->first == iter->first || iter->first == player) // player is not AI-controlled
@ -1053,10 +1130,12 @@ namespace MWMechanics
//KnockedOutOneFrameLogic //KnockedOutOneFrameLogic
//Used for "OnKnockedOut" command //Used for "OnKnockedOut" command
//Put here to ensure that it's run for PRECISELY one frame. //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); 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.setKnockedDownOneFrame(false);
stats.setKnockedDownOverOneFrame(true); stats.setKnockedDownOverOneFrame(true);
} }

View file

@ -51,14 +51,17 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
float x = pos.pos[0] - tPos.pos[0]; float x = pos.pos[0] - tPos.pos[0];
float y = pos.pos[1] - tPos.pos[1]; 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().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
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);
//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<MWWorld::Ptr> actors; std::vector<MWWorld::Ptr> actors;
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(Ogre::Vector3(pos.pos[0],pos.pos[1],pos.pos[2]),100,actors); MWBase::Environment::get().getMechanicsManager()->getActorsInRange(Ogre::Vector3(pos.pos[0],pos.pos[1],pos.pos[2]),100,actors);
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); it++) { for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); it++) {

View file

@ -75,6 +75,7 @@ namespace MWMechanics
} }
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); 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 MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const leaderPos = actor.getRefData().getPosition().pos;

View file

@ -16,16 +16,16 @@
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) 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) 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) MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded)
: mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("") : 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; return TypeIdFollow;
} }
bool MWMechanics::AiFollow::isCommanded() const
{
return mCommanded;
}
void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::auto_ptr<ESM::AiSequence::AiFollow> follow(new ESM::AiSequence::AiFollow()); std::auto_ptr<ESM::AiSequence::AiFollow> follow(new ESM::AiSequence::AiFollow());
@ -109,6 +114,7 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co
follow->mRemainingDuration = mRemainingDuration; follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId; follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow; follow->mAlwaysFollow = mAlwaysFollow;
follow->mCommanded = mCommanded;
ESM::AiSequence::AiPackageContainer package; ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Follow; package.mType = ESM::AiSequence::Ai_Follow;
@ -120,6 +126,7 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mActorId(follow->mTargetId), mCellId(follow->mCellId) , mActorId(follow->mTargetId), mCellId(follow->mCellId)
, mCommanded(follow->mCommanded)
{ {
} }

View file

@ -27,7 +27,7 @@ namespace MWMechanics
/// Follow Actor for duration or until you arrive at a position in a cell /// 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); AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
/// Follow Actor indefinitively /// Follow Actor indefinitively
AiFollow(const std::string &ActorId); AiFollow(const std::string &ActorId, bool commanded=false);
AiFollow(const ESM::AiSequence::AiFollow* follow); AiFollow(const ESM::AiSequence::AiFollow* follow);
@ -44,10 +44,13 @@ namespace MWMechanics
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
bool isCommanded() const;
private: private:
/// This will make the actor always follow. /// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow; bool mAlwaysFollow;
bool mCommanded;
float mRemainingDuration; // Seconds float mRemainingDuration; // Seconds
float mX; float mX;
float mY; float mY;

View file

@ -82,6 +82,20 @@ std::list<AiPackage*>::const_iterator AiSequence::end() const
return mPackages.end(); return mPackages.end();
} }
void AiSequence::erase(std::list<AiPackage*>::const_iterator package)
{
// Not sure if manually terminated packages should trigger mDone, probably not?
for(std::list<AiPackage*>::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 bool AiSequence::isInCombat() const
{ {
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for(std::list<AiPackage*>::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) 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) if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
{ {
// Notify AiWander of our current position so we can return to it after combat finished // Notify AiWander of our current position so we can return to it after combat finished

View file

@ -54,6 +54,8 @@ namespace MWMechanics
std::list<AiPackage*>::const_iterator begin() const; std::list<AiPackage*>::const_iterator begin() const;
std::list<AiPackage*>::const_iterator end() const; std::list<AiPackage*>::const_iterator end() const;
void erase (std::list<AiPackage*>::const_iterator package);
/// Returns currently executing AiPackage type /// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/ /** \see enum AiPackage::TypeId **/
int getTypeId() const; int getTypeId() const;

View file

@ -395,22 +395,35 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
CharacterState walkState = runStateToWalkState(mMovementState); CharacterState walkState = runStateToWalkState(mMovementState);
const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState)); const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState));
anim = stateinfo->groupname; anim = stateinfo->groupname;
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(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;
}
} }
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, mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); 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 isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool flying = world->isFlying(mPtr); 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.getMovementVector(mPtr);
Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition); Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition);
if(vec.z > 0.0f) // to avoid slow-down when jumping 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.x = vecXY.x;
vec.y = vecXY.y; vec.y = vecXY.y;
} }
else else
vec.normalise(); vec.normalise();
if(mHitState != CharState_None && mJumpState == JumpState_None) if(mHitState != CharState_None && mJumpState == JumpState_None)
@ -1352,8 +1395,7 @@ void CharacterController::update(float duration)
} }
else else
{ {
if(!(vec.z > 0.0f)) mJumpState = JumpState_None;
mJumpState = JumpState_None;
vec.z = 0.0f; vec.z = 0.0f;
inJump = false; inJump = false;
@ -1482,6 +1524,8 @@ void CharacterController::update(float duration)
else if (mAnimation) else if (mAnimation)
mAnimation->updateEffects(duration); mAnimation->updateEffects(duration);
mSkipAnim = false; mSkipAnim = false;
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
} }

View file

@ -169,12 +169,12 @@ namespace MWMechanics
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
const MWWorld::Class &othercls = victim.getClass(); if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
if(!othercls.isActor()) // Can't hit non-actors // Can't hit non-actors or dead actors
return; {
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); reduceWeaponCondition(0.f, false, weapon, attacker);
if(otherstats.isDead()) // Can't hit dead actors
return; return;
}
if(attacker.getRefData().getHandle() == "player") if(attacker.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->setEnemy(victim); MWBase::Environment::get().getWindowManager()->setEnemy(victim);
@ -189,6 +189,7 @@ namespace MWMechanics
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f) if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
{ {
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false); victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
return; return;
} }
@ -209,6 +210,8 @@ namespace MWMechanics
damage *= fDamageStrengthBase + damage *= fDamageStrengthBase +
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1); (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
adjustWeaponDamage(damage, weapon);
reduceWeaponCondition(damage, true, weapon, attacker);
if(attacker.getRefData().getHandle() == "player") if(attacker.getRefData().getHandle() == "player")
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0); 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<ESM::GameSetting>().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);
}
}
} }

View file

@ -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); 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 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, void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
const Ogre::Vector3& hitPosition); 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. /// Applies damage to attacker based on the victim's elemental shields.
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); 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 #endif

View file

@ -227,7 +227,9 @@ namespace MWMechanics
Flag_ForceRun = 1, Flag_ForceRun = 1,
Flag_ForceSneak = 2, Flag_ForceSneak = 2,
Flag_Run = 4, Flag_Run = 4,
Flag_Sneak = 8 Flag_Sneak = 8,
Flag_ForceJump = 16,
Flag_ForceMoveJump = 32
}; };
enum Stance enum Stance
{ {

View file

@ -973,6 +973,9 @@ namespace MWMechanics
if (!it->getClass().isNpc()) if (!it->getClass().isNpc())
continue; continue;
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue;
// Will the witness report the crime? // Will the witness report the crime?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
{ {
@ -1071,6 +1074,9 @@ namespace MWMechanics
if (*it != victim && type == OT_Assault) if (*it != victim && type == OT_Assault)
aggression = iFightAttacking; aggression = iFightAttacking;
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue;
if (it->getClass().isClass(*it, "guard")) if (it->getClass().isClass(*it, "guard"))
{ {
// Mark as Alarmed for dialogue // 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<MWWorld::Ptr> 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) bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
{ {
if (observer.getClass().getCreatureStats(observer).isDead()) if (observer.getClass().getCreatureStats(observer).isDead())

View file

@ -119,6 +119,7 @@ namespace MWMechanics
OffenseType type, int arg=0); OffenseType type, int arg=0);
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0); 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 /// 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); 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 /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so

View file

@ -34,7 +34,6 @@ MWMechanics::NpcStats::NpcStats()
, mProfit(0) , mProfit(0)
, mTimeToStartDrowning(20.0) , mTimeToStartDrowning(20.0)
, mLastDrowningHit(0) , mLastDrowningHit(0)
, mLevelHealthBonus(0)
{ {
mSkillIncreases.resize (ESM::Attribute::Length, 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 // "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, // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance" // the Health increase is calculated from the increased Endurance"
mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->getFloat());
updateHealth();
setLevel(getLevel()+1); setLevel(getLevel()+1);
} }
@ -273,7 +271,7 @@ void MWMechanics::NpcStats::updateHealth()
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase(); const int strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(static_cast<int> (0.5 * (strength + endurance)) + mLevelHealthBonus); setHealth(static_cast<int> (0.5 * (strength + endurance)));
} }
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
@ -497,7 +495,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mTimeToStartDrowning = mTimeToStartDrowning; state.mTimeToStartDrowning = mTimeToStartDrowning;
state.mLastDrowningHit = mLastDrowningHit; state.mLastDrowningHit = mLastDrowningHit;
state.mLevelHealthBonus = mLevelHealthBonus;
} }
void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
@ -549,5 +546,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
mTimeToStartDrowning = state.mTimeToStartDrowning; mTimeToStartDrowning = state.mTimeToStartDrowning;
mLastDrowningHit = state.mLastDrowningHit; mLastDrowningHit = state.mLastDrowningHit;
mLevelHealthBonus = state.mLevelHealthBonus;
} }

View file

@ -52,8 +52,6 @@ namespace MWMechanics
/// time since last hit from drowning /// time since last hit from drowning
float mLastDrowningHit; float mLastDrowningHit;
float mLevelHealthBonus;
public: public:
NpcStats(); NpcStats();
@ -104,7 +102,7 @@ namespace MWMechanics
void updateHealth(); void updateHealth();
///< Calculate health based on endurance and strength. ///< 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); void flagAsUsed (const std::string& id);

View file

@ -72,7 +72,7 @@ namespace MWMechanics
return schoolSkillMap[school]; 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); CreatureStats& stats = actor.getClass().getCreatureStats(actor);
@ -123,14 +123,17 @@ namespace MWMechanics
if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player") if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
castChance = 100; 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 = const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
return getSpellSuccessChance(spell, actor, effectiveSchool); return getSpellSuccessChance(spell, actor, effectiveSchool, cap);
} }
@ -184,6 +187,10 @@ namespace MWMechanics
float resisted = 0; float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) 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 resistance = getEffectResistanceAttribute(effectId, magicEffects);
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
@ -191,12 +198,13 @@ namespace MWMechanics
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa // 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()) if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
{ {
float castChance = getSpellSuccessChance(spell, caster); castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
if (castChance > 0)
x *= 50 / castChance;
} }
if (castChance > 0)
x *= 50 / castChance;
float roll = static_cast<float>(std::rand()) / RAND_MAX * 100; float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
@ -230,6 +238,44 @@ namespace MWMechanics
return -(resistance-100) / 100.f; 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<ESM::Creature>()->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) CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target)
: mCaster(caster) : mCaster(caster)
, mTarget(target) , mTarget(target)
@ -318,23 +364,8 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectIt->mEffectID); effectIt->mEffectID);
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer))
{
if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
continue; 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}");
continue;
}
// If player is healing someone, show the target's HP bar // If player is healing someone, show the target's HP bar
if (castByPlayer && target != caster if (castByPlayer && target != caster

View file

@ -26,11 +26,12 @@ namespace MWMechanics
* @param spell spell to cast * @param spell spell to cast
* @param actor calculate spell success chance for this actor (depends on actor's skills) * @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 * @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! * @param cap cap the result to 100%?
* @return success chance from 0 to 100 (in percent) * @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 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); 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 std::string& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);

View file

@ -17,6 +17,7 @@
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include <components/esm/loadench.hpp> #include <components/esm/loadench.hpp>
#include <components/esm/loadstat.hpp> #include <components/esm/loadstat.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <libs/openengine/ogre/lights.hpp> #include <libs/openengine/ogre/lights.hpp>
@ -467,7 +468,13 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
const std::string stop = groupname+": stop"; const std::string stop = groupname+": stop";
float starttime = std::numeric_limits<float>::max(); float starttime = std::numeric_limits<float>::max();
float stoptime = 0.0f; 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()); NifOgre::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin());
while(keyiter != keys.rend()) while(keyiter != keys.rend())
{ {
@ -476,8 +483,18 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
starttime = keyiter->first; starttime = keyiter->first;
break; break;
} }
else if(keyiter->second == loopstop || keyiter->second == stop) ++keyiter;
}
keyiter = keys.rbegin();
while(keyiter != keys.rend())
{
if (keyiter->second == stop)
stoptime = keyiter->first; stoptime = keyiter->first;
else if (keyiter->second == loopstop)
{
stoptime = keyiter->first;
break;
}
++keyiter; ++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) if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename)
return; return;
// fix texture extension to .dds std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture);
if (texture.size() > 4)
{
texture[texture.size()-3] = 'd';
texture[texture.size()-2] = 'd';
texture[texture.size()-1] = 's';
}
EffectParams params; EffectParams params;
params.mModelName = model; params.mModelName = model;
@ -1255,7 +1266,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex) for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{ {
Ogre::TextureUnitState* tus = pass->getTextureUnitState(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; tex<pass->getNumTextureUnitStates(); ++tex) for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{ {
Ogre::TextureUnitState* tus = pass->getTextureUnitState(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); addExtraLight(mInsert->getCreator(), mObjectRoot, light);
} }
void ObjectAnimation::removeParticles()
{
for (unsigned int i=0; i<mObjectRoot->mParticles.size(); ++i)
{
mObjectRoot->mSceneMgr->destroyParticleSystem(mObjectRoot->mParticles[i]);
}
mObjectRoot->mParticles.clear();
}
class FindEntityTransparency { class FindEntityTransparency {
public: public:

View file

@ -306,6 +306,7 @@ public:
virtual void attachArrow() {} virtual void attachArrow() {}
virtual void releaseArrow() {} virtual void releaseArrow() {}
void enableLights(bool enable); void enableLights(bool enable);
virtual void enableHeadAnimation(bool enable) {}
Ogre::AxisAlignedBox getWorldBounds(); Ogre::AxisAlignedBox getWorldBounds();
@ -323,6 +324,7 @@ public:
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model); ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model);
void addLight(const ESM::Light *light); void addLight(const ESM::Light *light);
void removeParticles();
bool canBatch() const; bool canBatch() const;
void fillBatch(Ogre::StaticGeometry *sg); void fillBatch(Ogre::StaticGeometry *sg);

View file

@ -37,6 +37,7 @@ namespace MWRender
, mViewport(NULL) , mViewport(NULL)
, mCamera(NULL) , mCamera(NULL)
, mNode(NULL) , mNode(NULL)
, mRecover(false)
{ {
mCharacter.mCell = NULL; mCharacter.mCell = NULL;
} }
@ -46,6 +47,16 @@ namespace MWRender
} }
void CharacterPreview::onFrame()
{
if (mRecover)
{
setupRenderTarget();
mRenderTarget->update();
mRecover = false;
}
}
void CharacterPreview::setup () void CharacterPreview::setup ()
{ {
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
@ -83,19 +94,10 @@ namespace MWRender
mCamera->setNearClipDistance (0.01); mCamera->setNearClipDistance (0.01);
mCamera->setFarClipDistance (1000); mCamera->setFarClipDistance (1000);
mTexture = Ogre::TextureManager::getSingleton().getByName (mName); mTexture = Ogre::TextureManager::getSingleton().createManual(mName,
if (mTexture.isNull ()) Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET, this);
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);
mRenderTarget = mTexture->getBuffer()->getRenderTarget(); setupRenderTarget();
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);
onSetup (); onSetup ();
} }
@ -107,6 +109,7 @@ namespace MWRender
mSceneMgr->destroyAllCameras(); mSceneMgr->destroyAllCameras();
delete mAnimation; delete mAnimation;
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
Ogre::TextureManager::getSingleton().remove(mName);
} }
} }
@ -127,12 +130,39 @@ namespace MWRender
onSetup(); onSetup();
} }
void CharacterPreview::loadResource(Ogre::Resource *resource)
{
Ogre::Texture* tex = dynamic_cast<Ogre::Texture*>(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) InventoryPreview::InventoryPreview(MWWorld::Ptr character)
: CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0))
, mSelectionBuffer(NULL) , mSelectionBuffer(NULL)
, mSizeX(0)
, mSizeY(0)
{ {
} }
@ -141,7 +171,21 @@ namespace MWRender
delete mSelectionBuffer; 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(); mAnimation->updateParts();
@ -197,15 +241,25 @@ namespace MWRender
mAnimation->runAnimation(0.0f); 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); 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(); mRenderTarget->update();
mSelectionBuffer->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) int InventoryPreview::getSlotSelected (int posX, int posY)
{ {
return mSelectionBuffer->getSelected (posX, posY); return mSelectionBuffer->getSelected (posX, posY);
@ -243,6 +297,7 @@ namespace MWRender
void RaceSelectionPreview::render() void RaceSelectionPreview::render()
{ {
mTexture->load();
mRenderTarget->update(); mRenderTarget->update();
} }

View file

@ -22,7 +22,7 @@ namespace MWRender
class NpcAnimation; class NpcAnimation;
class CharacterPreview class CharacterPreview : public Ogre::ManualResourceLoader
{ {
public: public:
CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name,
@ -34,6 +34,13 @@ namespace MWRender
virtual void rebuild(); virtual void rebuild();
void onFrame();
void loadResource(Ogre::Resource *resource);
private:
bool mRecover; // Texture content was lost and needs to be re-rendered
private: private:
CharacterPreview(const CharacterPreview&); CharacterPreview(const CharacterPreview&);
CharacterPreview& operator=(const CharacterPreview&); CharacterPreview& operator=(const CharacterPreview&);
@ -41,6 +48,8 @@ namespace MWRender
protected: protected:
virtual bool renderHeadOnly() { return false; } virtual bool renderHeadOnly() { return false; }
virtual void setupRenderTarget();
Ogre::TexturePtr mTexture; Ogre::TexturePtr mTexture;
Ogre::RenderTarget* mRenderTarget; Ogre::RenderTarget* mRenderTarget;
Ogre::Viewport* mViewport; Ogre::Viewport* mViewport;
@ -72,11 +81,17 @@ namespace MWRender
virtual ~InventoryPreview(); virtual ~InventoryPreview();
virtual void onSetup(); 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); int getSlotSelected(int posX, int posY);
protected:
virtual void setupRenderTarget();
private: private:
int mSizeX;
int mSizeY;
OEngine::Render::SelectionBuffer* mSelectionBuffer; OEngine::Render::SelectionBuffer* mSelectionBuffer;
}; };

View file

@ -1,5 +1,7 @@
#include "effectmanager.hpp" #include "effectmanager.hpp"
#include <components/misc/resourcehelpers.hpp>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreParticleSystem.h> #include <OgreParticleSystem.h>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
@ -21,15 +23,6 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition);
sceneNode->setScale(scale,scale,scale); 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); NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
// TODO: turn off shadow casting // TODO: turn off shadow casting
@ -44,6 +37,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
if (!textureOverride.empty()) if (!textureOverride.empty())
{ {
std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(textureOverride);
for(size_t i = 0;i < scene->mParticles.size(); ++i) for(size_t i = 0;i < scene->mParticles.size(); ++i)
{ {
Ogre::ParticleSystem* partSys = scene->mParticles[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; tex<pass->getNumTextureUnitStates(); ++tex) for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{ {
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
tus->setTextureName("textures\\" + textureOverride); tus->setTextureName(correctedTexture);
} }
} }
} }

View file

@ -98,6 +98,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
return; return;
Ogre::Image image; Ogre::Image image;
tex->load();
tex->convertToImage(image); tex->convertToImage(image);
Ogre::DataStreamPtr encoded = image.encode("tga"); Ogre::DataStreamPtr encoded = image.encode("tga");
@ -137,6 +138,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
return; return;
Ogre::Image image; Ogre::Image image;
tex->load();
tex->convertToImage(image); tex->convertToImage(image);
fog->mFogTextures.push_back(ESM::FogTexture()); 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 // copy to the texture
// NOTE: Could be optimized later. We actually only need to update the region that changed. // 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. // Not a big deal at the moment, the FoW is only 32x32 anyway.

View file

@ -67,11 +67,16 @@ namespace MWRender
{ {
HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference) 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(); resetBlinkTimer();
} }
void HeadAnimationTime::setEnabled(bool enabled)
{
mEnabled = enabled;
}
void HeadAnimationTime::resetBlinkTimer() void HeadAnimationTime::resetBlinkTimer()
{ {
mBlinkTimer = -(2 + (std::rand() / double(RAND_MAX*1.0)) * 6); mBlinkTimer = -(2 + (std::rand() / double(RAND_MAX*1.0)) * 6);
@ -79,6 +84,9 @@ void HeadAnimationTime::resetBlinkTimer()
void HeadAnimationTime::update(float dt) void HeadAnimationTime::update(float dt)
{ {
if (!mEnabled)
return;
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) if (MWBase::Environment::get().getSoundManager()->sayDone(mReference))
{ {
mBlinkTimer += dt; 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) void NpcAnimation::preRender(Ogre::Camera *camera)
{ {
Animation::preRender(camera); Animation::preRender(camera);

View file

@ -26,6 +26,8 @@ private:
float mBlinkTimer; float mBlinkTimer;
bool mEnabled;
float mValue; float mValue;
private: private:
void resetBlinkTimer(); void resetBlinkTimer();
@ -34,6 +36,8 @@ public:
void update(float dt); void update(float dt);
void setEnabled(bool enabled);
void setTalkStart(float value); void setTalkStart(float value);
void setTalkStop(float value); void setTalkStop(float value);
void setBlinkStart(float value); void setBlinkStart(float value);
@ -125,6 +129,8 @@ public:
ViewMode viewMode=VM_Normal); ViewMode viewMode=VM_Normal);
virtual ~NpcAnimation(); virtual ~NpcAnimation();
virtual void enableHeadAnimation(bool enable);
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
virtual Ogre::Vector3 runAnimation(float timepassed); virtual Ogre::Vector3 runAnimation(float timepassed);

View file

@ -73,14 +73,19 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
ptr.getRefData().setBaseNode(insert); 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); insertBegin(ptr);
std::auto_ptr<ObjectAnimation> anim(new ObjectAnimation(ptr, mesh)); std::auto_ptr<ObjectAnimation> anim(new ObjectAnimation(ptr, mesh));
if(ptr.getTypeName() == typeid(ESM::Light).name()) if(ptr.getTypeName() == typeid(ESM::Light).name())
anim->addLight(ptr.get<ESM::Light>()->mBase); {
if (addLight)
anim->addLight(ptr.get<ESM::Light>()->mBase);
else
anim->removeParticles();
}
if (!mesh.empty()) if (!mesh.empty())
{ {

View file

@ -41,7 +41,7 @@ public:
, mRootNode(NULL) , mRootNode(NULL)
{} {}
~Objects(){} ~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); ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr);

View file

@ -23,6 +23,7 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/terrain/defaultworld.hpp> #include <components/terrain/defaultworld.hpp>
#include <components/terrain/terraingrid.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -45,7 +46,6 @@
#include "globalmap.hpp" #include "globalmap.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "effectmanager.hpp" #include "effectmanager.hpp"
#include "terraingrid.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -212,11 +212,6 @@ MWRender::Actors& RenderingManager::getActors(){
return *mActors; return *mActors;
} }
OEngine::Render::Fader* RenderingManager::getFader()
{
return mRendering.getFader();
}
MWRender::Camera* RenderingManager::getCamera() const MWRender::Camera* RenderingManager::getCamera() const
{ {
return mCamera; return mCamera;
@ -345,7 +340,7 @@ void RenderingManager::update (float duration, bool paused)
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
int blind = player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; 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(); setAmbientMode();
// player position // player position
@ -1050,7 +1045,7 @@ void RenderingManager::enableTerrain(bool enable)
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
else 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); Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
Settings::Manager::getBool("split", "Shadows")); Settings::Manager::getBool("split", "Shadows"));

View file

@ -4,8 +4,6 @@
#include "sky.hpp" #include "sky.hpp"
#include "debugging.hpp" #include "debugging.hpp"
#include <openengine/ogre/fader.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -98,8 +96,6 @@ public:
void toggleLight(); void toggleLight();
bool toggleRenderMode(int mode); bool toggleRenderMode(int mode);
OEngine::Render::Fader* getFader();
void removeCell (MWWorld::CellStore *store); void removeCell (MWWorld::CellStore *store);
/// \todo this function should be removed later. Instead the rendering subsystems should track /// \todo this function should be removed later. Instead the rendering subsystems should track

View file

@ -19,6 +19,7 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/nifogre/ogrenifloader.hpp> #include <components/nifogre/ogrenifloader.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp> #include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
@ -294,7 +295,12 @@ void SkyManager::create()
// Stars // Stars
mAtmosphereNight = mRootNode->createChildSceneNode(); 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++) for(size_t i = 0, matidx = 0;i < objects->mEntities.size();i++)
{ {
Entity* night1_ent = objects->mEntities[i]; Entity* night1_ent = objects->mEntities[i];
@ -584,13 +590,13 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
if (mClouds != weather.mCloudTexture) 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; mClouds = weather.mCloudTexture;
} }
if (mNextClouds != weather.mNextCloudTexture) 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; mNextClouds = weather.mNextCloudTexture;
} }

View file

@ -1,21 +1,11 @@
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include <OgreVector2.h>
#include <OgreTextureManager.h>
#include <OgreStringConverter.h>
#include <OgreRenderSystem.h>
#include <OgreResourceGroupManager.h>
#include <OgreResourceBackgroundQueue.h>
#include <OgreRoot.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include <components/terrain/quadtreenode.hpp>
namespace MWRender namespace MWRender
{ {
@ -59,515 +49,4 @@ namespace MWRender
return esmStore.get<ESM::LandTexture>().find(index, plugin); return esmStore.get<ESM::LandTexture>().find(index, plugin);
} }
bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 &center, 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<float>().max();
max = -std::numeric_limits<float>().max();
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
{
for (int col=0; col<ESM::Land::LAND_SIZE; ++col)
{
float h = land->mLandData->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<float>& positions,
std::vector<float>& normals,
std::vector<Ogre::uint8>& 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; col<ESM::Land::LAND_SIZE; col += increment)
{
vertX = vertX_;
for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment)
{
positions[vertX*numVerts*3 + vertY*3] = ((vertX/float(numVerts-1)-0.5) * size * 8192);
positions[vertX*numVerts*3 + vertY*3 + 1] = ((vertY/float(numVerts-1)-0.5) * size * 8192);
if (land)
positions[vertX*numVerts*3 + vertY*3 + 2] = land->mLandData->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(x<ESM::Land::LAND_TEXTURE_SIZE);
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
ESM::Land* land = getLand(cellX, cellY);
if (land)
{
int tex = land->mLandData->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<Terrain::QuadTreeNode*>& nodes, std::vector<Terrain::LayerCollection>& out, bool pack)
{
for (std::vector<Terrain::QuadTreeNode*>::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<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
{
getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList);
}
void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter,
bool pack, std::vector<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &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<UniqueTextureId> 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<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
textureIndices.insert(id);
}
// Makes sure the indices are sorted, or rather,
// retrieved as sorted. This is important to keep the splatting order
// consistent across cells.
std::map<UniqueTextureId, int> textureIndicesMap;
for (std::set<UniqueTextureId>::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; i<numBlendmaps; ++i)
{
Ogre::PixelFormat format = pack ? Ogre::PF_A8B8G8R8 : Ogre::PF_A8;
Ogre::uchar* pData =
OGRE_ALLOC_T(Ogre::uchar, blendmapSize*blendmapSize*channels, Ogre::MEMCATEGORY_GENERAL);
memset(pData, 0, blendmapSize*blendmapSize*channels);
for (int y=0; y<blendmapSize; ++y)
{
for (int x=0; x<blendmapSize; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
int layerIndex = textureIndicesMap.find(id)->second;
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<int>(nX * factor);
int startY = static_cast<int>(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;
}
} }

View file

@ -1,15 +1,13 @@
#ifndef MWRENDER_TERRAINSTORAGE_H #ifndef MWRENDER_TERRAINSTORAGE_H
#define MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H
#include <components/esm/loadland.hpp> #include <components/esmterrain/storage.hpp>
#include <components/esm/loadltex.hpp>
#include <components/terrain/storage.hpp>
namespace MWRender 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: private:
virtual ESM::Land* getLand (int cellX, int cellY); virtual ESM::Land* getLand (int cellX, int cellY);
@ -18,92 +16,6 @@ namespace MWRender
/// Get bounds of the whole terrain in cell units /// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); 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<float>& positions,
std::vector<float>& normals,
std::vector<Ogre::uint8>& 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<Ogre::PixelBox>& blendmaps,
std::vector<Terrain::LayerInfo>& 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<Terrain::QuadTreeNode*>& nodes, std::vector<Terrain::LayerCollection>& 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 <texture id, plugin id>
typedef std::pair<short, short> UniqueTextureId;
UniqueTextureId getVtexIndexAt(int cellX, int cellY,
int x, int y);
std::string getTextureName (UniqueTextureId id);
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
Terrain::LayerInfo getLayerInfo(const std::string& texture);
// Non-virtual
void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
std::vector<Ogre::PixelBox>& blendmaps,
std::vector<Terrain::LayerInfo>& layerList);
}; };
} }

View file

@ -121,6 +121,34 @@ namespace MWScript
} }
}; };
template <class R>
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 R>
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 R> template <class R>
class OpGetForceSneak : public Interpreter::Opcode0 class OpGetForceSneak : public Interpreter::Opcode0
{ {
@ -169,27 +197,54 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision); interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision);
//Force Run
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun, interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun)); new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit, interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun)); new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit, interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun)); new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
//Force Jump
interpreter.installSegment5 (Compiler::Control::opcodeClearForceJump,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceJumpExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));
interpreter.installSegment5 (Compiler::Control::opcodeForceJump,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));
interpreter.installSegment5 (Compiler::Control::opcodeForceJumpExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceJump));
//Force MoveJump
interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJump,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceMoveJumpExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));
interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJump,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));
interpreter.installSegment5 (Compiler::Control::opcodeForceMoveJumpExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceMoveJump));
//Force Sneak
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit, interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak)); new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit, interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak)); new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning); interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning);
interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking); interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun<ImplicitRef>); interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun<ImplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun<ExplicitRef>); interpreter.installSegment5 (Compiler::Control::opcodeGetForceRunExplicit, new OpGetForceRun<ExplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceJump, new OpGetForceJump<ImplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceJumpExplicit, new OpGetForceJump<ExplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJump, new OpGetForceMoveJump<ImplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceMoveJumpExplicit, new OpGetForceMoveJump<ExplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak<ImplicitRef>); interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneak, new OpGetForceSneak<ImplicitRef>);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak<ExplicitRef>); interpreter.installSegment5 (Compiler::Control::opcodeGetForceSneakExplicit, new OpGetForceSneak<ExplicitRef>);
} }

View file

@ -413,5 +413,13 @@ op 0x2000254: HurtStandingActor
op 0x2000255: HurtStandingActor, explicit op 0x2000255: HurtStandingActor, explicit
op 0x2000256: HurtCollidingActor op 0x2000256: HurtCollidingActor
op 0x2000257: HurtCollidingActor, explicit 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

View file

@ -3,8 +3,6 @@
#include <cstdlib> #include <cstdlib>
#include <libs/openengine/ogre/fader.hpp>
#include <components/compiler/extensions.hpp> #include <components/compiler/extensions.hpp>
#include <components/compiler/opcodes.hpp> #include <components/compiler/opcodes.hpp>
#include <components/compiler/locals.hpp> #include <components/compiler/locals.hpp>
@ -248,7 +246,7 @@ namespace MWScript
Interpreter::Type_Float time = runtime[0].mFloat; Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop(); 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; Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop(); 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; Interpreter::Type_Float time = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWBase::Environment::get().getWorld()->getFader()->fadeTo(alpha, time); MWBase::Environment::get().getWindowManager()->fadeScreenTo(alpha, time);
} }
}; };

View file

@ -637,8 +637,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{ {
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = ""; std::string factionID = "";
if(arg0 >0) if(arg0 >0)
{ {
@ -647,6 +645,8 @@ namespace MWScript
} }
else else
{ {
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{ {
factionID = ""; factionID = "";
@ -913,8 +913,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{ {
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = ""; std::string factionID = "";
if(arg0 >0 ) if(arg0 >0 )
{ {
@ -923,6 +921,8 @@ namespace MWScript
} }
else else
{ {
MWWorld::Ptr ptr = R()(runtime);
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty()) if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{ {
factionID = ""; factionID = "";

View file

@ -327,7 +327,7 @@ namespace MWScript
} }
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
ptr.getClass().adjustPosition(ptr); ptr.getClass().adjustPosition(ptr, false);
} }
else else
{ {
@ -363,8 +363,19 @@ namespace MWScript
runtime.pop(); runtime.pop();
int cx,cy; int cx,cy;
MWBase::Environment::get().getWorld()->positionToIndex(x,y,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 ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
@ -374,7 +385,7 @@ namespace MWScript
zRot = zRot/60.; zRot = zRot/60.;
} }
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
ptr.getClass().adjustPosition(ptr); ptr.getClass().adjustPosition(ptr, false);
} }
}; };

View file

@ -322,7 +322,7 @@ namespace MWWorld
return std::make_pair (1, ""); return std::make_pair (1, "");
} }
void Class::adjustPosition(const MWWorld::Ptr& ptr) const void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{ {
} }

Some files were not shown because too many files have changed in this diff Show more