From ee81698089652ae92c78967570a41c4b21bddbbb Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 17:40:44 +0200 Subject: [PATCH 01/97] Always control movement by animation except for first person or in-air --- apps/openmw/mwmechanics/character.cpp | 19 ++++++++++++++----- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2c5d68ceb7..2f33c05916 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -361,9 +361,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat * beginning. */ int mode = ((movement == mCurrentMovement) ? 2 : 1); + mMovementAnimationControlled = true; + mAnimation->disable(mCurrentMovement); mCurrentMovement = movement; - mMovementAnimVelocity = 0.0f; if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; @@ -383,16 +384,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f) { - mMovementAnimVelocity = vel; 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); } @@ -506,6 +509,7 @@ void CharacterController::playDeath(float startpoint, CharacterState death) mJumpState = JumpState_None; mAnimation->disable(mCurrentJump); mCurrentJump = ""; + mMovementAnimationControlled = true; mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", startpoint, 0); @@ -547,7 +551,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mIdleState(CharState_None) , mMovementState(CharState_None) , mMovementSpeed(0.0f) - , mMovementAnimVelocity(0.0f) + , mMovementAnimationControlled(true) , mDeathState(CharState_None) , mHitState(CharState_None) , mUpperBodyState(UpperCharState_Nothing) @@ -1241,6 +1245,7 @@ void CharacterController::update(float duration) if (inwater || flying) cls.getCreatureStats(mPtr).land(); + bool inJump = true; if(!onground && !flying && !inwater) { // In the air (either getting up —ascending part of jump— or falling). @@ -1330,6 +1335,8 @@ void CharacterController::update(float duration) mJumpState = JumpState_None; vec.z = 0.0f; + inJump = false; + if(std::abs(vec.x/2.0f) > std::abs(vec.y)) { if(vec.x > 0.0f) @@ -1391,6 +1398,8 @@ void CharacterController::update(float duration) forcestateupdate = updateCreatureState() || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); + if (inJump) + mMovementAnimationControlled = false; if (!mSkipAnim) { @@ -1402,7 +1411,7 @@ void CharacterController::update(float duration) else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); - if (mMovementAnimVelocity == 0) + if (!mMovementAnimationControlled) world->queueMovement(mPtr, vec); } else @@ -1446,7 +1455,7 @@ void CharacterController::update(float duration) } // Update movement - if(mMovementAnimVelocity > 0) + if(mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); } else if (mAnimation) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 59c20db8fe..b1e1738bd9 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -147,7 +147,7 @@ class CharacterController CharacterState mMovementState; std::string mCurrentMovement; float mMovementSpeed; - float mMovementAnimVelocity; + bool mMovementAnimationControlled; CharacterState mDeathState; std::string mCurrentDeath; From 0a2d5d34bcf5059536dd1fcacda77e2b403c4016 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 19:37:12 +0200 Subject: [PATCH 02/97] Cancel queued view mode switch when switching view mode (Fixes #1618) --- apps/openmw/mwrender/camera.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9e683cc159..4580bae70b 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -149,6 +149,8 @@ namespace MWRender mViewModeToggleQueued = true; return; } + else + mViewModeToggleQueued = false; mFirstPersonView = !mFirstPersonView; processViewChange(); From a3a8f4da705560545ea220b4d5c26a1a8e8cf804 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 19:56:38 +0200 Subject: [PATCH 03/97] More font hackery. Will it ever end? Fixes #1606 --- apps/openmw/mwgui/fontloader.cpp | 35 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp index b7c2007b46..92d9a25b61 100644 --- a/apps/openmw/mwgui/fontloader.cpp +++ b/apps/openmw/mwgui/fontloader.cpp @@ -260,21 +260,28 @@ namespace MWGui // 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. - // Same for U+2013 - std::map additional; - additional[39] = 0x2019; // apostrophe - additional[45] = 0x2013; // dash - if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437) + if (mEncoding == ToUTF8::CP437) { - MyGUI::xml::ElementPtr code = codes->createChild("Code"); - code->addAttribute("index", additional[i]); - code->addAttribute("coord", MyGUI::utility::toString(x1) + " " - + MyGUI::utility::toString(y1) + " " - + MyGUI::utility::toString(w) + " " - + MyGUI::utility::toString(h)); - code->addAttribute("advance", data[i].width); - code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " - + MyGUI::utility::toString((fontSize-data[i].ascent))); + std::multimap additional; + additional.insert(std::make_pair(39, 0x2019)); // apostrophe + additional.insert(std::make_pair(45, 0x2013)); // dash + additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark + additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark + for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) + { + if (it->first != i) + continue; + + MyGUI::xml::ElementPtr code = codes->createChild("Code"); + code->addAttribute("index", it->second); + code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + code->addAttribute("advance", data[i].width); + code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + } } // ASCII vertical bar, use this as text input cursor From 742d008765f1ba2aa0b9ad94ccd7cf29d82fbca5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 20:28:52 +0200 Subject: [PATCH 04/97] Fix initializing CharacterController with fists or spell equipped --- apps/openmw/mwmechanics/character.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2f33c05916..d4ddf53cd0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -574,10 +574,14 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if (cls.hasInventoryStore(mPtr)) { getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); + if (mWeaponType != WeapType_None) + { + mUpperBodyState = UpperCharState_WeapEquiped; + getWeaponGroup(mWeaponType, mCurrentWeapon); + } + if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { - getWeaponGroup(mWeaponType, mCurrentWeapon); - mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->showWeapons(true); mAnimation->setWeaponGroup(mCurrentWeapon); } From ad90d20ad8dc6e1d3f3fa28f0ecf3ef1bf986297 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 21:00:12 +0200 Subject: [PATCH 05/97] Don't silently skip unknown NPC subrecords --- components/esm/loadnpc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index e5b851bf0c..2fe9fe3c11 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -10,8 +10,6 @@ namespace ESM void NPC::load(ESMReader &esm) { - //mNpdt52.mGold = -10; - mPersistent = esm.getRecordFlags() & 0x0400; mModel = esm.getHNOString("MODL"); @@ -63,7 +61,6 @@ void NPC::load(ESMReader &esm) } } mAiPackage.load(esm); - esm.skipRecord(); } void NPC::save(ESMWriter &esm) const { From df80c436fb5d46773a29d3306db382534faa1f3b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 5 Jul 2014 18:24:55 +0200 Subject: [PATCH 06/97] Fix compile error for OPENMW_USE_FFMPEG=0 --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 03e74697c1..409e273887 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1088,7 +1088,7 @@ public: void close() { } - bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) + bool update() { return false; } }; From 03f67cd6bdc84da0c42f37acd73fe568a054084f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 8 Jul 2014 12:39:12 +0200 Subject: [PATCH 07/97] moved low level widgets from CSVWorld into a separate namespace/directory --- apps/opencs/CMakeLists.txt | 18 +++++++++++------- apps/opencs/view/render/scenewidget.cpp | 6 +++--- apps/opencs/view/render/scenewidget.hpp | 4 ++-- apps/opencs/view/render/worldspacewidget.cpp | 11 ++++++----- apps/opencs/view/render/worldspacewidget.hpp | 4 ++-- .../view/{world => widget}/scenetool.cpp | 4 ++-- .../view/{world => widget}/scenetool.hpp | 6 +++--- .../view/{world => widget}/scenetoolbar.cpp | 8 ++++---- .../view/{world => widget}/scenetoolbar.hpp | 6 +++--- .../view/{world => widget}/scenetoolmode.cpp | 8 ++++---- .../view/{world => widget}/scenetoolmode.hpp | 6 +++--- apps/opencs/view/world/previewsubview.cpp | 8 ++++---- apps/opencs/view/world/scenesubview.cpp | 18 ++++++++++-------- apps/opencs/view/world/scenesubview.hpp | 13 +++++++++---- 14 files changed, 66 insertions(+), 54 deletions(-) rename apps/opencs/view/{world => widget}/scenetool.cpp (77%) rename apps/opencs/view/{world => widget}/scenetool.hpp (81%) rename apps/opencs/view/{world => widget}/scenetoolbar.cpp (66%) rename apps/opencs/view/{world => widget}/scenetoolbar.hpp (83%) rename apps/opencs/view/{world => widget}/scenetoolmode.cpp (82%) rename apps/opencs/view/{world => widget}/scenetoolmode.hpp (88%) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 288e0394c8..5d8d99f276 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -59,8 +59,17 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator - cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable + cellcreator referenceablecreator referencecreator scenesubview + infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable + ) + +opencs_units_noqt (view/world + subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate + scripthighlighter idvalidator dialoguecreator + ) + +opencs_units (view/widget + scenetoolbar scenetool scenetoolmode ) opencs_units (view/render @@ -73,11 +82,6 @@ opencs_units_noqt (view/render lightingbright object cell ) -opencs_units_noqt (view/world - subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate - scripthighlighter idvalidator dialoguecreator - ) - opencs_units (view/tools reportsubview diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 76f6db385d..3728dd72d9 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -11,7 +11,7 @@ #include #include -#include "../world/scenetoolmode.hpp" +#include "../widget/scenetoolmode.hpp" #include "navigation.hpp" #include "lighting.hpp" @@ -54,9 +54,9 @@ namespace CSVRender timer->start (20); /// \todo make this configurable } - CSVWorld::SceneToolMode *SceneWidget::makeLightingSelector (CSVWorld::SceneToolbar *parent) + CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) { - CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); + CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); tool->addButton (":door.png", "day"); /// \todo replace icons tool->addButton (":GMST.png", "night"); diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index f6b41942f7..8b01e71857 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -16,7 +16,7 @@ namespace Ogre class RenderWindow; } -namespace CSVWorld +namespace CSVWidget { class SceneToolMode; class SceneToolbar; @@ -38,7 +38,7 @@ namespace CSVRender QPaintEngine* paintEngine() const; - CSVWorld::SceneToolMode *makeLightingSelector (CSVWorld::SceneToolbar *parent); + CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent); ///< \attention The created tool is not added to the toolbar (via addTool). Doing that /// is the responsibility of the calling function. diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index fee2f0a160..5b3dc57e23 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -7,8 +7,9 @@ #include -#include "../world/scenetoolmode.hpp" -#include +#include "../../model/world/universalid.hpp" + +#include "../widget/scenetoolmode.hpp" CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) : SceneWidget (parent), mDocument(document) @@ -53,10 +54,10 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode() setNavigation (&m1st); } -CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( - CSVWorld::SceneToolbar *parent) +CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( + CSVWidget::SceneToolbar *parent) { - CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); + CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); tool->addButton (":door.png", "1st"); /// \todo replace icons tool->addButton (":GMST.png", "free"); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 2af90b0feb..3b96779a8c 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -13,7 +13,7 @@ namespace CSMWorld { class UniversalId; } -namespace CSVWorld +namespace CSVWidget { class SceneToolMode; class SceneToolbar; @@ -49,7 +49,7 @@ namespace CSVRender WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0); - CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); + CSVWidget::SceneToolMode *makeNavigationSelector (CSVWidget::SceneToolbar *parent); ///< \attention The created tool is not added to the toolbar (via addTool). Doing that /// is the responsibility of the calling function. diff --git a/apps/opencs/view/world/scenetool.cpp b/apps/opencs/view/widget/scenetool.cpp similarity index 77% rename from apps/opencs/view/world/scenetool.cpp rename to apps/opencs/view/widget/scenetool.cpp index 612b4c6d39..e498fdf639 100644 --- a/apps/opencs/view/world/scenetool.cpp +++ b/apps/opencs/view/widget/scenetool.cpp @@ -3,7 +3,7 @@ #include "scenetoolbar.hpp" -CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) +CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); @@ -12,7 +12,7 @@ CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) connect (this, SIGNAL (clicked()), this, SLOT (openRequest())); } -void CSVWorld::SceneTool::openRequest() +void CSVWidget::SceneTool::openRequest() { showPanel (parentWidget()->mapToGlobal (pos())); } diff --git a/apps/opencs/view/world/scenetool.hpp b/apps/opencs/view/widget/scenetool.hpp similarity index 81% rename from apps/opencs/view/world/scenetool.hpp rename to apps/opencs/view/widget/scenetool.hpp index 07e8b58d72..312d8e00cd 100644 --- a/apps/opencs/view/world/scenetool.hpp +++ b/apps/opencs/view/widget/scenetool.hpp @@ -1,9 +1,9 @@ -#ifndef CSV_WORLD_SCENETOOL_H -#define CSV_WORLD_SCENETOOL_H +#ifndef CSV_WIDGET_SCENETOOL_H +#define CSV_WIDGET_SCENETOOL_H #include -namespace CSVWorld +namespace CSVWidget { class SceneToolbar; diff --git a/apps/opencs/view/world/scenetoolbar.cpp b/apps/opencs/view/widget/scenetoolbar.cpp similarity index 66% rename from apps/opencs/view/world/scenetoolbar.cpp rename to apps/opencs/view/widget/scenetoolbar.cpp index d60240da77..59a3a1d6e2 100644 --- a/apps/opencs/view/world/scenetoolbar.cpp +++ b/apps/opencs/view/widget/scenetoolbar.cpp @@ -5,7 +5,7 @@ #include "scenetool.hpp" -CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) +CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) : QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6) { setFixedWidth (mButtonSize); @@ -18,17 +18,17 @@ CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) setLayout (mLayout); } -void CSVWorld::SceneToolbar::addTool (SceneTool *tool) +void CSVWidget::SceneToolbar::addTool (SceneTool *tool) { mLayout->addWidget (tool, 0, Qt::AlignTop); } -int CSVWorld::SceneToolbar::getButtonSize() const +int CSVWidget::SceneToolbar::getButtonSize() const { return mButtonSize; } -int CSVWorld::SceneToolbar::getIconSize() const +int CSVWidget::SceneToolbar::getIconSize() const { return mIconSize; } \ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolbar.hpp b/apps/opencs/view/widget/scenetoolbar.hpp similarity index 83% rename from apps/opencs/view/world/scenetoolbar.hpp rename to apps/opencs/view/widget/scenetoolbar.hpp index 731806cc5f..0ef84f488e 100644 --- a/apps/opencs/view/world/scenetoolbar.hpp +++ b/apps/opencs/view/widget/scenetoolbar.hpp @@ -1,11 +1,11 @@ -#ifndef CSV_WORLD_SCENETOOLBAR_H -#define CSV_WORLD_SCENETOOLBAR_H +#ifndef CSV_WIDGET_SCENETOOLBAR_H +#define CSV_WIDGET_SCENETOOLBAR_H #include class QVBoxLayout; -namespace CSVWorld +namespace CSVWidget { class SceneTool; diff --git a/apps/opencs/view/world/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp similarity index 82% rename from apps/opencs/view/world/scenetoolmode.cpp rename to apps/opencs/view/widget/scenetoolmode.cpp index 73b01ae3a8..72efae0df1 100644 --- a/apps/opencs/view/world/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -7,7 +7,7 @@ #include "scenetoolbar.hpp" -CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent) +CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent) : SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()) { mPanel = new QFrame (this, Qt::Popup); @@ -19,13 +19,13 @@ CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent) mPanel->setLayout (mLayout); } -void CSVWorld::SceneToolMode::showPanel (const QPoint& position) +void CSVWidget::SceneToolMode::showPanel (const QPoint& position) { mPanel->move (position); mPanel->show(); } -void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::string& id) +void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); @@ -42,7 +42,7 @@ void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::str setIcon (button->icon()); } -void CSVWorld::SceneToolMode::selected() +void CSVWidget::SceneToolMode::selected() { std::map::const_iterator iter = mButtons.find (dynamic_cast (sender())); diff --git a/apps/opencs/view/world/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp similarity index 88% rename from apps/opencs/view/world/scenetoolmode.hpp rename to apps/opencs/view/widget/scenetoolmode.hpp index a156c0c95a..175c53f966 100644 --- a/apps/opencs/view/world/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -1,5 +1,5 @@ -#ifndef CSV_WORLD_SCENETOOL_MODE_H -#define CSV_WORLD_SCENETOOL_MODE_H +#ifndef CSV_WIDGET_SCENETOOL_MODE_H +#define CSV_WIDGET_SCENETOOL_MODE_H #include "scenetool.hpp" @@ -7,7 +7,7 @@ class QHBoxLayout; -namespace CSVWorld +namespace CSVWidget { class SceneToolbar; diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 49e6d93618..bae0abb8bb 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -5,8 +5,8 @@ #include "../render/previewwidget.hpp" -#include "scenetoolbar.hpp" -#include "scenetoolmode.hpp" +#include "../widget/scenetoolbar.hpp" +#include "../widget/scenetoolmode.hpp" CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id) @@ -28,9 +28,9 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo else mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this); - SceneToolbar *toolbar = new SceneToolbar (48+6, this); + CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this); - SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); + CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); layout->addWidget (toolbar, 0); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 36cce9ecd7..0ff540ec28 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -17,9 +17,11 @@ #include "../render/pagedworldspacewidget.hpp" #include "../render/unpagedworldspacewidget.hpp" +#include "../widget/scenetoolbar.hpp" +#include "../widget/scenetoolmode.hpp" + #include "tablebottombox.hpp" #include "creator.hpp" -#include "scenetoolmode.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mLayout(new QHBoxLayout), mDocument(document), mScene(NULL), mToolbar(NULL) @@ -95,18 +97,18 @@ void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&))); } -CSVWorld::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type) +CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type) { - CSVWorld::SceneToolbar* toolbar = new SceneToolbar (48+6, this); + CSVWidget::SceneToolbar* toolbar = new CSVWidget::SceneToolbar (48+6, this); - SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar); + CSVWidget::SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar); toolbar->addTool (navigationTool); - SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar); + CSVWidget::SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); /* Add buttons specific to the type. For now no need for it. - * + * switch (type) { case widget_Paged: @@ -188,7 +190,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI { CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; - SceneToolbar* toolbar = NULL; + CSVWidget::SceneToolbar* toolbar = NULL; switch (mScene->getDropRequirements(CSVRender::WorldspaceWidget::getDropType(data))) { @@ -217,7 +219,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI } } -void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWorld::SceneToolbar* toolbar) +void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar) { assert(mLayout); diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index b9ecbe931c..b9dcdd6a38 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -4,7 +4,6 @@ #include #include "../doc/subview.hpp" -#include "scenetoolbar.hpp" class QModelIndex; @@ -25,6 +24,11 @@ namespace CSVRender class UnpagedWorldspaceWidget; } +namespace CSVWidget +{ + class SceneToolbar; +} + namespace CSVWorld { class Table; @@ -39,7 +43,7 @@ namespace CSVWorld CSVRender::WorldspaceWidget *mScene; QHBoxLayout* mLayout; CSMDoc::Document& mDocument; - SceneToolbar* mToolbar; + CSVWidget::SceneToolbar* mToolbar; public: @@ -59,14 +63,15 @@ namespace CSVWorld void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget); - void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, SceneToolbar* toolbar); + void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar); enum widgetType { widget_Paged, widget_Unpaged }; - SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type); + + CSVWidget::SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type); private slots: From 0704fa2b3dda4945b392139b220e49d40a19b695 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 9 Jul 2014 10:23:01 +0200 Subject: [PATCH 08/97] Reset local rotation axis in SetAngle (Fixes #1630) --- apps/openmw/mwscript/transformationextensions.cpp | 8 ++++++++ apps/openmw/mwworld/worldimp.cpp | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 3860257ad6..a041049ca4 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -86,16 +86,24 @@ namespace MWScript float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); + MWWorld::LocalRotation localRot = ptr.getRefData().getLocalRotation(); + if (axis == "x") { + localRot.rot[0] = 0; + ptr.getRefData().setLocalRotation(localRot); MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } else if (axis == "y") { + localRot.rot[1] = 0; + ptr.getRefData().setLocalRotation(localRot); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } else if (axis == "z") { + localRot.rot[2] = 0; + ptr.getRefData().setLocalRotation(localRot); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } else diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 33405b4d86..30c90cfedc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1123,7 +1123,10 @@ namespace MWWorld ptr.getRefData().setPosition(pos); - mWorldScene->updateObjectRotation(ptr); + if (ptr.getClass().isActor()) + mWorldScene->updateObjectRotation(ptr); + else + mWorldScene->updateObjectLocalRotation(ptr); } void World::localRotateObject (const Ptr& ptr, float x, float y, float z) From 930f782bc590414d5cea08defd522742ac12d3a6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 10:09:21 +0200 Subject: [PATCH 09/97] SceneWidget::setLighting was missing an update call --- apps/opencs/view/render/scenewidget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 3728dd72d9..e93bf347e4 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -347,6 +347,9 @@ namespace CSVRender mLighting = lighting; mLighting->activate (mSceneMgr, mHasDefaultAmbient ? &mDefaultAmbient : 0); + + if (mWindow) + mWindow->update(); } void SceneWidget::selectLightingMode (const std::string& mode) From dd0aa20390968d5f7a3d637a0b427d872c98190d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:03:55 +0200 Subject: [PATCH 10/97] custom push button --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/widget/pushbutton.cpp | 29 +++++++++++++++++++++++ apps/opencs/view/widget/pushbutton.hpp | 28 ++++++++++++++++++++++ apps/opencs/view/widget/scenetoolmode.cpp | 10 ++++---- apps/opencs/view/widget/scenetoolmode.hpp | 3 ++- 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 apps/opencs/view/widget/pushbutton.cpp create mode 100644 apps/opencs/view/widget/pushbutton.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5d8d99f276..c03cc3138b 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -69,7 +69,7 @@ opencs_units_noqt (view/world ) opencs_units (view/widget - scenetoolbar scenetool scenetoolmode + scenetoolbar scenetool scenetoolmode pushbutton ) opencs_units (view/render diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp new file mode 100644 index 0000000000..8b4cf65e39 --- /dev/null +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -0,0 +1,29 @@ + +#include "pushbutton.hpp" + +#include +#include + +void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event) +{ + if (event->key()!=Qt::Key_Shift) + mKeepOpen = false; + + QPushButton::keyPressEvent (event); +} + +void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) +{ + mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier); + QPushButton::mouseReleaseEvent (event); +} + +CSVWidget::PushButton::PushButton (const QIcon& icon, const QString& text, QWidget *parent) +: QPushButton (icon, text, parent), mKeepOpen (false) +{ +} + +bool CSVWidget::PushButton::hasKeepOpen() const +{ + return mKeepOpen; +} \ No newline at end of file diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp new file mode 100644 index 0000000000..aa9da48857 --- /dev/null +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -0,0 +1,28 @@ +#ifndef CSV_WIDGET_PUSHBUTTON_H +#define CSV_WIDGET_PUSHBUTTON_H + +#include + +namespace CSVWidget +{ + class PushButton : public QPushButton + { + Q_OBJECT + + bool mKeepOpen; + + protected: + + virtual void keyPressEvent (QKeyEvent *event); + + virtual void mouseReleaseEvent (QMouseEvent *event); + + public: + + PushButton (const QIcon& icon, const QString& text, QWidget *parent = 0); + + bool hasKeepOpen() const; + }; +} + +#endif diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 72efae0df1..87abd81405 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -6,6 +6,7 @@ #include #include "scenetoolbar.hpp" +#include "pushbutton.hpp" CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent) : SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()) @@ -27,7 +28,7 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { - QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); @@ -44,12 +45,13 @@ void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::st void CSVWidget::SceneToolMode::selected() { - std::map::const_iterator iter = - mButtons.find (dynamic_cast (sender())); + std::map::const_iterator iter = + mButtons.find (dynamic_cast (sender())); if (iter!=mButtons.end()) { - mPanel->hide(); + if (!iter->first->hasKeepOpen()) + mPanel->hide(); setIcon (iter->first->icon()); emit modeChanged (iter->second); diff --git a/apps/opencs/view/widget/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp index 175c53f966..0ad5243b3c 100644 --- a/apps/opencs/view/widget/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -10,6 +10,7 @@ class QHBoxLayout; namespace CSVWidget { class SceneToolbar; + class PushButton; ///< \brief Mode selector tool class SceneToolMode : public SceneTool @@ -18,7 +19,7 @@ namespace CSVWidget QWidget *mPanel; QHBoxLayout *mLayout; - std::map mButtons; // widget, id + std::map mButtons; // widget, id int mButtonSize; int mIconSize; From 50ee815dd8e777db3cdf4d32dde0a643f16ab302 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:23:48 +0200 Subject: [PATCH 11/97] make buttons in mode panel act like radiobuttons --- apps/opencs/view/widget/pushbutton.cpp | 1 + apps/opencs/view/widget/scenetoolmode.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 8b4cf65e39..61afa0d9d8 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -21,6 +21,7 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) CSVWidget::PushButton::PushButton (const QIcon& icon, const QString& text, QWidget *parent) : QPushButton (icon, text, parent), mKeepOpen (false) { + setCheckable (true); } bool CSVWidget::PushButton::hasKeepOpen() const diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 87abd81405..5f2eecdc36 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -40,7 +40,10 @@ void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::st connect (button, SIGNAL (clicked()), this, SLOT (selected())); if (mButtons.size()==1) + { setIcon (button->icon()); + button->setChecked (true); + } } void CSVWidget::SceneToolMode::selected() @@ -53,6 +56,10 @@ void CSVWidget::SceneToolMode::selected() if (!iter->first->hasKeepOpen()) mPanel->hide(); + for (std::map::const_iterator iter2 = mButtons.begin(); + iter2!=mButtons.end(); ++iter2) + iter2->first->setChecked (iter2==iter); + setIcon (iter->first->icon()); emit modeChanged (iter->second); } From 365b7218781d3f5df48ae89709fad588d89c8dff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:32:20 +0200 Subject: [PATCH 12/97] return/enter activate the selected button --- apps/opencs/view/widget/pushbutton.cpp | 11 +++++++++++ apps/opencs/view/widget/pushbutton.hpp | 2 ++ 2 files changed, 13 insertions(+) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 61afa0d9d8..8b70cb6b81 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -12,6 +12,17 @@ void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event) QPushButton::keyPressEvent (event); } +void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event) +{ + if (event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter) + { + mKeepOpen = event->modifiers() & Qt::ShiftModifier; + emit clicked(); + } + + QPushButton::keyReleaseEvent (event); +} + void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) { mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier); diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index aa9da48857..961b3ed11b 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -15,6 +15,8 @@ namespace CSVWidget virtual void keyPressEvent (QKeyEvent *event); + virtual void keyReleaseEvent (QKeyEvent *event); + virtual void mouseReleaseEvent (QMouseEvent *event); public: From 8b239df1b1cd3c5817ffa5d9942610ce4e0649b8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:34:09 +0200 Subject: [PATCH 13/97] removed text argument from button constructor, because toolbar buttons will always be icon only --- apps/opencs/view/widget/pushbutton.cpp | 4 ++-- apps/opencs/view/widget/pushbutton.hpp | 2 +- apps/opencs/view/widget/scenetoolmode.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 8b70cb6b81..b010a86680 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -29,8 +29,8 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) QPushButton::mouseReleaseEvent (event); } -CSVWidget::PushButton::PushButton (const QIcon& icon, const QString& text, QWidget *parent) -: QPushButton (icon, text, parent), mKeepOpen (false) +CSVWidget::PushButton::PushButton (const QIcon& icon, QWidget *parent) +: QPushButton (icon, "", parent), mKeepOpen (false) { setCheckable (true); } diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 961b3ed11b..984a77d578 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -21,7 +21,7 @@ namespace CSVWidget public: - PushButton (const QIcon& icon, const QString& text, QWidget *parent = 0); + PushButton (const QIcon& icon, QWidget *parent = 0); bool hasKeepOpen() const; }; diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 5f2eecdc36..4d76215bbd 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -28,7 +28,7 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { - PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); From 44b95bbd7be6c940509decb633dd0a2fad61e1e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:44:01 +0200 Subject: [PATCH 14/97] added push mode and icon-less push button constructor --- apps/opencs/view/widget/pushbutton.cpp | 10 ++++++++-- apps/opencs/view/widget/pushbutton.hpp | 6 +++++- apps/opencs/view/widget/scenetoolmode.cpp | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index b010a86680..8fb90c4d00 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -29,10 +29,16 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) QPushButton::mouseReleaseEvent (event); } -CSVWidget::PushButton::PushButton (const QIcon& icon, QWidget *parent) +CSVWidget::PushButton::PushButton (const QIcon& icon, bool push, QWidget *parent) : QPushButton (icon, "", parent), mKeepOpen (false) { - setCheckable (true); + setCheckable (!push); +} + +CSVWidget::PushButton::PushButton (bool push, QWidget *parent) +: QPushButton (parent), mKeepOpen (false) +{ + setCheckable (!push); } bool CSVWidget::PushButton::hasKeepOpen() const diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 984a77d578..7c2a2caca0 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -21,7 +21,11 @@ namespace CSVWidget public: - PushButton (const QIcon& icon, QWidget *parent = 0); + /// \param push Do not maintain a toggle state + PushButton (const QIcon& icon, bool push = false, QWidget *parent = 0); + + /// \param push Do not maintain a toggle state + PushButton (bool push = false, QWidget *parent = 0); bool hasKeepOpen() const; }; diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 4d76215bbd..cbf20d7dcb 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -28,7 +28,7 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { - PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), mPanel); + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), false, mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); From d6c0b8d0ac704917160b75b27fb9c5b53ffdc849 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 11:45:45 +0200 Subject: [PATCH 15/97] use custom button on toolbar (top level) --- apps/opencs/view/widget/scenetool.cpp | 2 +- apps/opencs/view/widget/scenetool.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/widget/scenetool.cpp b/apps/opencs/view/widget/scenetool.cpp index e498fdf639..36d1f66101 100644 --- a/apps/opencs/view/widget/scenetool.cpp +++ b/apps/opencs/view/widget/scenetool.cpp @@ -3,7 +3,7 @@ #include "scenetoolbar.hpp" -CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) +CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : PushButton (true, parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); diff --git a/apps/opencs/view/widget/scenetool.hpp b/apps/opencs/view/widget/scenetool.hpp index 312d8e00cd..24099683e0 100644 --- a/apps/opencs/view/widget/scenetool.hpp +++ b/apps/opencs/view/widget/scenetool.hpp @@ -1,14 +1,14 @@ #ifndef CSV_WIDGET_SCENETOOL_H #define CSV_WIDGET_SCENETOOL_H -#include +#include "pushbutton.hpp" namespace CSVWidget { class SceneToolbar; ///< \brief Tool base class - class SceneTool : public QPushButton + class SceneTool : public PushButton { Q_OBJECT From 57b9b8d8848780a7e7922365ba750cd2190770d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 12:13:27 +0200 Subject: [PATCH 16/97] set focus when opening a toolbar panel --- apps/opencs/view/widget/scenetoolmode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index cbf20d7dcb..0d8526a30c 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -24,6 +24,9 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) { mPanel->move (position); mPanel->show(); + + if (!mButtons.empty()) + mButtons.begin()->first->setFocus (Qt::OtherFocusReason); } void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) From 67ad7d5b0ecd2c179c200401290cb7a1cb0d780b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 12:20:48 +0200 Subject: [PATCH 17/97] fixed scene subview focus problem --- apps/opencs/view/world/scenesubview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 0ff540ec28..4430d28eb6 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -242,4 +242,5 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW mLayout->addWidget (mScene, 1); mScene->selectDefaultNavigationMode(); + setFocusProxy (mScene); } \ No newline at end of file From 32e48edb83b63c3f3c8f2308256dfa43871de202 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 12:53:57 +0200 Subject: [PATCH 18/97] toolbar tooltip system --- apps/opencs/view/widget/pushbutton.cpp | 25 +++++++++++++++++++---- apps/opencs/view/widget/pushbutton.hpp | 11 ++++++++-- apps/opencs/view/widget/scenetool.cpp | 2 +- apps/opencs/view/widget/scenetoolmode.cpp | 2 +- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 8fb90c4d00..52c78ff1cd 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -4,6 +4,20 @@ #include #include +void CSVWidget::PushButton::setExtendedToolTip (const std::string& text) +{ + std::string tooltip = text; + + if (tooltip.empty()) + tooltip = "(Tool tip not implemented yet)"; + + if (!mPush) + tooltip += + "

(left click to activate,
shift-left click to activate and keep panel open)"; + + setToolTip (QString::fromUtf8 (tooltip.c_str())); +} + void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event) { if (event->key()!=Qt::Key_Shift) @@ -29,16 +43,19 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) QPushButton::mouseReleaseEvent (event); } -CSVWidget::PushButton::PushButton (const QIcon& icon, bool push, QWidget *parent) -: QPushButton (icon, "", parent), mKeepOpen (false) +CSVWidget::PushButton::PushButton (const QIcon& icon, bool push, const std::string& tooltip, + QWidget *parent) +: QPushButton (icon, "", parent), mKeepOpen (false), mPush (push) { setCheckable (!push); + setExtendedToolTip (tooltip); } -CSVWidget::PushButton::PushButton (bool push, QWidget *parent) -: QPushButton (parent), mKeepOpen (false) +CSVWidget::PushButton::PushButton (bool push, const std::string& tooltip, QWidget *parent) +: QPushButton (parent), mKeepOpen (false), mPush (push) { setCheckable (!push); + setExtendedToolTip (tooltip); } bool CSVWidget::PushButton::hasKeepOpen() const diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 7c2a2caca0..6cd4f65ab4 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -10,6 +10,11 @@ namespace CSVWidget Q_OBJECT bool mKeepOpen; + bool mPush; + + private: + + void setExtendedToolTip (const std::string& text); protected: @@ -22,10 +27,12 @@ namespace CSVWidget public: /// \param push Do not maintain a toggle state - PushButton (const QIcon& icon, bool push = false, QWidget *parent = 0); + PushButton (const QIcon& icon, bool push = false, const std::string& tooltip = "", + QWidget *parent = 0); /// \param push Do not maintain a toggle state - PushButton (bool push = false, QWidget *parent = 0); + PushButton (bool push = false, const std::string& tooltip = "", + QWidget *parent = 0); bool hasKeepOpen() const; }; diff --git a/apps/opencs/view/widget/scenetool.cpp b/apps/opencs/view/widget/scenetool.cpp index 36d1f66101..d247096b03 100644 --- a/apps/opencs/view/widget/scenetool.cpp +++ b/apps/opencs/view/widget/scenetool.cpp @@ -3,7 +3,7 @@ #include "scenetoolbar.hpp" -CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : PushButton (true, parent) +CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : PushButton (true, "", parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 0d8526a30c..152a7c0ced 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -31,7 +31,7 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { - PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), false, mPanel); + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), false, "", mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); From 61a02d8a40385840d1277d14ec08c61d4d621bda Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 13:04:30 +0200 Subject: [PATCH 19/97] replaced custom toolbutton push flag with type enum --- apps/opencs/view/widget/pushbutton.cpp | 32 ++++++++++++++++------- apps/opencs/view/widget/pushbutton.hpp | 16 +++++++++--- apps/opencs/view/widget/scenetool.cpp | 3 ++- apps/opencs/view/widget/scenetoolmode.cpp | 3 ++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 52c78ff1cd..35bed1627d 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -11,9 +11,23 @@ void CSVWidget::PushButton::setExtendedToolTip (const std::string& text) if (tooltip.empty()) tooltip = "(Tool tip not implemented yet)"; - if (!mPush) - tooltip += - "

(left click to activate,
shift-left click to activate and keep panel open)"; + switch (mType) + { + case Type_TopMode: + + tooltip += + "

(left click to change mode)"; + + break; + + case Type_Mode: + + tooltip += + "

(left click to activate," + "
shift-left click to activate and keep panel open)"; + + break; + } setToolTip (QString::fromUtf8 (tooltip.c_str())); } @@ -43,18 +57,18 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) QPushButton::mouseReleaseEvent (event); } -CSVWidget::PushButton::PushButton (const QIcon& icon, bool push, const std::string& tooltip, +CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const std::string& tooltip, QWidget *parent) -: QPushButton (icon, "", parent), mKeepOpen (false), mPush (push) +: QPushButton (icon, "", parent), mKeepOpen (false), mType (type) { - setCheckable (!push); + setCheckable (type==Type_Mode); setExtendedToolTip (tooltip); } -CSVWidget::PushButton::PushButton (bool push, const std::string& tooltip, QWidget *parent) -: QPushButton (parent), mKeepOpen (false), mPush (push) +CSVWidget::PushButton::PushButton (Type type, const std::string& tooltip, QWidget *parent) +: QPushButton (parent), mKeepOpen (false), mType (type) { - setCheckable (!push); + setCheckable (type==Type_Mode); setExtendedToolTip (tooltip); } diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 6cd4f65ab4..099cba3580 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -9,8 +9,18 @@ namespace CSVWidget { Q_OBJECT + public: + + enum Type + { + Type_TopMode, // top level button for mode selector panel + Type_Mode // mode button + }; + + private: + bool mKeepOpen; - bool mPush; + Type mType; private: @@ -27,11 +37,11 @@ namespace CSVWidget public: /// \param push Do not maintain a toggle state - PushButton (const QIcon& icon, bool push = false, const std::string& tooltip = "", + PushButton (const QIcon& icon, Type type, const std::string& tooltip = "", QWidget *parent = 0); /// \param push Do not maintain a toggle state - PushButton (bool push = false, const std::string& tooltip = "", + PushButton (Type type, const std::string& tooltip = "", QWidget *parent = 0); bool hasKeepOpen() const; diff --git a/apps/opencs/view/widget/scenetool.cpp b/apps/opencs/view/widget/scenetool.cpp index d247096b03..e3f2dfd1c8 100644 --- a/apps/opencs/view/widget/scenetool.cpp +++ b/apps/opencs/view/widget/scenetool.cpp @@ -3,7 +3,8 @@ #include "scenetoolbar.hpp" -CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) : PushButton (true, "", parent) +CSVWidget::SceneTool::SceneTool (SceneToolbar *parent) +: PushButton (PushButton::Type_TopMode, "", parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 152a7c0ced..629cf9415e 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -31,7 +31,8 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) { - PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), false, "", mPanel); + PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode, + "", mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); From 490442cf6214b7ef8c66453638f7b27cae7a1e83 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Jul 2014 13:18:24 +0200 Subject: [PATCH 20/97] added lighting mode tooltips --- apps/opencs/view/render/scenewidget.cpp | 20 +++++++++++++++++--- apps/opencs/view/widget/scenetoolmode.cpp | 5 +++-- apps/opencs/view/widget/scenetoolmode.hpp | 3 ++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index e93bf347e4..8f6b74765f 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -58,9 +58,23 @@ namespace CSVRender { CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); - tool->addButton (":door.png", "day"); /// \todo replace icons - tool->addButton (":GMST.png", "night"); - tool->addButton (":Info.png", "bright"); + /// \todo replace icons + tool->addButton (":door.png", "day", + "Day" + "

  • Cell specific ambient in interiors
  • " + "
  • Low ambient in exteriors
  • " + "
  • Strong directional light source/lir>" + "
  • This mode closely resembles day time in-game
"); + tool->addButton (":GMST.png", "night", + "Night" + "
  • Cell specific ambient in interiors
  • " + "
  • Low ambient in exteriors
  • " + "
  • Weak directional light source
  • " + "
  • This mode closely resembles night time in-game
"); + tool->addButton (":Info.png", "bright", + "Bright" + "
  • Maximum ambient
  • " + "
  • Strong directional light source
"); connect (tool, SIGNAL (modeChanged (const std::string&)), this, SLOT (selectLightingMode (const std::string&))); diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 629cf9415e..412959c7c4 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -29,10 +29,11 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) mButtons.begin()->first->setFocus (Qt::OtherFocusReason); } -void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id) +void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id, + const std::string& tooltip) { PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode, - "", mPanel); + tooltip, mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); diff --git a/apps/opencs/view/widget/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp index 0ad5243b3c..5f18f712f9 100644 --- a/apps/opencs/view/widget/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -29,7 +29,8 @@ namespace CSVWidget virtual void showPanel (const QPoint& position); - void addButton (const std::string& icon, const std::string& id); + void addButton (const std::string& icon, const std::string& id, + const std::string& tooltip = ""); signals: From 53c70dbdaac4a1eac7c83c6f3736fd4a41cdb9f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 04:25:56 +0200 Subject: [PATCH 21/97] Exit dialogue when going to jail (Fixes #1635) --- apps/openmw/mwworld/worldimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 30c90cfedc..3868348820 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2652,6 +2652,8 @@ namespace MWWorld { mGoToJail = false; + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); + MWWorld::Ptr player = getPlayerPtr(); teleportToClosestMarker(player, "prisonmarker"); int bounty = player.getClass().getNpcStats(player).getBounty(); From 5fbce67b9d2a63800d406d3d83dfa8ae805f73f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 04:30:44 +0200 Subject: [PATCH 22/97] Don't redraw ItemView if the size did not change --- apps/openmw/mwgui/itemview.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index fdaf930399..b86f610341 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -140,26 +140,28 @@ void ItemView::onMouseWheel(MyGUI::Widget *_sender, int _rel) void ItemView::setSize(const MyGUI::IntSize &_value) { + bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setSize(_value); - update(); + if (changed) + update(); } void ItemView::setSize(int _width, int _height) { - Base::setSize(_width, _height); - update(); + setSize(MyGUI::IntSize(_width, _height)); } void ItemView::setCoord(const MyGUI::IntCoord &_value) { + bool changed = (_value.width != getWidth() || _value.height != getHeight()); Base::setCoord(_value); - update(); + if (changed) + update(); } void ItemView::setCoord(int _left, int _top, int _width, int _height) { - Base::setCoord(_left, _top, _width, _height); - update(); + setCoord(MyGUI::IntCoord(_left, _top, _width, _height)); } void ItemView::registerComponents() From 048d07b29905522b83336cf097b2fbe704de3f59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 11:57:21 +0200 Subject: [PATCH 23/97] Show owner of doors in tooltip --- apps/openmw/mwclass/door.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index e435511b99..677ad462e0 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -250,8 +250,11 @@ namespace MWClass text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) + { text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); - + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + } info.text = text; return info; From d91d5992697e3edf9f93ccfb85ae34b0a95436b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jul 2014 06:43:04 +0200 Subject: [PATCH 24/97] Disable quickload when in modal dialog (Fixes #1641) --- apps/openmw/mwinput/inputmanagerimp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index afdde6fb01..8d4c53921c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -707,11 +707,13 @@ namespace MWInput } void InputManager::quickLoad() { - MWBase::Environment::get().getStateManager()->quickLoad(); + if (!MyGUI::InputManager::getInstance().isModalAny()) + MWBase::Environment::get().getStateManager()->quickLoad(); } void InputManager::quickSave() { - MWBase::Environment::get().getStateManager()->quickSave(); + if (!MyGUI::InputManager::getInstance().isModalAny()) + MWBase::Environment::get().getStateManager()->quickSave(); } void InputManager::toggleSpell() { From 1c41ce9b9d95e09f68cf304bd2342617742eceaa Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 07:31:18 +0200 Subject: [PATCH 25/97] Implement Hrnchamd's player and NPC autocalc spells (Some unclarities remaining, XXX) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/npc.cpp | 49 +++- apps/openmw/mwmechanics/autocalcspell.cpp | 214 ++++++++++++++++++ apps/openmw/mwmechanics/autocalcspell.hpp | 31 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 96 ++++++-- components/esm/loadspel.hpp | 4 +- 6 files changed, 368 insertions(+), 28 deletions(-) create mode 100644 apps/openmw/mwmechanics/autocalcspell.cpp create mode 100644 apps/openmw/mwmechanics/autocalcspell.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5c5b0d16ca..23ba78dbad 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle + disease pickpocket levelledlist combat steering obstacle autocalcspell ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c003e0b227..80900d4655 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -22,6 +22,7 @@ #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/disease.hpp" #include "../mwmechanics/combat.hpp" +#include "../mwmechanics/autocalcspell.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -193,18 +194,6 @@ namespace majorMultiplier = 1.0f; break; } - if (class_->mData.mSkills[k][1] == skillIndex) - { - // Major skill -> add starting spells for this skill if existing - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) - { - if (it->mData.mFlags & ESM::Spell::F_Autocalc - && MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex) - npcStats.getSpells().add(it->mId); - } - } } // is this skill in the same Specialization as the class? @@ -223,6 +212,42 @@ namespace + specBonus + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100)); } + + int skills[ESM::Skill::Length]; + for (int i=0; imId << std::endl; + std::cout << "Skills: " << std::endl; + for (int i=0; i spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race); + std::cout << "Spells: " << spells.size() << std::endl; + for (std::set::iterator it = spells.begin(); it != spells.end(); ++it) + { + std::cout << *it << ", "; + npcStats.getSpells().add(*it); + } + std::cout << std::endl; + + const char* compare[] = { "weary","dire noise","reflect","weak spelldrinker","absorb endurance","absorb personality","absorb speed","absorb strength","absorb willpower","fortify alteration skill","fortify illusion skill","fortify unarmored skill","fortify mysticism skill","fortify restoration skill","assured sublime wisdom","surpassing sublime wisdom","surpassing golden wisdom","blood gift","surpassing silver wisdom","surpassing unseen wisdom","surpassing green wisdom","powerwell","orc's strength","surpassing fluid evasion","poet's whim","rapid regenerate","dispel","shadow weave" }; + int n = sizeof(compare) / sizeof(compare[0]); + std::set compareSet; + for (int i=0; i::iterator it = compareSet.begin(); it != compareSet.end(); ++it) + { + std::cout << *it << ", "; + } + std::cout << std::endl; } } diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp new file mode 100644 index 0000000000..5659959e84 --- /dev/null +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -0,0 +1,214 @@ +#include "autocalcspell.hpp" + +#include + +#include "../mwworld/esmstore.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + + +namespace MWMechanics +{ + + struct SchoolCaps + { + int mCount; + int mLimit; + bool mReachedLimit; + int mMinCost; + std::string mWeakestSpell; + }; + + std::set autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) + { + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); + float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + static int iAutoSpellSchoolMax[6]; + static bool init = false; + if (!init) + { + for (int i=0; i<6; ++i) + { + const std::string& gmstName = "iAutoSpell" + schools[i] + "Max"; + iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt(); + } + init = true; + } + + std::map schoolCaps; + for (int i=0; i<6; ++i) + { + SchoolCaps caps; + caps.mCount = 0; + caps.mLimit = iAutoSpellSchoolMax[i]; + caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0; + caps.mMinCost = INT_MAX; + caps.mWeakestSpell.clear(); + schoolCaps[i] = caps; + } + + std::set selectedSpells; + + const MWWorld::Store &spells = + MWBase::Environment::get().getWorld()->getStore().get(); + for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) + { + const ESM::Spell* spell = &*iter; + + if (spell->mData.mType != ESM::Spell::ST_Spell) + continue; + if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) + continue; + static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt(); + if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) + continue; + + if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) + continue; + + if (!attrSkillCheck(spell, actorSkills, actorAttributes)) + continue; + + int school; + float skillTerm; + calcWeakestSchool(spell, actorSkills, school, skillTerm); + assert(school >= 0 && school < 6); + SchoolCaps& cap = schoolCaps[school]; + + if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) + continue; + + static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat(); + if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) + continue; + + selectedSpells.insert(spell->mId); + + if (cap.mReachedLimit) + { + selectedSpells.erase(cap.mWeakestSpell); + + // Note: not school specific + cap.mMinCost = INT_MAX; + for (std::set::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + { + const ESM::Spell* testSpell = spells.find(*weakIt); + if (testSpell->mData.mCost < cap.mMinCost) // XXX what if 2 candidates have the same cost? + { + cap.mMinCost = testSpell->mData.mCost; + cap.mWeakestSpell = testSpell->mId; + } + } + } + else + { + cap.mCount += 1; + if (cap.mCount == cap.mLimit) + cap.mReachedLimit = true; + + if (spell->mData.mCost < cap.mMinCost) + { + cap.mWeakestSpell = spell->mId; + cap.mMinCost = spell->mData.mCost; + } + } + } + return selectedSpells; + } + + bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) + { + const std::vector& effects = spell->mEffects.mList; + for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) + { + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); + static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->getInt(); + + if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) + { + assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length); + if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin) + return false; + } + + if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) + { + assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length); + if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin) + return false; + } + } + + return true; + } + + ESM::Skill::SkillEnum mapSchoolToSkill(int school) + { + std::map schoolSkillMap; // maps spell school to skill id + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; + assert(schoolSkillMap.find(school) != schoolSkillMap.end()); + return schoolSkillMap[school]; + } + + void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm) + { + float minChance = FLT_MAX; + + const ESM::EffectList& effects = spell->mEffects; + for (std::vector::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it) + { + const ESM::ENAMstruct& effect = *it; + float x = effect.mDuration; + + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) + x = std::max(1.f, x); + + x *= 0.1f * magicEffect->mData.mBaseCost; + x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); + x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; // XXX spell.radius + if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) // XXX effect.flags & CAST_TARGET + x *= 1.5f; + + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEffectCostMult")->getFloat(); + x *= fEffectCostMult; + + float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)]; + if (s - x < minChance) + { + minChance = s - x; + effectiveSchool = magicEffect->mData.mSchool; + skillTerm = s; + } + } + } + + float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool) + { + if (spell->mData.mType != ESM::Spell::ST_Spell) + return 100.f; + + if (spell->mData.mFlags & ESM::Spell::F_Always) + return 100.f; + + float skillTerm; + if (effectiveSchool != -1) + skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)]; + else + calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this + + float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck]; + return castChance; + } +} diff --git a/apps/openmw/mwmechanics/autocalcspell.hpp b/apps/openmw/mwmechanics/autocalcspell.hpp new file mode 100644 index 0000000000..8ba7d833f7 --- /dev/null +++ b/apps/openmw/mwmechanics/autocalcspell.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_AUTOCALCSPELL_H +#define OPENMW_AUTOCALCSPELL_H + +#include +#include + +#include +#include +#include + +namespace MWMechanics +{ + +/// Contains algorithm for calculating an NPC's spells based on stats +/// @note We might want to move this code to a component later, so the editor can use it for preview purposes + +std::set autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); + +// Helpers + +bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); + +ESM::Skill::SkillEnum mapSchoolToSkill(int school); + +void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm); + +float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool); + +} + +#endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6e515142de..073cb5f74c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -19,6 +19,7 @@ #include #include "spellcasting.hpp" +#include "autocalcspell.hpp" namespace { @@ -155,19 +156,6 @@ namespace MWMechanics npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + bonus); } - - if (i==1) - { - // Major skill - add starting spells for this skill if existing - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) - { - if (it->mData.mFlags & ESM::Spell::F_PCStart - && spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index) - creatureStats.getSpells().add(it->mId); - } - } } } @@ -190,6 +178,88 @@ namespace MWMechanics } } + // F_PCStart spells + static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->getFloat(); + + float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); + bool reachedLimit = false; + const ESM::Spell* weakestSpell = NULL; + int minCost = INT_MAX; + + std::set selectedSpells; + + const ESM::Race* race = NULL; + if (mRaceSelected) + race = esmStore.get().find(player->mRace); + + int skills[ESM::Skill::Length]; + for (int i=0; i &spells = + esmStore.get(); + for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) + { + const ESM::Spell* spell = &*iter; + + if (spell->mData.mType != ESM::Spell::ST_Spell) + continue; + if (!(spell->mData.mFlags & ESM::Spell::F_PCStart)) + continue; + if (reachedLimit && spell->mData.mCost <= minCost) + continue; + if (selectedSpells.find(spell->mId) != selectedSpells.end()) + continue; + if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) + continue; + if (baseMagicka < spell->mData.mCost) + continue; + + static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->getFloat(); + if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance) + continue; + + if (!attrSkillCheck(spell, skills, attributes)) + continue; + + selectedSpells.insert(spell->mId); + + if (reachedLimit) + { + selectedSpells.erase(weakestSpell->mId); + + minCost = INT_MAX; + for (std::set::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + { + const ESM::Spell* testSpell = esmStore.get().find(*weakIt); + if (testSpell->mData.mCost < minCost) // XXX what if 2 candidates have the same cost? + // Note iAutoPCSpellMax is 100 by default, so reachedLimit is very unlikely to happen + { + minCost = testSpell->mData.mCost; + weakestSpell = testSpell; + } + } + } + else + { + if (spell->mData.mCost < minCost) + { + weakestSpell = spell; + minCost = weakestSpell->mData.mCost; + } + static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->getInt(); + if (selectedSpells.size() == iAutoPCSpellMax) + reachedLimit = true; + } + } + + for (std::set::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) + creatureStats.getSpells().add(*it); + // forced update and current value adjustments mActors.updateActor (ptr, 0); diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index cbf5366c4b..4bd2210ec0 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -27,8 +27,8 @@ struct Spell enum Flags { - F_Autocalc = 1, - F_PCStart = 2, + F_Autocalc = 1, // Can be selected by NPC spells auto-calc + F_PCStart = 2, // Can be selected by player spells auto-calc F_Always = 4 // Casting always succeeds }; From b4f8edc186772f029411cbd317b1bf6551a2f436 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 18:25:29 +0200 Subject: [PATCH 26/97] Use spell effect range type, not base effect --- apps/openmw/mwmechanics/autocalcspell.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 5659959e84..d2b6c46e3b 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -177,8 +177,8 @@ namespace MWMechanics x *= 0.1f * magicEffect->mData.mBaseCost; x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); - x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; // XXX spell.radius - if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) // XXX effect.flags & CAST_TARGET + x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; + if (effect.mRange == ESM::RT_Target) x *= 1.5f; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEffectCostMult")->getFloat(); From 479f248c1ddebc532edae65ae954c3be1b367407 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jul 2014 18:33:39 +0200 Subject: [PATCH 27/97] Use spell effect range type, not base effect (applied to spellcasting code) --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 6467730dda..1b0c444ab7 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -92,7 +92,7 @@ namespace MWMechanics x *= 0.1 * magicEffect->mData.mBaseCost; x *= 0.5 * (it->mMagnMin + it->mMagnMax); x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; - if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) + if (it->mRange == ESM::RT_Target) x *= 1.5; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( "fEffectCostMult")->getFloat(); From 83819b2894371d25f4be0ec79c108bda5ec06178 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jul 2014 08:05:10 +0200 Subject: [PATCH 28/97] Traverse spells in record order from content files. bronrod_the_roarer is perfect now. Other NPCs have some differences. --- apps/openmw/mwclass/npc.cpp | 28 ++---------------- apps/openmw/mwmechanics/autocalcspell.cpp | 32 +++++++++++++++----- apps/openmw/mwmechanics/autocalcspell.hpp | 2 +- apps/openmw/mwworld/store.hpp | 36 +++++++++++++++++++---- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 80900d4655..31e09bbacf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -221,33 +221,9 @@ namespace for (int i=0; imId << std::endl; - std::cout << "Skills: " << std::endl; - for (int i=0; i spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race); - std::cout << "Spells: " << spells.size() << std::endl; - for (std::set::iterator it = spells.begin(); it != spells.end(); ++it) - { - std::cout << *it << ", "; + std::vector spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race); + for (std::vector::iterator it = spells.begin(); it != spells.end(); ++it) npcStats.getSpells().add(*it); - } - std::cout << std::endl; - - const char* compare[] = { "weary","dire noise","reflect","weak spelldrinker","absorb endurance","absorb personality","absorb speed","absorb strength","absorb willpower","fortify alteration skill","fortify illusion skill","fortify unarmored skill","fortify mysticism skill","fortify restoration skill","assured sublime wisdom","surpassing sublime wisdom","surpassing golden wisdom","blood gift","surpassing silver wisdom","surpassing unseen wisdom","surpassing green wisdom","powerwell","orc's strength","surpassing fluid evasion","poet's whim","rapid regenerate","dispel","shadow weave" }; - int n = sizeof(compare) / sizeof(compare[0]); - std::set compareSet; - for (int i=0; i::iterator it = compareSet.begin(); it != compareSet.end(); ++it) - { - std::cout << *it << ", "; - } - std::cout << std::endl; } } diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index d2b6c46e3b..255decdf75 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -20,7 +20,7 @@ namespace MWMechanics std::string mWeakestSpell; }; - std::set autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) + std::vector autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); @@ -53,10 +53,13 @@ namespace MWMechanics schoolCaps[i] = caps; } - std::set selectedSpells; + std::vector selectedSpells; const MWWorld::Store &spells = MWBase::Environment::get().getWorld()->getStore().get(); + + // Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the + // Store must preserve the record ordering as it was in the content files. for (MWWorld::Store::iterator iter = spells.begin(); iter != spells.end(); ++iter) { const ESM::Spell* spell = &*iter; @@ -88,18 +91,32 @@ namespace MWMechanics if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) continue; - selectedSpells.insert(spell->mId); + selectedSpells.push_back(spell->mId); if (cap.mReachedLimit) { - selectedSpells.erase(cap.mWeakestSpell); + std::vector::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell); + if (found != selectedSpells.end()) + selectedSpells.erase(found); - // Note: not school specific cap.mMinCost = INT_MAX; - for (std::set::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = spells.find(*weakIt); - if (testSpell->mData.mCost < cap.mMinCost) // XXX what if 2 candidates have the same cost? + + //int testSchool; + //float dummySkillTerm; + //calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm); + + // Note: if there are multiple spells with the same cost, we pick the first one we found. + // So the algorithm depends on the iteration order of the outer loop. + if ( + // There is a huge bug here. It is not checked that weakestSpell is of the correct school. + // As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school + // already erased it, and so the number of spells would often exceed the sum of limits. + // This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested. + //testSchool == school && + testSpell->mData.mCost < cap.mMinCost) { cap.mMinCost = testSpell->mData.mCost; cap.mWeakestSpell = testSpell->mId; @@ -119,6 +136,7 @@ namespace MWMechanics } } } + return selectedSpells; } diff --git a/apps/openmw/mwmechanics/autocalcspell.hpp b/apps/openmw/mwmechanics/autocalcspell.hpp index 8ba7d833f7..1912c75c42 100644 --- a/apps/openmw/mwmechanics/autocalcspell.hpp +++ b/apps/openmw/mwmechanics/autocalcspell.hpp @@ -14,7 +14,7 @@ namespace MWMechanics /// Contains algorithm for calculating an NPC's spells based on stats /// @note We might want to move this code to a component later, so the editor can use it for preview purposes -std::set autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); +std::vector autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race); // Helpers diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 3e7e7a5f98..9a442387b3 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -209,8 +209,6 @@ namespace MWWorld } void setUp() { - //std::sort(mStatic.begin(), mStatic.end(), RecordCmp()); - mShared.clear(); mShared.reserve(mStatic.size()); typename std::map::iterator it = mStatic.begin(); @@ -675,18 +673,15 @@ namespace MWWorld } void setUp() { - //typedef std::vector::iterator Iterator; typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; - //std::sort(mInt.begin(), mInt.end(), RecordCmp()); mSharedInt.clear(); mSharedInt.reserve(mInt.size()); for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { mSharedInt.push_back(&(it->second)); } - //std::sort(mExt.begin(), mExt.end(), ExtCmp()); mSharedExt.clear(); mSharedExt.reserve(mExt.size()); for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { @@ -1147,6 +1142,37 @@ namespace MWWorld } }; + + // Specialisation for ESM::Spell to preserve record order as it was in the content files. + // The NPC spell autocalc code heavily depends on this order. + // We could also do this in the base class, but it's usually not a good idea to depend on record order. + template<> + inline void Store::clearDynamic() + { + // remove the dynamic part of mShared + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + mDynamic.clear(); + } + + template<> + inline void Store::load(ESM::ESMReader &esm, const std::string &id) { + std::string idLower = Misc::StringUtils::lowerCase(id); + + std::pair inserted = mStatic.insert(std::make_pair(idLower, ESM::Spell())); + if (inserted.second) + mShared.push_back(&mStatic[idLower]); + + inserted.first->second.mId = idLower; + inserted.first->second.load(esm); + } + + template<> + inline void Store::setUp() + { + // remove the dynamic part of mShared + mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); + } + } //end namespace #endif From a1639371d3f2e637036c40222082dc85f4775072 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jul 2014 14:05:57 +0200 Subject: [PATCH 29/97] Fix rounding for skill/attribute autocalc --- apps/openmw/mwclass/npc.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 31e09bbacf..b05bc438cd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -54,6 +54,24 @@ namespace return new NpcCustomData (*this); } + int is_even(double d) { + double int_part; + modf(d / 2.0, &int_part); + return 2.0 * int_part == d; + } + + int round_ieee_754(double d) { + double i = floor(d); + d -= i; + if(d < 0.5) + return i; + if(d > 0.5) + return i + 1.0; + if(is_even(i)) + return i; + return i + 1.0; + } + void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) { // race bonus @@ -109,8 +127,9 @@ namespace } modifierSum += add; } - creatureStats.setAttribute(attribute, std::min(creatureStats.getAttribute(attribute).getBase() - + static_cast((level-1) * modifierSum+0.5), 100) ); + creatureStats.setAttribute(attribute, std::min( + round_ieee_754(creatureStats.getAttribute(attribute).getBase() + + (level-1) * modifierSum), 100) ); } // initial health @@ -206,11 +225,12 @@ namespace npcStats.getSkill(skillIndex).setBase( std::min( - npcStats.getSkill(skillIndex).getBase() + round_ieee_754( + npcStats.getSkill(skillIndex).getBase() + 5 + raceBonus + specBonus - + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100)); + + (level-1) * (majorMultiplier + specMultiplier)), 100)); } int skills[ESM::Skill::Length]; From fd3f1093626ed666ae177e0bb8febd2a0c09db8b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jul 2014 14:43:57 +0200 Subject: [PATCH 30/97] Fix underflow for NPCs with level 0 --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b05bc438cd..e2cb8ba7af 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -230,7 +230,7 @@ namespace + 5 + raceBonus + specBonus - + (level-1) * (majorMultiplier + specMultiplier)), 100)); + +(int(level)-1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0 } int skills[ESM::Skill::Length]; From d63dd881130d87ef11463b72796eacbad7dd5c63 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jul 2014 17:35:38 +0200 Subject: [PATCH 31/97] Apply autocalc corrections to player starting spells --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 073cb5f74c..d7c3e2f56d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -186,7 +186,7 @@ namespace MWMechanics const ESM::Spell* weakestSpell = NULL; int minCost = INT_MAX; - std::set selectedSpells; + std::vector selectedSpells; const ESM::Race* race = NULL; if (mRaceSelected) @@ -212,8 +212,6 @@ namespace MWMechanics continue; if (reachedLimit && spell->mData.mCost <= minCost) continue; - if (selectedSpells.find(spell->mId) != selectedSpells.end()) - continue; if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end()) continue; if (baseMagicka < spell->mData.mCost) @@ -226,18 +224,19 @@ namespace MWMechanics if (!attrSkillCheck(spell, skills, attributes)) continue; - selectedSpells.insert(spell->mId); + selectedSpells.push_back(spell->mId); if (reachedLimit) { - selectedSpells.erase(weakestSpell->mId); + std::vector::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId); + if (it != selectedSpells.end()) + selectedSpells.erase(it); minCost = INT_MAX; - for (std::set::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) + for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = esmStore.get().find(*weakIt); - if (testSpell->mData.mCost < minCost) // XXX what if 2 candidates have the same cost? - // Note iAutoPCSpellMax is 100 by default, so reachedLimit is very unlikely to happen + if (testSpell->mData.mCost < minCost) { minCost = testSpell->mData.mCost; weakestSpell = testSpell; @@ -257,7 +256,7 @@ namespace MWMechanics } } - for (std::set::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) + for (std::vector::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) creatureStats.getSpells().add(*it); // forced update and current value adjustments From 7316f4dde464a58a6ec91a4a292ce3c7558d3584 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Jul 2014 12:17:24 +0200 Subject: [PATCH 32/97] increased version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9de8efeb54..cb9a54a6cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 30) +set(OPENMW_VERSION_MINOR 31) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/readme.txt b/readme.txt index 92cb35f31f..90f223a646 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.30.0 +Version: 0.31.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 7cd819f24f3b213c4407ff1fbb5bdb82428e0d84 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 4 Jul 2014 08:58:54 +0200 Subject: [PATCH 33/97] updated changelog --- readme.txt | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/readme.txt b/readme.txt index 90f223a646..bafca48a47 100644 --- a/readme.txt +++ b/readme.txt @@ -96,6 +96,188 @@ Allowed options: CHANGELOG +0.31.0 + +Bug #245: Cloud direction and weather systems differ from Morrowind +Bug #275: Local Map does not always show objects that span multiple cells +Bug #538: Update CenterOnCell (COC) function behavior +Bug #618: Local and World Map Textures are sometimes Black +Bug #640: Water behaviour at night +Bug #668: OpenMW doesn't support non-latin paths on Windows +Bug #746: OpenMW doesn't check if the background music was already played +Bug #747: Door is stuck if cell is left before animation finishes +Bug #772: Disabled statics are visible on map +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #869: Dead bodies don't collide with anything +Bug #894: Various character creation issues +Bug #897/#1369: opencs Segmentation Fault after "new" or "load" +Bug #899: Various jumping issues +Bug #952: Reflection effects are one frame delayed +Bug #993: Able to interact with world during Wait/Rest dialog +Bug #995: Dropped items can be placed inside the wall +Bug #1008: Corpses always face up upon reentering the cell +Bug #1035: Random colour patterns appearing in automap +Bug #1037: Footstep volume issues +Bug #1047: Creation of wrong links in dialogue window +Bug #1129: Summoned creature time life duration seems infinite +Bug #1134: Crimes can be committed against hostile NPCs +Bug #1136: Creature run speed formula is incorrect +Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell +Bug #1155: NPCs killing each other +Bug #1166: Bittercup script still does not work +Bug #1178: .bsa file names are case sensitive. +Bug #1179: Crash after trying to load game after being killed +Bug #1180: Changing footstep sound location +Bug #1196: Jumping not disabled when showing messageboxes +Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works +Bug #1217: Container content changes based on the current position of the mouse +Bug #1234: Loading/saving issues with dynamic records +Bug #1277: Text pasted into the console appears twice +Bug #1284: Crash on New Game +Bug #1303: It's possible to skip the chargen +Bug #1304: Slaughterfish should not detect the player unless the player is in the water +Bug #1311: Editor: deleting Record Filter line does not reset the filter +Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp +Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table +Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running. +Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them. +Bug #1335: Actors ignore vertical axis when deciding to attack +Bug #1338: Unknown toggle option for shadows +Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process +Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard. +Bug #1348: Regression: Bug #1098 has returned with a vengeance +Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated +Bug #1352: Disabling an ESX file does not disable dependent ESX files +Bug #1355: CppCat Checks OpenMW +Bug #1356: Incorrect voice type filtering for sleep interrupts +Bug #1357: Restarting the game clears saves +Bug #1360: Seyda Neen silk rider dialog problem +Bug #1361: Some lights don't work +Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu +Bug #1370: Animation compilation mod does not work properly +Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla +Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog +Bug #1378: Installs to /usr/local are not working +Bug #1380: Loading a save file fail if one of the content files is disabled +Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp" +Bug #1386: Arkngthand door will not open +Bug #1388: Segfault when modifying View Distance in Menu options +Bug #1389: Crash when loading a save after dying +Bug #1390: Apostrophe characters not displayed [French version] +Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI. +Bug #1393: Coin icon during the level up dialogue are off of the background +Bug #1394: Alt+F4 doesn't work on Win version +Bug #1395: Changing rings switches only the last one put on +Bug #1396: Pauldron parts aren't showing when the robe is equipped +Bug #1402: Dialogue of some shrines have wrong button orientation +Bug #1403: Items are floating in the air when they're dropped onto dead bodies. +Bug #1404: Forearms are not rendered on Argonian females +Bug #1407: Alchemy allows making potions from two of the same item +Bug #1408: "Max sale" button gives you all the items AND all the trader's gold +Bug #1409: Rest "Until Healed" broken for characters with stunted magicka. +Bug #1412: Empty travel window opens while playing through start game +Bug #1413: Save game ignores missing writing permission +Bug #1414: The Underground 2 ESM Error +Bug #1416: Not all splash screens in the Splash directory are used +Bug #1417: Loading saved game does not terminate +Bug #1419: Skyrim: Home of the Nords error +Bug #1422: ClearInfoActor +Bug #1423: ForceGreeting closes existing dialogue windows +Bug #1425: Cannot load save game +Bug #1426: Read skill books aren't stored in savegame +Bug #1427: Useless items can be set under hotkeys +Bug #1429: Text variables in journal +Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing +Bug #1435: Stealing priceless items is without punishment +Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air +Bug #1440: Topic selection menu should be wider +Bug #1441: Dropping items on the rug makes them inaccessible +Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime +Bug #1444: Arrows and bolts are not dropped where the cursor points +Bug #1445: Security trainers offering acrobatics instead +Bug #1447: Character dash not displayed, French edition +Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue +Bug #1454: Script error in SkipTutorial +Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE +Bug #1457: Heart of Lorkan comes after you when attacking it +Bug #1458: Modified Keybindings are not remembered +Bug #1459: Dura Gra-Bol doesn't respond to PC attack +Bug #1462: Interior cells not loaded with Morrowind Patch active +Bug #1469: Item tooltip should show the base value, not real value +Bug #1477: Death count is not stored in savegame +Bug #1478: AiActivate does not trigger activate scripts +Bug #1481: Weapon not rendered when partially submerged in water +Bug #1483: Enemies are attacking even while dying +Bug #1486: ESM Error: Don't know what to do with INFO +Bug #1490: Arrows shot at PC can end up in inventory +Bug #1492: Monsters respawn on top of one another +Bug #1493: Dialogue box opens with follower NPC even if NPC is dead +Bug #1494: Paralysed cliffracers remain airbourne +Bug #1495: Dialogue box opens with follower NPC even the game is paused +Bug #1496: GUI messages are not cleared when loading another saved game +Bug #1499: Underwater sound sometimes plays when transitioning from interior. +Bug #1500: Targetted spells and water. +Bug #1502: Console error message on info refusal +Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow +Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius +Bug #1516: PositionCell doesn't move actors to current cell +Bug #1518: ForceGreeting broken for explicit references +Bug #1522: Crash after attempting to play non-music file +Bug #1523: World map empty after loading interior save +Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons +Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood +Bug #1527: Werewolf: Detect life detects wrong type of actor +Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal) +Bug #1530: Selected text in the console has the same color as the background +Bug #1539: Barilzar's Mazed Band: Tribunal +Bug #1542: Looping taunts from NPC`s after death: Tribunal +Bug #1543: OpenCS crash when using drag&drop in script editor +Bug #1547: Bamz-Amschend: Centurion Archers combat problem +Bug #1548: The Missing Hand: Tribunal +Bug #1549: The Mad God: Tribunal, Dome of Serlyn +Bug #1557: A bounty is calculated from actual item cost +Bug #1562: Invisible terrain on top of Red Mountain +Bug #1564: Cave of the hidden music: Bloodmoon +Bug #1567: Editor: Deleting of referenceables does not work +Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen. +Bug #1574: Solstheim: Drauger cant inflict damage on player +Bug #1578: Solstheim: Bonewolf running animation not working +Bug #1585: Particle effects on PC are stopped when paralyzed +Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed +Bug #1590: Failed to save game: compile error +Bug #1598: Segfault when making Drain/Fortify Skill spells +Bug #1599: Unable to switch to fullscreen +Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed +Feature #32: Periodic Cleanup/Refill +Feature #41: Precipitation and weather particles +Feature #568: Editor: Configuration setup +Feature #649: Editor: Threaded loading +Feature #930: Editor: Cell record saving +Feature #934: Editor: Body part table +Feature #935: Editor: Enchantment effect table +Feature #1162: Dialogue merging +Feature #1174: Saved Game: add missing creature state +Feature #1177: Saved Game: fog of war state +Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed +Feature #1314: Make NPCs and creatures fight each other +Feature #1315: Crime: Murder +Feature #1321: Sneak skill enhancements +Feature #1323: Handle restocking items +Feature #1332: Saved Game: levelled creatures +Feature #1347: modFactionReaction script instruction +Feature #1362: Animated main menu support +Feature #1433: Store walk/run toggle +Feature #1449: Use names instead of numbers for saved game files and folders +Feature #1453: Adding Delete button to the load menu +Feature #1460: Enable Journal screen while in dialogue +Feature #1480: Play Battle music when in combat +Feature #1501: Followers unable to fast travel with you +Feature #1520: Disposition and distance-based aggression/ShouldAttack +Feature #1595: Editor: Object rendering in cells +Task #940: Move license to locations where applicable +Task #1333: Remove cmake git tag reading +Task #1566: Editor: Object rendering refactoring + 0.30.0 Bug #416: Extreme shaking can occur during cell transitions while moving From 41d73377b0fa0fd6960b3d4be899bd2ffe2ccaca Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 4 Jul 2014 23:19:40 +0200 Subject: [PATCH 34/97] Use explicit C locale in Misc::StringUtils (Fixes #1216) --- components/misc/stringops.cpp | 55 +---------------------------------- components/misc/stringops.hpp | 32 ++++++-------------- 2 files changed, 10 insertions(+), 77 deletions(-) diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index 0bc8e290a1..0f801e5549 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -12,59 +12,6 @@ namespace Misc { -bool begins(const char* str1, const char* str2) -{ - while(*str2) - { - if(*str1 == 0 || *str1 != *str2) return false; - - str1++; - str2++; - } - return true; -} - -bool ends(const char* str1, const char* str2) -{ - int len1 = strlen(str1); - int len2 = strlen(str2); - - if(len1 < len2) return false; - - return strcmp(str2, str1+len1-len2) == 0; -} - -// True if the given chars match, case insensitive -static bool icmp(char a, char b) -{ - if(a >= 'A' && a <= 'Z') - a += 'a' - 'A'; - if(b >= 'A' && b <= 'Z') - b += 'a' - 'A'; - - return a == b; -} - -bool ibegins(const char* str1, const char* str2) -{ - while(*str2) - { - if(*str1 == 0 || !icmp(*str1,*str2)) return false; - - str1++; - str2++; - } - return true; -} - -bool iends(const char* str1, const char* str2) -{ - int len1 = strlen(str1); - int len2 = strlen(str2); - - if(len1 < len2) return false; - - return strcasecmp(str2, str1+len1-len2) == 0; -} +std::locale StringUtils::mLocale = std::locale::classic(); } diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index d41463cfce..04dedb0721 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -4,15 +4,18 @@ #include #include #include +#include namespace Misc { class StringUtils { + + static std::locale mLocale; struct ci { - bool operator()(int x, int y) const { - return std::tolower(x) < std::tolower(y); + bool operator()(char x, char y) const { + return std::tolower(x, StringUtils::mLocale) < std::tolower(y, StringUtils::mLocale); } }; @@ -28,7 +31,7 @@ public: std::string::const_iterator xit = x.begin(); std::string::const_iterator yit = y.begin(); for (; xit != x.end(); ++xit, ++yit) { - if (std::tolower(*xit) != std::tolower(*yit)) { + if (std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) { return false; } } @@ -42,7 +45,7 @@ public: for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) { int res = *xit - *yit; - if(res != 0 && std::tolower(*xit) != std::tolower(*yit)) + if(res != 0 && std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) return (res > 0) ? 1 : -1; } if(len > 0) @@ -57,12 +60,8 @@ public: /// Transforms input string to lower case w/o copy static std::string &toLower(std::string &inout) { - std::transform( - inout.begin(), - inout.end(), - inout.begin(), - (int (*)(int)) std::tolower - ); + for (unsigned int i=0; i Date: Sat, 5 Jul 2014 07:24:30 +0200 Subject: [PATCH 35/97] Fix travis --- .../components/misc/test_stringops.cpp | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/apps/openmw_test_suite/components/misc/test_stringops.cpp b/apps/openmw_test_suite/components/misc/test_stringops.cpp index 44587c445f..55fe0e0c27 100644 --- a/apps/openmw_test_suite/components/misc/test_stringops.cpp +++ b/apps/openmw_test_suite/components/misc/test_stringops.cpp @@ -12,68 +12,3 @@ struct StringOpsTest : public ::testing::Test { } }; - -TEST_F(StringOpsTest, begins_matching) -{ - ASSERT_TRUE(Misc::begins("abc", "a")); - ASSERT_TRUE(Misc::begins("abc", "ab")); - ASSERT_TRUE(Misc::begins("abc", "abc")); - ASSERT_TRUE(Misc::begins("abcd", "abc")); -} - -TEST_F(StringOpsTest, begins_not_matching) -{ - ASSERT_FALSE(Misc::begins("abc", "b")); - ASSERT_FALSE(Misc::begins("abc", "bc")); - ASSERT_FALSE(Misc::begins("abc", "bcd")); - ASSERT_FALSE(Misc::begins("abc", "abcd")); -} - -TEST_F(StringOpsTest, ibegins_matching) -{ - ASSERT_TRUE(Misc::ibegins("Abc", "a")); - ASSERT_TRUE(Misc::ibegins("aBc", "ab")); - ASSERT_TRUE(Misc::ibegins("abC", "abc")); - ASSERT_TRUE(Misc::ibegins("abcD", "abc")); -} - -TEST_F(StringOpsTest, ibegins_not_matching) -{ - ASSERT_FALSE(Misc::ibegins("abc", "b")); - ASSERT_FALSE(Misc::ibegins("abc", "bc")); - ASSERT_FALSE(Misc::ibegins("abc", "bcd")); - ASSERT_FALSE(Misc::ibegins("abc", "abcd")); -} - -TEST_F(StringOpsTest, ends_matching) -{ - ASSERT_TRUE(Misc::ends("abc", "c")); - ASSERT_TRUE(Misc::ends("abc", "bc")); - ASSERT_TRUE(Misc::ends("abc", "abc")); - ASSERT_TRUE(Misc::ends("abcd", "abcd")); -} - -TEST_F(StringOpsTest, ends_not_matching) -{ - ASSERT_FALSE(Misc::ends("abc", "b")); - ASSERT_FALSE(Misc::ends("abc", "ab")); - ASSERT_FALSE(Misc::ends("abc", "bcd")); - ASSERT_FALSE(Misc::ends("abc", "abcd")); -} - -TEST_F(StringOpsTest, iends_matching) -{ - ASSERT_TRUE(Misc::iends("Abc", "c")); - ASSERT_TRUE(Misc::iends("aBc", "bc")); - ASSERT_TRUE(Misc::iends("abC", "abc")); - ASSERT_TRUE(Misc::iends("abcD", "abcd")); -} - -TEST_F(StringOpsTest, iends_not_matching) -{ - ASSERT_FALSE(Misc::iends("abc", "b")); - ASSERT_FALSE(Misc::iends("abc", "ab")); - ASSERT_FALSE(Misc::iends("abc", "bcd")); - ASSERT_FALSE(Misc::iends("abc", "abcd")); -} - From f2799ea1d9c986ace162f69526612bfcfc7dc1e6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jul 2014 23:37:59 +0200 Subject: [PATCH 36/97] Reset item model when reference is reset (Fixes #1628) This caused crashes when the window was resized after the reference no longer exists (e.g. when a savegame is loaded) --- apps/openmw/mwgui/companionwindow.cpp | 5 +++++ apps/openmw/mwgui/companionwindow.hpp | 2 ++ apps/openmw/mwgui/container.cpp | 6 ++++++ apps/openmw/mwgui/container.hpp | 2 ++ apps/openmw/mwgui/tradewindow.cpp | 6 ++++++ apps/openmw/mwgui/tradewindow.hpp | 1 + 6 files changed, 22 insertions(+) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index d0ac3e7c36..8d199e7275 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -153,6 +153,11 @@ void CompanionWindow::onReferenceUnavailable() MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); } +void CompanionWindow::resetReference() +{ + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); +} } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 006d0a3c35..dc460e2fc2 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -20,6 +20,8 @@ namespace MWGui virtual void exit(); + virtual void resetReference(); + void open(const MWWorld::Ptr& npc); void onFrame (); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 011feb4d35..8da3def5fa 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -258,6 +258,12 @@ namespace MWGui onTakeAllButtonClicked(mTakeButton); } + void ContainerWindow::resetReference() + { + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); + } + void ContainerWindow::close() { WindowBase::close(); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 5446a4ab73..79951f70e9 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -54,6 +54,8 @@ namespace MWGui void open(const MWWorld::Ptr& container, bool loot=false); virtual void close(); + virtual void resetReference(); + virtual void exit(); private: diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c0a51311f5..19187cde1f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -531,4 +531,10 @@ namespace MWGui sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); } } + + void TradeWindow::resetReference() + { + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index cc70f1ae96..b487a8870e 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -37,6 +37,7 @@ namespace MWGui virtual void exit(); + virtual void resetReference(); private: ItemView* mItemView; From 8b404ee255d816bcbede0c0d5b51100ddb7b8b60 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 06:42:24 +0200 Subject: [PATCH 37/97] Fix not being able to exit dialogue using Escape when a Goodbye link appears --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 ++- apps/openmw/mwgui/dialogue.cpp | 9 +++++++-- apps/openmw/mwinput/inputmanagerimp.cpp | 8 -------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 8d9dc670f2..5d55a7a6f3 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -505,9 +505,10 @@ namespace MWDialogue void DialogueManager::askQuestion (const std::string& question, int choice) { + mIsInChoice = true; + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->addChoice(question, choice); - mIsInChoice = true; } void DialogueManager::goodbye() diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a6ab1f122b..2283e0cbe7 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -273,8 +273,13 @@ namespace MWGui { if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) && !mGoodbye) - return; - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + { + // in choice, not allowed to escape, but give access to main menu to allow loading other saves + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); + } + else + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } void DialogueWindow::onWindowResize(MyGUI::Window* _sender) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8d4c53921c..067ad72bb8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -686,14 +686,6 @@ namespace MWInput return; } - if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue) { //Give access to the main menu when at a choice in dialogue - if(MWBase::Environment::get().getDialogueManager()->isInChoice()) { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); - return; - } - } - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); From d0e1210dd7be3db928cbebae2b8511af460ef199 Mon Sep 17 00:00:00 2001 From: slothlife Date: Sun, 13 Jul 2014 01:39:42 -0500 Subject: [PATCH 38/97] Fix for Bullet debug assert After changing the vector comparison to squaredDistance, the threshold for rejecting similar vectors needed to be readjusted. --- apps/openmw/mwworld/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 2013937612..daad5b0e69 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -364,7 +364,7 @@ namespace MWWorld continue; // velocity updated, calculate nextpos again } - if(newPosition.squaredDistance(nextpos) > 0.00000001*0.00000001) + if(newPosition.squaredDistance(nextpos) > 0.0001) { // trace to where character would go if there were no obstructions tracer.doTrace(colobj, newPosition, nextpos, engine); From c9150c9680d430335203a791d00d8044b4d85acb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 10:35:11 +0200 Subject: [PATCH 39/97] Initialize character skeleton to a suitable pose (Fixes #1473) --- apps/openmw/mwmechanics/character.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d4ddf53cd0..638feeef37 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -607,6 +607,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(mDeathState == CharState_None) refreshCurrentAnims(mIdleState, mMovementState, true); + + mAnimation->runAnimation(0.f); } CharacterController::~CharacterController() From e855e5531837e410bb347c88d9f11b9142b69fe4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Jul 2014 10:52:33 +0200 Subject: [PATCH 40/97] added tooltip to user settings button on startup window --- apps/opencs/view/doc/startup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 5d59492c64..799a07e14b 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -81,6 +81,7 @@ QWidget *CSVDoc::StartupDialogue::createTools() config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); config->setIcon (QIcon (":startup/configure")); + config->setToolTip ("Open user settings"); layout->addWidget (config); From 26f87f5d235630d122270998ea79ef32ba33854b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Jul 2014 12:15:05 +0200 Subject: [PATCH 41/97] added navigation mode button tooltips --- apps/opencs/view/render/worldspacewidget.cpp | 31 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 5b3dc57e23..33f27c8bae 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -59,9 +59,34 @@ CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( { CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); - tool->addButton (":door.png", "1st"); /// \todo replace icons - tool->addButton (":GMST.png", "free"); - tool->addButton (":Info.png", "orbit"); + /// \todo replace icons + /// \todo consider user-defined button-mapping + tool->addButton (":door.png", "1st", + "First Person" + "
  • Mouse-Look while holding the left button
  • " + "
  • WASD movement keys
  • " + "
  • Mouse wheel moves the camera forawrd/backward
  • " + "
  • Stafing (also vertically) by holding the left mouse button and control
  • " + "
  • Camera is held upright
  • " + "
  • Hold shift to speed up movement
  • " + "
"); + tool->addButton (":GMST.png", "free", + "Free Camera" + "
  • Mouse-Look while holding the left button
  • " + "
  • Stafing (also vertically) via WASD or by holding the left mouse button and control
  • " + "
  • Mouse wheel moves the camera forawrd/backward
  • " + "
  • Roll camera with Q and E keys
  • " + "
  • Hold shift to speed up movement
  • " + "
"); + tool->addButton (":Info.png", "orbit", + "Orbiting Camera" + "
  • Always facing the centre point
  • " + "
  • Rotate around the centre point via WASD or by moving the mouse while holding the left button
  • " + "
  • Mouse wheel moves camera away or towards centre point but can not pass through it
  • " + "
  • Roll camera with Q and E keys
  • " + "
  • Stafing (also vertically) by holding the left mouse button and control (includes relocation of the centre point)
  • " + "
  • Hold shift to speed up movement
  • " + "
"); connect (tool, SIGNAL (modeChanged (const std::string&)), this, SLOT (selectNavigationMode (const std::string&))); From c2ea8f3f0a6483a55a8234c59c1377303528c38f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Jul 2014 14:21:50 +0200 Subject: [PATCH 42/97] added self-adjusting tooltips to the top-level toolbar buttons --- apps/opencs/view/render/scenewidget.cpp | 2 +- apps/opencs/view/render/worldspacewidget.cpp | 2 +- apps/opencs/view/widget/pushbutton.cpp | 21 ++++++++++++-------- apps/opencs/view/widget/pushbutton.hpp | 10 +++++++--- apps/opencs/view/widget/scenetoolmode.cpp | 20 ++++++++++++++++--- apps/opencs/view/widget/scenetoolmode.hpp | 7 +++++-- 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 8f6b74765f..5cad25b254 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -56,7 +56,7 @@ namespace CSVRender CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) { - CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); + CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode"); /// \todo replace icons tool->addButton (":door.png", "day", diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 33f27c8bae..d3413a29dd 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -57,7 +57,7 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode() CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( CSVWidget::SceneToolbar *parent) { - CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent); + CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Camera Mode"); /// \todo replace icons /// \todo consider user-defined button-mapping diff --git a/apps/opencs/view/widget/pushbutton.cpp b/apps/opencs/view/widget/pushbutton.cpp index 35bed1627d..056e548fbc 100644 --- a/apps/opencs/view/widget/pushbutton.cpp +++ b/apps/opencs/view/widget/pushbutton.cpp @@ -4,11 +4,11 @@ #include #include -void CSVWidget::PushButton::setExtendedToolTip (const std::string& text) +void CSVWidget::PushButton::setExtendedToolTip (const QString& text) { - std::string tooltip = text; + QString tooltip = text; - if (tooltip.empty()) + if (tooltip.isEmpty()) tooltip = "(Tool tip not implemented yet)"; switch (mType) @@ -29,7 +29,7 @@ void CSVWidget::PushButton::setExtendedToolTip (const std::string& text) break; } - setToolTip (QString::fromUtf8 (tooltip.c_str())); + setToolTip (tooltip); } void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event) @@ -57,16 +57,16 @@ void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event) QPushButton::mouseReleaseEvent (event); } -CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const std::string& tooltip, +CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip, QWidget *parent) -: QPushButton (icon, "", parent), mKeepOpen (false), mType (type) +: QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { setCheckable (type==Type_Mode); setExtendedToolTip (tooltip); } -CSVWidget::PushButton::PushButton (Type type, const std::string& tooltip, QWidget *parent) -: QPushButton (parent), mKeepOpen (false), mType (type) +CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent) +: QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip) { setCheckable (type==Type_Mode); setExtendedToolTip (tooltip); @@ -75,4 +75,9 @@ CSVWidget::PushButton::PushButton (Type type, const std::string& tooltip, QWidge bool CSVWidget::PushButton::hasKeepOpen() const { return mKeepOpen; +} + +QString CSVWidget::PushButton::getBaseToolTip() const +{ + return mToolTip; } \ No newline at end of file diff --git a/apps/opencs/view/widget/pushbutton.hpp b/apps/opencs/view/widget/pushbutton.hpp index 099cba3580..9b90bd0c66 100644 --- a/apps/opencs/view/widget/pushbutton.hpp +++ b/apps/opencs/view/widget/pushbutton.hpp @@ -21,10 +21,11 @@ namespace CSVWidget bool mKeepOpen; Type mType; + QString mToolTip; private: - void setExtendedToolTip (const std::string& text); + void setExtendedToolTip (const QString& text); protected: @@ -37,14 +38,17 @@ namespace CSVWidget public: /// \param push Do not maintain a toggle state - PushButton (const QIcon& icon, Type type, const std::string& tooltip = "", + PushButton (const QIcon& icon, Type type, const QString& tooltip = "", QWidget *parent = 0); /// \param push Do not maintain a toggle state - PushButton (Type type, const std::string& tooltip = "", + PushButton (Type type, const QString& tooltip = "", QWidget *parent = 0); bool hasKeepOpen() const; + + /// Return tooltip used at construction (without any button-specific modifications) + QString getBaseToolTip() const; }; } diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 412959c7c4..9a97924a8b 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -8,8 +8,20 @@ #include "scenetoolbar.hpp" #include "pushbutton.hpp" -CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent) -: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()) +void CSVWidget::SceneToolMode::adjustToolTip (const PushButton *activeMode) +{ + QString toolTip = mToolTip; + + toolTip += "

Currently selected: " + activeMode->getBaseToolTip(); + + toolTip += "

(left click to change mode)"; + + setToolTip (toolTip); +} + +CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip) +: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), + mToolTip (toolTip) { mPanel = new QFrame (this, Qt::Popup); @@ -30,7 +42,7 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) } void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id, - const std::string& tooltip) + const QString& tooltip) { PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode, tooltip, mPanel); @@ -48,6 +60,7 @@ void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::st { setIcon (button->icon()); button->setChecked (true); + adjustToolTip (button); } } @@ -66,6 +79,7 @@ void CSVWidget::SceneToolMode::selected() iter2->first->setChecked (iter2==iter); setIcon (iter->first->icon()); + adjustToolTip (iter->first); emit modeChanged (iter->second); } } \ No newline at end of file diff --git a/apps/opencs/view/widget/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp index 5f18f712f9..92f031fe7e 100644 --- a/apps/opencs/view/widget/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -22,15 +22,18 @@ namespace CSVWidget std::map mButtons; // widget, id int mButtonSize; int mIconSize; + QString mToolTip; + + void adjustToolTip (const PushButton *activeMode); public: - SceneToolMode (SceneToolbar *parent); + SceneToolMode (SceneToolbar *parent, const QString& toolTip); virtual void showPanel (const QPoint& position); void addButton (const std::string& icon, const std::string& id, - const std::string& tooltip = ""); + const QString& tooltip = ""); signals: From 7c7c71428afc4a6290d4f0972175b898819754d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 14:26:59 +0200 Subject: [PATCH 43/97] Don't crash if certain class doesn't exist --- apps/openmw/mwgui/levelupdialog.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eaa8227225..5c698258ae 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -132,22 +132,23 @@ namespace MWGui const ESM::Class *cls = world->getStore().get().find(playerData->mClass); - // Vanilla uses thief.dds for custom classes. A player with a custom class - // doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as - // "$dynamic0". This check should resolve bug #1260. - // Choosing Stealth specialization and Speed/Agility as attributes. if(world->getStore().get().isDynamic(cls->mId)) { + // Vanilla uses thief.dds for custom classes. + // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found. MWWorld::SharedIterator it = world->getStore().get().begin(); for(; it != world->getStore().get().end(); it++) { if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) break; } - mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds"); + if (it == world->getStore().get().end()) + it = world->getStore().get().begin(); + if (it != world->getStore().get().end()) + cls = &*it; } - else - mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); + + mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); From f31a5490f34383492e4153f7e39e1c7128b11c55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 19:35:26 +0200 Subject: [PATCH 44/97] Create shaders on first render in shader based MyGUI manager --- libs/openengine/gui/manager.cpp | 48 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 383d37640b..028192e9f5 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -135,28 +135,6 @@ public: setRenderWindow(_window); setSceneManager(_scene); - // ADDED - sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); - mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getVertexProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); - mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getVertexProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); - mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getFragmentProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); - mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getFragmentProgram()->_getBindingDelegate(); - - MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully initialized"); mIsInitialise = true; @@ -359,6 +337,30 @@ public: } } + void initShaders() + { + // ADDED + sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + } + void doRender(IVertexBuffer* _buffer, ITexture* _texture, size_t _count) { if (getManualRender()) @@ -368,6 +370,8 @@ public: } // ADDED + if (!mVertexProgramNoTexture) + initShaders(); if (_texture) { From 688d413b8c3b21c5d3d7d4ba09ccfb265dae071e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 14 Jul 2014 11:06:12 +0200 Subject: [PATCH 45/97] when scene toolbar gains focus, focus first button --- apps/opencs/view/widget/scenetoolbar.cpp | 8 ++++++++ apps/opencs/view/widget/scenetoolbar.hpp | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/apps/opencs/view/widget/scenetoolbar.cpp b/apps/opencs/view/widget/scenetoolbar.cpp index 59a3a1d6e2..f06c856cb9 100644 --- a/apps/opencs/view/widget/scenetoolbar.cpp +++ b/apps/opencs/view/widget/scenetoolbar.cpp @@ -5,6 +5,14 @@ #include "scenetool.hpp" +void CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event) +{ + QWidget::focusInEvent (event); + + if (mLayout->count()) + dynamic_cast (*mLayout->itemAt (0)).widget()->setFocus(); +} + CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) : QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6) { diff --git a/apps/opencs/view/widget/scenetoolbar.hpp b/apps/opencs/view/widget/scenetoolbar.hpp index 0ef84f488e..bd609078db 100644 --- a/apps/opencs/view/widget/scenetoolbar.hpp +++ b/apps/opencs/view/widget/scenetoolbar.hpp @@ -17,6 +17,10 @@ namespace CSVWidget int mButtonSize; int mIconSize; + protected: + + virtual void focusInEvent (QFocusEvent *event); + public: SceneToolbar (int buttonSize, QWidget *parent = 0); From 92af0938567b2acd70a9a20bdc089c4b59b9d2e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 14 Jul 2014 11:19:59 +0200 Subject: [PATCH 46/97] new keyboard shortcut (T): move focus from scene widget to toolbar --- apps/opencs/view/render/scenewidget.cpp | 7 ++++++- apps/opencs/view/render/scenewidget.hpp | 4 ++++ apps/opencs/view/world/previewsubview.cpp | 1 + apps/opencs/view/world/scenesubview.cpp | 2 ++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 5cad25b254..a37d1071a3 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -51,7 +52,11 @@ namespace CSVRender QTimer *timer = new QTimer (this); connect (timer, SIGNAL (timeout()), this, SLOT (update())); - timer->start (20); /// \todo make this configurable + timer->start (20); ///< \todo make this configurable + + /// \todo make shortcut configurable + QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this); + connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest())); } CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 8b01e71857..8f548f4831 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -111,6 +111,10 @@ namespace CSVRender void update(); void selectLightingMode (const std::string& mode); + + signals: + + void focusToolbarRequest(); }; } diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index bae0abb8bb..599e9cb9d8 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -46,6 +46,7 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)), this, SLOT (referenceableIdChanged (const std::string&))); + connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus())); } void CSVWorld::PreviewSubView::setEditLock (bool locked) {} diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 4430d28eb6..60988e1bb0 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -238,6 +238,8 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW mScene = widget; mToolbar = toolbar; + connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus())); + mLayout->addWidget (mToolbar, 0); mLayout->addWidget (mScene, 1); From 17fc570e38b669bc17540d9255de77334f9de9eb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 14 Jul 2014 13:49:55 +0200 Subject: [PATCH 47/97] new keyboard shortcut (T): move focus from toolbar to scene widget --- apps/opencs/view/render/scenewidget.cpp | 2 +- apps/opencs/view/widget/scenetoolbar.cpp | 7 ++++++- apps/opencs/view/widget/scenetoolbar.hpp | 4 ++++ apps/opencs/view/world/previewsubview.cpp | 1 + apps/opencs/view/world/scenesubview.cpp | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index a37d1071a3..ebd3eb7644 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -55,7 +55,7 @@ namespace CSVRender timer->start (20); ///< \todo make this configurable /// \todo make shortcut configurable - QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this); + QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest())); } diff --git a/apps/opencs/view/widget/scenetoolbar.cpp b/apps/opencs/view/widget/scenetoolbar.cpp index f06c856cb9..eac9bcec32 100644 --- a/apps/opencs/view/widget/scenetoolbar.cpp +++ b/apps/opencs/view/widget/scenetoolbar.cpp @@ -2,6 +2,7 @@ #include "scenetoolbar.hpp" #include +#include #include "scenetool.hpp" @@ -24,6 +25,10 @@ CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); setLayout (mLayout); + + /// \todo make shortcut configurable + QShortcut *focusScene = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); + connect (focusScene, SIGNAL (activated()), this, SIGNAL (focusSceneRequest())); } void CSVWidget::SceneToolbar::addTool (SceneTool *tool) @@ -39,4 +44,4 @@ int CSVWidget::SceneToolbar::getButtonSize() const int CSVWidget::SceneToolbar::getIconSize() const { return mIconSize; -} \ No newline at end of file +} diff --git a/apps/opencs/view/widget/scenetoolbar.hpp b/apps/opencs/view/widget/scenetoolbar.hpp index bd609078db..1902ba2bed 100644 --- a/apps/opencs/view/widget/scenetoolbar.hpp +++ b/apps/opencs/view/widget/scenetoolbar.hpp @@ -30,6 +30,10 @@ namespace CSVWidget int getButtonSize() const; int getIconSize() const; + + signals: + + void focusSceneRequest(); }; } diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 599e9cb9d8..1e106c69fe 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -47,6 +47,7 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)), this, SLOT (referenceableIdChanged (const std::string&))); connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus())); + connect (toolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus())); } void CSVWorld::PreviewSubView::setEditLock (bool locked) {} diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 60988e1bb0..dc1525b05e 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -239,6 +239,7 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW mToolbar = toolbar; connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus())); + connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus())); mLayout->addWidget (mToolbar, 0); mLayout->addWidget (mScene, 1); From 0430558c3cac7b1805d017e6c466aca5db98aab9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 14 Jul 2014 14:17:27 +0200 Subject: [PATCH 48/97] fixed focussed button in mode tool panel when panel is opened --- apps/opencs/view/widget/scenetoolmode.cpp | 7 ++++--- apps/opencs/view/widget/scenetoolmode.hpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/widget/scenetoolmode.cpp b/apps/opencs/view/widget/scenetoolmode.cpp index 9a97924a8b..caedfa3ee8 100644 --- a/apps/opencs/view/widget/scenetoolmode.cpp +++ b/apps/opencs/view/widget/scenetoolmode.cpp @@ -21,7 +21,7 @@ void CSVWidget::SceneToolMode::adjustToolTip (const PushButton *activeMode) CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip) : SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()), - mToolTip (toolTip) + mToolTip (toolTip), mFirst (0) { mPanel = new QFrame (this, Qt::Popup); @@ -37,8 +37,8 @@ void CSVWidget::SceneToolMode::showPanel (const QPoint& position) mPanel->move (position); mPanel->show(); - if (!mButtons.empty()) - mButtons.begin()->first->setFocus (Qt::OtherFocusReason); + if (mFirst) + mFirst->setFocus (Qt::OtherFocusReason); } void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id, @@ -58,6 +58,7 @@ void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::st if (mButtons.size()==1) { + mFirst = button; setIcon (button->icon()); button->setChecked (true); adjustToolTip (button); diff --git a/apps/opencs/view/widget/scenetoolmode.hpp b/apps/opencs/view/widget/scenetoolmode.hpp index 92f031fe7e..9959f98355 100644 --- a/apps/opencs/view/widget/scenetoolmode.hpp +++ b/apps/opencs/view/widget/scenetoolmode.hpp @@ -23,6 +23,7 @@ namespace CSVWidget int mButtonSize; int mIconSize; QString mToolTip; + PushButton *mFirst; void adjustToolTip (const PushButton *activeMode); From 6cd739678af152c7f47958e1b65bb2d411125e43 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 14 Jul 2014 16:53:58 +0200 Subject: [PATCH 49/97] Fix dangling model pointer after reference cleanup (Fixes #1653) --- apps/openmw/mwgui/companionwindow.cpp | 2 ++ apps/openmw/mwgui/container.cpp | 2 ++ apps/openmw/mwgui/tradewindow.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 8d199e7275..6ac8c9d187 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -157,6 +157,8 @@ void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mModel = NULL; + mSortModel = NULL; } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 8da3def5fa..65a9e07c40 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -262,6 +262,8 @@ namespace MWGui { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mModel = NULL; + mSortModel = NULL; } void ContainerWindow::close() diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 19187cde1f..c56c2ee94a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -536,5 +536,7 @@ namespace MWGui { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mTradeModel = NULL; + mSortModel = NULL; } } From 58396915305aa69b3d968b49853e2494e48ff1da Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 14 Jul 2014 17:27:36 +0200 Subject: [PATCH 50/97] Check for container organic flag before checking weight (Fixes #1654) --- apps/openmw/mwgui/container.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 65a9e07c40..f0212031e4 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -195,15 +195,6 @@ namespace MWGui { if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - // check that we don't exceed container capacity - MWWorld::Ptr item = mDragAndDrop->mItem.mBase; - float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; - if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); - return; - } - // check container organic flag MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mFlags & ESM::Container::Organic) @@ -212,6 +203,15 @@ namespace MWGui messageBox("#{sContentsMessage2}"); return; } + + // check that we don't exceed container capacity + MWWorld::Ptr item = mDragAndDrop->mItem.mBase; + float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; + if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return; + } } mDragAndDrop->drop(mModel, mItemView); From 1a1f5dfc4a1ada1ebc842075129b7bef8f169c51 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jul 2014 12:46:52 +0200 Subject: [PATCH 51/97] added tooltips to dialgoue subview buttons --- apps/opencs/view/world/dialoguesubview.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index ad2db87231..bcf108934f 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -426,25 +426,32 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM QHBoxLayout *buttonsLayout = new QHBoxLayout; QToolButton* prevButton = new QToolButton(mainWidget); prevButton->setIcon(QIcon(":/go-previous.png")); + prevButton->setToolTip ("Switch to previous record"); QToolButton* nextButton = new QToolButton(mainWidget); nextButton->setIcon(QIcon(":/go-next.png")); + nextButton->setToolTip ("Switch to next record"); buttonsLayout->addWidget(prevButton, 0); buttonsLayout->addWidget(nextButton, 1); buttonsLayout->addStretch(2); QToolButton* cloneButton = new QToolButton(mainWidget); cloneButton->setIcon(QIcon(":/edit-clone.png")); + cloneButton->setToolTip ("Clone record"); QToolButton* addButton = new QToolButton(mainWidget); addButton->setIcon(QIcon(":/add.png")); + addButton->setToolTip ("Add new record"); QToolButton* deleteButton = new QToolButton(mainWidget); deleteButton->setIcon(QIcon(":/edit-delete.png")); + deleteButton->setToolTip ("Delete record"); QToolButton* revertButton = new QToolButton(mainWidget); revertButton->setIcon(QIcon(":/edit-undo.png")); + revertButton->setToolTip ("Revert record"); if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) { QToolButton* previewButton = new QToolButton(mainWidget); previewButton->setIcon(QIcon(":/edit-preview.png")); + previewButton->setToolTip ("Open a preview of this record"); buttonsLayout->addWidget(previewButton); connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); } @@ -453,6 +460,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM { QToolButton* viewButton = new QToolButton(mainWidget); viewButton->setIcon(QIcon(":/cell.png")); + viewButton->setToolTip ("Open a scene view of the cell this record is located in"); buttonsLayout->addWidget(viewButton); connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); } From f43a10b7b99c6520873a8b36eefe4a8209281aee Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 21:10:30 +0200 Subject: [PATCH 52/97] Adjust disease contraction according to Hrnchamd's research --- apps/openmw/mwmechanics/disease.hpp | 58 ++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index 5f73e8acd9..05ce1c7ae4 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -14,9 +14,11 @@ namespace MWMechanics { /// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him) + /// @param actor The actor that will potentially catch diseases. Currently only the player can catch diseases. + /// @param carrier The disease carrier. inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier) { - if (!carrier.getClass().isActor()) + if (!carrier.getClass().isActor() || actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) return; float fDiseaseXferChance = @@ -27,25 +29,47 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); - if (spell->mData.mType == ESM::Spell::ST_Disease - || spell->mData.mType == ESM::Spell::ST_Blight) - { - float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (roll < fDiseaseXferChance) - { - // Contracted disease! - actor.getClass().getCreatureStats(actor).getSpells().add(it->first); - if (actor.getRefData().getHandle() == "player") - { - std::string msg = "sMagicContractDisease"; - msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); - if (msg.find("%s") != std::string::npos) - msg.replace(msg.find("%s"), 2, spell->mName); - MWBase::Environment::get().getWindowManager()->messageBox(msg); - } + if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId)) + continue; + + bool hasCorprusEffect = false; + for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) + { + if (effectIt->mEffectID == ESM::MagicEffect::Corprus) + { + hasCorprusEffect = true; + break; } } + + float resist = 0.f; + if (hasCorprusEffect) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCorprusDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCorprusDisease).mMagnitude); + else if (spell->mData.mType == ESM::Spell::ST_Disease) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCommonDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCommonDisease).mMagnitude); + else if (spell->mData.mType == ESM::Spell::ST_Blight) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistBlightDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToBlightDisease).mMagnitude); + else + continue; + + int x = fDiseaseXferChance * 100 * resist; + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; // [0, 9999] + + if (roll < x) + { + // Contracted disease! + actor.getClass().getCreatureStats(actor).getSpells().add(it->first); + + std::string msg = "sMagicContractDisease"; + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + } } } } From 123157b216919f58a6d72fd5b0c8366e2e85a486 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 21:53:11 +0200 Subject: [PATCH 53/97] Implement elemental shield damage to attacker (Feature #1121) --- apps/openmw/mwclass/creature.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/combat.cpp | 48 ++++++++++++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 3 ++ 4 files changed, 55 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index feb6a27147..e781348028 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -330,6 +330,8 @@ namespace MWClass } } + MWMechanics::applyElementalShields(ptr, victim); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e2cb8ba7af..a5175c1f6c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -630,6 +630,8 @@ namespace MWClass } } + MWMechanics::applyElementalShields(ptr, victim); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6feb5879b0..19f00e81b9 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -256,4 +256,52 @@ namespace MWMechanics return hitchance; } + void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim) + { + for (int i=0; i<3; ++i) + { + float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).mMagnitude; + + if (!magnitude) + continue; + + CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); + float saveTerm = attacker.getClass().getSkill(attacker, ESM::Skill::Destruction) + + 0.2f * attackerStats.getAttribute(ESM::Attribute::Willpower).getModified() + + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); + + int fatigueMax = attackerStats.getFatigue().getModified(); + int fatigueCurrent = attackerStats.getFatigue().getCurrent(); + + float normalisedFatigue = fatigueMax==0 ? 1 : std::max (0.0f, static_cast (fatigueCurrent)/fatigueMax); + + saveTerm *= 1.25f * normalisedFatigue; + + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + float x = std::max(0.f, saveTerm - roll); + + int element = ESM::MagicEffect::FireDamage; + if (i == 1) + element = ESM::MagicEffect::ShockDamage; + if (i == 2) + element = ESM::MagicEffect::FrostDamage; + + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(element); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(element); + float elementResistance = 0; + if (resistanceEffect != -1) + elementResistance += attackerStats.getMagicEffects().get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + elementResistance -= attackerStats.getMagicEffects().get(weaknessEffect).mMagnitude; + + x = std::min(100.f, x + elementResistance); + + static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); + x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); + MWMechanics::DynamicStat health = attackerStats.getHealth(); + health.setCurrent(health.getCurrent() - x); + attackerStats.setHealth(health); + } + } + } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index bc58227bf6..eb89cb820e 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -19,6 +19,9 @@ void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MW /// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue); +/// Applies damage to attacker based on the victim's elemental shields. +void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); + } #endif From 20a0040bdb88084846c5da633f82635318428cc5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 22:06:46 +0200 Subject: [PATCH 54/97] Apply elemental shield magnitude to element resistance (Closes #1121) --- apps/openmw/mwmechanics/combat.cpp | 8 +----- apps/openmw/mwmechanics/spellcasting.cpp | 32 ++++++++++++++++-------- apps/openmw/mwmechanics/spellcasting.hpp | 5 ++++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 19f00e81b9..5908e79cd2 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -286,13 +286,7 @@ namespace MWMechanics if (i == 2) element = ESM::MagicEffect::FrostDamage; - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(element); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(element); - float elementResistance = 0; - if (resistanceEffect != -1) - elementResistance += attackerStats.getMagicEffects().get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - elementResistance -= attackerStats.getMagicEffects().get(weaknessEffect).mMagnitude; + float elementResistance = MWMechanics::getEffectResistanceAttribute(element, &attackerStats.getMagicEffects()); x = std::min(100.f, x + elementResistance); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1b0c444ab7..9253a9453c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -148,6 +148,27 @@ namespace MWMechanics return school; } + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) + { + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + + float resistance = 0; + if (resistanceEffect != -1) + resistance += actorEffects->get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + resistance -= actorEffects->get(weaknessEffect).mMagnitude; + + if (effectId == ESM::MagicEffect::FireDamage) + resistance += actorEffects->get(ESM::MagicEffect::FireShield).mMagnitude; + if (effectId == ESM::MagicEffect::ShockDamage) + resistance += actorEffects->get(ESM::MagicEffect::LightningShield).mMagnitude; + if (effectId == ESM::MagicEffect::FrostDamage) + resistance += actorEffects->get(ESM::MagicEffect::FrostShield).mMagnitude; + + return resistance; + } + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { @@ -163,16 +184,7 @@ namespace MWMechanics float resisted = 0; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) { - - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - - float resistance = 0; - if (resistanceEffect != -1) - resistance += magicEffects->get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - resistance -= magicEffects->get(weaknessEffect).mMagnitude; - + float resistance = getEffectResistanceAttribute(effectId, magicEffects); float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index b526a43530..2de187ae41 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -35,6 +35,11 @@ namespace MWMechanics int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); + /// Get the resistance attribute against an effect for a given actor. This will add together + /// ResistX and Weakness to X effects relevant against the given effect. + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects); + + /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). /// @return >=100 for fully resisted. can also return negative value for damage amplification. /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. From 08ce6ed7fba940a89f95c294b87dfab43f557e22 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 16 Jul 2014 15:30:06 +0200 Subject: [PATCH 55/97] Attempt to restack item after repair or recharge (Fixes #1656) --- apps/openmw/mwgui/merchantrepair.cpp | 5 ++- apps/openmw/mwgui/recharge.cpp | 2 ++ apps/openmw/mwmechanics/repair.cpp | 7 +++-- apps/openmw/mwworld/containerstore.cpp | 28 +++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 5 +++ apps/openmw/mwworld/inventorystore.cpp | 42 +++++++++++++------------- 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 49cc60d8aa..0d1a570008 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -117,15 +117,18 @@ void MerchantRepair::exit() void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + // repair MWWorld::Ptr item = *sender->getUserData(); item.getCellRef().setCharge(item.getClass().getItemMaxHealth(item)); + player.getClass().getContainerStore(player).restack(item); + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); int price = boost::lexical_cast(sender->getUserString("Price")); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startRepair(mActor); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 0795642736..9c43b14163 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -165,6 +165,8 @@ void Recharge::onItemClicked(MyGUI::Widget *sender) item.getCellRef().setEnchantmentCharge( std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(enchantment->mData.mCharge))); + player.getClass().getContainerStore(player).restack(item); + player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 9f2c851cf8..6d6f889edc 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -57,10 +57,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair)); itemToRepair.getCellRef().setCharge(charge); + // attempt to re-stack item, in case it was fully repaired + MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair); + // set the OnPCRepair variable on the item's script - std::string script = itemToRepair.getClass().getScript(itemToRepair); + std::string script = stacked->getClass().getScript(itemToRepair); if(script != "") - itemToRepair.getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); + stacked->getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); // increase skill player.getClass().skillUsageSucceeded(player, ESM::Skill::Armorer, 0); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index e330ddaeed..18ebd82dbe 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -141,6 +141,34 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) remove(ptr, ptr.getRefData().getCount()-1, container); } +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item) +{ + MWWorld::ContainerStoreIterator retval = end(); + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (item == *iter) + { + retval = iter; + break; + } + } + + if (retval == end()) + throw std::runtime_error("item is not from this container"); + + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, item)) + { + iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount()); + item.getRefData().setCount(0); + retval = iter; + break; + } + } + return retval; +} + bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { const MWWorld::Class& cls1 = ptr1.getClass(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 7c81bdb6e7..6d9d7a6bb1 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -134,6 +134,11 @@ namespace MWWorld void unstack (const Ptr& ptr, const Ptr& container); ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). + MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item); + ///< Attempt to re-stack an item in this container. + /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted. + /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item. + /// @return How many items with refID \a id are in this container? int count (const std::string& id); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2eb8aeb465..8914400238 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -512,28 +512,21 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c // empty this slot mSlots[slot] = end(); - // restack the previously equipped item with other (non-equipped) items - for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + if (it->getRefData().getCount()) { - if (stacks(*iter, *it)) - { - iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); - it->getRefData().setCount(0); - retval = iter; - break; - } - } + retval = restack(*it); - if (actor.getRefData().getHandle() == "player") - { - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - const std::string& script = it->getClass().getScript(*it); - if (script != "") - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - - if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + if (actor.getRefData().getHandle() == "player") { - mSelectedEnchantItem = end(); + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared + const std::string& script = it->getClass().getScript(*it); + if (script != "") + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + + if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + { + mSelectedEnchantItem = end(); + } } } @@ -633,8 +626,15 @@ void MWWorld::InventoryStore::rechargeItems(float duration) static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( "fMagicItemRechargePerSecond")->getFloat(); - it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, - it->second)); + if (it->first->getCellRef().getEnchantmentCharge() <= it->second) + { + it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, + it->second)); + + // attempt to restack when fully recharged + if (it->first->getCellRef().getEnchantmentCharge() == it->second) + it->first = restack(*it->first); + } } } From 876af8f52961cd03b65d8d2ca76d38fc72269175 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 16 Jul 2014 20:09:42 +0200 Subject: [PATCH 56/97] Fix not saved Attacked flag (Fixes #1657) --- components/esm/creaturestats.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 0a9a361093..37f0cc63c8 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -33,6 +33,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mAlarmed = false; esm.getHNOT (mAlarmed, "ALRM"); + mAttacked = false; + esm.getHNOT (mAttacked, "ATKD"); + mHostile = false; esm.getHNOT (mHostile, "HOST"); @@ -142,6 +145,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mAlarmed) esm.writeHNT ("ALRM", mAlarmed); + if (mAttacked) + esm.writeHNT ("ATKD", mAttacked); + if (mHostile) esm.writeHNT ("HOST", mHostile); From 41c17bccb6f168448a80389a471b836837fb0470 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 14:39:17 +0200 Subject: [PATCH 57/97] Exit if an unknown encoding option is specified --- components/to_utf8/to_utf8.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 59a9aff80f..c53cf62b51 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -4,6 +4,7 @@ #include #include #include +#include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library @@ -329,8 +330,10 @@ ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) return ToUTF8::WINDOWS_1250; else if (encodingName == "win1251") return ToUTF8::WINDOWS_1251; - else + else if (encodingName == "win1252") return ToUTF8::WINDOWS_1252; + else + throw std::runtime_error(std::string("Unknown encoding '") + encodingName + std::string("', see openmw --help for available options.")); } std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) @@ -339,6 +342,8 @@ std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) return "Using Central and Eastern European font encoding."; else if (encodingName == "win1251") return "Using Cyrillic font encoding."; - else + else if (encodingName == "win1252") return "Using default (English) font encoding."; + else + throw std::runtime_error(std::string("Unknown encoding '") + encodingName + std::string("', see openmw --help for available options.")); } From 2a510573b8460ba860e880c829d42be7919c4e00 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 15:27:56 +0200 Subject: [PATCH 58/97] Remove unused implementation file --- components/CMakeLists.txt | 2 +- components/terrain/backgroundloader.cpp | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 components/terrain/backgroundloader.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b8ebb84b19..8f9fb17af7 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -72,7 +72,7 @@ add_component_dir (translation add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world defaultworld storage material buffercache defs backgroundloader + quadtreenode chunk world defaultworld storage material buffercache defs ) add_component_dir (loadinglistener diff --git a/components/terrain/backgroundloader.cpp b/components/terrain/backgroundloader.cpp deleted file mode 100644 index e69de29bb2..0000000000 From f9a39138ccca492445631543fc0ca333b49551d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 16:15:25 +0200 Subject: [PATCH 59/97] Add missing endline --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 5d55a7a6f3..6c801f755c 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -266,7 +266,7 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); + std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; } } } From 4d39d77eaa39f2c904591eb3b3e1d82494f32d07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 20:40:40 +0200 Subject: [PATCH 60/97] Fix incompatible encoding names in ContentModel (bug uncovered by 41c17bccb6f16) --- components/contentselector/model/contentmodel.cpp | 3 ++- components/contentselector/model/contentmodel.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index f5bc2369a4..e76930e81e 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -23,6 +23,7 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { + mEncoding = encoding; if (encoding == QLatin1String("win1252")) mCodec = QTextCodec::codecForName("windows-1252"); @@ -449,7 +450,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) try { ESM::ESMReader fileReader; ToUTF8::Utf8Encoder encoder = - ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString()); + ToUTF8::calculateEncoding(mEncoding.toStdString()); fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 6d147bce98..7b2000b510 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -64,6 +64,7 @@ namespace ContentSelectorModel ContentFileList mFiles; QHash mCheckStates; QTextCodec *mCodec; + QString mEncoding; public: From d4ffd30f98fe50e1810236b26a6925781717f50d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 20:57:37 +0200 Subject: [PATCH 61/97] Implement Rank filter with no faction given (Fixes #1660) --- apps/openmw/mwdialogue/filter.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 08cdb1d003..daa9a684a4 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -76,8 +76,18 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const } else if (info.mData.mRank != -1) { - // if there is a rank condition, but the NPC is not in a faction, always fail - return false; + if (isCreature) + return false; + + // Rank requirement, but no faction given. Use the actor's faction, if there is one. + MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); + + if (!stats.getFactionRanks().size()) + return false; + + // check rank + if (stats.getFactionRanks().begin()->second < info.mData.mRank) + return false; } // Gender From 192d5ca08f4ef6bbe5a31f71857a11366203b273 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 22:20:37 +0200 Subject: [PATCH 62/97] Allow overriding OGRE_PLUGIN_DIR in cmake command line --- CMakeLists.txt | 6 ++++-- components/ogreinit/ogreinit.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb9a54a6cd..ee3850ddbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,12 +325,14 @@ else () add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() -add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") -add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) # make it empty so plugin loading code can check this and try to find plugins inside app bundle add_definitions(-DOGRE_PLUGIN_DIR="") else() + if (NOT DEFINED ${OGRE_PLUGIN_DIR}) + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + endif() + add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") endif() diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 77dbcb1ee1..515c0875ad 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -193,7 +193,7 @@ namespace OgreInit pluginDir = Ogre::macFrameworksPath(); #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; + pluginDir = OGRE_PLUGIN_DIR; #endif } Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); From 1320ac6983912ce0ec7fdaadf2eca5768f684d55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Jul 2014 15:58:25 +0200 Subject: [PATCH 63/97] Ensure non-negative particle life time, add size assertion --- components/nifogre/ogrenifloader.cpp | 4 ++-- components/nifogre/particles.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index eed320756c..81b2e55d2b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -762,8 +762,8 @@ class NIFObjectLoader else emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); - emitter->setTimeToLive(partctrl->lifetime, - partctrl->lifetime + partctrl->lifetimeRandom); + emitter->setTimeToLive(std::max(0.f, partctrl->lifetime), + std::max(0.f, partctrl->lifetime + partctrl->lifetimeRandom)); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index a1433a6690..47dce774ff 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -448,12 +448,14 @@ public: if(life_time-particle_time < mGrowTime) { Ogre::Real scale = (life_time-particle_time) / mGrowTime; + assert (scale >= 0); width *= scale; height *= scale; } if(particle_time < mFadeTime) { Ogre::Real scale = particle_time / mFadeTime; + assert (scale >= 0); width *= scale; height *= scale; } @@ -479,12 +481,14 @@ public: if(life_time-particle_time < mGrowTime) { Ogre::Real scale = (life_time-particle_time) / mGrowTime; + assert (scale >= 0); width *= scale; height *= scale; } if(particle_time < mFadeTime) { Ogre::Real scale = particle_time / mFadeTime; + assert (scale >= 0); width *= scale; height *= scale; } From d5381e1bc44f85f4be7bba8aca0c982ef34429ff Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sat, 19 Jul 2014 14:04:38 +0200 Subject: [PATCH 64/97] Dynamically resize skill widgets to fit their contents (Fixes #1610) --- apps/openmw/mwgui/statswindow.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 6c4e00ae5c..b11258f1cd 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -321,6 +321,11 @@ namespace MWGui skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + // resize dynamically according to text size + int textWidthPlusMargin = skillValueWidget->getTextSize().width + 12; + skillValueWidget->setCoord(coord2.left + coord2.width - textWidthPlusMargin, coord2.top, textWidthPlusMargin, coord2.height); + skillNameWidget->setSize(skillNameWidget->getSize() + MyGUI::IntSize(coord2.width - textWidthPlusMargin, 0)); + mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); From 63fd04882a7ee406a667ebad905a8b2334911b81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Jul 2014 23:49:54 +0200 Subject: [PATCH 65/97] Make sure crime gold discounts don't reduce price to zero --- apps/openmw/mwworld/worldimp.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3868348820..80ff0fdfa8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2586,9 +2586,15 @@ namespace MWWorld float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); - int discount = bounty*fCrimeGoldDiscountMult; + int discount = bounty * fCrimeGoldDiscountMult; int turnIn = bounty * fCrimeGoldTurnInMult; + if (bounty > 0) + { + discount = std::max(1, discount); + turnIn = std::max(1, turnIn); + } + mGlobalVariables["pchascrimegold"].setInteger((bounty <= playerGold) ? 1 : 0); mGlobalVariables["pchasgolddiscount"].setInteger((discount <= playerGold) ? 1 : 0); From 0f2305ae43fc4bd0d3cbc411f659409561d4be60 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 20 Jul 2014 13:07:47 +0200 Subject: [PATCH 66/97] Clear selected spell when window manager is cleared --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 865ad1dca9..668211db29 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1529,6 +1529,8 @@ namespace MWGui mCompanionWindow->resetReference(); mConsole->resetReference(); + mSelectedSpell.clear(); + mGuiModes.clear(); MWBase::Environment::get().getInputManager()->changeInputMode(false); updateVisible(); From 691ba02115d49f36a3dae68bcb9b649f7f200544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 14:12:27 +0200 Subject: [PATCH 67/97] Reduce wepaon condition even if attack misses --- apps/openmw/mwclass/creature.cpp | 17 +++++++++++++++++ apps/openmw/mwclass/npc.cpp | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e781348028..d521b1b330 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -254,6 +254,23 @@ 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); + } return; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a5175c1f6c..0dc679e1cf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -527,6 +527,24 @@ 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); + } + return; } From 5bbf07976f7f3f070d87c9c80b71283cacda1401 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 14:41:39 +0200 Subject: [PATCH 68/97] Consider Shield spell effect for creature armor rating --- apps/openmw/mwclass/creature.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d521b1b330..737ed002f9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -637,8 +637,7 @@ namespace MWClass float Creature::getArmorRating (const MWWorld::Ptr& ptr) const { - /// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour) - return 0.f; + return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; } float Creature::getCapacity (const MWWorld::Ptr& ptr) const From 28a0899d2ba6c731bdb3a361b4d51da77595eab1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 16:22:52 +0200 Subject: [PATCH 69/97] Implement difficulty scaling (Fixes #1505) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/creature.cpp | 4 + apps/openmw/mwclass/npc.cpp | 4 + apps/openmw/mwgui/settingswindow.cpp | 13 ++ apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwmechanics/combat.cpp | 5 + apps/openmw/mwmechanics/difficultyscaling.cpp | 38 +++++ apps/openmw/mwmechanics/difficultyscaling.hpp | 12 ++ files/mygui/openmw_settings_window.layout | 141 ++++++++++-------- files/settings-default.cfg | 2 + 10 files changed, 159 insertions(+), 63 deletions(-) create mode 100644 apps/openmw/mwmechanics/difficultyscaling.cpp create mode 100644 apps/openmw/mwmechanics/difficultyscaling.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 23ba78dbad..0dda1131be 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle autocalcspell + disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 737ed002f9..c93fcc2981 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -9,6 +9,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/disease.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -412,6 +413,9 @@ namespace MWClass if(ishealth) { + if (!attacker.isEmpty()) + damage = scaleDamage(damage, attacker, ptr); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; setActorHealth(ptr, health, attacker); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0dc679e1cf..f81c37c103 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -23,6 +23,7 @@ #include "../mwmechanics/disease.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/autocalcspell.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -788,6 +789,9 @@ namespace MWClass if(ishealth) { + if (!attacker.isEmpty()) + damage = scaleDamage(damage, attacker, ptr); + if(damage > 0.0f) sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f4602b064c..09d2fc19c9 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -157,6 +157,8 @@ namespace MWGui { configureWidgets(mMainWidget); + setTitle("#{sOptions}"); + getWidget(mOkButton, "OkButton"); getWidget(mResolutionList, "ResolutionList"); getWidget(mFullscreenButton, "FullscreenButton"); @@ -174,6 +176,7 @@ namespace MWGui getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mRefractionButton, "RefractionButton"); + getWidget(mDifficultySlider, "DifficultySlider"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled); @@ -227,6 +230,10 @@ namespace MWGui MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getInt("field of view", "General"))) + ")"); + + MyGUI::TextBox* diffText; + getWidget(diffText, "DifficultyText"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); } void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) @@ -406,6 +413,12 @@ namespace MWGui getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(value)) + ")"); } + if (scroller == mDifficultySlider) + { + MyGUI::TextBox* diffText; + getWidget(diffText, "DifficultyText"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(value)) + ")"); + } } else { diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 37f2c8af06..cbfb5cfe1d 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -30,6 +30,7 @@ namespace MWGui MyGUI::Button* mVSyncButton; MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mFOVSlider; + MyGUI::ScrollBar* mDifficultySlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::ComboBox* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 5908e79cd2..6e7bc9b94c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -10,6 +10,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -292,6 +293,10 @@ namespace MWMechanics static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); + + // Note swapped victim and attacker, since the attacker takes the damage here. + x = scaleDamage(x, victim, attacker); + MWMechanics::DynamicStat health = attackerStats.getHealth(); health.setCurrent(health.getCurrent() - x); attackerStats.setHealth(health); diff --git a/apps/openmw/mwmechanics/difficultyscaling.cpp b/apps/openmw/mwmechanics/difficultyscaling.cpp new file mode 100644 index 0000000000..05ab12ccd2 --- /dev/null +++ b/apps/openmw/mwmechanics/difficultyscaling.cpp @@ -0,0 +1,38 @@ +#include "difficultyscaling.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/esmstore.hpp" + +#include + +float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim) +{ + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // [-100, 100] + int difficultySetting = Settings::Manager::getInt("difficulty", "Game"); + + static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); + + float difficultyTerm = 0.01f * difficultySetting; + + float x = 0; + if (victim == player) + { + if (difficultyTerm > 0) + x = fDifficultyMult * difficultyTerm; + else + x = difficultyTerm / fDifficultyMult; + } + else if (attacker == player) + { + if (difficultyTerm > 0) + x = -difficultyTerm / fDifficultyMult; + else + x = fDifficultyMult * (-difficultyTerm); + } + + damage *= 1 + x; + return damage; +} diff --git a/apps/openmw/mwmechanics/difficultyscaling.hpp b/apps/openmw/mwmechanics/difficultyscaling.hpp new file mode 100644 index 0000000000..168cf10556 --- /dev/null +++ b/apps/openmw/mwmechanics/difficultyscaling.hpp @@ -0,0 +1,12 @@ +#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H +#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H + +namespace MWWorld +{ + class Ptr; +} + +/// Scales damage dealt to an actor based on difficulty setting +float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); + +#endif diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 9ecae465c6..5945c68d77 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,7 +1,7 @@ - + @@ -12,47 +12,86 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -63,7 +102,7 @@ - + @@ -74,7 +113,7 @@ - + @@ -85,28 +124,6 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/files/settings-default.cfg b/files/settings-default.cfg index fac2a0f977..ab9de47a4c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -170,6 +170,8 @@ always run = false # Always use the most powerful attack when striking with a weapon (chop, slash or thrust) best attack = false +difficulty = 0 + [Saves] character = # Save when resting From 9897f14c3c3e2e5d8587a4850431690ed0a538d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 16:52:57 +0200 Subject: [PATCH 70/97] Don't block when actor is paralyzed --- apps/openmw/mwmechanics/combat.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6e7bc9b94c..1f26e8f7d2 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -55,11 +55,24 @@ namespace MWMechanics if (!blocker.getClass().hasInventoryStore(blocker)) return false; - if (blocker.getClass().getCreatureStats(blocker).getKnockedDown() - || blocker.getClass().getCreatureStats(blocker).getHitRecovery()) + MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); + + if (blockerStats.getKnockedDown() // Used for both knockout or knockdown + || blockerStats.getHitRecovery() + || blockerStats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0) + return false; + + // Don't block when in spellcasting state (shield is equipped, but not visible) + if (blockerStats.getDrawState() == DrawState_Spell) return false; MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); + + // Don't block when in hand-to-hand combat (shield is equipped, but not visible) + if (blockerStats.getDrawState() == DrawState_Weapon && + inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end()) + return false; + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) return false; @@ -73,17 +86,6 @@ namespace MWMechanics if (angle.valueDegrees() > gmst.find("fCombatBlockRightAngle")->getFloat()) return false; - MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); - - // Don't block when in spellcasting state (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Spell) - return false; - - // Don't block when in hand-to-hand combat (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Weapon && - inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end()) - return false; - MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() From 6d794dac6905e28e61c170c10dc47e6c7da6ffc7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 17:09:55 +0200 Subject: [PATCH 71/97] Add missing messagebox for spell lock and unlock success --- apps/openmw/mwmechanics/spellcasting.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9253a9453c..67da992009 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -496,7 +496,11 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::Lock) { if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); target.getCellRef().setLockLevel(magnitude); + } } else if (effectId == ESM::MagicEffect::Open) { @@ -504,12 +508,14 @@ namespace MWMechanics { if (target.getCellRef().getLockLevel() > 0) { - //Door not already unlocked MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); if (!caster.isEmpty() && caster.getClass().isActor()) MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); + + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}"); } - target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); //unlocks the door + target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); From 1636fd66dba12330c122f60b1ab5432b5d65f88d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:26:26 +0200 Subject: [PATCH 72/97] Don't add creature base damage to weapon damage (UESP was wrong) --- apps/openmw/mwclass/creature.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c93fcc2981..ffd9eda26a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -308,13 +308,13 @@ namespace MWClass attack = weapon.get()->mBase->mData.mThrust; if(attack) { - float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + 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); - weaponDamage *= float(weaphealth) / weapmaxhealth; + damage *= float(weaphealth) / weapmaxhealth; if (!MWBase::Environment::get().getWorld()->getGodModeState()) { @@ -329,8 +329,6 @@ namespace MWClass if (weapon.getCellRef().getCharge() == 0) weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr); } - - damage += weaponDamage; } // Apply "On hit" enchanted weapons From 90a96cd7d81c8c6b6397d8a8ed7cf54486381dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:34:20 +0200 Subject: [PATCH 73/97] Fix bug where actors in combat with multiple other actors where not regarded as in combat with a specific actor --- apps/openmw/mwmechanics/actors.cpp | 8 ++------ apps/openmw/mwmechanics/aisequence.cpp | 24 ++++++++++++++++++++++++ apps/openmw/mwmechanics/aisequence.hpp | 6 ++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6ea65275ba..9a4cd44500 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1281,12 +1281,8 @@ namespace MWMechanics { const MWWorld::Class &cls = iter->getClass(); CreatureStats &stats = cls.getCreatureStats(*iter); - if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat) - { - MWMechanics::AiCombat* package = static_cast(stats.getAiSequence().getActivePackage()); - if(package->getTarget() == actor) - list.push_front(*iter); - } + if (!stats.isDead() && stats.getAiSequence().isInCombat(actor)) + list.push_front(*iter); } return list; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 3aeeee65a4..02f00dfc67 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -72,6 +72,30 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const return true; } +bool AiSequence::isInCombat() const +{ + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + return true; + } + return false; +} + +bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const +{ + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + { + const AiCombat *combat = static_cast(*it); + if (combat->getTarget() == actor) + return true; + } + } + return false; +} + bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const { bool firstCombatFound = false; diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index b789d33cd0..bc20dc61b0 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -63,6 +63,12 @@ namespace MWMechanics /// Return true and assign target if combat package is currently active, return false otherwise bool getCombatTarget (MWWorld::Ptr &targetActor) const; + /// Is there any combat package? + bool isInCombat () const; + + /// Are we in combat with this particular actor? + bool isInCombat (const MWWorld::Ptr& actor) const; + bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const; ///< Function assumes that actor can have only 1 target apart player From 804f1a5e59769187d4ddcdc8f20cffab4167815e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:35:35 +0200 Subject: [PATCH 74/97] Adjust combat mechanics - Projectiles can not trigger critical hits - Critical hits are only possible if the target is not in combat (Fixes #1669) - Hand-to-hand deals damage to health during entire duration of knockdown animation (including standing up) --- apps/openmw/mwclass/npc.cpp | 22 ++++++++++++++-------- apps/openmw/mwmechanics/combat.cpp | 14 ++------------ apps/openmw/mwmechanics/creaturestats.hpp | 3 ++- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f81c37c103..67f955d1b0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -600,8 +600,8 @@ namespace MWClass damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); - healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) - || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); + healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0) + || otherstats.getKnockedDown(); if(stats.isWerewolf()) { healthdmg = true; @@ -623,15 +623,21 @@ namespace MWClass sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); } if(ptr.getRefData().getHandle() == "player") + { skillUsageSucceeded(ptr, weapskill, 0); - bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); - if(!detected) - { - damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence(); + + bool unaware = !seq.isInCombat() + && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); + if(unaware) + { + damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } } + if (othercls.getCreatureStats(victim).getKnockedDown()) damage *= store.find("fCombatKODamageMult")->getFloat(); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 1f26e8f7d2..c13eac98c3 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -147,11 +147,7 @@ namespace MWMechanics float resistance = std::min(100.f, stats.getMagicEffects().get(ESM::MagicEffect::ResistNormalWeapons).mMagnitude - stats.getMagicEffects().get(ESM::MagicEffect::WeaknessToNormalWeapons).mMagnitude); - float multiplier = 0; - if (resistance >= 0) - multiplier = 1 - resistance / 100.f; - else - multiplier = -(resistance-100) / 100.f; + float multiplier = 1.f - resistance / 100.f; if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) @@ -213,16 +209,10 @@ namespace MWMechanics damage *= fDamageStrengthBase + (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1); + if(attacker.getRefData().getHandle() == "player") attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0); - bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim); - if(!detected) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); - } if (victim.getClass().getCreatureStats(victim).getKnockedDown()) damage *= gmst.find("fCombatKODamageMult")->getFloat(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index b365a0b89c..a83da4249d 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -208,7 +208,8 @@ namespace MWMechanics float getEvasion() const; void setKnockedDown(bool value); - ///Returns true for the entire duration of the actor being knocked down + /// Returns true for the entire duration of the actor being knocked down or knocked out, + /// including transition animations (falling down & standing up) bool getKnockedDown() const; void setKnockedDownOneFrame(bool value); ///Returns true only for the first frame of the actor being knocked out; used for "onKnockedOut" command From 5629803a084a5621c23db440e97f77dec7c8499c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 23:08:22 +0200 Subject: [PATCH 75/97] Implement correct armor mitigation mechanics --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/npc.cpp | 19 +++++++++++++------ apps/openmw/mwclass/npc.hpp | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ffd9eda26a..a74ee6fd67 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -394,6 +394,9 @@ namespace MWClass if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage < 0.001f) + damage = 0; + if (damage > 0.f) { // Check for knockdown @@ -409,6 +412,8 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + damage = std::max(1.f, damage); + if(ishealth) { if (!attacker.isEmpty()) @@ -639,6 +644,7 @@ namespace MWClass float Creature::getArmorRating (const MWWorld::Ptr& ptr) const { + // Note this is currently unused. Creatures do not use armor mitigation. return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 67f955d1b0..b9c049987e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -280,6 +280,7 @@ namespace MWClass gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); gmst.fDamageStrengthBase = store.find("fDamageStrengthBase"); gmst.fDamageStrengthMult = store.find("fDamageStrengthMult"); + gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult"); inited = true; } @@ -706,6 +707,9 @@ namespace MWClass if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage < 0.001f) + damage = 0; + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying @@ -734,7 +738,7 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage + if(damage > 0 && ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage { // Hit percentages: // cuirass = 30% @@ -754,9 +758,12 @@ namespace MWClass }; int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; - float damagediff = damage; - damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); - damagediff -= damage; + float unmitigatedDamage = damage; + float x = damage / (damage + getArmorRating(ptr)); + damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); + int damageDiff = unmitigatedDamage - damage; + if (damage < 1) + damage = 1; MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); @@ -764,13 +771,13 @@ namespace MWClass if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { int armorhealth = armor.getClass().getItemHealth(armor); - armorhealth -= std::min(std::max(1, (int)damagediff), + armorhealth -= std::min(std::max(1, damageDiff), armorhealth); armor.getCellRef().setCharge(armorhealth); // Armor broken? unequip it if (armorhealth == 0) - inv.unequipItem(armor, ptr); + armor = *inv.unequipItem(armor, ptr); if (ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index d6b1f8c268..b0ca0a658e 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -40,6 +40,7 @@ namespace MWClass const ESM::GameSetting *iKnockDownOddsBase; const ESM::GameSetting *fDamageStrengthBase; const ESM::GameSetting *fDamageStrengthMult; + const ESM::GameSetting *fCombatArmorMinMult; }; static const GMST& getGmst(); From 10d4da79944de255bc536aeb085c6259740b000e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 01:17:01 +0200 Subject: [PATCH 76/97] Fix date time in journal entries being off by one day (Fixes #1668) --- apps/openmw/mwgui/journalviewmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index b9ddb7daa9..b71e64a191 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -259,7 +259,7 @@ struct JournalViewModelImpl : JournalViewModel os << itr->mDayOfMonth << ' ' << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth) - << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + << " (" << dayStr << " " << (itr->mDay) << ')'; timestamp_buffer = os.str (); } From 127add7623e92ccbff375fbf676e615ebf09ad72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 18:19:45 +0200 Subject: [PATCH 77/97] Don't allow dialogue with werewolf NPCs --- apps/openmw/mwclass/npc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b9c049987e..9042d42313 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -885,6 +885,7 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") return boost::shared_ptr(new MWWorld::ActionTalk(actor)); + // Werewolfs can't activate NPCs if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -895,6 +896,7 @@ namespace MWClass return action; } + if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); if(ptr.getClass().getCreatureStats(ptr).isHostile()) @@ -902,8 +904,10 @@ namespace MWClass if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing + // Can't talk to werewolfs + if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf()) + return boost::shared_ptr (new MWWorld::FailedAction("")); return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); - } MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) From d43c21a9fc73532c06c1f99e31ecf90486af6e41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 20:36:26 +0200 Subject: [PATCH 78/97] Make NPCs react to being attacked by other actors (Fixes #1588) --- apps/openmw/mwclass/npc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9042d42313..49e016d4f9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -680,6 +680,14 @@ namespace MWClass !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); From c0645d4978fcf71f7449df6d1a39b1c408780d10 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 20:37:14 +0200 Subject: [PATCH 79/97] Increase death count immediately on death (Bug #1588) This is required for scripts using getDeadCount as reaction to onDeath that rely on the increased value. --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 + apps/openmw/mwmechanics/actors.cpp | 137 ++++++++++-------- apps/openmw/mwmechanics/actors.hpp | 3 + apps/openmw/mwmechanics/creaturestats.cpp | 7 + .../mwmechanics/mechanicsmanagerimp.cpp | 4 + .../mwmechanics/mechanicsmanagerimp.hpp | 4 + 6 files changed, 100 insertions(+), 59 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index f2b71bd4cc..f718972b93 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -196,6 +196,10 @@ namespace MWBase /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9a4cd44500..809e948e72 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1029,10 +1029,6 @@ namespace MWMechanics iter->second->update(duration); } - // Kill dead actors, update some variables - - int hostilesCount = 0; // need to know this to play Battle music - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); @@ -1048,68 +1044,23 @@ namespace MWMechanics stats.setKnockedDownOneFrame(false); stats.setKnockedDownOverOneFrame(true); } + } + + int hostilesCount = 0; // need to know this to play Battle music + + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); if(!stats.isDead()) { if (stats.isHostile()) hostilesCount++; - - if(iter->second->isDead()) - { - // Actor has been resurrected. Notify the CharacterController and re-enable collision. - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); - iter->second->resurrect(); - } - - if(!stats.isDead()) - continue; - } - - // If it's the player and God Mode is turned on, keep it alive - if (iter->first.getRefData().getHandle()=="player" && - MWBase::Environment::get().getWorld()->getGodModeState()) - { - MWMechanics::DynamicStat stat (stats.getHealth()); - - if (stat.getModified()<1) - { - stat.setModified(1, 0); - stats.setHealth(stat); - } - stats.resurrect(); - continue; - } - - if (iter->second->kill()) - { - ++mDeathCount[cls.getId(iter->first)]; - - // Make sure spell effects with CasterLinked flag are removed - for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) - { - MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); - spells.purge(stats.getActorId()); - } - - // Apply soultrap - if (iter->first.getTypeName() == typeid(ESM::Creature).name()) - { - SoulTrap soulTrap (iter->first); - stats.getActiveSpells().visitEffectSources(soulTrap); - } - - // Reset magic effects and recalculate derived effects - // One case where we need this is to make sure bound items are removed upon death - stats.setMagicEffects(MWMechanics::MagicEffects()); - stats.getActiveSpells().clear(); - calculateCreatureStatModifiers(iter->first, 0); - - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); - - if (cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } + killDeadActors(); + // check if we still have any player enemies to switch music static bool isBattleMusic = false; @@ -1184,6 +1135,74 @@ namespace MWMechanics } } } + + void Actors::killDeadActors() + { + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); + + if(!stats.isDead()) + { + if(iter->second->isDead()) + { + // Actor has been resurrected. Notify the CharacterController and re-enable collision. + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); + iter->second->resurrect(); + } + + if(!stats.isDead()) + continue; + } + + // If it's the player and God Mode is turned on, keep it alive + if (iter->first.getRefData().getHandle()=="player" && + MWBase::Environment::get().getWorld()->getGodModeState()) + { + MWMechanics::DynamicStat stat (stats.getHealth()); + + if (stat.getModified()<1) + { + stat.setModified(1, 0); + stats.setHealth(stat); + } + stats.resurrect(); + continue; + } + + if (iter->second->kill()) + { + ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; + + // Make sure spell effects with CasterLinked flag are removed + for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(stats.getActorId()); + } + + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + stats.getActiveSpells().clear(); + calculateCreatureStatModifiers(iter->first, 0); + + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); + + if (cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } + } + } + void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4784162f48..cda6abaeaa 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -96,6 +96,9 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + ///@see MechanicsManager::killDeadActors + void killDeadActors (); + void forceStateUpdate(const MWWorld::Ptr &ptr); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index af35a109a6..71217dbb97 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace MWMechanics { @@ -191,6 +192,12 @@ namespace MWMechanics mDied = true; mDead = true; + + if (mDied) + // Must increase death count immediately. There are scripts that use getDeadCount as reaction to onDeath + // and rely on the increased value. + // Would be more appropriate to use a killActor(actor) function, but we don't have access to the Ptr in CreatureStats. + MWBase::Environment::get().getMechanicsManager()->killDeadActors(); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d7c3e2f56d..2d3bc066f6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -653,6 +653,10 @@ namespace MWMechanics return mActors.countDeaths (id); } + void MechanicsManager::killDeadActors() + { + mActors.killDeadActors(); + } void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 596e6887ec..365e240677 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -160,6 +160,10 @@ namespace MWMechanics /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors(); }; } From 3b67e9b329e0fd1e4a6e6c00a54033007504dd6e Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Mon, 21 Jul 2014 20:56:02 +0200 Subject: [PATCH 80/97] Rework level up dialog to behave more like vanilla --- apps/openmw/mwgui/levelupdialog.cpp | 103 ++++++++++++++--------- apps/openmw/mwgui/levelupdialog.hpp | 7 +- files/mygui/openmw_levelup_dialog.layout | 2 +- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eaa8227225..bf6181d76c 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -16,9 +16,10 @@ namespace MWGui { - + const unsigned int LevelupDialog::sMaxCoins = 3; LevelupDialog::LevelupDialog() - : WindowBase("openmw_levelup_dialog.layout") + : WindowBase("openmw_levelup_dialog.layout"), + mCoinCount(sMaxCoins) { getWidget(mOkButton, "OkButton"); getWidget(mClassImage, "ClassImage"); @@ -46,12 +47,10 @@ namespace MWGui mAttributeMultipliers.push_back(t); } - int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; - for (int i=0; i<3; ++i) + for (unsigned int i = 0; i < mCoinCount; ++i) { - MyGUI::ImageBox* image = mMainWidget->createWidget("ImageBox", MyGUI::IntCoord(curX,250,16,16), MyGUI::Align::Default); + MyGUI::ImageBox* image = mCoinBox->createWidget("ImageBox", MyGUI::IntCoord(0,0,16,16), MyGUI::Align::Default); image->setImageTexture ("icons\\tx_goldicon.dds"); - curX += 24+2; mCoins.push_back(image); } @@ -61,15 +60,15 @@ namespace MWGui void LevelupDialog::setAttributeValues() { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player); + MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); - for (int i=0; i<8; ++i) + for (int i = 0; i < 8; ++i) { - int val = creatureStats.getAttribute (i).getBase (); + int val = creatureStats.getAttribute(i).getBase(); if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) { - val += pcStats.getLevelupAttributeMultiplier (i); + val += pcStats.getLevelupAttributeMultiplier(i); } if (val >= 100) @@ -80,20 +79,21 @@ namespace MWGui } - void LevelupDialog::resetCoins () + void LevelupDialog::resetCoins() { - int curX = 0; - for (int i=0; i<3; ++i) + const int coinSpacing = 10; + int curX = mCoinBox->getWidth()/2 - (coinSpacing*(mCoinCount - 1) + 16*mCoinCount)/2; + for (unsigned int i=0; idetachFromWidget(); image->attachToWidget(mCoinBox); image->setCoord(MyGUI::IntCoord(curX,0,16,16)); - curX += 24+2; + curX += 16+coinSpacing; } } - void LevelupDialog::assignCoins () + void LevelupDialog::assignCoins() { resetCoins(); for (unsigned int i=0; igetPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player); - MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); - - mSpentAttributes.clear(); - resetCoins(); - - setAttributeValues(); + MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); + MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); const ESM::NPC *playerData = player.get()->mBase; @@ -144,70 +139,98 @@ namespace MWGui if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) break; } - mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds"); + mClassImage->setImageTexture("textures\\levelup\\" + it->mId + ".dds"); } else - mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); + mClassImage->setImageTexture("textures\\levelup\\" + cls->mId + ".dds"); int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); std::string levelupdescription; - if(level>20) + if(level > 20) levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); else levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast(level)); mLevelDescription->setCaption (levelupdescription); - for (int i=0; i<8; ++i) + unsigned int availableAttributes = 0; + for (int i = 0; i < 8; ++i) { MyGUI::TextBox* text = mAttributeMultipliers[i]; - int mult = pcStats.getLevelupAttributeMultiplier (i); - text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); + if (pcStats.getAttribute(i).getBase() < 100) + { + mAttributes[i]->setEnabled(true); + availableAttributes++; + + int mult = pcStats.getLevelupAttributeMultiplier (i); + text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); + } + else + { + mAttributes[i]->setEnabled(false); + + text->setCaption(""); + } } + mCoinCount = std::min(sMaxCoins, availableAttributes); + + for (unsigned int i = 0; i < sMaxCoins; i++) + { + if (i < mCoinCount) + mCoins[i]->attachToWidget(mCoinBox); + else + mCoins[i]->detachFromWidget(); + } + + mSpentAttributes.clear(); + resetCoins(); + + setAttributeValues(); + center(); } - void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) + void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player); - if (mSpentAttributes.size() < 3) - MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}"); + if (mSpentAttributes.size() < mCoinCount) + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage36}"); else { // increase attributes - for (int i=0; i<3; ++i) + for (unsigned int i = 0; i < mCoinCount; ++i) { MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]); - attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); + attribute.setBase(attribute.getBase() + pcStats.getLevelupAttributeMultiplier(mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); pcStats.setAttribute(mSpentAttributes[i], attribute); } - pcStats.levelUp (); + pcStats.levelUp(); - MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Levelup); } } - void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender) + void LevelupDialog::onAttributeClicked(MyGUI::Widget *sender) { int attribute = *sender->getUserData(); std::vector::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); if (found != mSpentAttributes.end()) - mSpentAttributes.erase (found); + mSpentAttributes.erase(found); else { - if (mSpentAttributes.size() == 3) - mSpentAttributes[2] = attribute; + if (mSpentAttributes.size() == mCoinCount) + mSpentAttributes[mCoinCount - 1] = attribute; else mSpentAttributes.push_back(attribute); } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 69afbf0897..5ca9c8c758 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -28,8 +28,11 @@ namespace MWGui std::vector mSpentAttributes; - void onOkButtonClicked (MyGUI::Widget* sender); - void onAttributeClicked (MyGUI::Widget* sender); + unsigned int mCoinCount; + static const unsigned int sMaxCoins; + + void onOkButtonClicked(MyGUI::Widget* sender); + void onAttributeClicked(MyGUI::Widget* sender); void assignCoins(); void resetCoins(); diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 0b12d7a052..07e1e560b5 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -23,7 +23,7 @@ - + From 171c61022d432508e39616d413fd5a677f8c6eff Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 21 Jul 2014 21:16:30 +0200 Subject: [PATCH 81/97] Added initialization of weapRange in AiCombat::execute(). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 2606daa88e..c0a97a1b1f 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -265,7 +265,7 @@ namespace MWMechanics const ESM::Weapon *weapon = NULL; MWMechanics::WeaponType weaptype; - float weapRange; + float weapRange = 1.0f; actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); @@ -300,7 +300,7 @@ namespace MWMechanics else //is creature { weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) + weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit) } float rangeAttack; From 038a811f16797148519d62112d263db2c34c178e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 23:44:33 +0200 Subject: [PATCH 82/97] Rename travel window Ok button to Cancel (Fixes #1676) --- files/mygui/openmw_travel_window.layout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout index 683d47fe71..30b470e08d 100644 --- a/files/mygui/openmw_travel_window.layout +++ b/files/mygui/openmw_travel_window.layout @@ -27,7 +27,7 @@ - + From e24173f94acf12f6e48d1eb8f09a983d36b59303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Mon, 21 Jul 2014 23:56:38 +0100 Subject: [PATCH 83/97] Fix bug 1482: - put powers on cooldown only when they are used succedsfully --- apps/openmw/mwworld/worldimp.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3868348820..93316f8765 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2316,15 +2316,10 @@ namespace MWWorld } // If this is a power, check if it was already used in the last 24h - if (!fail && spell->mData.mType == ESM::Spell::ST_Power) + if (!fail && spell->mData.mType == ESM::Spell::ST_Power && !stats.getSpells().canUsePower(spell->mId)) { - if (stats.getSpells().canUsePower(spell->mId)) - stats.getSpells().usePower(spell->mId); - else - { - message = "#{sPowerAlreadyUsed}"; - fail = true; - } + message = "#{sPowerAlreadyUsed}"; + fail = true; } // Reduce mana @@ -2357,6 +2352,10 @@ namespace MWWorld if (!selectedSpell.empty()) { const ESM::Spell* spell = getStore().get().search(selectedSpell); + + // A power can be used once per 24h + if (spell->mData.mType == ESM::Spell::ST_Power) + stats.getSpells().usePower(spell->mId); cast.cast(spell); } From 2b57c7fa6788199d9ea1bd3e36b84a8e499790ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 17:05:05 +0200 Subject: [PATCH 84/97] Use CellRef's global variable to disable owner (Fixes #1677) --- apps/esmtool/esmtool.cpp | 1 + apps/openmw/mwclass/activator.cpp | 1 + apps/openmw/mwclass/apparatus.cpp | 1 + apps/openmw/mwclass/armor.cpp | 1 + apps/openmw/mwclass/book.cpp | 1 + apps/openmw/mwclass/clothing.cpp | 1 + apps/openmw/mwclass/container.cpp | 1 + apps/openmw/mwclass/door.cpp | 1 + apps/openmw/mwclass/ingredient.cpp | 1 + apps/openmw/mwclass/light.cpp | 1 + apps/openmw/mwclass/lockpick.cpp | 1 + apps/openmw/mwclass/misc.cpp | 1 + apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwclass/probe.cpp | 1 + apps/openmw/mwclass/repair.cpp | 1 + apps/openmw/mwclass/weapon.cpp | 1 + apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 7 +++++++ apps/openmw/mwworld/cellref.cpp | 5 +++++ apps/openmw/mwworld/cellref.hpp | 5 +++++ components/esm/cellref.cpp | 6 +++--- components/esm/cellref.hpp | 9 +++++---- 21 files changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index eef96c8c99..a09b87ad4e 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -249,6 +249,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; + std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 043aadd359..37189e05fb 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -99,6 +99,7 @@ namespace MWClass { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index d61ba038a3..a27762a579 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -129,6 +129,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index b29bf36b2e..71426c7235 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -249,6 +249,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0adee57e31..e7b811936d 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -141,6 +141,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index dc98e323e1..bf3d0c12c1 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -195,6 +195,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 53add42746..aba3b84665 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -237,6 +237,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 677ad462e0..e75eda7266 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -254,6 +254,7 @@ namespace MWClass text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); } info.text = text; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 6830929239..17ae61c0d0 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -149,6 +149,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 8a2c20f699..611c86f678 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -189,6 +189,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index bc68551291..5dc7180b2a 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -143,6 +143,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1044fb01d4..c5796ddbd9 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -184,6 +184,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 440121d35b..66555444ff 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -153,6 +153,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index ed8625eec0..61609bde03 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -142,6 +142,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index d7a0805349..f1f947d77a 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -146,6 +146,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 66affa599e..2208d717a6 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -353,6 +353,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2d3bc066f6..1f81eda7d6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -38,6 +38,13 @@ namespace isFactionOwned = true; } + const std::string& globalVariable = item.getCellRef().getGlobalVariable(); + if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1) + { + isOwned = false; + isFactionOwned = false; + } + if (!item.getCellRef().getOwner().empty()) victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().getOwner(), true); diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index f16d8e3d17..056737be6f 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -88,6 +88,11 @@ namespace MWWorld return mCellRef.mOwner; } + std::string CellRef::getGlobalVariable() const + { + return mCellRef.mGlobalVariable; + } + void CellRef::setOwner(const std::string &owner) { if (owner != mCellRef.mOwner) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 689671c025..e9ef343fca 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -61,6 +61,11 @@ namespace MWWorld std::string getOwner() const; void setOwner(const std::string& owner); + // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed + // even if it has an Owner field. + // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. + std::string getGlobalVariable() const; + // ID of creature trapped in this soul gem std::string getSoul() const; void setSoul(const std::string& soul); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 84c638d9c7..409ae02d01 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -27,7 +27,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHNOT (mScale, "XSCL"); mOwner = esm.getHNOString ("ANAM"); - mGlob = esm.getHNOString ("BNAM"); + mGlobalVariable = esm.getHNOString ("BNAM"); mSoul = esm.getHNOString ("XSOL"); mFaction = esm.getHNOString ("CNAM"); @@ -90,7 +90,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons } esm.writeHNOCString("ANAM", mOwner); - esm.writeHNOCString("BNAM", mGlob); + esm.writeHNOCString("BNAM", mGlobalVariable); esm.writeHNOCString("XSOL", mSoul); esm.writeHNOCString("CNAM", mFaction); @@ -144,7 +144,7 @@ void ESM::CellRef::blank() mRefID.clear(); mScale = 1; mOwner.clear(); - mGlob.clear(); + mGlobalVariable.clear(); mSoul.clear(); mFaction.clear(); mFactIndex = -1; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index b875781209..25ea243ef0 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -35,12 +35,13 @@ namespace ESM float mScale; // Scale applied to mesh - // The NPC that owns this object (and will get angry if you steal - // it) + // The NPC that owns this object (and will get angry if you steal it) std::string mOwner; - // I have no idea, looks like a link to a global variable? - std::string mGlob; + // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed + // even if it has an Owner field. + // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. + std::string mGlobalVariable; // ID of creature trapped in this soul gem std::string mSoul; From ca45a63cf79d262048972c5a3eec44f9288baeb7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 17:55:54 +0200 Subject: [PATCH 85/97] Close doors instantly when using Lock instruction --- apps/openmw/mwbase/world.hpp | 5 +++-- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 8 +++++++ apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++++++----------- apps/openmw/mwworld/worldimp.hpp | 5 +++-- 5 files changed, 32 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 49ac5bc159..29f326a1b8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -398,8 +398,9 @@ namespace MWBase /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door) = 0; - /// open or close a non-teleport door as specified - virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0; + /// update movement state of a non-teleport door as specified + /// @param state see MWClass::setDoorState + virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index e75eda7266..5e7534660c 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -62,7 +62,7 @@ namespace MWClass const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); if (customData.mDoorState > 0) { - MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); + MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); } } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a4c74be6b2..4e0257d821 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -147,6 +147,14 @@ namespace MWScript } ptr.getClass().lock (ptr, lockLevel); + + // Instantly reset door to closed state + // This is done when using Lock in scripts, but not when using Lock spells. + if (ptr.getTypeName() == typeid(ESM::Door).name()) + { + MWBase::Environment::get().getWorld()->activateDoor(ptr, 0); + MWBase::Environment::get().getWorld()->localRotateObject(ptr, 0, 0, 0); + } } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 80ff0fdfa8..fb520dae4e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1123,6 +1123,9 @@ namespace MWWorld ptr.getRefData().setPosition(pos); + if(ptr.getRefData().getBaseNode() == 0) + return; + if (ptr.getClass().isActor()) mWorldScene->updateObjectRotation(ptr); else @@ -1131,19 +1134,19 @@ namespace MWWorld void World::localRotateObject (const Ptr& ptr, float x, float y, float z) { + LocalRotation rot = ptr.getRefData().getLocalRotation(); + rot.rot[0]=Ogre::Degree(x).valueRadians(); + rot.rot[1]=Ogre::Degree(y).valueRadians(); + rot.rot[2]=Ogre::Degree(z).valueRadians(); + + wrap(rot.rot[0]); + wrap(rot.rot[1]); + wrap(rot.rot[2]); + + ptr.getRefData().setLocalRotation(rot); + if (ptr.getRefData().getBaseNode() != 0) { - LocalRotation rot = ptr.getRefData().getLocalRotation(); - rot.rot[0]=Ogre::Degree(x).valueRadians(); - rot.rot[1]=Ogre::Degree(y).valueRadians(); - rot.rot[2]=Ogre::Degree(z).valueRadians(); - - wrap(rot.rot[0]); - wrap(rot.rot[1]); - wrap(rot.rot[2]); - - ptr.getRefData().setLocalRotation(rot); - mWorldScene->updateObjectLocalRotation(ptr); } } @@ -1983,11 +1986,12 @@ namespace MWWorld mDoorStates[door] = state; } - void World::activateDoor(const Ptr &door, bool open) + void World::activateDoor(const Ptr &door, int state) { - int state = open ? 1 : 2; door.getClass().setDoorState(door, state); mDoorStates[door] = state; + if (state == 0) + mDoorStates.erase(door); } bool World::getPlayerStandingOn (const MWWorld::Ptr& object) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 08d7eb42dd..dda4429630 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -473,8 +473,9 @@ namespace MWWorld /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door); - /// open or close a non-teleport door as specified - virtual void activateDoor(const MWWorld::Ptr& door, bool open); + /// update movement state of a non-teleport door as specified + /// @param state see MWClass::setDoorState + virtual void activateDoor(const MWWorld::Ptr& door, int state); virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object From 432fb751bf505eb6e6c724b2aacf1c5e4d09617d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Wed, 23 Jul 2014 21:25:28 +0100 Subject: [PATCH 86/97] Implement fatigue restoration to match Morrowind. Fatigue restoration doesn't depend on encuberance or EndFatigueMult. --- apps/openmw/mwmechanics/actors.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6ea65275ba..92d5ac68ef 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -326,11 +326,9 @@ namespace MWMechanics // restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); - float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); - - float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); - x *= fEndFatigueMult * endurance; + float x = fFatigueReturnBase + fFatigueReturnMult * endurance; + DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); From 75366b2e37bb1db5ae69644a552c944d8a21f1e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 20:03:35 +0200 Subject: [PATCH 87/97] Implement CellRef's faction rank (rank requirement to use item) --- apps/esmtool/esmtool.cpp | 2 ++ apps/opencs/model/world/columnimp.hpp | 4 ++-- apps/openmw/mwclass/activator.cpp | 4 +--- apps/openmw/mwclass/apparatus.cpp | 4 +--- apps/openmw/mwclass/armor.cpp | 4 +--- apps/openmw/mwclass/book.cpp | 4 +--- apps/openmw/mwclass/clothing.cpp | 4 +--- apps/openmw/mwclass/container.cpp | 4 +--- apps/openmw/mwclass/door.cpp | 4 +--- apps/openmw/mwclass/ingredient.cpp | 4 +--- apps/openmw/mwclass/light.cpp | 4 +--- apps/openmw/mwclass/lockpick.cpp | 4 +--- apps/openmw/mwclass/misc.cpp | 4 +--- apps/openmw/mwclass/potion.cpp | 4 +--- apps/openmw/mwclass/probe.cpp | 4 +--- apps/openmw/mwclass/repair.cpp | 4 +--- apps/openmw/mwclass/weapon.cpp | 4 +--- apps/openmw/mwgui/tooltips.cpp | 11 +++++++++++ apps/openmw/mwgui/tooltips.hpp | 3 +++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- apps/openmw/mwworld/cellref.cpp | 5 +++++ apps/openmw/mwworld/cellref.hpp | 3 +++ apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 +++++----- components/esm/cellref.hpp | 5 ++--- 25 files changed, 52 insertions(+), 57 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a09b87ad4e..ea908590a4 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -250,6 +250,8 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; + std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; + std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 0c033593c8..3fad05f651 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -977,13 +977,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mFactIndex; + return record.get().mFactionRank; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mFactIndex = data.toInt(); + record2.mFactionRank = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 37189e05fb..5bd144a731 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -97,9 +97,7 @@ namespace MWClass std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index a27762a579..a162c3edd0 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -127,9 +127,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 71426c7235..e102e7cf1b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -247,9 +247,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index e7b811936d..824b69daf3 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -139,9 +139,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index bf3d0c12c1..bbe5f60bf9 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -193,9 +193,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index aba3b84665..362b97902a 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -235,9 +235,7 @@ namespace MWClass text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 5e7534660c..d0ba37f0bb 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -251,10 +251,8 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); } info.text = text; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 17ae61c0d0..fa03f23ff1 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -147,9 +147,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 611c86f678..8237b07f29 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -187,9 +187,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 5dc7180b2a..b0129f403e 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -141,9 +141,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index c5796ddbd9..74b10ee3d3 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -182,9 +182,7 @@ namespace MWClass } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 66555444ff..734385a6a8 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -151,9 +151,7 @@ namespace MWClass info.isPotion = true; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 61609bde03..5df7da2b0e 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -140,9 +140,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f1f947d77a..ed09a06da3 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -144,9 +144,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 2208d717a6..6f75095ff7 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -351,9 +351,7 @@ namespace MWClass info.remainingEnchantCharge = ptr.getCellRef().getEnchantmentCharge(); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 676a9ee63d..e09ff2487f 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -547,6 +547,17 @@ namespace MWGui return " (" + boost::lexical_cast(value) + ")"; } + std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) + { + std::string ret; + ret += getMiscString(cellref.getOwner(), "Owner"); + ret += getMiscString(cellref.getFaction(), "Faction"); + if (cellref.getFactionRank() > 0) + ret += getValueString(cellref.getFactionRank(), "Rank"); + ret += getMiscString(cellref.getGlobalVariable(), "Global"); + return ret; + } + bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 4e73cc5551..8b6174b876 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -66,6 +66,9 @@ namespace MWGui static std::string getCountString(const int value); ///< @return blank string if count is 1, or else " (value)" + static std::string getCellRefString(const MWWorld::CellRef& cellref); + ///< Returns a string containing debug tooltip information about the given cellref. + // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, int skillId); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1f81eda7d6..861d5e1100 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -34,7 +34,9 @@ namespace if (!faction.empty() && ptr.getClass().isNpc()) { const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + std::map::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction)); + if (found == factions.end() + || found->second < item.getCellRef().getFactionRank()) isFactionOwned = true; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 056737be6f..cdf08e6ed3 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -93,6 +93,11 @@ namespace MWWorld return mCellRef.mGlobalVariable; } + int CellRef::getFactionRank() const + { + return mCellRef.mFactionRank; + } + void CellRef::setOwner(const std::string &owner) { if (owner != mCellRef.mOwner) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index e9ef343fca..5776557398 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -75,6 +75,9 @@ namespace MWWorld std::string getFaction() const; void setFaction (const std::string& faction); + // PC faction rank required to use the item. Sometimes is -1, which means "any rank". + int getFactionRank() const; + // Lock level for doors and containers // Positive for a locked door. 0 for a door that was never locked. // For an unlocked door, it is set to -(previous locklevel) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index b77257e47e..fc8329815a 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefNum.mContentFile = -1; cellRef.mRefID = name; cellRef.mScale = 1; - cellRef.mFactIndex = 0; + cellRef.mFactionRank = 0; cellRef.mCharge = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 409ae02d01..1560c0d8b7 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -31,8 +31,8 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mSoul = esm.getHNOString ("XSOL"); mFaction = esm.getHNOString ("CNAM"); - mFactIndex = -2; - esm.getHNOT (mFactIndex, "INDX"); + mFactionRank = -2; + esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; mCharge = -1; @@ -94,8 +94,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNOCString("XSOL", mSoul); esm.writeHNOCString("CNAM", mFaction); - if (mFactIndex != -2) { - esm.writeHNT("INDX", mFactIndex); + if (mFactionRank != -2) { + esm.writeHNT("INDX", mFactionRank); } if (mEnchantmentCharge != -1) @@ -147,7 +147,7 @@ void ESM::CellRef::blank() mGlobalVariable.clear(); mSoul.clear(); mFaction.clear(); - mFactIndex = -1; + mFactionRank = -2; mCharge = 0; mEnchantmentCharge = 0; mGoldValue = 0; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 25ea243ef0..de1a5bedcf 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -50,9 +50,8 @@ namespace ESM // you take it and are not a faction member) std::string mFaction; - // INDX might be PC faction rank required to use the item? Sometimes - // is -1, which I assume means "any rank". - int mFactIndex; + // PC faction rank required to use the item. Sometimes is -1, which means "any rank". + int mFactionRank; // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. From 07cfa2abd50ca161a793ee797f4e87e2bad6c8c0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 23:05:40 +0200 Subject: [PATCH 88/97] Set texture unit indices for GLSL ES --- extern/shiny/Main/MaterialInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index b8032d681e..128cc593b2 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -163,7 +163,7 @@ namespace sh mTexUnits.push_back(texUnit); // set texture unit indices (required by GLSL) - if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && (mFactory->getCurrentLanguage () == Language_GLSL || mFactory->getCurrentLanguage() == Language_GLSLES)) { pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); From 103325bfb8430f5a8065b919da070345f5e2b9ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 23 Jul 2014 22:04:18 +0200 Subject: [PATCH 89/97] Change HUD enemy health bar to use actorId --- apps/openmw/mwgui/hud.cpp | 12 ++++++++---- apps/openmw/mwgui/hud.hpp | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b39fd0db73..3e753b7f9b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -92,6 +92,7 @@ namespace MWGui , mSpellVisible(true) , mWorldMouseOver(false) , mEnemyHealthTimer(-1) + , mEnemyActorId(-1) , mIsDrowning(false) , mWeaponSpellTimer(0.f) , mDrowningFlashTheta(0.f) @@ -609,7 +610,10 @@ namespace MWGui void HUD::updateEnemyHealthBar() { - MWMechanics::CreatureStats& stats = mEnemy.getClass().getCreatureStats(mEnemy); + MWWorld::Ptr enemy = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mEnemyActorId); + if (enemy.isEmpty()) + return; + MWMechanics::CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); mEnemyHealth->setProgressRange(100); // Health is usually cast to int before displaying. Actors die whenever they are < 1 health. // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) @@ -620,7 +624,7 @@ namespace MWGui { mSpellIcons->updateWidgets(mEffectBox, true); - if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) + if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) { updateEnemyHealthBar(); } @@ -634,7 +638,7 @@ namespace MWGui void HUD::setEnemy(const MWWorld::Ptr &enemy) { - mEnemy = enemy; + mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); mEnemyHealthTimer = 5; if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); @@ -644,7 +648,7 @@ namespace MWGui void HUD::resetEnemy() { - mEnemy = MWWorld::Ptr(); + mEnemyActorId = -1; mEnemyHealthTimer = -1; } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index f451ea4d2d..56b0ef7b55 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -104,7 +104,7 @@ namespace MWGui SpellIcons* mSpellIcons; - MWWorld::Ptr mEnemy; + int mEnemyActorId; float mEnemyHealthTimer; bool mIsDrowning; From fc618cb3ea956e5bb4e626e962c2c36518113bd9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 00:25:02 +0200 Subject: [PATCH 90/97] Use separate function for fatigue restoration during resting/waiting --- apps/openmw/mwmechanics/actors.cpp | 28 ++++++++++++++++++++++++---- apps/openmw/mwmechanics/actors.hpp | 4 +++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 809e948e72..acca713334 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -184,7 +184,7 @@ namespace MWMechanics calculateCreatureStatModifiers (ptr, duration); // fatigue restoration - calculateRestoration(ptr, duration, false); + calculateRestoration(ptr, duration); } void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) @@ -293,7 +293,7 @@ namespace MWMechanics creatureStats.setFatigue(fatigue); } - void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep) + void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; @@ -331,10 +331,30 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; + DynamicStat fatigue = stats.getFatigue(); + fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); + stats.setFatigue (fatigue); + } + + void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) + { + if (ptr.getClass().getCreatureStats(ptr).isDead()) + return; + + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); + + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + // restore fatigue + float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); + float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + + float x = fFatigueReturnBase + fFatigueReturnMult * endurance; + DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); - } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) @@ -1206,7 +1226,7 @@ namespace MWMechanics void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - calculateRestoration(iter->first, 3600, sleep); + restoreDynamicStats(iter->first, sleep); } int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index cda6abaeaa..4b5d77a7f8 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -36,7 +36,7 @@ namespace MWMechanics void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateNpcStatModifiers (const MWWorld::Ptr& ptr); - void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep); + void calculateRestoration (const MWWorld::Ptr& ptr, float duration); void updateDrowning (const MWWorld::Ptr& ptr, float duration); @@ -90,6 +90,8 @@ namespace MWMechanics void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. + void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep); + int getHoursToRest(const MWWorld::Ptr& ptr) const; ///< Calculate how many hours the given actor needs to rest in order to be fully healed From 7ae9bbb1a5d8db85d09f572109884e23c6eef442 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 00:59:58 +0200 Subject: [PATCH 91/97] Implement iCrimeThresholdMultiplier --- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index acca713334..8f12098277 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -860,7 +860,7 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile()) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()); + int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); // Force dialogue on sight if bounty is greater than the cutoff // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff @@ -868,7 +868,11 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player), ptr); + static int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); + if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); + else + creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } From 4e0c133fb35d1ef9d31336e25b11c0266daee4a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 02:19:36 +0200 Subject: [PATCH 92/97] Add comment to Cell NAM0 (it's a RefId counter) --- apps/esmtool/record.cpp | 2 +- components/esm/loadcell.cpp | 10 +++++----- components/esm/loadcell.hpp | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e4ada7d189..f8bc2af619 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -513,7 +513,7 @@ void Record::print() else std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; - std::cout << " NAM0: " << mData.mNAM0 << std::endl; + std::cout << " RefId counter: " << mData.mRefIdCounter << std::endl; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 83864569fb..0a25fce846 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -58,7 +58,7 @@ void Cell::load(ESMReader &esm, bool saveContext) void Cell::loadCell(ESMReader &esm, bool saveContext) { - mNAM0 = 0; + mRefIdCounter = 0; if (mData.mFlags & Interior) { @@ -92,7 +92,7 @@ void Cell::loadCell(ESMReader &esm, bool saveContext) esm.getHNOT(mMapColor, "NAM5"); } if (esm.isNextSub("NAM0")) { - esm.getHT(mNAM0); + esm.getHT(mRefIdCounter); } if (saveContext) { @@ -150,8 +150,8 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("NAM5", mMapColor); } - if (mNAM0 != 0) - esm.writeHNT("NAM0", mNAM0); + if (mRefIdCounter != 0) + esm.writeHNT("NAM0", mRefIdCounter); } void Cell::restore(ESMReader &esm, int iCtx) const @@ -220,7 +220,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mWater = 0; mWaterInt = false; mMapColor = 0; - mNAM0 = 0; + mRefIdCounter = 0; mData.mFlags = 0; mData.mX = 0; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index fb4b6b28ac..5f889a55b8 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -94,7 +94,10 @@ struct Cell float mWater; // Water level bool mWaterInt; int mMapColor; - int mNAM0; + // Counter for RefIds. This is only used during content file editing and has no impact on gameplay. + // It prevents overwriting previous refIDs, even if they were deleted. + // as that would collide with refs when a content file is upgraded. + int mRefIdCounter; // References "leased" from another cell (i.e. a different cell // introduced this ref, and it has been moved here by a plugin) From d4ff17f1c5bf1641bbf8fcdb2ae2984664eb04f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 02:20:16 +0200 Subject: [PATCH 93/97] Remove unused FLTV and NAM0 from CellRef --- apps/openmw/mwworld/manualref.hpp | 2 -- components/esm/cellref.cpp | 28 +++++++--------------------- components/esm/cellref.hpp | 6 ------ 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index fc8329815a..0becd75242 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -35,8 +35,6 @@ namespace MWWorld cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; - cellRef.mFltv = 0; - cellRef.mNam0 = 0; LiveCellRef ref(cellRef, base); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 1560c0d8b7..7947cdba4a 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -6,10 +6,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) { - // NAM0 sometimes appears here, sometimes further on - mNam0 = 0; + // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that + // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. + // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, + // because any item can theoretically be moved by a script. if (esm.isNextSub ("NAM0")) - esm.getHT (mNam0); + esm.skipHSub(); if (wideRefNum) esm.getHNT (mRefNum, "FRMR", 8); @@ -60,20 +62,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); - mFltv = 0; esm.getHNOT (mReferenceBlocked, "UNAM"); - esm.getHNOT (mFltv, "FLTV"); esm.getHNOT(mPos, "DATA", 24); - // Number of references in the cell? Maximum once in each cell, - // but not always at the beginning, and not always right. In other - // words, completely useless. - // Update: Well, maybe not completely useless. This might actually be - // number_of_references + number_of_references_moved_here_Across_boundaries, - // and could be helpful for collecting these weird moved references. - if (esm.isNextSub ("NAM0")) - esm.getHT (mNam0); + if (esm.isNextSub("NAM0")) + esm.skipHSub(); } void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const @@ -127,14 +121,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - if (!inInventory && mFltv != 0) - esm.writeHNT("FLTV", mFltv); - if (!inInventory) esm.writeHNT("DATA", mPos, 24); - - if (!inInventory && mNam0 != 0) - esm.writeHNT("NAM0", mNam0); } void ESM::CellRef::blank() @@ -156,8 +144,6 @@ void ESM::CellRef::blank() mKey.clear(); mTrap.clear(); mReferenceBlocked = 0; - mFltv = 0; - mNam0 = 0; mTeleport = false; for (int i=0; i<3; ++i) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index de1a5bedcf..9c57061b00 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -82,12 +82,6 @@ namespace ESM // -1 is not blocked, otherwise it is blocked. signed char mReferenceBlocked; - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza - // Brindisi Dorom", where it has the value 100. Also only for - // activators. - int mFltv; - int mNam0; - // Position and rotation of this object within the cell Position mPos; From 4b3e12bfb4fe5450a3ad22bdb12e04760ed8e558 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 18:03:11 +0200 Subject: [PATCH 94/97] Fix pinned widgets receiving mouse clicks in gamemode (Fixes #1686) --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 668211db29..9ebafc90c2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -268,7 +268,7 @@ namespace MWGui mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); trackWindow(mCompanionWindow, "companion"); - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows"); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Overlay"); mHud->setVisible(mHudEnabled); From 4ec51b386a9d4000295263cee221d37ac7d37b59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 19:00:57 +0200 Subject: [PATCH 95/97] Add ManualResourceLoader for fog of war textures (Fixes #1675) --- apps/openmw/mwrender/localmap.cpp | 28 +++++++++++++++++++++++++++- apps/openmw/mwrender/localmap.hpp | 5 ++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 62907fcc35..271cbbfe1c 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -340,7 +340,9 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName) sFogOfWarResolution, sFogOfWarResolution, 0, PF_A8R8G8B8, - TU_DYNAMIC_WRITE_ONLY); + TU_DYNAMIC_WRITE_ONLY, + this // ManualResourceLoader required if the texture contents are lost (due to lost devices nonsense that can occur with D3D) + ); } return tex; @@ -457,6 +459,30 @@ bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interi return alpha < 200; } +void LocalMap::loadResource(Ogre::Resource* resource) +{ + std::string resourceName = resource->getName(); + size_t pos = resourceName.find("_fog"); + if (pos != std::string::npos) + resourceName = resourceName.substr(0, pos); + if (mBuffers.find(resourceName) == mBuffers.end()) + { + // create a buffer to use for dynamic operations + std::vector buffer; + + // initialize to (0, 0, 0, 1) + buffer.resize(sFogOfWarResolution*sFogOfWarResolution, 0xFF000000); + mBuffers[resourceName] = buffer; + } + + std::vector& buffer = mBuffers[resourceName]; + + Ogre::Texture* tex = dynamic_cast(resource); + tex->createInternalResources(); + memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); + tex->getBuffer()->unlock(); +} + void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { if (sFogOfWarSkip != 0) diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index babf7224ed..1572800e5a 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace MWWorld { @@ -23,12 +24,14 @@ namespace MWRender /// /// \brief Local map rendering /// - class LocalMap + class LocalMap : public Ogre::ManualResourceLoader { public: LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering); ~LocalMap(); + virtual void loadResource(Ogre::Resource* resource); + /** * Clear all savegame-specific data (i.e. fog of war textures) */ From 919632a505547a6a58548ac09313a62b77cc34c2 Mon Sep 17 00:00:00 2001 From: crysthala Date: Thu, 24 Jul 2014 13:12:44 -0500 Subject: [PATCH 96/97] render lighting icons --- files/opencs/Lightbulb-48.png | Bin 0 -> 8092 bytes files/opencs/Moon-48.png | Bin 0 -> 7186 bytes files/opencs/Sun-48.png | Bin 0 -> 8364 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/opencs/Lightbulb-48.png create mode 100644 files/opencs/Moon-48.png create mode 100644 files/opencs/Sun-48.png diff --git a/files/opencs/Lightbulb-48.png b/files/opencs/Lightbulb-48.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a45528ef258d5680f8dd717d013544be5c80fa GIT binary patch literal 8092 zcmV;NA7kK&P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000!nNkldky4gj{xE-C4ukDGRtbu^Tbt31dx$!4?bPT?RDg849anO0BGN9zCVZNzG9Q- z2q23j*VSe$3-o&c<+PYKp32s&x-DttCxU%Cck}hQTC?~(e z#2B+vO086y3h*z!eb2qS;oB>|t{LFbHM~A!p6@B=sti!I4g2Gg&33_|xCEFbiLCRS zllRQE4v$Q0an50_WfI2%$PWMj|Nm6aF-pT=96<5^0YtBmu8xi(U4)R5jB(0`y@5kV z@8A&HBG`bFWYMW0^aO&6icrOnQgMjd7$v4(Bw%??1v9+yH@xM!Gz>#L&pZ2FEcmG^ zin8RZC`j`htJPX-7)BjrSw>k^ywC5L=^m-xC;9F(Nw1hB345K+NeuwJ<_T}}E|1X$ z!{I82N9#O)_PBp}qB|Jq8rW^OCDWRCHs$o(C;kW-T^w^Wec+(q*Y5v0KupUL)3Ri% z+1$JwkGYLwuA_);)0Aa3pp+7&lkrhAt*;xK^Y zKl~F4qJ#7g(6K}G9|&rOib0Np&|5Cff>}BUb(IzrZA9Dx-I}0VhPb4PMzmCmFF9@W z@|Ga23l08g$`AB4^ zVYet*X%2o%{MzehKQoBs&T?t&aFWY;!QjAvs6ITP*UqSSmZ`U=X?AANYu`969CLDW z;{~4-MM$zdrJE+VjXzvh;%Mei{uU27zPe^cmc=WehQmUVU zg+d~gN)e02h(@D?Rh5?=0pdR+b@$rT9S30i2iqU8i(SfS?cHDqWm8(3>P|HMVG1&&8yWS&U95~U#*D)dN<%GW3%xMTH@X`qz6@bymPT7_qCLqW?a9e&lbn;B zq{P3NpSWp~De9z+Jjp_iR6b8K zmm{9dvX#m3$I=pQZEbG{x-AwlJv}Yg8x3}KAW9*Z&S_m`UjUavRQ01BlNny#Yi4@nq5ktvP>9|P|J?*i{r zsvIC?jG=5_A(XhsYAnpNNQ7Sk0h$8+{r&qk6oss=qvkYXxj#|$t8A#}P%}N4wGXM( zTPW8*;?{57B#K|Lk-o~B`iK?f1*?j}pxypmQ((;L6n05^M~=l%Gg+eP7;<`vgn64s zPcEY7zdja}w9HXPZpB^4=KIGw%5ivY6B0d`%6GyDp zi%DHaj%$Qfh08OSd9DRnQ~zS=#bchwLo7!^EWdoo4<1kL_~4*8e)6Pv{jL8KuuGD# zOOiP4a0tiHkZ=qQiSOO+d}wnM|MlyvDGEVn6H3fRD z)?Ix%Fwpn)g$sP?ay48N6OD0~t6_II8efc$H;&qDX-ij^=(bq?Rdb-XcUMlQvm6Wt z2?PQxEG#fTKabDn!|V0p^ZDrP=-Btaf&RX}T}@3*vADR1-|uH`ZjPCm89W{jZnvA+ z*;x+k-{18%;1%U%78BJSh4GshfL^OiNx|OQHq(O_;8W&umQV>KO9A;yNnLGQ;y)*aD2F$MyiXk>EKAhL{ zJLmsT&i8%)I~7wVPZri^WeE$DlZB)O3xxC~OWx01yY|~dRaLs<$B&y2A30*#w{PEr zrRnLPBqb#YIk~w)YHF%5?}ft}^A-&O=FXWTY~Qx6Ef$Xh5RJzPN24g=FtVyL0JL^= zupv8pU200o>j5%Wub!aQYDob7{ryDaaU!wU^XB0&QYeI1mhnmwQZ&l?_3JhB#|!v) z=1f7a*E5(%&_6KneDYWfDXijCf_Q=wZdt}7Nw@+596lfZaG12Tw3->yr;iSpHg&2{ zQC{vCOe9`T9*n4X6c^5r5i!_}UD9F;7_j>Lu=zaLyk4wsH=^IqM^mP}H!9$>)YKUa zdzL^%#-*5XgdXBht{?_Ah(Qf~@)azB`Om2~0rxT;Y!OxYIrsU-0kpX38Wwk>e zDn%6BiW!H}j8i#-TdlyW?#8X`!5ONcPdbCie*=TN8@)rsV7Jq%)smB!mpLNf@2gj% z(Rds+8pWeHafBY?QcmMjOOc|v1Y;`+#a7{uSp`2sOmhQV8pR&1xs(u>aF z;Kzyz-H3pCjYgGK6;CjLO@4|!SdTogha*Sla__-3E?#<8&TfP?Y3g<>hEYBSLv{H&}z`r+}_UGtgJW3l+40Sn}pxaoKbB)A11em z!KLMT%X##kMvUI`7(5ra(DVyB&s`o{J7_ib&}p%$JhAlB*=uJ1p+mIk^*lD4S(}wryL`n8VO)SA(I2XeHEZUX-EQ=v z$mLtN=ro&oVm5Q~!UfuTdibHDLe9#`5wdb}gmo_*#i)Sc#($`)YHEC1tCfRCkJ4c@ z(rK}9r=x>bgMm*|QWg%+im!%)Fh>h^6(^*lbtQB$;e1CnM~Zgd6R3`uF=%gL}Ozk4Gj&PJ9m!a;^LPH zWq6I-wxp-0&%Sx%2A3{fqQ1VK+S*!9o;=C1W5=kjt|l`xGxAy+439>6d3i#9e*Qaq z%gWGbG%uUi)YNeJ@L>)fJjk-8OaB>)Ov7v3zG(5HMJZ3Zx~QwGgsB$ zs;W3}-~dHMMU1tmghmTW7r(Zf?RUFX|b&7)^sI;I22!aw5gCRhGOOl&} zK-h;`Yo~Q;5$F_(MU#?1Ai#ixog}OwEJ2pYk^Bot?? zG1fEld^q2J&zb+3|NH*F_3~Z&TK}Xm@vXOn`Sa!pZ%>^nL`|G1q@|?^W5IrVx6htEi{4{Ha2$Um{(sNqFK05c=OFS zg(*{}2#K?23*#qDc)0I<*QD})2}Dns^7xDyGqTIe%O8gA1-iPs>3n2_>R(9Jjt-Q* zSuGF@;#U;Bih@5FLof<)3YZ^={6pBK^VMf7;Edpy|OZY(YrE=A$^sZ%5*Ca(U!1cp8PtnlnJ z&j_;;5^^L-qQ6#n^r)!e4tyaQcgT%XwIHdjh)M&Zau>UD3%mRcHu*Z%Km}I+O)UO9 zn0&REyiJ%q2HHJVOl}F2%Zb_P#3V_yI~o15vMg(DAxhaUtFN=T|6hgyR}t-zt4!=WC-p?-y=?nF}aaHzS6Y969efKC1i zYv34WUn%XrYZ%;j(Yu<^JB{cZb_}A3UKG*WY?$3{zP@>r`5Mjd=4&+14_aW&nl;A@ z4;&Eb`$7B9xDxKb6Y}CzEq(n<1rGHTlDZdX@JrmmwRk!{!W&+WFT4nEM+Ux*Ogy0# zxI!NzDeJJy*;oR5F?mm5a9^R#Sxt-7N~_&WtIbB6%|?sOhQaCNi`-l>W66@^gBJM5 z=FL?XE?%U+*ZFT7<-@635S2Pa^$L=53}-MGSMaZRJ5~?~FF=mO5bT^xuyZ`2&T#}g zC!j>4354VDhLUlqtFg_m ztFZ>wWAxMWs2yh5F|ids_(wT1@*H3kD~H*Tz2sMT&Bw7|@nGlwQ5B+M(na;001 zMDWNm&VV1g&xyrn!0fBW>@UX>IEtv`BB`HJayE&Ewgf^waVU|=1S8`KMt*}5nLwy# zDvfRLQd0Up4rMjAz-Jged9*qYb6-49y|t1WQv=n8HmY?x>P;s07Z-QOy!YO`cVl9P z4q6~OI$DU1juz(5oxAwL<;%$7FiyW8yU&Z+D`N8KFnX#m`M$>FFUB3&$Fb9^xmmp$ zZ|5Iz1()LrX5tEF;0k8qjjZNI)nbmG%EH-^gTcLrR%a27;-$Xu^;8*Jx!2YgzNm=! zgoMQtqoRbUsHh)t4`gHr=@}V9T6((heti6>O`m`6@~A3KzaOjHO}pEH!PSo5-H6`( zZ|rIX$4mc7`Ry;TD*G_`wqy2f#pL@JChu0vft_5swUHxd{)RD7%6;(?^|l+-n5(F6 zzt3HLE7b-AwMHXU z{`?Iwv9XWM(`baBL_nLC_K;MqR#=poDQMHugxPcE2q~$lLTp^zlS`H^y;0ZJhCz~O zv0BkP9OM-gaHsJ;jb;<|Mk957+^nl3J3pT~lj&jbYc(}!7cIK+PE5>`NedPT@d*h+ zl13wFQd7UR2LC95)b#Z4MVpZ!EJ#ik=4&*YuHU_j&f%chYUOl!IfqZ4q|t1q)?j$B zO1HODSX@j|Ss7J29n}T{rPr@BJ1J???4%@NK}w1+KRNk(8MMF~ufP6-7&~^XuqZ35 zw8d_xO_He5>&e-%1D(@Jqt!}-#X^h2!Io{?sBCVg)?}heuSdIRQR%3WBZXH-kN!an zT3}W}!hpoZ#*X{LM<4avx_z6QH*RqK>Q$~@x$>X}xOkBZ=g(7CRz~UBvz+Uz1WHOu zICOKKAU{LqS0S`T6;5+qRA6%a=25+_)3Je(9wrULG+*7>Gd& z%$+l5K;mL!Cx7|nmozmsasK>yii?XmcI+5OjvV3O!GrAIzn{H(_p*EUZg%e6Nq&Al zd3kwk-MW>+!a~-pS;N%m=*clNW(Wf@Xo198vj${leEitOix&q^pFT}xWhF&LMI1PA z;9J4_zj}Z0?CflEa&p+dZ5xTRW(B84M~|I8ZJIC;gBFO5jUAAfn3$*L%$ZZZYSpUl zl`B`uD^{$Kmn~Z+FI~D+&dSP?7iVV5nVFgLqKphVJuOX6(`x0E)KoboB}Gn3O6s0I qZCd%HsHmqVPnskQ#Q)0qp8)_W!P`#dU)3G}0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000p^NkllBSKz5Op zwJigKe^7`9OZ-$Ex-u^#x`{n=R*Ps1Abm`H56H{{r00000|Nml8 zRFG#-R8U|D2o7_4^X2D%Iayf-c{w?(I3zNbfk9eIis9G)|NozV`~P27Muq_!_6Z1K z@bM3ZxT&je@{Nb@fBk>_@!x-0X=w&zoLyMX5EK^0;OQHP(2!kNI{ohJ@BeST{QCd; z(@+29WMvsVy!;ph^!xL;YCq^*@qR^dO0 zhy?^Q?+A+@=u|4HQ>i3{TuwTJsXj>MKM_d3!T0zIp5tU4l6LAJR5u1!?f`tLS9lUR zxZ_!?nD-Mu@~G#MYuv4937vO#wRrrlU1ONEeBbIRY&N#0E>OO z{C$n{3%2Ws2v<0Rt6&;V{}V2}Nu1v&aCRHR>2(ynsy=&9uc{2J!ZK-eh89mE2dDoL zm)_Lxyq zEE*_6FmZK|K?hAtjKRf$m}oGvNMb-OMkA#q#d7I|(wi+Ur530K0(l&cMU9e_;g@{V z`y0OJeO}5x@|iN2%*1k9x-)#=>x(8Kk=C8e=crJsL^V`-Nkn2vEs?3UjZCd=q;E8m zlI3&&5ZwlP`UdQ#mUc286NAZ2j6zMEzDf9n)5XP9a=Mr%ZJ!a_1|pjWc=lp^uU@Ms zPRADv%V|l)=}D>95+i4S3wVQx=G-2HHxCfb>?587%$;4GJ=Q@ggzMHpvRH4COvRDv zR~P*yxY0lK==-j)55e>vzGZ>djxMvM_a+_90CjZtP`%DT8or6Tdk2peJbXM72&VV2 zy0MEuY6t#g4o^qNcC@^K{)eu?ND5X%7V8jk`T_)Z?>$%!riFd;C4a!1$ilmpMN50v z)Y-DK!-SnFDW%G)8ZtDukzBfa&gB4j6IpoHvRIC7V=1 znmflvo{z&B*nndt1-ow@Gu|ZZ{uG{!zD9?oZ>-|{g`)EE3SzYll)qP>rm8b-d3u#hyG0~r8U%ezT+i8+jNdJjV;u3>$W}c0&uKu;q#uGru{4+cC=Kj zP?5M&wi9ra%_f;b)eHcR)ou6ylnO*ihxw{2rPWlUY9L8s-+mmcm8o5iBU#3WLv8e;>*64`|<01p2q=2kY*uQ#IofV-frtDBlyJ18SF zdvCz~-}-{{t4z7w_X7RHM#OTYSW#-4NU3SwAK><`z`n8xn{NZQ1|nOd_kHD!G!&0Vz}shXaHF zOoz)Y?lqWP0hnEDV8^qff7J1W1WHTi&{=Nr;Q@0VAIxs1-0Tj(gs!|6)MWg&V3_5MpjNQeUOrR zZfPBudZm2Av4U}j4`z1=iaO1AnOQl90yMRDk)&FAFFQA1LDh;n8ZwwMvEXOQ=b7?| zpHD74$0yf%rjrtr4+JO^SH5-m`i+Hty$Mpein#@Oe4%Q21JTLH?91W1&hQFPu(c5LPW%oiAl8D>{pYLML$a0m;niiNe~+s z4{`Ac%ckg5sFy*}n=4F;8zW~%)1_&(!QiU39h1(uB zKvFs&DII8O23Ril2R8dlr`=sWIE#qg-Z|_VpT+(u#Il-JWO>sriA(`XLbGgAsMMg= z(jZr9LT#)1n+$~B`Bh;*+q3^b*Uao34o?c;UE{OZGd_ns-dTM8jkoI5B>iiH(G1HW zJ(mkCZvrjDfx%=0g48{p0kK2|Y3UgeC8fvPW)X2<0?`wgvAf2ETBM@Ug%rbX&&(2o zSRz|elrm)+Kc&n}hbq3>hsaypL zLi0qDD+!?W2H5)YUdYX}gO=fdGuytqfg^uE-Z_nko>4!#7Jxeg#hP(ix1PGw_coBj{KjXqE?pq#-&c4qn=^8%#@S;MhOjc1|LqdpH0c$WM-Ylh|=j zh&i6IS^Vrq6&lU?TS#Ao+!KiC8WNx#gY)3Q zDAm^QL)Y*OzH{dMR7^~qDk3}#eiIq_(D)G*83~aQ5%BD@&%xTY>mXY!19BV&~8lwg;gHCIaArG!;lx&LKaZI)4deO7*7o>z{|n@bD0Pc-SgPPDurc zOa;-=v5=IK8ptTiY=CBY@#j}>;@Av^po6t+Yo8BWdcC1~wy9?f9YfQ2qoy4hF84xu zX7&#=vn3#vs~|;`21&^xkSWyPn}OK)M1Z)sgr^ENZTHiVdsb+n|sG_`|hH4f@D@vdLw{L zL4+ih$bV}4-Ih<tZ@y73jaHfd!ZCv)iHogJ>%HY>&1&# zEAXH1o|!5-bfj8Cv#(q2n|Cmr$tqWoX04vjRS;SWZOAP!**5K9cx&l_!=<%vo%~?t zT4fWuM`y9M&nK+=8@loAl}e-yrqg1n0u*Wr6l!Wdvo7rPWh#xd__ZU0y<@@(q}Dz3 zFt~FbJP2Lc(H~0b!39HkV8QTpYadoQ28AVrV-Txd!&vVb!#d9>Ry&9BcKZNUwDe(F zqwrtC)oK@Bt9Ie_+D^Pt-;Kq8J>iq7G!nUr1hG^BIWi^W$dr#ZK-YVc*i*y&;#J@^Wa~%1~wysCZN3KE|xb38i7D75bQL9?Lcs2a=ljQH?G&X z@m5nWeo^JX10`?xl2g){u$3zzBCw&12oDeGe;Qyo6Y%DI&`^3v&&+jc4@dqQsgTzo)3>?B#O&vc`#ZU1M158NHiX3g546C?v_TBuQ$)PPK>21?I^ zOsNKyhK7w9S@6P!=oJ*hzhJZ$i1X}QP84o^xoUUOp_aYHCGMg_N1gi)9(C?Nc+|P) zPp`Rm?LXMOY5VT4O!AcV;lJI00s#F U^IfI7vj6}907*qoM6N<$f+3E*%K!iX literal 0 HcmV?d00001 diff --git a/files/opencs/Sun-48.png b/files/opencs/Sun-48.png new file mode 100644 index 0000000000000000000000000000000000000000..dbdede349a4417d6d67e5e22c2192c50b00e71ad GIT binary patch literal 8364 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 Date: Thu, 24 Jul 2014 20:52:14 +0200 Subject: [PATCH 97/97] Ignore FLTV from old savegames --- components/esm/cellref.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 7947cdba4a..29d26d013f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -63,6 +63,8 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mTrap = esm.getHNOString ("TNAM"); esm.getHNOT (mReferenceBlocked, "UNAM"); + if (esm.isNextSub("FLTV")) // no longer used + esm.skipHSub(); esm.getHNOT(mPos, "DATA", 24);