mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:45:32 +00:00
Merge branch 'scriptedit' into run
This commit is contained in:
commit
232c6c9ad3
160 changed files with 2574 additions and 1619 deletions
|
@ -54,6 +54,10 @@ endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
|||
# Macros
|
||||
include(OpenMWMacros)
|
||||
|
||||
if (ANDROID)
|
||||
set(CMAKE_FIND_ROOT_PATH ${OPENMW_DEPENDENCIES_DIR} "${CMAKE_FIND_ROOT_PATH}")
|
||||
endif (ANDROID)
|
||||
|
||||
# doxygen main page
|
||||
|
||||
configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp")
|
||||
|
@ -102,7 +106,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
|
|||
|
||||
set(OENGINE_OGRE
|
||||
${LIBDIR}/openengine/ogre/renderer.cpp
|
||||
${LIBDIR}/openengine/ogre/fader.cpp
|
||||
${LIBDIR}/openengine/ogre/lights.cpp
|
||||
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
|
||||
${LIBDIR}/openengine/ogre/imagerotate.cpp
|
||||
|
@ -233,7 +236,12 @@ if(OGRE_STATIC)
|
|||
list(APPEND OGRE_STATIC_PLUGINS ${Cg_LIBRARIES})
|
||||
endif(Cg_FOUND)
|
||||
|
||||
if (ANDROID)
|
||||
add_static_ogre_plugin(RenderSystem_GLES2)
|
||||
else ()
|
||||
add_static_ogre_plugin(RenderSystem_GL)
|
||||
endif ()
|
||||
|
||||
if(WIN32)
|
||||
add_static_ogre_plugin(RenderSystem_Direct3D9)
|
||||
endif(WIN32)
|
||||
|
|
|
@ -50,7 +50,7 @@ QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orien
|
|||
return QVariant();
|
||||
|
||||
if (role==ColumnBase::Role_Flags)
|
||||
return ColumnBase::Flag_Table;
|
||||
return section==0 ? ColumnBase::Flag_Table : 0;
|
||||
|
||||
switch (section)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,8 @@ void CSVWidget::PushButton::setExtendedToolTip()
|
|||
"<p>(left click to activate,"
|
||||
"<br>shift-left click to activate and keep panel open)";
|
||||
|
||||
break;
|
||||
|
||||
case Type_Toggle:
|
||||
|
||||
tooltip += "<p>(left click to ";
|
||||
|
|
|
@ -6,14 +6,35 @@
|
|||
#include <QRegExp>
|
||||
#include <QString>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
|
||||
#include "../../model/world/universalid.hpp"
|
||||
#include "../../model/world/tablemimedata.hpp"
|
||||
|
||||
CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& document) :
|
||||
#include "scripthighlighter.hpp"
|
||||
|
||||
CSVWorld::ScriptEdit::ChangeLock::ChangeLock (ScriptEdit& edit) : mEdit (edit)
|
||||
{
|
||||
++mEdit.mChangeLocked;
|
||||
}
|
||||
|
||||
CSVWorld::ScriptEdit::ChangeLock::~ChangeLock()
|
||||
{
|
||||
--mEdit.mChangeLocked;
|
||||
}
|
||||
|
||||
|
||||
CSVWorld::ScriptEdit::ScriptEdit (const CSMDoc::Document& document, QWidget* parent) :
|
||||
QTextEdit (parent),
|
||||
mDocument (document),
|
||||
mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive)
|
||||
mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive),
|
||||
mChangeLocked (0)
|
||||
{
|
||||
setAcceptRichText (false);
|
||||
setLineWrapMode (QTextEdit::NoWrap);
|
||||
setTabStopWidth (4);
|
||||
setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
|
||||
|
||||
mAllowedTypes <<CSMWorld::UniversalId::Type_Journal
|
||||
<<CSMWorld::UniversalId::Type_Global
|
||||
<<CSMWorld::UniversalId::Type_Topic
|
||||
|
@ -40,7 +61,22 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& docum
|
|||
<<CSMWorld::UniversalId::Type_Probe
|
||||
<<CSMWorld::UniversalId::Type_Repair
|
||||
<<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)
|
||||
|
@ -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…
|
||||
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();
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
#ifndef SCRIPTEDIT_H
|
||||
#define SCRIPTEDIT_H
|
||||
|
||||
#include <qtextedit.h>
|
||||
#include <QTextEdit>
|
||||
#include <QVector>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../../model/world/universalid.hpp"
|
||||
|
||||
|
@ -16,11 +17,43 @@ namespace CSMDoc
|
|||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class ScriptHighlighter;
|
||||
|
||||
class ScriptEdit : public QTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptEdit (QWidget* parent, const CSMDoc::Document& document);
|
||||
|
||||
class ChangeLock
|
||||
{
|
||||
ScriptEdit& mEdit;
|
||||
|
||||
ChangeLock (const ChangeLock&);
|
||||
ChangeLock& operator= (const ChangeLock&);
|
||||
|
||||
public:
|
||||
|
||||
ChangeLock (ScriptEdit& edit);
|
||||
~ChangeLock();
|
||||
};
|
||||
|
||||
friend class ChangeLock;
|
||||
|
||||
private:
|
||||
|
||||
int mChangeLocked;
|
||||
ScriptHighlighter *mHighlighter;
|
||||
QTimer mUpdateTimer;
|
||||
|
||||
public:
|
||||
|
||||
ScriptEdit (const CSMDoc::Document& document, QWidget* parent);
|
||||
|
||||
/// Should changes to the data be ignored (i.e. not cause updated)?
|
||||
///
|
||||
/// \note This mechanism is used to avoid infinite update recursions
|
||||
bool isChangeLocked() const;
|
||||
|
||||
private:
|
||||
QVector<CSMWorld::UniversalId::Type> mAllowedTypes;
|
||||
|
@ -34,6 +67,12 @@ namespace CSVWorld
|
|||
void dragMoveEvent (QDragMoveEvent* event);
|
||||
|
||||
bool stringNeedsQuote(const std::string& id) const;
|
||||
|
||||
private slots:
|
||||
|
||||
void idListChanged();
|
||||
|
||||
void updateHighlighting();
|
||||
};
|
||||
}
|
||||
#endif // SCRIPTEDIT_H
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QTextEdit>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
#include "../../model/world/universalid.hpp"
|
||||
#include "../../model/world/data.hpp"
|
||||
|
@ -12,28 +10,12 @@
|
|||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
|
||||
#include "scripthighlighter.hpp"
|
||||
#include "scriptedit.hpp"
|
||||
|
||||
CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view)
|
||||
{
|
||||
++mView.mChangeLocked;
|
||||
}
|
||||
|
||||
CSVWorld::ScriptSubView::ChangeLock::~ChangeLock()
|
||||
{
|
||||
--mView.mChangeLocked;
|
||||
}
|
||||
|
||||
CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
|
||||
: SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0)
|
||||
: SubView (id), mDocument (document), mColumn (-1)
|
||||
{
|
||||
setWidget (mEditor = new ScriptEdit (this, mDocument));
|
||||
|
||||
mEditor->setAcceptRichText (false);
|
||||
mEditor->setLineWrapMode (QTextEdit::NoWrap);
|
||||
mEditor->setTabStopWidth (4);
|
||||
mEditor->setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead
|
||||
setWidget (mEditor = new ScriptEdit (mDocument, this));
|
||||
|
||||
mModel = &dynamic_cast<CSMWorld::IdTable&> (
|
||||
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));
|
||||
|
@ -58,14 +40,6 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc:
|
|||
|
||||
connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
|
||||
this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int)));
|
||||
|
||||
connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged()));
|
||||
|
||||
mHighlighter = new ScriptHighlighter (document.getData(), mEditor->document());
|
||||
|
||||
connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting()));
|
||||
|
||||
mUpdateTimer.setSingleShot (true);
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::setEditLock (bool locked)
|
||||
|
@ -73,20 +47,12 @@ void CSVWorld::ScriptSubView::setEditLock (bool locked)
|
|||
mEditor->setReadOnly (locked);
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::idListChanged()
|
||||
{
|
||||
mHighlighter->invalidateIds();
|
||||
|
||||
if (!mUpdateTimer.isActive())
|
||||
mUpdateTimer.start (0);
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::textChanged()
|
||||
{
|
||||
if (mChangeLocked)
|
||||
if (mEditor->isChangeLocked())
|
||||
return;
|
||||
|
||||
ChangeLock lock (*this);
|
||||
ScriptEdit::ChangeLock lock (*mEditor);
|
||||
|
||||
mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel,
|
||||
mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText()));
|
||||
|
@ -94,10 +60,10 @@ void CSVWorld::ScriptSubView::textChanged()
|
|||
|
||||
void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
if (mChangeLocked)
|
||||
if (mEditor->isChangeLocked())
|
||||
return;
|
||||
|
||||
ChangeLock lock (*this);
|
||||
ScriptEdit::ChangeLock lock (*mEditor);
|
||||
|
||||
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
|
||||
|
||||
|
@ -118,12 +84,3 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
|
|||
deleteLater();
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::updateHighlighting()
|
||||
{
|
||||
if (mChangeLocked)
|
||||
return;
|
||||
|
||||
ChangeLock lock (*this);
|
||||
|
||||
mHighlighter->rehighlight();
|
||||
}
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#include "../doc/subview.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
class QTextEdit;
|
||||
class QModelIndex;
|
||||
|
||||
namespace CSMDoc
|
||||
|
@ -20,34 +17,16 @@ namespace CSMWorld
|
|||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class ScriptHighlighter;
|
||||
class ScriptEdit;
|
||||
|
||||
class ScriptSubView : public CSVDoc::SubView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTextEdit *mEditor;
|
||||
ScriptEdit *mEditor;
|
||||
CSMDoc::Document& mDocument;
|
||||
CSMWorld::IdTable *mModel;
|
||||
int mColumn;
|
||||
int mChangeLocked;
|
||||
ScriptHighlighter *mHighlighter;
|
||||
QTimer mUpdateTimer;
|
||||
|
||||
class ChangeLock
|
||||
{
|
||||
ScriptSubView& mView;
|
||||
|
||||
ChangeLock (const ChangeLock&);
|
||||
ChangeLock& operator= (const ChangeLock&);
|
||||
|
||||
public:
|
||||
|
||||
ChangeLock (ScriptSubView& view);
|
||||
~ChangeLock();
|
||||
};
|
||||
|
||||
friend class ChangeLock;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -57,17 +36,11 @@ namespace CSVWorld
|
|||
|
||||
public slots:
|
||||
|
||||
void idListChanged();
|
||||
|
||||
void textChanged();
|
||||
|
||||
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
|
||||
|
||||
private slots:
|
||||
|
||||
void updateHighlighting();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
# local files
|
||||
set(GAME
|
||||
main.cpp
|
||||
engine.cpp
|
||||
)
|
||||
if(NOT WIN32)
|
||||
if (NOT ANDROID)
|
||||
set(GAME
|
||||
main.cpp
|
||||
engine.cpp
|
||||
)
|
||||
else()
|
||||
set(GAME
|
||||
main.cpp
|
||||
android_main.c
|
||||
engine.cpp
|
||||
)
|
||||
endif()
|
||||
if(NOT WIN32 AND NOT ANDROID)
|
||||
set(GAME ${GAME} crashcatcher.cpp)
|
||||
endif()
|
||||
set(GAME_HEADER
|
||||
|
@ -15,7 +23,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst effectmanager weaponanimation terraingrid
|
||||
terrainstorage renderconst effectmanager weaponanimation
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -33,7 +41,7 @@ add_openmw_dir (mwgui
|
|||
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
|
||||
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
|
||||
tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog
|
||||
recharge mode videowidget backgroundimage itemwidget
|
||||
recharge mode videowidget backgroundimage itemwidget screenfader
|
||||
)
|
||||
|
||||
add_openmw_dir (mwdialogue
|
||||
|
@ -82,19 +90,33 @@ add_openmw_dir (mwbase
|
|||
)
|
||||
|
||||
# Main executable
|
||||
set(BOOST_COMPONENTS system filesystem program_options thread wave)
|
||||
if (ANDROID)
|
||||
set(BOOST_COMPONENTS system filesystem program_options thread wave atomic)
|
||||
else ()
|
||||
set(BOOST_COMPONENTS system filesystem program_options thread wave)
|
||||
endif ()
|
||||
|
||||
if(WIN32)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||
endif(WIN32)
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
|
||||
add_executable(openmw
|
||||
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
|
||||
${OPENMW_FILES}
|
||||
${GAME} ${GAME_HEADER}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
)
|
||||
if (NOT ANDROID)
|
||||
add_executable(openmw
|
||||
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
|
||||
${OPENMW_FILES}
|
||||
${GAME} ${GAME_HEADER}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
)
|
||||
else ()
|
||||
add_library(openmw
|
||||
SHARED
|
||||
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
|
||||
${OPENMW_FILES}
|
||||
${GAME} ${GAME_HEADER}
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
|
||||
# when we change the backend.
|
||||
|
@ -116,6 +138,23 @@ target_link_libraries(openmw
|
|||
components
|
||||
)
|
||||
|
||||
if (ANDROID)
|
||||
target_link_libraries(openmw
|
||||
${OGRE_STATIC_PLUGINS}
|
||||
EGL
|
||||
android
|
||||
log
|
||||
dl
|
||||
MyGUI.OgrePlatform
|
||||
MyGUIEngineStatic
|
||||
Plugin_StrangeButtonStatic
|
||||
cpufeatures
|
||||
BulletCollision
|
||||
BulletDynamics
|
||||
LinearMath
|
||||
)
|
||||
endif (ANDROID)
|
||||
|
||||
if (USE_SYSTEM_TINYXML)
|
||||
target_link_libraries(openmw ${TINYXML_LIBRARIES})
|
||||
endif()
|
||||
|
|
43
apps/openmw/android_main.c
Normal file
43
apps/openmw/android_main.c
Normal 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__ */
|
||||
|
|
@ -181,7 +181,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
|||
, mActivationDistanceOverride(-1)
|
||||
, mGrab(true)
|
||||
, mScriptBlacklistUse (true)
|
||||
|
||||
, mExportFonts(false)
|
||||
{
|
||||
std::srand ( std::time(NULL) );
|
||||
MWClass::registerClasses();
|
||||
|
@ -372,7 +372,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
|
||||
MWGui::WindowManager* window = new MWGui::WindowManager(
|
||||
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
|
||||
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding);
|
||||
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts);
|
||||
mEnvironment.setWindowManager (window);
|
||||
|
||||
// Create sound system
|
||||
|
@ -576,4 +576,9 @@ void OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)
|
|||
void OMW::Engine::setScriptBlacklistUse (bool use)
|
||||
{
|
||||
mScriptBlacklistUse = use;
|
||||
}
|
||||
}
|
||||
|
||||
void OMW::Engine::enableFontExport(bool exportFonts)
|
||||
{
|
||||
mExportFonts = exportFonts;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ namespace OMW
|
|||
// Grab mouse?
|
||||
bool mGrab;
|
||||
|
||||
bool mExportFonts;
|
||||
|
||||
Compiler::Extensions mExtensions;
|
||||
Compiler::Context *mScriptContext;
|
||||
|
||||
|
@ -187,6 +189,8 @@ namespace OMW
|
|||
|
||||
void setScriptBlacklistUse (bool use);
|
||||
|
||||
void enableFontExport(bool exportFonts);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager& mCfgMgr;
|
||||
};
|
||||
|
|
|
@ -168,6 +168,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
|
||||
("no-grab", "Don't grab mouse cursor")
|
||||
|
||||
("export-fonts", bpo::value<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");
|
||||
|
||||
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.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
|
||||
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
|
||||
engine.enableFontExport(variables["export-fonts"].as<bool>());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ namespace MWBase
|
|||
OffenseType type, int arg=0) = 0;
|
||||
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||
OffenseType type, int arg=0) = 0;
|
||||
virtual void actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
|
||||
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
||||
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0;
|
||||
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
|
||||
|
|
|
@ -331,6 +331,15 @@ namespace MWBase
|
|||
virtual void removeCurrentModal(MWGui::WindowModal* input) = 0;
|
||||
|
||||
virtual void pinWindow (MWGui::GuiWindow window) = 0;
|
||||
|
||||
/// Fade the screen in, over \a time seconds
|
||||
virtual void fadeScreenIn(const float time) = 0;
|
||||
/// Fade the screen out to black, over \a time seconds
|
||||
virtual void fadeScreenOut(const float time) = 0;
|
||||
/// Fade the screen to a specified percentage of black, over \a time seconds
|
||||
virtual void fadeScreenTo(const int percent, const float time) = 0;
|
||||
/// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading.
|
||||
virtual void setScreenFactor (float factor) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -17,11 +17,6 @@ namespace Ogre
|
|||
|
||||
namespace OEngine
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class Fader;
|
||||
}
|
||||
|
||||
namespace Physic
|
||||
{
|
||||
class PhysicEngine;
|
||||
|
@ -113,9 +108,6 @@ namespace MWBase
|
|||
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
const std::map<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 *getInterior (const std::string& name) = 0;
|
||||
|
@ -271,8 +263,9 @@ namespace MWBase
|
|||
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
|
||||
virtual std::pair<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.
|
||||
/// @param force do this even if the ptr is flying
|
||||
|
||||
virtual void fixPosition (const MWWorld::Ptr& actor) = 0;
|
||||
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
|
||||
|
|
|
@ -157,15 +157,19 @@ namespace MWClass
|
|||
|
||||
float iWeight = gmst.find (typeGmst)->getInt();
|
||||
|
||||
if (iWeight * gmst.find ("fLightMaxMod")->getFloat()>=
|
||||
ref->mBase->mData.mWeight)
|
||||
float epsilon = 5e-4;
|
||||
|
||||
if (ref->mBase->mData.mWeight == 0)
|
||||
return ESM::Skill::Unarmored;
|
||||
|
||||
if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon)
|
||||
return ESM::Skill::LightArmor;
|
||||
|
||||
if (iWeight * gmst.find ("fMedMaxMod")->getFloat()>=
|
||||
ref->mBase->mData.mWeight)
|
||||
if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon)
|
||||
return ESM::Skill::MediumArmor;
|
||||
|
||||
return ESM::Skill::HeavyArmor;
|
||||
else
|
||||
return ESM::Skill::HeavyArmor;
|
||||
}
|
||||
|
||||
int Armor::getValue (const MWWorld::Ptr& ptr) const
|
||||
|
|
|
@ -148,9 +148,9 @@ namespace MWClass
|
|||
return ref->mBase->mId;
|
||||
}
|
||||
|
||||
void Creature::adjustPosition(const MWWorld::Ptr& ptr) const
|
||||
void Creature::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->adjustPosition(ptr);
|
||||
MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);
|
||||
}
|
||||
|
||||
void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
|
||||
|
@ -255,23 +255,7 @@ namespace MWClass
|
|||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||
{
|
||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
|
||||
|
||||
// Weapon health is reduced by 1 even if the attack misses
|
||||
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||
|
||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
{
|
||||
weaphealth -= std::min(1, weaphealth);
|
||||
weapon.getCellRef().setCharge(weaphealth);
|
||||
}
|
||||
|
||||
// Weapon broken? unequip it
|
||||
if (weapon.getCellRef().getCharge() == 0)
|
||||
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
|
||||
}
|
||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -298,7 +282,6 @@ namespace MWClass
|
|||
|
||||
if (!weapon.isEmpty())
|
||||
{
|
||||
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
||||
const unsigned char *attack = NULL;
|
||||
if(type == ESM::Weapon::AT_Chop)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
|
@ -309,26 +292,10 @@ namespace MWClass
|
|||
if(attack)
|
||||
{
|
||||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||
damage *= float(weaphealth) / weapmaxhealth;
|
||||
|
||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
{
|
||||
// Reduce weapon charge by at least one, but cap at 0
|
||||
weaphealth -= std::min(std::max(1,
|
||||
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
||||
|
||||
weapon.getCellRef().setCharge(weaphealth);
|
||||
}
|
||||
|
||||
// Weapon broken? unequip it
|
||||
if (weapon.getCellRef().getCharge() == 0)
|
||||
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
|
||||
}
|
||||
damage *= gmst.find("fDamageStrengthBase")->getFloat() +
|
||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.find("fDamageStrengthMult")->getFloat() * 0.1);
|
||||
MWMechanics::adjustWeaponDamage(damage, weapon);
|
||||
MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
|
||||
}
|
||||
|
||||
// Apply "On hit" enchanted weapons
|
||||
|
@ -366,16 +333,11 @@ namespace MWClass
|
|||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
// Self defense
|
||||
if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr))
|
||||
|| attacker == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
&& !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
|
||||
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
|
||||
if ((canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures
|
||||
// (they have no movement or attacks anyway)
|
||||
)
|
||||
&& !attacker.isEmpty())
|
||||
{
|
||||
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
|
||||
// Note: accidental or collateral damage attacks are ignored.
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
||||
MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
}
|
||||
|
||||
if(!successful)
|
||||
|
@ -406,18 +368,21 @@ namespace MWClass
|
|||
|
||||
if (damage > 0.f)
|
||||
{
|
||||
// Check for knockdown
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
|
||||
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
|
||||
* getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
|
||||
if (!attacker.isEmpty())
|
||||
{
|
||||
getCreatureStats(ptr).setKnockedDown(true);
|
||||
// Check for knockdown
|
||||
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
|
||||
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
|
||||
* getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
|
||||
int roll = std::rand()/ (static_cast<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);
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@ namespace MWClass
|
|||
|
||||
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const;
|
||||
|
||||
virtual void adjustPosition(const MWWorld::Ptr& ptr) const;
|
||||
virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
|
||||
///< Adjust position to stand on ground. Must be called post model load
|
||||
/// @param force do this even if the ptr is flying
|
||||
|
||||
virtual std::string getName (const MWWorld::Ptr& ptr) const;
|
||||
///< \return name (the one that is to be presented to the user; not the internal one);
|
||||
|
|
|
@ -56,8 +56,11 @@ namespace MWClass
|
|||
{
|
||||
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
|
||||
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
|
||||
|
|
|
@ -403,9 +403,9 @@ namespace MWClass
|
|||
return ref->mBase->mId;
|
||||
}
|
||||
|
||||
void Npc::adjustPosition(const MWWorld::Ptr& ptr) const
|
||||
void Npc::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->adjustPosition(ptr);
|
||||
MWBase::Environment::get().getWorld()->adjustPosition(ptr, force);
|
||||
}
|
||||
|
||||
void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
|
||||
|
@ -529,24 +529,7 @@ namespace MWClass
|
|||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||
{
|
||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
|
||||
|
||||
// Weapon health is reduced by 1 even if the attack misses
|
||||
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||
|
||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
{
|
||||
weaphealth -= std::min(1, weaphealth);
|
||||
weapon.getCellRef().setCharge(weaphealth);
|
||||
}
|
||||
|
||||
// Weapon broken? unequip it
|
||||
if (weaphealth == 0)
|
||||
weapon = *inv.unequipItem(weapon, ptr);
|
||||
}
|
||||
|
||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -555,7 +538,6 @@ namespace MWClass
|
|||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||
if(!weapon.isEmpty())
|
||||
{
|
||||
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
||||
const unsigned char *attack = NULL;
|
||||
if(type == ESM::Weapon::AT_Chop)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
|
@ -568,27 +550,9 @@ namespace MWClass
|
|||
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
|
||||
damage *= gmst.fDamageStrengthBase->getFloat() +
|
||||
(stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
|
||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||
|
||||
damage *= float(weaphealth) / weapmaxhealth;
|
||||
|
||||
if (!MWBase::Environment::get().getWorld()->getGodModeState())
|
||||
{
|
||||
// Reduce weapon charge by at least one, but cap at 0
|
||||
weaphealth -= std::min(std::max(1,
|
||||
(int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
|
||||
|
||||
weapon.getCellRef().setCharge(weaphealth);
|
||||
}
|
||||
|
||||
// Weapon broken? unequip it
|
||||
if (weaphealth == 0)
|
||||
weapon = *inv.unequipItem(weapon, ptr);
|
||||
}
|
||||
}
|
||||
MWMechanics::adjustWeaponDamage(damage, weapon);
|
||||
MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
|
||||
healthdmg = true;
|
||||
}
|
||||
else
|
||||
|
@ -675,26 +639,13 @@ namespace MWClass
|
|||
|
||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||
|
||||
if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
// Attacking peaceful NPCs is a crime
|
||||
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)
|
||||
&& !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||
|
||||
if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)
|
||||
&& !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker))
|
||||
{
|
||||
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
|
||||
// Note: accidental or collateral damage attacks are ignored.
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
|
||||
}
|
||||
}
|
||||
|
||||
bool wasDead = getCreatureStats(ptr).isDead();
|
||||
|
||||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
if (!attacker.isEmpty())
|
||||
MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
|
||||
if(!successful)
|
||||
{
|
||||
// TODO: Handle HitAttemptOnMe script function
|
||||
|
@ -721,7 +672,7 @@ namespace MWClass
|
|||
if (damage < 0.001f)
|
||||
damage = 0;
|
||||
|
||||
if(damage > 0.0f)
|
||||
if(damage > 0.0f && !attacker.isEmpty())
|
||||
{
|
||||
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
||||
// something, alert the character controller, scripts, etc.
|
||||
|
@ -749,7 +700,7 @@ namespace MWClass
|
|||
else
|
||||
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
|
||||
|
||||
if(damage > 0 && ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage
|
||||
if(damage > 0 && ishealth)
|
||||
{
|
||||
// Hit percentages:
|
||||
// cuirass = 30%
|
||||
|
@ -967,8 +918,6 @@ namespace MWClass
|
|||
|
||||
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
|
||||
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
|
||||
if(npcdata->mNpcStats.isWerewolf())
|
||||
runSpeed *= gmst.fWereWolfRunMult->getFloat();
|
||||
|
||||
float moveSpeed;
|
||||
if(normalizedEncumbrance >= 1.0f)
|
||||
|
@ -1000,6 +949,9 @@ namespace MWClass
|
|||
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
|
||||
moveSpeed *= 0.75f;
|
||||
|
||||
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
|
||||
moveSpeed *= gmst.fWereWolfRunMult->getFloat();
|
||||
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,9 @@ namespace MWClass
|
|||
|
||||
virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const;
|
||||
|
||||
virtual void adjustPosition(const MWWorld::Ptr& ptr) const;
|
||||
virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
|
||||
///< Adjust position to stand on ground. Must be called post model load
|
||||
/// @param force do this even if the ptr is flying
|
||||
|
||||
virtual std::string getName (const MWWorld::Ptr& ptr) const;
|
||||
///< \return name (the one that is to be presented to the user; not the internal one);
|
||||
|
|
|
@ -145,11 +145,8 @@ namespace MWClass
|
|||
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)
|
||||
{
|
||||
it->mKnown = ( (i == 0 && alchemySkill >= fWortChanceValue)
|
||||
|| (i == 1 && alchemySkill >= fWortChanceValue*2)
|
||||
|| (i == 2 && alchemySkill >= fWortChanceValue*3)
|
||||
|| (i == 3 && alchemySkill >= fWortChanceValue*4));
|
||||
|
||||
it->mKnown = (i <= 1 && alchemySkill >= fWortChanceValue)
|
||||
|| (i <= 3 && alchemySkill >= fWortChanceValue*2);
|
||||
++i;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,16 @@
|
|||
namespace MWGui
|
||||
{
|
||||
|
||||
DragAndDrop::DragAndDrop()
|
||||
: mDraggedWidget(NULL)
|
||||
, mDraggedCount(0)
|
||||
, mSourceModel(NULL)
|
||||
, mSourceView(NULL)
|
||||
, mSourceSortModel(NULL)
|
||||
, mIsOnDragAndDrop(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count)
|
||||
{
|
||||
mItem = sourceModel->getItem(index);
|
||||
|
@ -36,7 +46,6 @@ namespace MWGui
|
|||
mSourceView = sourceView;
|
||||
mSourceSortModel = sortModel;
|
||||
mIsOnDragAndDrop = true;
|
||||
mDragAndDropWidget->setVisible(true);
|
||||
|
||||
// If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend
|
||||
// immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive,
|
||||
|
@ -73,8 +82,13 @@ namespace MWGui
|
|||
mSourceSortModel->addDragItem(mItem.mBase, count);
|
||||
}
|
||||
|
||||
ItemWidget* baseWidget = mDragAndDropWidget->createWidget<ItemWidget>
|
||||
("MW_ItemIcon", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);
|
||||
ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget<ItemWidget>("MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop");
|
||||
|
||||
Controllers::ControllerFollowMouse* controller =
|
||||
MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerFollowMouse::getClassTypeName())
|
||||
->castType<Controllers::ControllerFollowMouse>();
|
||||
MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller);
|
||||
|
||||
mDraggedWidget = baseWidget;
|
||||
baseWidget->setItem(mItem.mBase);
|
||||
baseWidget->setNeedMouseFocus(false);
|
||||
|
@ -99,8 +113,6 @@ namespace MWGui
|
|||
std::string sound = mItem.mBase.getClass().getDownSoundId(mItem.mBase);
|
||||
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
||||
|
||||
mDragAndDropWidget->setVisible(false);
|
||||
|
||||
// If item is dropped where it was taken from, we don't need to do anything -
|
||||
// otherwise, do the transfer
|
||||
if (targetModel != mSourceModel)
|
||||
|
|
|
@ -33,13 +33,14 @@ namespace MWGui
|
|||
public:
|
||||
bool mIsOnDragAndDrop;
|
||||
MyGUI::Widget* mDraggedWidget;
|
||||
MyGUI::Widget* mDragAndDropWidget;
|
||||
ItemModel* mSourceModel;
|
||||
ItemView* mSourceView;
|
||||
SortFilterItemModel* mSourceSortModel;
|
||||
ItemStack mItem;
|
||||
int mDraggedCount;
|
||||
|
||||
DragAndDrop();
|
||||
|
||||
void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
|
||||
void drop (ItemModel* targetModel, ItemView* targetView);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "controllers.hpp"
|
||||
|
||||
#include <MyGUI_InputManager.h>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
namespace Controllers
|
||||
|
@ -50,5 +52,17 @@ namespace MWGui
|
|||
{
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
void ControllerFollowMouse::prepareItem(MyGUI::Widget *_widget)
|
||||
{
|
||||
}
|
||||
|
||||
bool ControllerFollowMouse::addTime(MyGUI::Widget *_widget, float _time)
|
||||
{
|
||||
_widget->setPosition(MyGUI::InputManager::getInstance().getMousePosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,17 @@ namespace MWGui
|
|||
bool mEnabled;
|
||||
float mTimeLeft;
|
||||
};
|
||||
|
||||
/// Automatically positions a widget below the mouse cursor.
|
||||
class ControllerFollowMouse :
|
||||
public MyGUI::ControllerItem
|
||||
{
|
||||
MYGUI_RTTI_DERIVED( ControllerFollowMouse )
|
||||
|
||||
private:
|
||||
bool addTime(MyGUI::Widget* _widget, float _time);
|
||||
void prepareItem(MyGUI::Widget* _widget);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ namespace MWGui
|
|||
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
||||
item.getClass().getValue(item));
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MWGui
|
|||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
void FontLoader::loadAllFonts()
|
||||
void FontLoader::loadAllFonts(bool exportToFile)
|
||||
{
|
||||
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
|
||||
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
|
||||
|
@ -142,7 +142,7 @@ namespace MWGui
|
|||
Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "*.fnt");
|
||||
for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource)
|
||||
{
|
||||
loadFont(*resource);
|
||||
loadFont(*resource, exportToFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ namespace MWGui
|
|||
float ascent;
|
||||
} GlyphInfo;
|
||||
|
||||
void FontLoader::loadFont(const std::string &fileName)
|
||||
void FontLoader::loadFont(const std::string &fileName, bool exportToFile)
|
||||
{
|
||||
Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName);
|
||||
|
||||
|
@ -221,6 +221,9 @@ namespace MWGui
|
|||
width, height, 0, Ogre::PF_BYTE_RGBA);
|
||||
texture->loadImage(image);
|
||||
|
||||
if (exportToFile)
|
||||
image.save(resourceName + ".png");
|
||||
|
||||
// Register the font with MyGUI
|
||||
MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>(
|
||||
MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont"));
|
||||
|
@ -240,10 +243,10 @@ namespace MWGui
|
|||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
int x1 = data[i].top_left.x*width;
|
||||
int y1 = data[i].top_left.y*height;
|
||||
int w = data[i].top_right.x*width - x1;
|
||||
int h = data[i].bottom_left.y*height - y1;
|
||||
float x1 = data[i].top_left.x*width;
|
||||
float y1 = data[i].top_left.y*height;
|
||||
float w = data[i].top_right.x*width - x1;
|
||||
float h = data[i].bottom_left.y*height - y1;
|
||||
|
||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||
unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding));
|
||||
|
@ -257,16 +260,38 @@ namespace MWGui
|
|||
code->addAttribute("advance", data[i].width);
|
||||
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
|
||||
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
|
||||
code->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
|
||||
|
||||
// More hacks! The french game uses several win1252 characters that are not included
|
||||
// in the cp437 encoding of the font. Fall back to similar available characters.
|
||||
if (mEncoding == ToUTF8::CP437)
|
||||
{
|
||||
std::multimap<int, int> additional;
|
||||
std::multimap<int, int> additional; // <cp437, unicode>
|
||||
additional.insert(std::make_pair(39, 0x2019)); // apostrophe
|
||||
additional.insert(std::make_pair(45, 0x2013)); // dash
|
||||
additional.insert(std::make_pair(45, 0x2014)); // dash
|
||||
additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark
|
||||
additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark
|
||||
additional.insert(std::make_pair(44, 0x201A));
|
||||
additional.insert(std::make_pair(44, 0x201E));
|
||||
additional.insert(std::make_pair(43, 0x2020));
|
||||
additional.insert(std::make_pair(94, 0x02C6));
|
||||
additional.insert(std::make_pair(37, 0x2030));
|
||||
additional.insert(std::make_pair(83, 0x0160));
|
||||
additional.insert(std::make_pair(60, 0x2039));
|
||||
additional.insert(std::make_pair(79, 0x0152));
|
||||
additional.insert(std::make_pair(90, 0x017D));
|
||||
additional.insert(std::make_pair(39, 0x2019));
|
||||
additional.insert(std::make_pair(126, 0x02DC));
|
||||
additional.insert(std::make_pair(84, 0x2122));
|
||||
additional.insert(std::make_pair(83, 0x0161));
|
||||
additional.insert(std::make_pair(62, 0x203A));
|
||||
additional.insert(std::make_pair(111, 0x0153));
|
||||
additional.insert(std::make_pair(122, 0x017E));
|
||||
additional.insert(std::make_pair(89, 0x0178));
|
||||
additional.insert(std::make_pair(156, 0x00A2));
|
||||
additional.insert(std::make_pair(46, 0x2026));
|
||||
|
||||
for (std::multimap<int, int>::iterator it = additional.begin(); it != additional.end(); ++it)
|
||||
{
|
||||
if (it->first != i)
|
||||
|
@ -281,6 +306,7 @@ namespace MWGui
|
|||
code->addAttribute("advance", data[i].width);
|
||||
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
|
||||
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
|
||||
code->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,6 +322,7 @@ namespace MWGui
|
|||
cursorCode->addAttribute("advance", data[i].width);
|
||||
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
|
||||
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
|
||||
cursorCode->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
|
||||
}
|
||||
|
||||
// Question mark, use for NotDefined marker (used for glyphs not existing in the font)
|
||||
|
@ -310,6 +337,7 @@ namespace MWGui
|
|||
cursorCode->addAttribute("advance", data[i].width);
|
||||
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
|
||||
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
|
||||
cursorCode->addAttribute("size", MyGUI::IntSize(data[i].width, data[i].height));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +355,13 @@ namespace MWGui
|
|||
cursorCode->addAttribute("coord", "0 0 0 0");
|
||||
cursorCode->addAttribute("advance", "0");
|
||||
cursorCode->addAttribute("bearing", "0 0");
|
||||
cursorCode->addAttribute("size", "0 0");
|
||||
}
|
||||
|
||||
if (exportToFile)
|
||||
{
|
||||
xmlDocument.createDeclaration();
|
||||
xmlDocument.save(resourceName + ".xml");
|
||||
}
|
||||
|
||||
font->deserialization(root, MyGUI::Version(3,2,0));
|
||||
|
|
|
@ -12,12 +12,15 @@ namespace MWGui
|
|||
{
|
||||
public:
|
||||
FontLoader (ToUTF8::FromType encoding);
|
||||
void loadAllFonts ();
|
||||
|
||||
/// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files?
|
||||
void loadAllFonts (bool exportToFile);
|
||||
|
||||
private:
|
||||
ToUTF8::FromType mEncoding;
|
||||
|
||||
void loadFont (const std::string& fileName);
|
||||
/// @param exportToFile export the converted font (Image and XML with glyph metrics) to files?
|
||||
void loadFont (const std::string& fileName, bool exportToFile);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "formatting.hpp"
|
||||
|
||||
#include <components/interpreter/defines.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
|
@ -269,15 +270,6 @@ namespace MWGui
|
|||
void BookTextParser::parseImage(std::string tag, bool createWidget)
|
||||
{
|
||||
int src_start = tag.find("SRC=")+5;
|
||||
std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start);
|
||||
|
||||
// fix texture extension to .dds
|
||||
if (image.size() > 4)
|
||||
{
|
||||
image[image.size()-3] = 'd';
|
||||
image[image.size()-2] = 'd';
|
||||
image[image.size()-1] = 's';
|
||||
}
|
||||
|
||||
int width_start = tag.find("WIDTH=")+7;
|
||||
int width = boost::lexical_cast<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,
|
||||
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
|
||||
|
||||
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
|
||||
// So if the image isn't found, try appending the size.
|
||||
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image))
|
||||
{
|
||||
std::stringstream str;
|
||||
str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind("."));
|
||||
image = str.str();
|
||||
}
|
||||
|
||||
box->setImageTexture("bookart\\" + image);
|
||||
std::string image = Misc::ResourceHelpers::correctBookartPath(tag.substr(src_start, tag.find('"', src_start)-src_start), width, height);
|
||||
box->setImageTexture(image);
|
||||
box->setProperty("NeedMouse", "false");
|
||||
}
|
||||
|
||||
|
@ -406,7 +390,7 @@ namespace MWGui
|
|||
box->setTextAlign(mTextStyle.mTextAlign);
|
||||
box->setTextColour(mTextStyle.mColour);
|
||||
box->setFontName(mTextStyle.mFont);
|
||||
box->setCaption(realText);
|
||||
box->setCaption(MyGUI::TextIterator::toTagsString(realText));
|
||||
box->setSize(box->getSize().width, box->getTextSize().height);
|
||||
mHeight += box->getTextSize().height;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MWGui
|
|||
};
|
||||
|
||||
|
||||
HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop)
|
||||
HUD::HUD(int fpsLevel, DragAndDrop* dragAndDrop)
|
||||
: Layout("openmw_hud.layout")
|
||||
, mHealth(NULL)
|
||||
, mMagicka(NULL)
|
||||
|
@ -97,7 +97,7 @@ namespace MWGui
|
|||
, mWeaponSpellTimer(0.f)
|
||||
, mDrowningFlashTheta(0.f)
|
||||
{
|
||||
setCoord(0,0, width, height);
|
||||
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
||||
|
||||
// Energy bars
|
||||
getWidget(mHealthFrame, "HealthFrame");
|
||||
|
@ -405,11 +405,6 @@ namespace MWGui
|
|||
mDrowningFlashTheta += dt * Ogre::Math::TWO_PI;
|
||||
}
|
||||
|
||||
void HUD::onResChange(int width, int height)
|
||||
{
|
||||
setCoord(0, 0, width, height);
|
||||
}
|
||||
|
||||
void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent)
|
||||
{
|
||||
const ESM::Spell* spell =
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace MWGui
|
|||
class HUD : public OEngine::GUI::Layout, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop);
|
||||
HUD(int fpsLevel, DragAndDrop* dragAndDrop);
|
||||
virtual ~HUD();
|
||||
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||
void setFPS(float fps);
|
||||
|
@ -47,7 +47,6 @@ namespace MWGui
|
|||
void setCrosshairVisible(bool visible);
|
||||
|
||||
void onFrame(float dt);
|
||||
void onResChange(int width, int height);
|
||||
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace MWGui
|
|||
, mLastYSize(0)
|
||||
, mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()))
|
||||
, mPreviewDirty(true)
|
||||
, mPreviewResize(true)
|
||||
, mDragAndDrop(dragAndDrop)
|
||||
, mSelectedItem(-1)
|
||||
, mGuiMode(GM_Inventory)
|
||||
|
@ -91,8 +92,18 @@ namespace MWGui
|
|||
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
|
||||
mSortModel = new SortFilterItemModel(mTradeModel);
|
||||
mItemView->setModel(mSortModel);
|
||||
|
||||
mPreview.reset(NULL);
|
||||
mAvatarImage->setImageTexture("");
|
||||
MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture("CharacterPreview");
|
||||
if (tex)
|
||||
MyGUI::RenderManager::getInstance().destroyTexture(tex);
|
||||
|
||||
mPreview.reset(new MWRender::InventoryPreview(mPtr));
|
||||
mPreview->setup();
|
||||
|
||||
mPreviewDirty = true;
|
||||
mPreviewResize = true;
|
||||
}
|
||||
|
||||
void InventoryWindow::setGuiMode(GuiMode mode)
|
||||
|
@ -125,7 +136,7 @@ namespace MWGui
|
|||
Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height);
|
||||
|
||||
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
|
||||
mPreviewDirty = true;
|
||||
mPreviewResize = true;
|
||||
|
||||
mMainWidget->setPosition(pos);
|
||||
mMainWidget->setSize(size);
|
||||
|
@ -333,7 +344,7 @@ namespace MWGui
|
|||
{
|
||||
mLastXSize = mMainWidget->getSize().width;
|
||||
mLastYSize = mMainWidget->getSize().height;
|
||||
mPreviewDirty = true;
|
||||
mPreviewResize = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,6 +377,12 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void InventoryWindow::onTitleDoubleClicked()
|
||||
{
|
||||
if (!mPinned)
|
||||
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
|
||||
}
|
||||
|
||||
void InventoryWindow::useItem(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
const std::string& script = ptr.getClass().getScript(ptr);
|
||||
|
@ -403,9 +420,13 @@ namespace MWGui
|
|||
else
|
||||
mSkippedToEquip = ptr;
|
||||
|
||||
mItemView->update();
|
||||
if (isVisible())
|
||||
{
|
||||
mItemView->update();
|
||||
|
||||
notifyContentChanged();
|
||||
notifyContentChanged();
|
||||
}
|
||||
// else: will be updated in open()
|
||||
}
|
||||
|
||||
void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)
|
||||
|
@ -491,16 +512,23 @@ namespace MWGui
|
|||
|
||||
void InventoryWindow::doRenderUpdate ()
|
||||
{
|
||||
if (mPreviewDirty)
|
||||
mPreview->onFrame();
|
||||
if (mPreviewResize)
|
||||
{
|
||||
mPreviewDirty = false;
|
||||
mPreviewResize = false;
|
||||
MyGUI::IntSize size = mAvatarImage->getSize();
|
||||
|
||||
mPreview->update (size.width, size.height);
|
||||
mPreview->resize(size.width, size.height);
|
||||
|
||||
mAvatarImage->setImageTexture("CharacterPreview");
|
||||
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height)));
|
||||
mAvatarImage->setImageTile(MyGUI::IntSize(std::min(512, size.width), std::min(1024, size.height)));
|
||||
}
|
||||
if (mPreviewDirty)
|
||||
{
|
||||
mPreviewDirty = false;
|
||||
mPreview->update ();
|
||||
|
||||
mAvatarImage->setImageTexture("CharacterPreview");
|
||||
|
||||
mArmorRating->setCaptionWithReplacing ("#{sArmor}: "
|
||||
+ boost::lexical_cast<std::string>(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace MWGui
|
|||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
bool mPreviewDirty;
|
||||
bool mPreviewResize;
|
||||
int mSelectedItem;
|
||||
|
||||
MWWorld::Ptr mPtr;
|
||||
|
@ -98,6 +99,7 @@ namespace MWGui
|
|||
void onFilterChanged(MyGUI::Widget* _sender);
|
||||
void onAvatarClicked(MyGUI::Widget* _sender);
|
||||
void onPinToggled();
|
||||
void onTitleDoubleClicked();
|
||||
|
||||
void updateEncumbranceBar();
|
||||
void notifyContentChanged();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <MyGUI_FactoryManager.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
namespace MWGui
|
||||
|
@ -63,15 +65,7 @@ namespace MWGui
|
|||
|
||||
void ItemWidget::setIcon(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
// image
|
||||
std::string path = std::string("icons\\");
|
||||
path += ptr.getClass().getInventoryIcon(ptr);
|
||||
|
||||
std::string::size_type pos = path.rfind(".");
|
||||
if(pos != std::string::npos)
|
||||
path.erase(pos);
|
||||
path.append(".dds");
|
||||
setIcon(path);
|
||||
setIcon(Misc::ResourceHelpers::correctIconPath(ptr.getClass().getInventoryIcon(ptr)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -123,6 +123,9 @@ namespace
|
|||
|
||||
getPage (LeftBookPage)->adviseLinkClicked (callback);
|
||||
getPage (RightBookPage)->adviseLinkClicked (callback);
|
||||
|
||||
getPage (LeftBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
|
||||
getPage (RightBookPage)->eventMouseWheel += MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -488,6 +491,14 @@ namespace
|
|||
MWBase::Environment::get().getWindowManager ()->popGuiMode ();
|
||||
}
|
||||
|
||||
void notifyMouseWheel(MyGUI::Widget* sender, int rel)
|
||||
{
|
||||
if (rel < 0)
|
||||
notifyNextPage(sender);
|
||||
else
|
||||
notifyPrevPage(sender);
|
||||
}
|
||||
|
||||
void notifyNextPage(MyGUI::Widget* _sender)
|
||||
{
|
||||
if (!mStates.empty ())
|
||||
|
@ -509,7 +520,7 @@ namespace
|
|||
{
|
||||
unsigned int & page = mStates.top ().mPage;
|
||||
|
||||
if(page > 0)
|
||||
if(page >= 2)
|
||||
{
|
||||
page -= 2;
|
||||
updateShowingPages ();
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace MWGui
|
|||
, mProgress(0)
|
||||
, mVSyncWasEnabled(false)
|
||||
{
|
||||
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
||||
|
||||
getWidget(mLoadingText, "LoadingText");
|
||||
getWidget(mProgressBar, "ProgressBar");
|
||||
|
||||
|
@ -56,13 +58,6 @@ namespace MWGui
|
|||
mBackgroundImage->setVisible(visible);
|
||||
}
|
||||
|
||||
void LoadingScreen::onResChange(int w, int h)
|
||||
{
|
||||
setCoord(0,0,w,h);
|
||||
|
||||
mBackgroundImage->setCoord(MyGUI::IntCoord(0,0,w,h));
|
||||
}
|
||||
|
||||
void LoadingScreen::loadingOn()
|
||||
{
|
||||
// Early-out if already on
|
||||
|
|
|
@ -35,8 +35,6 @@ namespace MWGui
|
|||
void setLoadingProgress (const std::string& stage, int depth, int current, int total);
|
||||
void loadingDone();
|
||||
|
||||
void onResChange(int w, int h);
|
||||
|
||||
void updateWindow(Ogre::RenderWindow* rw) { mWindow = rw; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -536,6 +536,12 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void MapWindow::onTitleDoubleClicked()
|
||||
{
|
||||
if (!mPinned)
|
||||
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map);
|
||||
}
|
||||
|
||||
void MapWindow::open()
|
||||
{
|
||||
// force markers to foreground
|
||||
|
|
|
@ -150,6 +150,7 @@ namespace MWGui
|
|||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
virtual void onTitleDoubleClicked();
|
||||
|
||||
virtual void notifyPlayerUpdate();
|
||||
virtual void notifyMapChanged();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/esm/quickkeys.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -226,12 +227,9 @@ namespace MWGui
|
|||
esmStore.get<ESM::MagicEffect>().find(spell->mEffects.mList.front().mEffectID);
|
||||
|
||||
std::string path = effect->mIcon;
|
||||
int slashPos = path.find("\\");
|
||||
int slashPos = path.rfind('\\');
|
||||
path.insert(slashPos+1, "b_");
|
||||
path = std::string("icons\\") + path;
|
||||
int pos = path.rfind(".");
|
||||
path.erase(pos);
|
||||
path.append(".dds");
|
||||
path = Misc::ResourceHelpers::correctIconPath(path);
|
||||
|
||||
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||
button->setIcon(path);
|
||||
|
|
|
@ -181,6 +181,10 @@ void Recharge::onItemClicked(MyGUI::Widget *sender)
|
|||
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->getString();
|
||||
message = boost::str(boost::format(message) % gem.getClass().getName(gem));
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
||||
|
||||
// special case: readd Azura's Star
|
||||
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
||||
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
|
||||
}
|
||||
|
||||
updateView();
|
||||
|
|
111
apps/openmw/mwgui/screenfader.cpp
Normal file
111
apps/openmw/mwgui/screenfader.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
44
apps/openmw/mwgui/screenfader.hpp
Normal file
44
apps/openmw/mwgui/screenfader.hpp
Normal 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
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
@ -143,13 +145,7 @@ namespace MWGui
|
|||
|
||||
void EditEffectDialog::setMagicEffect (const ESM::MagicEffect *effect)
|
||||
{
|
||||
std::string icon = effect->mIcon;
|
||||
icon[icon.size()-3] = 'd';
|
||||
icon[icon.size()-2] = 'd';
|
||||
icon[icon.size()-1] = 's';
|
||||
icon = "icons\\" + icon;
|
||||
|
||||
mEffectImage->setImageTexture (icon);
|
||||
mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
|
||||
|
||||
mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (effect->mIndex)+"}");
|
||||
|
||||
|
@ -391,6 +387,14 @@ namespace MWGui
|
|||
|
||||
void SpellCreationDialog::notifyEffectsChanged ()
|
||||
{
|
||||
if (mEffects.empty())
|
||||
{
|
||||
mMagickaCost->setCaption("0");
|
||||
mPriceLabel->setCaption("0");
|
||||
mSuccessChance->setCaption("0");
|
||||
return;
|
||||
}
|
||||
|
||||
float y = 0;
|
||||
|
||||
const MWWorld::ESMStore &store =
|
||||
|
@ -398,7 +402,7 @@ namespace MWGui
|
|||
|
||||
for (std::vector<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 =
|
||||
store.get<ESM::MagicEffect>().find(it->mEffectID);
|
||||
|
@ -413,7 +417,7 @@ namespace MWGui
|
|||
y += x * fEffectCostMult;
|
||||
y = std::max(1.f,y);
|
||||
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::CastTarget)
|
||||
if (it->mRange == ESM::RT_Target)
|
||||
y *= 1.5;
|
||||
}
|
||||
|
||||
|
@ -520,16 +524,24 @@ namespace MWGui
|
|||
|
||||
void EffectEditorBase::onSelectAttribute ()
|
||||
{
|
||||
mAddEffectDialog.setVisible(true);
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
||||
|
||||
mAddEffectDialog.newEffect(effect);
|
||||
mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId());
|
||||
mAddEffectDialog.setVisible(true);
|
||||
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);
|
||||
mSelectAttributeDialog = 0;
|
||||
}
|
||||
|
||||
void EffectEditorBase::onSelectSkill ()
|
||||
{
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
||||
|
||||
mAddEffectDialog.newEffect(effect);
|
||||
mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId());
|
||||
mAddEffectDialog.setVisible(true);
|
||||
mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId ());
|
||||
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);
|
||||
mSelectSkillDialog = 0;
|
||||
}
|
||||
|
@ -554,11 +566,10 @@ namespace MWGui
|
|||
}
|
||||
|
||||
int buttonId = *sender->getUserData<int>();
|
||||
short effectId = mButtonMapping[buttonId];
|
||||
|
||||
mSelectedKnownEffectId = mButtonMapping[buttonId];
|
||||
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}");
|
||||
return;
|
||||
|
@ -566,9 +577,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);
|
||||
|
||||
mAddEffectDialog.newEffect (effect);
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
||||
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
{
|
||||
|
@ -588,6 +597,7 @@ namespace MWGui
|
|||
}
|
||||
else
|
||||
{
|
||||
mAddEffectDialog.newEffect(effect);
|
||||
mAddEffectDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace MWGui
|
|||
SelectSkillDialog* mSelectSkillDialog;
|
||||
|
||||
int mSelectedEffect;
|
||||
short mSelectedKnownEffectId;
|
||||
|
||||
std::vector<ESM::ENAMstruct> mEffects;
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -133,13 +135,7 @@ namespace MWGui
|
|||
("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);
|
||||
mWidgetMap[it->first] = image;
|
||||
|
||||
std::string icon = effect->mIcon;
|
||||
icon[icon.size()-3] = 'd';
|
||||
icon[icon.size()-2] = 'd';
|
||||
icon[icon.size()-1] = 's';
|
||||
icon = "icons\\" + icon;
|
||||
|
||||
image->setImageTexture(icon);
|
||||
image->setImageTexture(Misc::ResourceHelpers::correctIconPath(effect->mIcon));
|
||||
|
||||
std::string name = ESM::MagicEffect::effectIdToString (it->first);
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace MWGui
|
|||
, NoDrop(drag, mMainWidget)
|
||||
, mHeight(0)
|
||||
, mWidth(0)
|
||||
, mWindowSize(mMainWidget->getSize())
|
||||
{
|
||||
mSpellIcons = new SpellIcons();
|
||||
|
||||
|
@ -66,6 +67,12 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void SpellWindow::onTitleDoubleClicked()
|
||||
{
|
||||
if (!mPinned)
|
||||
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);
|
||||
}
|
||||
|
||||
void SpellWindow::open()
|
||||
{
|
||||
updateSpells();
|
||||
|
@ -302,7 +309,11 @@ namespace MWGui
|
|||
|
||||
void SpellWindow::onWindowResize(MyGUI::Window* _sender)
|
||||
{
|
||||
updateSpells();
|
||||
if (mMainWidget->getSize() != mWindowSize)
|
||||
{
|
||||
mWindowSize = mMainWidget->getSize();
|
||||
updateSpells();
|
||||
}
|
||||
}
|
||||
|
||||
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace MWGui
|
|||
int mHeight;
|
||||
int mWidth;
|
||||
|
||||
MyGUI::IntSize mWindowSize;
|
||||
|
||||
std::string mSpellToDelete;
|
||||
|
||||
void addGroup(const std::string& label, const std::string& label2);
|
||||
|
@ -42,6 +44,7 @@ namespace MWGui
|
|||
void onDeleteSpellAccept();
|
||||
|
||||
virtual void onPinToggled();
|
||||
virtual void onTitleDoubleClicked();
|
||||
virtual void open();
|
||||
|
||||
SpellIcons* mSpellIcons;
|
||||
|
|
|
@ -591,4 +591,10 @@ namespace MWGui
|
|||
{
|
||||
MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void StatsWindow::onTitleDoubleClicked()
|
||||
{
|
||||
if (!mPinned)
|
||||
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ namespace MWGui
|
|||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
virtual void onTitleDoubleClicked();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -319,20 +321,6 @@ namespace MWGui
|
|||
return tooltipSize;
|
||||
}
|
||||
|
||||
void ToolTips::findImageExtension(std::string& image)
|
||||
{
|
||||
int len = image.size();
|
||||
if (len < 4) return;
|
||||
|
||||
if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image))
|
||||
{
|
||||
// Change texture extension to .dds
|
||||
image[len-3] = 'd';
|
||||
image[len-2] = 'd';
|
||||
image[len-1] = 's';
|
||||
}
|
||||
}
|
||||
|
||||
MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info)
|
||||
{
|
||||
mDynamicToolTipBox->setVisible(true);
|
||||
|
@ -371,8 +359,7 @@ namespace MWGui
|
|||
const int imageCaptionHPadding = (caption != "" ? 8 : 0);
|
||||
const int imageCaptionVPadding = (caption != "" ? 4 : 0);
|
||||
|
||||
std::string realImage = "icons\\" + image;
|
||||
findImageExtension(realImage);
|
||||
std::string realImage = Misc::ResourceHelpers::correctIconPath(image);
|
||||
|
||||
MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget<MyGUI::EditBox>("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
|
||||
captionWidget->setProperty("Static", "true");
|
||||
|
@ -644,9 +631,7 @@ namespace MWGui
|
|||
|
||||
widget->setUserString("ToolTipType", "Layout");
|
||||
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
|
||||
std::string image = sign->mTexture;
|
||||
image.replace(image.size()-3, 3, "dds");
|
||||
widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image);
|
||||
widget->setUserString("ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture));
|
||||
std::string text;
|
||||
|
||||
text += sign->mName;
|
||||
|
@ -739,15 +724,9 @@ namespace MWGui
|
|||
const std::string &name = ESM::MagicEffect::effectIdToString (id);
|
||||
|
||||
std::string icon = effect->mIcon;
|
||||
|
||||
int slashPos = icon.find("\\");
|
||||
int slashPos = icon.rfind('\\');
|
||||
icon.insert(slashPos+1, "b_");
|
||||
|
||||
icon[icon.size()-3] = 'd';
|
||||
icon[icon.size()-2] = 'd';
|
||||
icon[icon.size()-1] = 's';
|
||||
|
||||
icon = "icons\\" + icon;
|
||||
icon = Misc::ResourceHelpers::correctIconPath(icon);
|
||||
|
||||
std::vector<std::string> schools;
|
||||
schools.push_back ("#{sSchoolAlteration}");
|
||||
|
|
|
@ -84,8 +84,6 @@ namespace MWGui
|
|||
|
||||
MWWorld::Ptr mFocusObject;
|
||||
|
||||
void findImageExtension(std::string& image);
|
||||
|
||||
MyGUI::IntSize getToolTipViaPtr (bool image=true);
|
||||
///< @return requested tooltip size
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ namespace MWGui
|
|||
it->mBase.getClass().getValue(it->mBase)
|
||||
* it->mCount);
|
||||
onCancelButtonClicked(mCancelButton);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
@ -164,14 +163,14 @@ namespace MWGui
|
|||
|
||||
// go back to game mode
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
|
||||
// advance time
|
||||
MWBase::Environment::get().getWorld ()->advanceTime (2);
|
||||
MWBase::Environment::get().getMechanicsManager()->rest(false);
|
||||
MWBase::Environment::get().getMechanicsManager()->rest(false);
|
||||
|
||||
MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.25);
|
||||
mFadeTimeRemaining = 0.5;
|
||||
}
|
||||
|
||||
|
@ -183,6 +182,6 @@ namespace MWGui
|
|||
mFadeTimeRemaining -= dt;
|
||||
|
||||
if (mFadeTimeRemaining <= 0)
|
||||
MWBase::Environment::get().getWorld ()->getFader()->fadeIn(0.25);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.25);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
|
@ -156,7 +155,7 @@ namespace MWGui
|
|||
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
npcStats.setGoldPool(npcStats.getGoldPool() + price);
|
||||
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
|
||||
ESM::Position pos = *_sender->getUserData<ESM::Position>();
|
||||
std::string cellname = _sender->getUserString("Destination");
|
||||
bool interior = _sender->getUserString("interior") == "y";
|
||||
|
@ -173,14 +172,15 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWorld()->advanceTime(hours);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
|
||||
// Teleports any followers, too.
|
||||
MWWorld::ActionTeleport action(interior ? cellname : "", pos);
|
||||
action.execute(player);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0);
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenIn(1);
|
||||
}
|
||||
|
||||
void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -128,7 +126,7 @@ namespace MWGui
|
|||
MWBase::Environment::get().getStateManager()->quickSave("Autosave");
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
world->getFader ()->fadeOut(0.2);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2);
|
||||
setVisible(false);
|
||||
mProgressBar.setVisible (true);
|
||||
|
||||
|
@ -179,8 +177,7 @@ namespace MWGui
|
|||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||
bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified())
|
||||
&& (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
|
||||
bool full = (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
|
||||
&& (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());
|
||||
MWMechanics::NpcStats& npcstats = player.getClass().getNpcStats(player);
|
||||
bool werewolf = npcstats.isWerewolf();
|
||||
|
@ -243,7 +240,7 @@ namespace MWGui
|
|||
|
||||
void WaitDialog::stopWaiting ()
|
||||
{
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2);
|
||||
mProgressBar.setVisible (false);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed);
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "videowidget.hpp"
|
||||
#include "backgroundimage.hpp"
|
||||
#include "itemwidget.hpp"
|
||||
#include "screenfader.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
@ -72,7 +73,7 @@ namespace MWGui
|
|||
WindowManager::WindowManager(
|
||||
const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre,
|
||||
const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts,
|
||||
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding)
|
||||
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts)
|
||||
: mConsoleOnlyScripts(consoleOnlyScripts)
|
||||
, mGuiManager(NULL)
|
||||
, mRendering(ogre)
|
||||
|
@ -112,6 +113,7 @@ namespace MWGui
|
|||
, mCompanionWindow(NULL)
|
||||
, mVideoBackground(NULL)
|
||||
, mVideoWidget(NULL)
|
||||
, mScreenFader(NULL)
|
||||
, mTranslationDataStorage (translationDataStorage)
|
||||
, mCharGen(NULL)
|
||||
, mInputBlocker(NULL)
|
||||
|
@ -146,7 +148,7 @@ namespace MWGui
|
|||
|
||||
// Load fonts
|
||||
FontLoader fontLoader (encoding);
|
||||
fontLoader.loadAllFonts();
|
||||
fontLoader.loadAllFonts(exportFonts);
|
||||
|
||||
//Register own widgets with MyGUI
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
|
||||
|
@ -171,18 +173,14 @@ namespace MWGui
|
|||
ItemWidget::registerComponents();
|
||||
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerRepeatClick>("Controller");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
|
||||
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
|
||||
MyGUI::ResourceManager::getInstance().load("core.xml");
|
||||
|
||||
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
|
||||
|
||||
// Get size info from the Gui object
|
||||
int w = MyGUI::RenderManager::getInstance().getViewSize().width;
|
||||
int h = MyGUI::RenderManager::getInstance().getViewSize().height;
|
||||
|
||||
mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ());
|
||||
mLoadingScreen->onResChange (w,h);
|
||||
|
||||
//set up the hardware cursor manager
|
||||
mCursorManager = new SFO::SDLCursorManager();
|
||||
|
@ -192,7 +190,6 @@ namespace MWGui
|
|||
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
|
||||
|
||||
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
|
||||
//SDL_ShowCursor(false);
|
||||
|
||||
mCursorManager->setEnabled(true);
|
||||
|
||||
|
@ -217,13 +214,7 @@ namespace MWGui
|
|||
int w = MyGUI::RenderManager::getInstance().getViewSize().width;
|
||||
int h = MyGUI::RenderManager::getInstance().getViewSize().height;
|
||||
|
||||
MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget");
|
||||
dragAndDropWidget->setVisible(false);
|
||||
|
||||
mDragAndDrop = new DragAndDrop();
|
||||
mDragAndDrop->mIsOnDragAndDrop = false;
|
||||
mDragAndDrop->mDraggedWidget = 0;
|
||||
mDragAndDrop->mDragAndDropWidget = dragAndDropWidget;
|
||||
|
||||
mRecharge = new Recharge();
|
||||
mMenu = new MainMenu(w,h);
|
||||
|
@ -245,7 +236,7 @@ namespace MWGui
|
|||
trackWindow(mDialogueWindow, "dialogue");
|
||||
mContainerWindow = new ContainerWindow(mDragAndDrop);
|
||||
trackWindow(mContainerWindow, "container");
|
||||
mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop);
|
||||
mHud = new HUD(mShowFPSLevel, mDragAndDrop);
|
||||
mToolTips = new ToolTips();
|
||||
mScrollWindow = new ScrollWindow();
|
||||
mBookWindow = new BookWindow();
|
||||
|
@ -267,8 +258,9 @@ namespace MWGui
|
|||
mSoulgemDialog = new SoulgemDialog(mMessageBoxManager);
|
||||
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
|
||||
trackWindow(mCompanionWindow, "companion");
|
||||
mScreenFader = new ScreenFader();
|
||||
|
||||
mInputBlocker = mGui->createWidget<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);
|
||||
|
||||
|
@ -357,6 +349,7 @@ namespace MWGui
|
|||
delete mCursorManager;
|
||||
delete mRecharge;
|
||||
delete mCompanionWindow;
|
||||
delete mScreenFader;
|
||||
|
||||
cleanupGarbage();
|
||||
|
||||
|
@ -858,6 +851,8 @@ namespace MWGui
|
|||
mCompanionWindow->checkReferenceAvailable();
|
||||
mConsole->checkReferenceAvailable();
|
||||
mCompanionWindow->onFrame();
|
||||
|
||||
mScreenFader->update(frameDuration);
|
||||
}
|
||||
|
||||
void WindowManager::changeCell(MWWorld::CellStore* cell)
|
||||
|
@ -1019,7 +1014,6 @@ namespace MWGui
|
|||
{
|
||||
sizeVideo(x, y);
|
||||
mGuiManager->windowResized();
|
||||
mLoadingScreen->onResChange (x,y);
|
||||
if (!mHud)
|
||||
return; // UI not initialized yet
|
||||
|
||||
|
@ -1033,7 +1027,6 @@ namespace MWGui
|
|||
it->first->setSize(size);
|
||||
}
|
||||
|
||||
mHud->onResChange(x, y);
|
||||
mConsole->onResChange(x, y);
|
||||
mMenu->onResChange(x, y);
|
||||
mSettingsWindow->center();
|
||||
|
@ -1042,8 +1035,6 @@ namespace MWGui
|
|||
mBookWindow->center();
|
||||
mQuickKeysMenu->center();
|
||||
mSpellBuyingWindow->center();
|
||||
mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
|
||||
mInputBlocker->setSize(MyGUI::IntSize(x,y));
|
||||
}
|
||||
|
||||
void WindowManager::pushGuiMode(GuiMode mode)
|
||||
|
@ -1688,4 +1679,24 @@ namespace MWGui
|
|||
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void WindowManager::fadeScreenIn(const float time)
|
||||
{
|
||||
mScreenFader->fadeIn(time);
|
||||
}
|
||||
|
||||
void WindowManager::fadeScreenOut(const float time)
|
||||
{
|
||||
mScreenFader->fadeOut(time);
|
||||
}
|
||||
|
||||
void WindowManager::fadeScreenTo(const int percent, const float time)
|
||||
{
|
||||
mScreenFader->fadeTo(percent, time);
|
||||
}
|
||||
|
||||
void WindowManager::setScreenFactor(float factor)
|
||||
{
|
||||
mScreenFader->setFactor(factor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace MWGui
|
|||
class CompanionWindow;
|
||||
class VideoWidget;
|
||||
class WindowModal;
|
||||
class ScreenFader;
|
||||
|
||||
class WindowManager : public MWBase::WindowManager
|
||||
{
|
||||
|
@ -96,7 +97,7 @@ namespace MWGui
|
|||
WindowManager(const Compiler::Extensions& extensions, int fpsLevel,
|
||||
OEngine::Render::OgreRenderer *mOgre, const std::string& logpath,
|
||||
const std::string& cacheDir, bool consoleOnlyScripts,
|
||||
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding);
|
||||
Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts);
|
||||
virtual ~WindowManager();
|
||||
|
||||
void initUI();
|
||||
|
@ -324,6 +325,15 @@ namespace MWGui
|
|||
|
||||
virtual void pinWindow (MWGui::GuiWindow window);
|
||||
|
||||
/// Fade the screen in, over \a time seconds
|
||||
virtual void fadeScreenIn(const float time);
|
||||
/// Fade the screen out to black, over \a time seconds
|
||||
virtual void fadeScreenOut(const float time);
|
||||
/// Fade the screen to a specified percentage of black, over \a time seconds
|
||||
virtual void fadeScreenTo(const int percent, const float time);
|
||||
/// Darken the screen by \a factor (1.0 = no darkening). Works independently from screen fading.
|
||||
virtual void setScreenFactor (float factor);
|
||||
|
||||
private:
|
||||
bool mConsoleOnlyScripts;
|
||||
|
||||
|
@ -373,6 +383,7 @@ namespace MWGui
|
|||
CompanionWindow* mCompanionWindow;
|
||||
MyGUI::ImageBox* mVideoBackground;
|
||||
VideoWidget* mVideoWidget;
|
||||
ScreenFader* mScreenFader;
|
||||
|
||||
Translation::Storage& mTranslationDataStorage;
|
||||
Cursor* mSoftwareCursor;
|
||||
|
|
|
@ -11,6 +11,17 @@ namespace MWGui
|
|||
mPinButton = window->getSkinWidget ("Button");
|
||||
|
||||
mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked);
|
||||
|
||||
MyGUI::Button* button = NULL;
|
||||
MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action");
|
||||
for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it)
|
||||
{
|
||||
if ((*it)->isUserString("HideWindowOnDoubleClick"))
|
||||
button = (*it)->castType<MyGUI::Button>();
|
||||
}
|
||||
|
||||
if (button)
|
||||
button->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WindowPinnableBase::onDoubleClick);
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
|
||||
|
@ -25,6 +36,11 @@ namespace MWGui
|
|||
onPinToggled();
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onDoubleClick(MyGUI::Widget *_sender)
|
||||
{
|
||||
onTitleDoubleClicked();
|
||||
}
|
||||
|
||||
void WindowPinnableBase::setPinned(bool pinned)
|
||||
{
|
||||
if (pinned != mPinned)
|
||||
|
|
|
@ -17,9 +17,11 @@ namespace MWGui
|
|||
|
||||
private:
|
||||
void onPinButtonClicked(MyGUI::Widget* _sender);
|
||||
void onDoubleClick(MyGUI::Widget* _sender);
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled() = 0;
|
||||
virtual void onTitleDoubleClicked() = 0;
|
||||
|
||||
MyGUI::Widget* mPinButton;
|
||||
bool mPinned;
|
||||
|
|
|
@ -805,16 +805,16 @@ namespace MWInput
|
|||
if (MyGUI::InputManager::getInstance ().isModalAny())
|
||||
return;
|
||||
|
||||
if((!MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||
|| MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue)
|
||||
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
|
||||
if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
|
||||
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed()
|
||||
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);
|
||||
}
|
||||
else if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Journal)
|
||||
else if(MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Journal))
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Journal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,58 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
|
|||
return false;
|
||||
}
|
||||
|
||||
class CheckActorCommanded : public MWMechanics::EffectSourceVisitor
|
||||
{
|
||||
MWWorld::Ptr mActor;
|
||||
public:
|
||||
bool mCommanded;
|
||||
CheckActorCommanded(MWWorld::Ptr actor)
|
||||
: mActor(actor)
|
||||
, mCommanded(false){}
|
||||
|
||||
virtual void visit (MWMechanics::EffectKey key,
|
||||
const std::string& sourceName, int casterActorId,
|
||||
float magnitude, float remainingTime = -1)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc())
|
||||
|| (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name()))
|
||||
&& casterActorId == player.getClass().getCreatureStats(player).getActorId()
|
||||
&& magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel())
|
||||
mCommanded = true;
|
||||
}
|
||||
};
|
||||
|
||||
void adjustCommandedActor (const MWWorld::Ptr& actor)
|
||||
{
|
||||
CheckActorCommanded check(actor);
|
||||
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
stats.getActiveSpells().visitEffectSources(check);
|
||||
|
||||
bool hasCommandPackage = false;
|
||||
|
||||
std::list<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)
|
||||
{
|
||||
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
|
||||
|
@ -268,7 +344,8 @@ namespace MWMechanics
|
|||
void Actors::adjustMagicEffects (const MWWorld::Ptr& creature)
|
||||
{
|
||||
CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature);
|
||||
|
||||
if (creatureStats.isDead())
|
||||
return;
|
||||
MagicEffects now = creatureStats.getSpells().getMagicEffects();
|
||||
|
||||
if (creature.getTypeName()==typeid (ESM::NPC).name())
|
||||
|
@ -290,17 +367,17 @@ namespace MWMechanics
|
|||
{
|
||||
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
|
||||
|
||||
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
|
||||
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
|
||||
int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getBase();
|
||||
int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getBase();
|
||||
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getModified();
|
||||
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||
int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getModified();
|
||||
|
||||
double magickaFactor =
|
||||
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5;
|
||||
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 1;
|
||||
|
||||
DynamicStat<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);
|
||||
creatureStats.setMagicka(magicka);
|
||||
|
||||
|
@ -612,9 +689,9 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
|
||||
bool found = creatureMap.find(it->first) != creatureMap.end();
|
||||
int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude;
|
||||
if (found != (magnitude > 0))
|
||||
|
@ -665,34 +742,31 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
{
|
||||
// Summon lifetime has expired. Try to delete the creature.
|
||||
int actorId = creatureMap[it->first];
|
||||
creatureMap.erase(it->first);
|
||||
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
|
||||
// plays though, which is a rather lame exploit in vanilla.
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||
|
||||
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<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);
|
||||
}
|
||||
// Effect has ended
|
||||
std::map<int, int>::iterator foundCreature = creatureMap.find(it->first);
|
||||
cleanupSummonedCreature(creatureStats, foundCreature->second);
|
||||
creatureMap.erase(foundCreature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
|
||||
{
|
||||
|
@ -1005,7 +1079,10 @@ namespace MWMechanics
|
|||
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
{
|
||||
if (timerUpdateAITargets == 0)
|
||||
{
|
||||
{
|
||||
if (iter->first != player)
|
||||
adjustCommandedActor(iter->first);
|
||||
|
||||
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||
{
|
||||
if (it->first == iter->first || iter->first == player) // player is not AI-controlled
|
||||
|
@ -1053,10 +1130,12 @@ namespace MWMechanics
|
|||
//KnockedOutOneFrameLogic
|
||||
//Used for "OnKnockedOut" command
|
||||
//Put here to ensure that it's run for PRECISELY one frame.
|
||||
if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Start it for one frame if nessesary
|
||||
if (stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
|
||||
{ //Start it for one frame if nessesary
|
||||
stats.setKnockedDownOneFrame(true);
|
||||
}
|
||||
else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Turn off KnockedOutOneframe
|
||||
else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame())
|
||||
{ //Turn off KnockedOutOneframe
|
||||
stats.setKnockedDownOneFrame(false);
|
||||
stats.setKnockedDownOverOneFrame(true);
|
||||
}
|
||||
|
|
|
@ -51,14 +51,17 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration
|
|||
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
|
||||
float x = pos.pos[0] - tPos.pos[0];
|
||||
float y = pos.pos[1] - tPos.pos[1];
|
||||
float dirToDoor = std::atan2(x,y) + pos.rot[2] + mAdjAngle; //Calculates the direction to the door, relative to the direction of the NPC
|
||||
// For example, if the NPC is directly facing the door this will be pi/2
|
||||
|
||||
// Make actor move away from the door
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = -1 * std::sin(dirToDoor); //I knew I'd use trig someday
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = -1 * std::cos(dirToDoor);
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
|
||||
//Make all nearby actors also avoid the door
|
||||
// Turn away from the door and move when turn completed
|
||||
if (zTurn(actor, Ogre::Radian(std::atan2(x,y) + mAdjAngle), Ogre::Degree(5)))
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
else
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
|
||||
// Make all nearby actors also avoid the door
|
||||
std::vector<MWWorld::Ptr> 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++) {
|
||||
|
|
|
@ -75,6 +75,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
||||
|
||||
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
|
||||
const float* const leaderPos = actor.getRefData().getPosition().pos;
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
|
||||
: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
|
||||
{
|
||||
}
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
|
||||
: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
|
||||
{
|
||||
}
|
||||
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
|
||||
: mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded)
|
||||
: mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,11 @@ int MWMechanics::AiFollow::getTypeId() const
|
|||
return TypeIdFollow;
|
||||
}
|
||||
|
||||
bool MWMechanics::AiFollow::isCommanded() const
|
||||
{
|
||||
return mCommanded;
|
||||
}
|
||||
|
||||
void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
{
|
||||
std::auto_ptr<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->mCellId = mCellId;
|
||||
follow->mAlwaysFollow = mAlwaysFollow;
|
||||
follow->mCommanded = mCommanded;
|
||||
|
||||
ESM::AiSequence::AiPackageContainer package;
|
||||
package.mType = ESM::AiSequence::Ai_Follow;
|
||||
|
@ -120,6 +126,7 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
|
|||
: mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration)
|
||||
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
|
||||
, mActorId(follow->mTargetId), mCellId(follow->mCellId)
|
||||
, mCommanded(follow->mCommanded)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace MWMechanics
|
|||
/// Follow Actor for duration or until you arrive at a position in a cell
|
||||
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
|
||||
/// Follow Actor indefinitively
|
||||
AiFollow(const std::string &ActorId);
|
||||
AiFollow(const std::string &ActorId, bool commanded=false);
|
||||
|
||||
AiFollow(const ESM::AiSequence::AiFollow* follow);
|
||||
|
||||
|
@ -44,10 +44,13 @@ namespace MWMechanics
|
|||
|
||||
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const;
|
||||
|
||||
bool isCommanded() const;
|
||||
|
||||
private:
|
||||
/// This will make the actor always follow.
|
||||
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
|
||||
bool mAlwaysFollow;
|
||||
bool mCommanded;
|
||||
float mRemainingDuration; // Seconds
|
||||
float mX;
|
||||
float mY;
|
||||
|
|
|
@ -82,6 +82,20 @@ std::list<AiPackage*>::const_iterator AiSequence::end() const
|
|||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
throw std::runtime_error("Can't add AI packages to player");
|
||||
|
||||
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
// Notify AiWander of our current position so we can return to it after combat finished
|
||||
|
|
|
@ -54,6 +54,8 @@ namespace MWMechanics
|
|||
std::list<AiPackage*>::const_iterator begin() const;
|
||||
std::list<AiPackage*>::const_iterator end() const;
|
||||
|
||||
void erase (std::list<AiPackage*>::const_iterator package);
|
||||
|
||||
/// Returns currently executing AiPackage type
|
||||
/** \see enum AiPackage::TypeId **/
|
||||
int getTypeId() const;
|
||||
|
|
|
@ -395,22 +395,35 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
CharacterState walkState = runStateToWalkState(mMovementState);
|
||||
const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState));
|
||||
anim = stateinfo->groupname;
|
||||
|
||||
if (mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
|
||||
speedmult = mMovementSpeed / vel;
|
||||
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,
|
||||
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
||||
}
|
||||
|
@ -1174,6 +1187,36 @@ void CharacterController::update(float duration)
|
|||
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||
bool flying = world->isFlying(mPtr);
|
||||
CreatureStats &stats = cls.getCreatureStats(mPtr);
|
||||
|
||||
//Force Jump Logic
|
||||
|
||||
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
|
||||
if(!inwater && !flying)
|
||||
{
|
||||
//Force Jump
|
||||
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))
|
||||
{
|
||||
if(onground)
|
||||
{
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 1;
|
||||
}
|
||||
else
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 0;
|
||||
}
|
||||
//Force Move Jump, only jump if they're otherwise moving
|
||||
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)
|
||||
{
|
||||
|
||||
if(onground)
|
||||
{
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 1;
|
||||
}
|
||||
else
|
||||
cls.getMovementSettings(mPtr).mPosition[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Ogre::Vector3 vec = cls.getMovementVector(mPtr);
|
||||
Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition);
|
||||
if(vec.z > 0.0f) // to avoid slow-down when jumping
|
||||
|
@ -1183,7 +1226,7 @@ void CharacterController::update(float duration)
|
|||
vec.x = vecXY.x;
|
||||
vec.y = vecXY.y;
|
||||
}
|
||||
else
|
||||
else
|
||||
vec.normalise();
|
||||
|
||||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
|
@ -1352,8 +1395,7 @@ void CharacterController::update(float duration)
|
|||
}
|
||||
else
|
||||
{
|
||||
if(!(vec.z > 0.0f))
|
||||
mJumpState = JumpState_None;
|
||||
mJumpState = JumpState_None;
|
||||
vec.z = 0.0f;
|
||||
|
||||
inJump = false;
|
||||
|
@ -1482,6 +1524,8 @@ void CharacterController::update(float duration)
|
|||
else if (mAnimation)
|
||||
mAnimation->updateEffects(duration);
|
||||
mSkipAnim = false;
|
||||
|
||||
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -169,12 +169,12 @@ namespace MWMechanics
|
|||
|
||||
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
|
||||
|
||||
const MWWorld::Class &othercls = victim.getClass();
|
||||
if(!othercls.isActor()) // Can't hit non-actors
|
||||
return;
|
||||
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
|
||||
if(otherstats.isDead()) // Can't hit dead actors
|
||||
if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
|
||||
// Can't hit non-actors or dead actors
|
||||
{
|
||||
reduceWeaponCondition(0.f, false, weapon, attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
if(attacker.getRefData().getHandle() == "player")
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||
|
@ -189,6 +189,7 @@ namespace MWMechanics
|
|||
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
|
||||
{
|
||||
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
|
||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -209,6 +210,8 @@ namespace MWMechanics
|
|||
damage *= fDamageStrengthBase +
|
||||
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
|
||||
|
||||
adjustWeaponDamage(damage, weapon);
|
||||
reduceWeaponCondition(damage, true, weapon, attacker);
|
||||
|
||||
if(attacker.getRefData().getHandle() == "player")
|
||||
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
|
||||
|
@ -295,4 +298,42 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void reduceWeaponCondition(float damage, bool hit, MWWorld::Ptr &weapon, const MWWorld::Ptr &attacker)
|
||||
{
|
||||
if (weapon.isEmpty())
|
||||
return;
|
||||
|
||||
if (!hit)
|
||||
damage = 0.f;
|
||||
|
||||
const bool weaphashealth = weapon.getClass().hasItemHealth(weapon);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weaphealth = weapon.getClass().getItemHealth(weapon);
|
||||
|
||||
const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker
|
|||
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
|
||||
|
||||
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
|
||||
/// @note \a victim may be empty (e.g. for a hit on terrain), a non-actor (environment objects) or an actor
|
||||
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
|
||||
const Ogre::Vector3& hitPosition);
|
||||
|
||||
|
@ -22,6 +23,15 @@ float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, in
|
|||
/// Applies damage to attacker based on the victim's elemental shields.
|
||||
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
|
||||
|
||||
/// @param damage Unmitigated weapon damage of the attack
|
||||
/// @param hit Was the attack successful?
|
||||
/// @param weapon The weapon used.
|
||||
/// @note if the weapon is unequipped as result of condition damage, a new Ptr will be assigned to \a weapon.
|
||||
void reduceWeaponCondition (float damage, bool hit, MWWorld::Ptr& weapon, const MWWorld::Ptr& attacker);
|
||||
|
||||
/// Adjust weapon damage based on its condition. A used weapon will be less effective.
|
||||
void adjustWeaponDamage (float& damage, const MWWorld::Ptr& weapon);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -227,7 +227,9 @@ namespace MWMechanics
|
|||
Flag_ForceRun = 1,
|
||||
Flag_ForceSneak = 2,
|
||||
Flag_Run = 4,
|
||||
Flag_Sneak = 8
|
||||
Flag_Sneak = 8,
|
||||
Flag_ForceJump = 16,
|
||||
Flag_ForceMoveJump = 32
|
||||
};
|
||||
enum Stance
|
||||
{
|
||||
|
|
|
@ -973,6 +973,9 @@ namespace MWMechanics
|
|||
if (!it->getClass().isNpc())
|
||||
continue;
|
||||
|
||||
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
||||
continue;
|
||||
|
||||
// Will the witness report the crime?
|
||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
||||
{
|
||||
|
@ -1071,6 +1074,9 @@ namespace MWMechanics
|
|||
if (*it != victim && type == OT_Assault)
|
||||
aggression = iFightAttacking;
|
||||
|
||||
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
||||
continue;
|
||||
|
||||
if (it->getClass().isClass(*it, "guard"))
|
||||
{
|
||||
// Mark as Alarmed for dialogue
|
||||
|
@ -1100,6 +1106,35 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker)
|
||||
{
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
return;
|
||||
|
||||
std::list<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)
|
||||
{
|
||||
if (observer.getClass().getCreatureStats(observer).isDead())
|
||||
|
|
|
@ -119,6 +119,7 @@ namespace MWMechanics
|
|||
OffenseType type, int arg=0);
|
||||
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||
OffenseType type, int arg=0);
|
||||
virtual void actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
|
||||
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
||||
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count);
|
||||
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
|
||||
|
|
|
@ -34,7 +34,6 @@ MWMechanics::NpcStats::NpcStats()
|
|||
, mProfit(0)
|
||||
, mTimeToStartDrowning(20.0)
|
||||
, mLastDrowningHit(0)
|
||||
, mLevelHealthBonus(0)
|
||||
{
|
||||
mSkillIncreases.resize (ESM::Attribute::Length, 0);
|
||||
}
|
||||
|
@ -262,8 +261,7 @@ void MWMechanics::NpcStats::levelUp()
|
|||
// "When you gain a level, in addition to increasing three primary attributes, your Health
|
||||
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
|
||||
// the Health increase is calculated from the increased Endurance"
|
||||
mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat();
|
||||
updateHealth();
|
||||
setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->getFloat());
|
||||
|
||||
setLevel(getLevel()+1);
|
||||
}
|
||||
|
@ -273,7 +271,7 @@ void MWMechanics::NpcStats::updateHealth()
|
|||
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
|
||||
|
||||
setHealth(static_cast<int> (0.5 * (strength + endurance)) + mLevelHealthBonus);
|
||||
setHealth(static_cast<int> (0.5 * (strength + endurance)));
|
||||
}
|
||||
|
||||
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
|
||||
|
@ -497,7 +495,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
|
|||
|
||||
state.mTimeToStartDrowning = mTimeToStartDrowning;
|
||||
state.mLastDrowningHit = mLastDrowningHit;
|
||||
state.mLevelHealthBonus = mLevelHealthBonus;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
|
||||
|
@ -549,5 +546,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
|
|||
|
||||
mTimeToStartDrowning = state.mTimeToStartDrowning;
|
||||
mLastDrowningHit = state.mLastDrowningHit;
|
||||
mLevelHealthBonus = state.mLevelHealthBonus;
|
||||
}
|
||||
|
|
|
@ -52,8 +52,6 @@ namespace MWMechanics
|
|||
/// time since last hit from drowning
|
||||
float mLastDrowningHit;
|
||||
|
||||
float mLevelHealthBonus;
|
||||
|
||||
public:
|
||||
|
||||
NpcStats();
|
||||
|
@ -104,7 +102,7 @@ namespace MWMechanics
|
|||
|
||||
void updateHealth();
|
||||
///< Calculate health based on endurance and strength.
|
||||
/// Called at character creation and at level up.
|
||||
/// Called at character creation.
|
||||
|
||||
void flagAsUsed (const std::string& id);
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace MWMechanics
|
|||
return schoolSkillMap[school];
|
||||
}
|
||||
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
|
||||
{
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
|
@ -123,14 +123,17 @@ namespace MWMechanics
|
|||
if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
|
||||
castChance = 100;
|
||||
|
||||
return std::max(0.f, std::min(100.f, castChance));
|
||||
if (!cap)
|
||||
return std::max(0.f, castChance);
|
||||
else
|
||||
return std::max(0.f, std::min(100.f, castChance));
|
||||
}
|
||||
|
||||
float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool)
|
||||
float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
|
||||
{
|
||||
const ESM::Spell* spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||
return getSpellSuccessChance(spell, actor, effectiveSchool);
|
||||
return getSpellSuccessChance(spell, actor, effectiveSchool, cap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -184,6 +187,10 @@ namespace MWMechanics
|
|||
float resisted = 0;
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
// Effects with no resistance attribute belonging to them can not be resisted
|
||||
if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
|
||||
return 0.f;
|
||||
|
||||
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
|
||||
|
||||
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
|
@ -191,12 +198,13 @@ namespace MWMechanics
|
|||
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
|
||||
|
||||
// This makes spells that are easy to cast harder to resist and vice versa
|
||||
float castChance = 100.f;
|
||||
if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
|
||||
{
|
||||
float castChance = getSpellSuccessChance(spell, caster);
|
||||
if (castChance > 0)
|
||||
x *= 50 / castChance;
|
||||
castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
|
||||
}
|
||||
if (castChance > 0)
|
||||
x *= 50 / castChance;
|
||||
|
||||
float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
|
||||
|
@ -230,6 +238,44 @@ namespace MWMechanics
|
|||
return -(resistance-100) / 100.f;
|
||||
}
|
||||
|
||||
/// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
|
||||
bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer)
|
||||
{
|
||||
switch (effectId)
|
||||
{
|
||||
case ESM::MagicEffect::Levitate:
|
||||
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled())
|
||||
{
|
||||
if (castByPlayer)
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ESM::MagicEffect::Soultrap:
|
||||
if (!target.getClass().isNpc() // no messagebox for NPCs
|
||||
&& (target.getTypeName() == typeid(ESM::Creature).name() && target.get<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)
|
||||
: mCaster(caster)
|
||||
, mTarget(target)
|
||||
|
@ -318,23 +364,8 @@ namespace MWMechanics
|
|||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
effectIt->mEffectID);
|
||||
|
||||
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate)
|
||||
{
|
||||
if (castByPlayer)
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
|
||||
if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer))
|
||||
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 (castByPlayer && target != caster
|
||||
|
|
|
@ -26,11 +26,12 @@ namespace MWMechanics
|
|||
* @param spell spell to cast
|
||||
* @param actor calculate spell success chance for this actor (depends on actor's skills)
|
||||
* @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here
|
||||
* @attention actor has to be an NPC and not a creature!
|
||||
* @return success chance from 0 to 100 (in percent)
|
||||
* @param cap cap the result to 100%?
|
||||
* @note actor can be an NPC or a creature
|
||||
* @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned.
|
||||
*/
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL);
|
||||
float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL);
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true);
|
||||
float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true);
|
||||
|
||||
int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor);
|
||||
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadstat.hpp>
|
||||
#include <components/misc/resourcehelpers.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";
|
||||
float starttime = std::numeric_limits<float>::max();
|
||||
float stoptime = 0.0f;
|
||||
// Have to find keys in reverse (see reset method)
|
||||
|
||||
// Pick the last Loop Stop key and the last Loop Start key.
|
||||
// This is required because of broken text keys in AshVampire.nif.
|
||||
// It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback
|
||||
// but the animation velocity calculation uses the second one.
|
||||
// As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated,
|
||||
// because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough.
|
||||
NifOgre::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin());
|
||||
while(keyiter != keys.rend())
|
||||
{
|
||||
|
@ -476,8 +483,18 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
|
|||
starttime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
else if(keyiter->second == loopstop || keyiter->second == stop)
|
||||
++keyiter;
|
||||
}
|
||||
keyiter = keys.rbegin();
|
||||
while(keyiter != keys.rend())
|
||||
{
|
||||
if (keyiter->second == stop)
|
||||
stoptime = keyiter->first;
|
||||
else if (keyiter->second == loopstop)
|
||||
{
|
||||
stoptime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
++keyiter;
|
||||
}
|
||||
|
||||
|
@ -1191,13 +1208,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
|
|||
if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename)
|
||||
return;
|
||||
|
||||
// fix texture extension to .dds
|
||||
if (texture.size() > 4)
|
||||
{
|
||||
texture[texture.size()-3] = 'd';
|
||||
texture[texture.size()-2] = 'd';
|
||||
texture[texture.size()-1] = 's';
|
||||
}
|
||||
std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture);
|
||||
|
||||
EffectParams params;
|
||||
params.mModelName = model;
|
||||
|
@ -1255,7 +1266,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
|
|||
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++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)
|
||||
{
|
||||
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
|
||||
tus->setTextureName("textures\\" + texture);
|
||||
tus->setTextureName(correctedTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1415,6 +1426,15 @@ void ObjectAnimation::addLight(const ESM::Light *light)
|
|||
addExtraLight(mInsert->getCreator(), mObjectRoot, light);
|
||||
}
|
||||
|
||||
void ObjectAnimation::removeParticles()
|
||||
{
|
||||
for (unsigned int i=0; i<mObjectRoot->mParticles.size(); ++i)
|
||||
{
|
||||
mObjectRoot->mSceneMgr->destroyParticleSystem(mObjectRoot->mParticles[i]);
|
||||
}
|
||||
mObjectRoot->mParticles.clear();
|
||||
}
|
||||
|
||||
|
||||
class FindEntityTransparency {
|
||||
public:
|
||||
|
|
|
@ -306,6 +306,7 @@ public:
|
|||
virtual void attachArrow() {}
|
||||
virtual void releaseArrow() {}
|
||||
void enableLights(bool enable);
|
||||
virtual void enableHeadAnimation(bool enable) {}
|
||||
|
||||
Ogre::AxisAlignedBox getWorldBounds();
|
||||
|
||||
|
@ -323,6 +324,7 @@ public:
|
|||
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model);
|
||||
|
||||
void addLight(const ESM::Light *light);
|
||||
void removeParticles();
|
||||
|
||||
bool canBatch() const;
|
||||
void fillBatch(Ogre::StaticGeometry *sg);
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace MWRender
|
|||
, mViewport(NULL)
|
||||
, mCamera(NULL)
|
||||
, mNode(NULL)
|
||||
, mRecover(false)
|
||||
{
|
||||
mCharacter.mCell = NULL;
|
||||
}
|
||||
|
@ -46,6 +47,16 @@ namespace MWRender
|
|||
|
||||
}
|
||||
|
||||
void CharacterPreview::onFrame()
|
||||
{
|
||||
if (mRecover)
|
||||
{
|
||||
setupRenderTarget();
|
||||
mRenderTarget->update();
|
||||
mRecover = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterPreview::setup ()
|
||||
{
|
||||
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
|
@ -83,19 +94,10 @@ namespace MWRender
|
|||
mCamera->setNearClipDistance (0.01);
|
||||
mCamera->setFarClipDistance (1000);
|
||||
|
||||
mTexture = Ogre::TextureManager::getSingleton().getByName (mName);
|
||||
if (mTexture.isNull ())
|
||||
mTexture = Ogre::TextureManager::getSingleton().createManual(mName,
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
mTexture = Ogre::TextureManager::getSingleton().createManual(mName,
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET, this);
|
||||
|
||||
mRenderTarget = mTexture->getBuffer()->getRenderTarget();
|
||||
mRenderTarget->removeAllViewports ();
|
||||
mViewport = mRenderTarget->addViewport(mCamera);
|
||||
mViewport->setOverlaysEnabled(false);
|
||||
mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
|
||||
mViewport->setShadowsEnabled(false);
|
||||
mRenderTarget->setActive(true);
|
||||
mRenderTarget->setAutoUpdated (false);
|
||||
setupRenderTarget();
|
||||
|
||||
onSetup ();
|
||||
}
|
||||
|
@ -107,6 +109,7 @@ namespace MWRender
|
|||
mSceneMgr->destroyAllCameras();
|
||||
delete mAnimation;
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
Ogre::TextureManager::getSingleton().remove(mName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +130,39 @@ namespace MWRender
|
|||
onSetup();
|
||||
}
|
||||
|
||||
void CharacterPreview::loadResource(Ogre::Resource *resource)
|
||||
{
|
||||
Ogre::Texture* tex = dynamic_cast<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)
|
||||
: CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0))
|
||||
, mSelectionBuffer(NULL)
|
||||
, mSizeX(0)
|
||||
, mSizeY(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -141,7 +171,21 @@ namespace MWRender
|
|||
delete mSelectionBuffer;
|
||||
}
|
||||
|
||||
void InventoryPreview::update(int sizeX, int sizeY)
|
||||
void InventoryPreview::resize(int sizeX, int sizeY)
|
||||
{
|
||||
mSizeX = sizeX;
|
||||
mSizeY = sizeY;
|
||||
|
||||
mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
|
||||
mTexture->load();
|
||||
|
||||
if (!mRenderTarget)
|
||||
setupRenderTarget();
|
||||
|
||||
mRenderTarget->update();
|
||||
}
|
||||
|
||||
void InventoryPreview::update()
|
||||
{
|
||||
mAnimation->updateParts();
|
||||
|
||||
|
@ -197,15 +241,25 @@ namespace MWRender
|
|||
|
||||
mAnimation->runAnimation(0.0f);
|
||||
|
||||
mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024)));
|
||||
|
||||
mNode->setOrientation (Ogre::Quaternion::IDENTITY);
|
||||
|
||||
mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
|
||||
mTexture->load();
|
||||
|
||||
if (!mRenderTarget)
|
||||
setupRenderTarget();
|
||||
|
||||
mRenderTarget->update();
|
||||
|
||||
mSelectionBuffer->update();
|
||||
}
|
||||
|
||||
void InventoryPreview::setupRenderTarget()
|
||||
{
|
||||
CharacterPreview::setupRenderTarget();
|
||||
mViewport->setDimensions (0, 0, std::min(1.f, float(mSizeX) / float(512)), std::min(1.f, float(mSizeY) / float(1024)));
|
||||
}
|
||||
|
||||
int InventoryPreview::getSlotSelected (int posX, int posY)
|
||||
{
|
||||
return mSelectionBuffer->getSelected (posX, posY);
|
||||
|
@ -243,6 +297,7 @@ namespace MWRender
|
|||
|
||||
void RaceSelectionPreview::render()
|
||||
{
|
||||
mTexture->load();
|
||||
mRenderTarget->update();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MWRender
|
|||
|
||||
class NpcAnimation;
|
||||
|
||||
class CharacterPreview
|
||||
class CharacterPreview : public Ogre::ManualResourceLoader
|
||||
{
|
||||
public:
|
||||
CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name,
|
||||
|
@ -34,6 +34,13 @@ namespace MWRender
|
|||
|
||||
virtual void rebuild();
|
||||
|
||||
void onFrame();
|
||||
|
||||
void loadResource(Ogre::Resource *resource);
|
||||
|
||||
private:
|
||||
bool mRecover; // Texture content was lost and needs to be re-rendered
|
||||
|
||||
private:
|
||||
CharacterPreview(const CharacterPreview&);
|
||||
CharacterPreview& operator=(const CharacterPreview&);
|
||||
|
@ -41,6 +48,8 @@ namespace MWRender
|
|||
protected:
|
||||
virtual bool renderHeadOnly() { return false; }
|
||||
|
||||
virtual void setupRenderTarget();
|
||||
|
||||
Ogre::TexturePtr mTexture;
|
||||
Ogre::RenderTarget* mRenderTarget;
|
||||
Ogre::Viewport* mViewport;
|
||||
|
@ -72,11 +81,17 @@ namespace MWRender
|
|||
virtual ~InventoryPreview();
|
||||
virtual void onSetup();
|
||||
|
||||
void update(int sizeX, int sizeY);
|
||||
void update(); // Render preview again, e.g. after changed equipment
|
||||
void resize(int sizeX, int sizeY);
|
||||
|
||||
int getSlotSelected(int posX, int posY);
|
||||
|
||||
protected:
|
||||
virtual void setupRenderTarget();
|
||||
|
||||
private:
|
||||
int mSizeX;
|
||||
int mSizeY;
|
||||
|
||||
OEngine::Render::SelectionBuffer* mSelectionBuffer;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "effectmanager.hpp"
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreParticleSystem.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);
|
||||
sceneNode->setScale(scale,scale,scale);
|
||||
|
||||
// fix texture extension to .dds
|
||||
if (textureOverride.size() > 4)
|
||||
{
|
||||
textureOverride[textureOverride.size()-3] = 'd';
|
||||
textureOverride[textureOverride.size()-2] = 'd';
|
||||
textureOverride[textureOverride.size()-1] = 's';
|
||||
}
|
||||
|
||||
|
||||
NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
|
||||
|
||||
// TODO: turn off shadow casting
|
||||
|
@ -44,6 +37,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
|
|||
|
||||
if (!textureOverride.empty())
|
||||
{
|
||||
std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(textureOverride);
|
||||
for(size_t i = 0;i < scene->mParticles.size(); ++i)
|
||||
{
|
||||
Ogre::ParticleSystem* partSys = scene->mParticles[i];
|
||||
|
@ -59,7 +53,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr
|
|||
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
|
||||
{
|
||||
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
|
||||
tus->setTextureName("textures\\" + textureOverride);
|
||||
tus->setTextureName(correctedTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
|
|||
return;
|
||||
|
||||
Ogre::Image image;
|
||||
tex->load();
|
||||
tex->convertToImage(image);
|
||||
|
||||
Ogre::DataStreamPtr encoded = image.encode("tga");
|
||||
|
@ -137,6 +138,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
|
|||
return;
|
||||
|
||||
Ogre::Image image;
|
||||
tex->load();
|
||||
tex->convertToImage(image);
|
||||
|
||||
fog->mFogTextures.push_back(ESM::FogTexture());
|
||||
|
@ -581,6 +583,8 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
|
|||
}
|
||||
}
|
||||
|
||||
tex->load();
|
||||
|
||||
// copy to the texture
|
||||
// NOTE: Could be optimized later. We actually only need to update the region that changed.
|
||||
// Not a big deal at the moment, the FoW is only 32x32 anyway.
|
||||
|
|
|
@ -67,11 +67,16 @@ namespace MWRender
|
|||
{
|
||||
|
||||
HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference)
|
||||
: mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0)
|
||||
: mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0), mEnabled(true)
|
||||
{
|
||||
resetBlinkTimer();
|
||||
}
|
||||
|
||||
void HeadAnimationTime::setEnabled(bool enabled)
|
||||
{
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
void HeadAnimationTime::resetBlinkTimer()
|
||||
{
|
||||
mBlinkTimer = -(2 + (std::rand() / double(RAND_MAX*1.0)) * 6);
|
||||
|
@ -79,6 +84,9 @@ void HeadAnimationTime::resetBlinkTimer()
|
|||
|
||||
void HeadAnimationTime::update(float dt)
|
||||
{
|
||||
if (!mEnabled)
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference))
|
||||
{
|
||||
mBlinkTimer += dt;
|
||||
|
@ -864,6 +872,11 @@ void NpcAnimation::setAlpha(float alpha)
|
|||
}
|
||||
}
|
||||
|
||||
void NpcAnimation::enableHeadAnimation(bool enable)
|
||||
{
|
||||
mHeadAnimationTime->setEnabled(enable);
|
||||
}
|
||||
|
||||
void NpcAnimation::preRender(Ogre::Camera *camera)
|
||||
{
|
||||
Animation::preRender(camera);
|
||||
|
|
|
@ -26,6 +26,8 @@ private:
|
|||
|
||||
float mBlinkTimer;
|
||||
|
||||
bool mEnabled;
|
||||
|
||||
float mValue;
|
||||
private:
|
||||
void resetBlinkTimer();
|
||||
|
@ -34,6 +36,8 @@ public:
|
|||
|
||||
void update(float dt);
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
void setTalkStart(float value);
|
||||
void setTalkStop(float value);
|
||||
void setBlinkStart(float value);
|
||||
|
@ -125,6 +129,8 @@ public:
|
|||
ViewMode viewMode=VM_Normal);
|
||||
virtual ~NpcAnimation();
|
||||
|
||||
virtual void enableHeadAnimation(bool enable);
|
||||
|
||||
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
|
||||
|
||||
virtual Ogre::Vector3 runAnimation(float timepassed);
|
||||
|
|
|
@ -73,14 +73,19 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
|
|||
ptr.getRefData().setBaseNode(insert);
|
||||
}
|
||||
|
||||
void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch)
|
||||
void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch, bool addLight)
|
||||
{
|
||||
insertBegin(ptr);
|
||||
|
||||
std::auto_ptr<ObjectAnimation> anim(new ObjectAnimation(ptr, mesh));
|
||||
|
||||
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())
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
, mRootNode(NULL)
|
||||
{}
|
||||
~Objects(){}
|
||||
void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false);
|
||||
void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false, bool addLight=false);
|
||||
|
||||
ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/terrain/defaultworld.hpp>
|
||||
#include <components/terrain/terraingrid.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -45,7 +46,6 @@
|
|||
#include "globalmap.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "effectmanager.hpp"
|
||||
#include "terraingrid.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -212,11 +212,6 @@ MWRender::Actors& RenderingManager::getActors(){
|
|||
return *mActors;
|
||||
}
|
||||
|
||||
OEngine::Render::Fader* RenderingManager::getFader()
|
||||
{
|
||||
return mRendering.getFader();
|
||||
}
|
||||
|
||||
MWRender::Camera* RenderingManager::getCamera() const
|
||||
{
|
||||
return mCamera;
|
||||
|
@ -345,7 +340,7 @@ void RenderingManager::update (float duration, bool paused)
|
|||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
|
||||
int blind = player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude;
|
||||
mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f)));
|
||||
MWBase::Environment::get().getWindowManager()->setScreenFactor(std::max(0.f, 1.f-(blind / 100.f)));
|
||||
setAmbientMode();
|
||||
|
||||
// player position
|
||||
|
@ -1050,7 +1045,7 @@ void RenderingManager::enableTerrain(bool enable)
|
|||
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
|
||||
else
|
||||
mTerrain = new MWRender::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
|
||||
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
|
||||
Settings::Manager::getBool("split", "Shadows"));
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "sky.hpp"
|
||||
#include "debugging.hpp"
|
||||
|
||||
#include <openengine/ogre/fader.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
@ -98,8 +96,6 @@ public:
|
|||
void toggleLight();
|
||||
bool toggleRenderMode(int mode);
|
||||
|
||||
OEngine::Render::Fader* getFader();
|
||||
|
||||
void removeCell (MWWorld::CellStore *store);
|
||||
|
||||
/// \todo this function should be removed later. Instead the rendering subsystems should track
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/nifogre/ogrenifloader.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
|
||||
|
||||
|
@ -294,7 +295,12 @@ void SkyManager::create()
|
|||
|
||||
// Stars
|
||||
mAtmosphereNight = mRootNode->createChildSceneNode();
|
||||
NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif");
|
||||
NifOgre::ObjectScenePtr objects;
|
||||
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("meshes\\sky_night_02.nif"))
|
||||
objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_02.nif");
|
||||
else
|
||||
objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif");
|
||||
|
||||
for(size_t i = 0, matidx = 0;i < objects->mEntities.size();i++)
|
||||
{
|
||||
Entity* night1_ent = objects->mEntities[i];
|
||||
|
@ -584,13 +590,13 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
|||
|
||||
if (mClouds != weather.mCloudTexture)
|
||||
{
|
||||
sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture);
|
||||
sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", Misc::ResourceHelpers::correctTexturePath(weather.mCloudTexture));
|
||||
mClouds = weather.mCloudTexture;
|
||||
}
|
||||
|
||||
if (mNextClouds != weather.mNextCloudTexture)
|
||||
{
|
||||
sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", "textures\\"+weather.mNextCloudTexture);
|
||||
sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", Misc::ResourceHelpers::correctTexturePath(weather.mNextCloudTexture));
|
||||
mNextClouds = weather.mNextCloudTexture;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
#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 "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include <components/terrain/quadtreenode.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
|
@ -59,515 +49,4 @@ namespace MWRender
|
|||
return esmStore.get<ESM::LandTexture>().find(index, plugin);
|
||||
}
|
||||
|
||||
bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max)
|
||||
{
|
||||
assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell");
|
||||
|
||||
/// \todo investigate if min/max heights should be stored at load time in ESM::Land instead
|
||||
|
||||
Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
|
||||
|
||||
assert(origin.x == (int) origin.x);
|
||||
assert(origin.y == (int) origin.y);
|
||||
|
||||
int cellX = origin.x;
|
||||
int cellY = origin.y;
|
||||
|
||||
const ESM::Land* land = getLand(cellX, cellY);
|
||||
if (!land)
|
||||
return false;
|
||||
|
||||
min = std::numeric_limits<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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#ifndef MWRENDER_TERRAINSTORAGE_H
|
||||
#define MWRENDER_TERRAINSTORAGE_H
|
||||
|
||||
#include <components/esm/loadland.hpp>
|
||||
#include <components/esm/loadltex.hpp>
|
||||
|
||||
#include <components/terrain/storage.hpp>
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class TerrainStorage : public Terrain::Storage
|
||||
/// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage.
|
||||
class TerrainStorage : public ESMTerrain::Storage
|
||||
{
|
||||
private:
|
||||
virtual ESM::Land* getLand (int cellX, int cellY);
|
||||
|
@ -18,92 +16,6 @@ namespace MWRender
|
|||
|
||||
/// Get bounds of the whole terrain in cell units
|
||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
||||
|
||||
/// Get the minimum and maximum heights of a terrain region.
|
||||
/// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
|
||||
/// Larger chunks can simply merge AABB of children.
|
||||
/// @param size size of the chunk in cell units
|
||||
/// @param center center of the chunk in cell units
|
||||
/// @param min min height will be stored here
|
||||
/// @param max max height will be stored here
|
||||
/// @return true if there was data available for this terrain chunk
|
||||
virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max);
|
||||
|
||||
/// Fill vertex buffers for a terrain chunk.
|
||||
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
|
||||
/// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue.
|
||||
/// @param lodLevel LOD level, 0 = most detailed
|
||||
/// @param size size of the terrain chunk in cell units
|
||||
/// @param center center of the chunk in cell units
|
||||
/// @param positions buffer to write vertices
|
||||
/// @param normals buffer to write vertex normals
|
||||
/// @param colours buffer to write vertex colours
|
||||
virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
|
||||
std::vector<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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
class OpGetForceSneak : public Interpreter::Opcode0
|
||||
{
|
||||
|
@ -169,27 +197,54 @@ namespace MWScript
|
|||
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision);
|
||||
|
||||
//Force Run
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun,
|
||||
new OpClearMovementFlag<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,
|
||||
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
|
||||
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit,
|
||||
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,
|
||||
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
|
||||
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit,
|
||||
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
|
||||
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning);
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking);
|
||||
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun<ImplicitRef>);
|
||||
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::opcodeGetForceSneakExplicit, new OpGetForceSneak<ExplicitRef>);
|
||||
}
|
||||
|
|
|
@ -413,5 +413,13 @@ op 0x2000254: HurtStandingActor
|
|||
op 0x2000255: HurtStandingActor, explicit
|
||||
op 0x2000256: HurtCollidingActor
|
||||
op 0x2000257: HurtCollidingActor, explicit
|
||||
op 0x2000258: ClearForceJump
|
||||
op 0x2000259: ClearForceJump, explicit reference
|
||||
op 0x200025a: ForceJump
|
||||
op 0x200025b: ForceJump, explicit reference
|
||||
op 0x200025c: ClearForceMoveJump
|
||||
op 0x200025d: ClearForceMoveJump, explicit reference
|
||||
op 0x200025e: ForceMoveJump
|
||||
op 0x200025f: ForceMoveJump, explicit reference
|
||||
|
||||
opcodes 0x2000258-0x3ffffff unused
|
||||
opcodes 0x2000260-0x3ffffff unused
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
||||
#include <components/compiler/extensions.hpp>
|
||||
#include <components/compiler/opcodes.hpp>
|
||||
#include <components/compiler/locals.hpp>
|
||||
|
@ -248,7 +246,7 @@ namespace MWScript
|
|||
Interpreter::Type_Float time = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWBase::Environment::get().getWorld()->getFader()->fadeIn(time);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenIn(time);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -261,7 +259,7 @@ namespace MWScript
|
|||
Interpreter::Type_Float time = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWBase::Environment::get().getWorld()->getFader()->fadeOut(time);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenOut(time);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -277,7 +275,7 @@ namespace MWScript
|
|||
Interpreter::Type_Float time = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWBase::Environment::get().getWorld()->getFader()->fadeTo(alpha, time);
|
||||
MWBase::Environment::get().getWindowManager()->fadeScreenTo(alpha, time);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -637,8 +637,6 @@ namespace MWScript
|
|||
|
||||
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string factionID = "";
|
||||
if(arg0 >0)
|
||||
{
|
||||
|
@ -647,6 +645,8 @@ namespace MWScript
|
|||
}
|
||||
else
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
|
||||
{
|
||||
factionID = "";
|
||||
|
@ -913,8 +913,6 @@ namespace MWScript
|
|||
|
||||
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string factionID = "";
|
||||
if(arg0 >0 )
|
||||
{
|
||||
|
@ -923,6 +921,8 @@ namespace MWScript
|
|||
}
|
||||
else
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
|
||||
{
|
||||
factionID = "";
|
||||
|
|
|
@ -327,7 +327,7 @@ namespace MWScript
|
|||
}
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||
|
||||
ptr.getClass().adjustPosition(ptr);
|
||||
ptr.getClass().adjustPosition(ptr, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -363,8 +363,19 @@ namespace MWScript
|
|||
runtime.pop();
|
||||
int cx,cy;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr,
|
||||
MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
|
||||
|
||||
// another morrowind oddity: player will be moved to the exterior cell at this location,
|
||||
// non-player actors will move within the cell they are in.
|
||||
if (ptr.getRefData().getHandle() == "player")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr,
|
||||
MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z);
|
||||
}
|
||||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
||||
|
@ -374,7 +385,7 @@ namespace MWScript
|
|||
zRot = zRot/60.;
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||
ptr.getClass().adjustPosition(ptr);
|
||||
ptr.getClass().adjustPosition(ptr, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@ namespace MWWorld
|
|||
return std::make_pair (1, "");
|
||||
}
|
||||
|
||||
void Class::adjustPosition(const MWWorld::Ptr& ptr) const
|
||||
void Class::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue