Merge branch 'scriptedit' into run

deque
Marc Zinnschlag 11 years ago
commit 232c6c9ad3

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

@ -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();

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

@ -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;
if (mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
speedmult = mMovementSpeed / vel;
else
// Another bug: when using a fallback animation (e.g. RunForward as fallback to SwimRunForward),
// then the equivalent Walk animation will not use a fallback, and if that animation doesn't exist
// we will play without any scaling.
// Makes the speed attribute of most water creatures totally useless.
// And again, this can not be fixed without patching game data.
speedmult = 1.f;
}
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
else if (mMovementSpeed > 0.0f)
else
{
// The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded.
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
mMovementAnimationControlled = false;
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
{
speedmult = mMovementSpeed / vel;
}
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
else if (mMovementSpeed > 0.0f)
{
// The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded.
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
mMovementAnimationControlled = false;
}
}
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
}
@ -1174,6 +1187,36 @@ void CharacterController::update(float duration)
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool flying = world->isFlying(mPtr);
CreatureStats &stats = cls.getCreatureStats(mPtr);
//Force Jump Logic
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
if(!inwater && !flying)
{
//Force Jump
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))
{
if(onground)
{
cls.getMovementSettings(mPtr).mPosition[2] = 1;
}
else
cls.getMovementSettings(mPtr).mPosition[2] = 0;
}
//Force Move Jump, only jump if they're otherwise moving
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)
{
if(onground)
{
cls.getMovementSettings(mPtr).mPosition[2] = 1;
}
else
cls.getMovementSettings(mPtr).mPosition[2] = 0;
}
}
//Ogre::Vector3 vec = cls.getMovementVector(mPtr);
Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition);
if(vec.z > 0.0f) // to avoid slow-down when jumping
@ -1183,7 +1226,7 @@ void CharacterController::update(float duration)
vec.x = vecXY.x;
vec.y = vecXY.y;
}
else
else
vec.normalise();
if(mHitState != CharState_None && mJumpState == JumpState_None)
@ -1352,8 +1395,7 @@ void CharacterController::update(float duration)
}
else
{
if(!(vec.z > 0.0f))
mJumpState = JumpState_None;
mJumpState = JumpState_None;
vec.z = 0.0f;
inJump = false;
@ -1482,6 +1524,8 @@ void CharacterController::update(float duration)
else if (mAnimation)
mAnimation->updateEffects(duration);
mSkipAnim = false;
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
}

@ -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}");
continue;
}
if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled() &&
(effectIt->mEffectID == ESM::MagicEffect::AlmsiviIntervention ||
effectIt->mEffectID == ESM::MagicEffect::DivineIntervention ||
effectIt->mEffectID == ESM::MagicEffect::Mark ||
effectIt->mEffectID == ESM::MagicEffect::Recall))
{
if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer))
continue;
}
// If player is healing someone, show the target's HP bar
if (castByPlayer && target != caster

@ -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 &center, float &min, float &max)
{
assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell");
/// \todo investigate if min/max heights should be stored at load time in ESM::Land instead
Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
assert(origin.x == (int) origin.x);
assert(origin.y == (int) origin.y);
int cellX = origin.x;
int cellY = origin.y;
const ESM::Land* land = getLand(cellX, cellY);
if (!land)
return false;
min = std::numeric_limits<float>().max();
max = -std::numeric_limits<float>().max();
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
{
for (int col=0; col<ESM::Land::LAND_SIZE; ++col)
{
float h = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
if (h > max)
max = h;
if (h < min)
min = h;
}
}
return true;
}
void TerrainStorage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row)
{
while (col >= ESM::Land::LAND_SIZE-1)
{
++cellY;
col -= ESM::Land::LAND_SIZE-1;
}
while (row >= ESM::Land::LAND_SIZE-1)
{
++cellX;
row -= ESM::Land::LAND_SIZE-1;
}
while (col < 0)
{
--cellY;
col += ESM::Land::LAND_SIZE-1;
}
while (row < 0)
{
--cellX;
row += ESM::Land::LAND_SIZE-1;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mHasData)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalise();
}
else
normal = Ogre::Vector3(0,0,1);
}
void TerrainStorage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row)
{
Ogre::Vector3 n1,n2,n3,n4;
fixNormal(n1, cellX, cellY, col+1, row);
fixNormal(n2, cellX, cellY, col-1, row);
fixNormal(n3, cellX, cellY, col, row+1);
fixNormal(n4, cellX, cellY, col, row-1);
normal = (n1+n2+n3+n4);
normal.normalise();
}
void TerrainStorage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row)
{
if (col == ESM::Land::LAND_SIZE-1)
{
++cellY;
col = 0;
}
if (row == ESM::Land::LAND_SIZE-1)
{
++cellX;
row = 0;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mLandData->mUsingColours)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
}
else
{
color.r = 1;
color.g = 1;
color.b = 1;
}
}
void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
std::vector<float>& positions,
std::vector<float>& normals,
std::vector<Ogre::uint8>& colours)
{
// LOD level n means every 2^n-th vertex is kept
size_t increment = 1 << lodLevel;
Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
assert(origin.x == (int) origin.x);
assert(origin.y == (int) origin.y);
int startX = origin.x;
int startY = origin.y;
size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1;
colours.resize(numVerts*numVerts*4);
positions.resize(numVerts*numVerts*3);
normals.resize(numVerts*numVerts*3);
Ogre::Vector3 normal;
Ogre::ColourValue color;
float vertY;
float vertX;
float vertY_ = 0; // of current cell corner
for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY)
{
float vertX_ = 0; // of current cell corner
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
{
ESM::Land* land = getLand(cellX, cellY);
if (land && !land->mHasData)
land = NULL;
bool hasColors = land && land->mLandData->mUsingColours;
int rowStart = 0;
int colStart = 0;
// Skip the first row / column unless we're at a chunk edge,
// since this row / column is already contained in a previous cell
if (colStart == 0 && vertY_ != 0)
colStart += increment;
if (rowStart == 0 && vertX_ != 0)
rowStart += increment;
vertY = vertY_;
for (int col=colStart; col<ESM::Land::LAND_SIZE; col += increment)
{
vertX = vertX_;
for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment)
{
positions[vertX*numVerts*3 + vertY*3] = ((vertX/float(numVerts-1)-0.5) * size * 8192);
positions[vertX*numVerts*3 + vertY*3 + 1] = ((vertY/float(numVerts-1)-0.5) * size * 8192);
if (land)
positions[vertX*numVerts*3 + vertY*3 + 2] = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
else
positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
if (land)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalise();
}
else
normal = Ogre::Vector3(0,0,1);
// Normals apparently don't connect seamlessly between cells
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixNormal(normal, cellX, cellY, col, row);
// some corner normals appear to be complete garbage (z < 0)
if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1))
averageNormal(normal, cellX, cellY, col, row);
assert(normal.z > 0);
normals[vertX*numVerts*3 + vertY*3] = normal.x;
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
if (hasColors)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
}
else
{
color.r = 1;
color.g = 1;
color.b = 1;
}
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixColour(color, cellX, cellY, col, row);
color.a = 1;
Ogre::uint32 rsColor;
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
memcpy(&colours[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32));
++vertX;
}
++vertY;
}
vertX_ = vertX;
}
vertY_ = vertY;
assert(vertX_ == numVerts); // Ensure we covered whole area
}
assert(vertY_ == numVerts); // Ensure we covered whole area
}
TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY,
int x, int y)
{
// For the first/last row/column, we need to get the texture from the neighbour cell
// to get consistent blending at the borders
--x;
if (x < 0)
{
--cellX;
x += ESM::Land::LAND_TEXTURE_SIZE;
}
if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
{
++cellY;
y -= ESM::Land::LAND_TEXTURE_SIZE;
}
assert(x<ESM::Land::LAND_TEXTURE_SIZE);
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
ESM::Land* land = getLand(cellX, cellY);
if (land)
{
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
if (tex == 0)
return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin
return std::make_pair(tex, land->mPlugin);
}
else
return std::make_pair(0,0);
}
std::string TerrainStorage::getTextureName(UniqueTextureId id)
{
if (id.first == 0)
return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded?
// NB: All vtex ids are +1 compared to the ltex ids
const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);
std::string texture = ltex->mTexture;
//TODO this is needed due to MWs messed up texture handling
texture = texture.substr(0, texture.rfind(".")) + ".dds";
return texture;
}
void TerrainStorage::getBlendmaps (const std::vector<Terrain::QuadTreeNode*>& nodes, std::vector<Terrain::LayerCollection>& out, bool pack)
{
for (std::vector<Terrain::QuadTreeNode*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
{
out.push_back(Terrain::LayerCollection());
out.back().mTarget = *it;
getBlendmapsImpl((*it)->getSize(), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers);
}
}
void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
bool pack, std::vector<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
{
getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList);
}
void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter,
bool pack, std::vector<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
{
// TODO - blending isn't completely right yet; the blending radius appears to be
// different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap
// and interpolate the rest of the cell by hand? :/
Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f);
int cellX = origin.x;
int cellY = origin.y;
// Save the used texture indices so we know the total number of textures
// and number of required blend maps
std::set<UniqueTextureId> textureIndices;
// Due to the way the blending works, the base layer will always shine through in between
// blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible).
// To get a consistent look, we need to make sure to use the same base layer in all cells.
// So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
textureIndices.insert(std::make_pair(0,0));
for (int y=0; y<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
textureIndices.insert(id);
}
// Makes sure the indices are sorted, or rather,
// retrieved as sorted. This is important to keep the splatting order
// consistent across cells.
std::map<UniqueTextureId, int> textureIndicesMap;
for (std::set<UniqueTextureId>::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it)
{
int size = textureIndicesMap.size();
textureIndicesMap[*it] = size;
layerList.push_back(getLayerInfo(getTextureName(*it)));
}
int numTextures = textureIndices.size();
// numTextures-1 since the base layer doesn't need blending
int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1);
int channels = pack ? 4 : 1;
// Second iteration - create and fill in the blend maps
const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
for (int i=0; i<numBlendmaps; ++i)
{
Ogre::PixelFormat format = pack ? Ogre::PF_A8B8G8R8 : Ogre::PF_A8;
Ogre::uchar* pData =
OGRE_ALLOC_T(Ogre::uchar, blendmapSize*blendmapSize*channels, Ogre::MEMCATEGORY_GENERAL);
memset(pData, 0, blendmapSize*blendmapSize*channels);
for (int y=0; y<blendmapSize; ++y)
{
for (int x=0; x<blendmapSize; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
int layerIndex = textureIndicesMap.find(id)->second;
int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1);
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
if (blendIndex == i)
pData[y*blendmapSize*channels + x*channels + channel] = 255;
else
pData[y*blendmapSize*channels + x*channels + channel] = 0;
}
}
blendmaps.push_back(Ogre::PixelBox(blendmapSize, blendmapSize, 1, format, pData));
}
}
float TerrainStorage::getHeightAt(const Ogre::Vector3 &worldPos)
{
int cellX = std::floor(worldPos.x / 8192.f);
int cellY = std::floor(worldPos.y / 8192.f);
ESM::Land* land = getLand(cellX, cellY);
if (!land)
return -2048;
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
// Normalized position in the cell
float nX = (worldPos.x - (cellX * 8192))/8192.f;
float nY = (worldPos.y - (cellY * 8192))/8192.f;
// get left / bottom points (rounded down)
float factor = ESM::Land::LAND_SIZE - 1.0f;
float invFactor = 1.0f / factor;
int startX = static_cast<int>(nX * factor);
int startY = static_cast<int>(nY * factor);
int endX = startX + 1;
int endY = startY + 1;
assert(endX < ESM::Land::LAND_SIZE);
assert(endY < ESM::Land::LAND_SIZE);
// now get points in terrain space (effectively rounding them to boundaries)
float startXTS = startX * invFactor;
float startYTS = startY * invFactor;
float endXTS = endX * invFactor;
float endYTS = endY * invFactor;
// get parametric from start coord to next point
float xParam = (nX - startXTS) * factor;
float yParam = (nY - startYTS) * factor;
/* For even / odd tri strip rows, triangles are this shape:
even odd
3---2 3---2
| / | | \ |
0---1 0---1
*/
// Build all 4 positions in normalized cell space, using point-sampled height
Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f);
Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f);
Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f);
Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f);
// define this plane in terrain space
Ogre::Plane plane;
// (At the moment, all rows have the same triangle alignment)
if (true)
{
// odd row
bool secondTri = ((1.0 - yParam) > xParam);
if (secondTri)
plane.redefine(v0, v1, v3);
else
plane.redefine(v1, v2, v3);
}
else
{
// even row
bool secondTri = (yParam > xParam);
if (secondTri)
plane.redefine(v0, v2, v3);
else
plane.redefine(v0, v1, v2);
}
// Solve plane equation for z
return (-plane.normal.x * nX
-plane.normal.y * nY
- plane.d) / plane.normal.z * 8192;
}
float TerrainStorage::getVertexHeight(const ESM::Land *land, int x, int y)
{
assert(x < ESM::Land::LAND_SIZE);
assert(y < ESM::Land::LAND_SIZE);
return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x];
}
Terrain::LayerInfo TerrainStorage::getLayerInfo(const std::string& texture)
{
// Already have this cached?
if (mLayerInfoMap.find(texture) != mLayerInfoMap.end())
return mLayerInfoMap[texture];
Terrain::LayerInfo info;
info.mParallax = false;
info.mSpecular = false;
info.mDiffuseMap = "textures\\" + texture;
std::string texture_ = texture;
boost::replace_last(texture_, ".", "_nh.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
{
info.mNormalMap = "textures\\" + texture_;
info.mParallax = true;
}
else
{
texture_ = texture;
boost::replace_last(texture_, ".", "_n.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
info.mNormalMap = "textures\\" + texture_;
}
texture_ = texture;
boost::replace_last(texture_, ".", "_diffusespec.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
{
info.mDiffuseMap = "textures\\" + texture_;
info.mSpecular = true;
}
// This wasn't cached, so the textures are probably not loaded either.
// Background load them so they are hopefully already loaded once we need them!
Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mDiffuseMap, "General");
if (!info.mNormalMap.empty())
Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mNormalMap, "General");
mLayerInfoMap[texture] = info;
return info;
}
Terrain::LayerInfo TerrainStorage::getDefaultLayer()
{
Terrain::LayerInfo info;
info.mDiffuseMap = "textures\\_land_default.dds";
info.mParallax = false;
info.mSpecular = false;
return info;
}
float TerrainStorage::getCellWorldSize()
{
return ESM::Land::REAL_SIZE;
}
int TerrainStorage::getCellVertices()
{
return ESM::Land::LAND_SIZE;
}
}

@ -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…
Cancel
Save