From bbcdfd4078275cbb5e3060e5b04645567b2297d3 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 6 Sep 2018 21:49:50 +0200 Subject: [PATCH 001/150] Implements vanilla's off-by-one error, fixing #4611 --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +++- apps/openmw/mwgui/spellcreationdialog.hpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 9 ++++++--- apps/openmw/mwmechanics/spellcasting.hpp | 4 ++-- files/settings-default.cfg | 3 +++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2e..bd54d47ee4 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -449,11 +449,13 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + static const bool vanillaCost = Settings::Manager::getBool("expensive spells", "Game"); + for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { const ESM::ENAMstruct& effect = *it; - y += std::max(1.f, MWMechanics::calcEffectCost(effect)); + y += std::max(1.f, MWMechanics::calcEffectCost(effect, vanillaCost)); if (effect.mRange == ESM::RT_Target) y *= 1.5; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index ec90fa3cec..084c3e1e1c 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "windowbase.hpp" #include "referenceinterface.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b857c6d7e2..83af249dac 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -49,13 +49,13 @@ namespace MWMechanics return schoolSkillMap[school]; } - float calcEffectCost(const ESM::ENAMstruct& effect) + float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - return calcEffectCost(effect, magicEffect); + return calcEffectCost(effect, magicEffect, customSpellCost); } - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost) { int minMagn = 1; int maxMagn = 1; @@ -68,6 +68,9 @@ namespace MWMechanics int duration = 0; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; + //The wonders of vanilla spellmaking + if (customSpellCost && duration < 1) + duration = 1; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 2844e7f23f..836473653d 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -25,8 +25,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); - float calcEffectCost(const ESM::ENAMstruct& effect); - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); + float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost = false); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost = false); bool isSummoningEffect(int effectId); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c2ac2eb1ca..352b86f658 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -229,6 +229,9 @@ barter disposition change is permanent = false # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) strength influences hand to hand = 0 + +# Makes custom spells cost the same as in vanilla +expensive spells = true [General] From 6705e5aae455219e908ffb3870f4c5d1872a5d4f Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 8 Sep 2018 11:21:43 +0200 Subject: [PATCH 002/150] forget about the setting till #2887 is implemented at least --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +--- apps/openmw/mwgui/spellcreationdialog.hpp | 1 - apps/openmw/mwmechanics/spellcasting.cpp | 11 ++++------- apps/openmw/mwmechanics/spellcasting.hpp | 4 ++-- files/settings-default.cfg | 5 +---- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index bd54d47ee4..65d66f9e2e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -449,13 +449,11 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - static const bool vanillaCost = Settings::Manager::getBool("expensive spells", "Game"); - for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { const ESM::ENAMstruct& effect = *it; - y += std::max(1.f, MWMechanics::calcEffectCost(effect, vanillaCost)); + y += std::max(1.f, MWMechanics::calcEffectCost(effect)); if (effect.mRange == ESM::RT_Target) y *= 1.5; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 084c3e1e1c..ec90fa3cec 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -3,7 +3,6 @@ #include #include -#include #include "windowbase.hpp" #include "referenceinterface.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 83af249dac..14dfac283d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -49,13 +49,13 @@ namespace MWMechanics return schoolSkillMap[school]; } - float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost) + float calcEffectCost(const ESM::ENAMstruct& effect) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - return calcEffectCost(effect, magicEffect, customSpellCost); + return calcEffectCost(effect, magicEffect); } - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost) + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) { int minMagn = 1; int maxMagn = 1; @@ -65,12 +65,9 @@ namespace MWMechanics maxMagn = effect.mMagnMax; } - int duration = 0; + int duration = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; - //The wonders of vanilla spellmaking - if (customSpellCost && duration < 1) - duration = 1; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 836473653d..2844e7f23f 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -25,8 +25,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); - float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost = false); - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost = false); + float calcEffectCost(const ESM::ENAMstruct& effect); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); bool isSummoningEffect(int effectId); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 352b86f658..09672aaac2 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -228,10 +228,7 @@ barter disposition change is permanent = false # Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) -strength influences hand to hand = 0 - -# Makes custom spells cost the same as in vanilla -expensive spells = true +strength influences hand to hand = 0 [General] From d39c4729d205a0dee9285ea72eee389f1dc3eb23 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 8 Sep 2018 21:14:47 +0200 Subject: [PATCH 003/150] add changelog entry --- CHANGELOG.md | 1 + files/settings-default.cfg | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900b..4042326d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice Bug #4608: Falling damage is applied twice + Bug #4611: Instant magic effects have 0 duration in custom spell cost calculations unlike vanilla Bug #4614: Crash due to division by zero when FlipController has no textures Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 09672aaac2..c2ac2eb1ca 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -228,7 +228,7 @@ barter disposition change is permanent = false # Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) -strength influences hand to hand = 0 +strength influences hand to hand = 0 [General] From 6ab42919cf3c2dbe75ac8af473fc0ebf8ff5a4d9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 8 Sep 2018 22:40:58 +0300 Subject: [PATCH 004/150] Make sure the actor is actually crouching/running before tweaking movement speed --- CHANGELOG.md | 1 + apps/openmw/mwclass/npc.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900b..28ff0ca93e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #4618: Sneaking is possible while the character is flying Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type + Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c247ee543e..3d019ea90d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -938,8 +938,8 @@ namespace MWClass const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); + bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Run); float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); @@ -964,7 +964,7 @@ namespace MWClass flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } - else if(world->isSwimming(ptr)) + else if (world->isSwimming(ptr)) { float swimSpeed = walkSpeed; if(running) @@ -974,7 +974,7 @@ namespace MWClass gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } - else if(running && !sneaking) + else if (running) moveSpeed = runSpeed; else moveSpeed = walkSpeed; From 702868255a473b2a8b46f78598686e6fe74745ea Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 9 Sep 2018 13:56:58 +0300 Subject: [PATCH 005/150] Use sTo GMST in spellmaking menu (feature #4636) --- CHANGELOG.md | 1 + apps/openmw/mwgui/spellcreationdialog.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4042326d76..ab0b2a9ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,7 @@ Feature #4625: Weapon priority: use weighted mean for melee damage rating Feature #4626: Weapon priority: account for weapon speed Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating + Feature #4636: Use sTo GMST in spellmaking menu Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2e..b4713291bb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -147,7 +147,9 @@ namespace MWGui mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); - mMagnitudeMaxValue->setCaption("- 1"); + static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + + mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); setVisible(true); @@ -312,8 +314,9 @@ namespace MWGui } mEffect.mMagnMax = pos+1; + static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); - mMagnitudeMaxValue->setCaption("- " + MyGUI::utility::toString(pos+1)); + mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos+1)); eventEffectModified(mEffect); } From 33a66b778ffa728ac150c475e0fe5355b47ae947 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 9 Sep 2018 23:10:09 +0400 Subject: [PATCH 006/150] Disable repeating for Accept action in keyboard navigation (bug #4260) --- CHANGELOG.md | 1 + apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/keyboardnavigation.cpp | 7 ++++++- apps/openmw/mwgui/keyboardnavigation.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++--- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900b..c95e45ec4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Bug #4230: AiTravel package issues break some Tribunal quests Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat + Bug #4260: Keyboard navigation makes persuasion exploitable Bug #4271: Scamp flickers when attacking Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39eed55379..564c66e757 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -350,7 +350,7 @@ namespace MWBase virtual const MWGui::TextColours& getTextColours() = 0; - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0; + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; }; } diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index cde8a17ccd..361ab407a6 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -172,7 +172,7 @@ enum Direction D_Prev }; -bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) +bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { if (!mEnabled) return false; @@ -192,7 +192,12 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) case MyGUI::KeyCode::Return: case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: + { + if (repeat) + return false; + return accept(); + } default: return false; } diff --git a/apps/openmw/mwgui/keyboardnavigation.hpp b/apps/openmw/mwgui/keyboardnavigation.hpp index 7caf25690d..2a094a2df2 100644 --- a/apps/openmw/mwgui/keyboardnavigation.hpp +++ b/apps/openmw/mwgui/keyboardnavigation.hpp @@ -14,7 +14,7 @@ namespace MWGui ~KeyboardNavigation(); /// @return Was the key handled by this class? - bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat); void saveFocus(int mode); void restoreFocus(int mode); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 45897b88cf..6007846fad 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2060,9 +2060,9 @@ namespace MWGui return mTextColours; } - bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text) + bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { - if (!mKeyboardNavigation->injectKeyPress(key, text)) + if (!mKeyboardNavigation->injectKeyPress(key, text, repeat)) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 657548397c..2e4fb8a02d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -379,7 +379,7 @@ namespace MWGui virtual const MWGui::TextColours& getTextColours(); - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false); private: const MWWorld::ESMStore* mStore; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b24c8cc149..b632bf6124 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -214,7 +214,7 @@ namespace MWInput break; } - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); } void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) @@ -720,7 +720,7 @@ namespace MWInput bool consumed = false; if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); if (SDL_IsTextInputActive() && // Little trick to check if key is printable ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) consumed = true; @@ -1153,7 +1153,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, mFakeDeviceID, mJoystickLastUsed)) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); } else if (mControlSwitch["playercontrols"]) mPlayer->activate(); From 269ef7a5596de88e4586b64c59a2407e6f9776c2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 10 Sep 2018 12:55:00 +0400 Subject: [PATCH 007/150] Disable repeating for ENTER key in GUI --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/alchemywindow.cpp | 3 +++ apps/openmw/mwgui/countdialog.cpp | 4 +++- apps/openmw/mwgui/enchantingdialog.cpp | 3 +++ apps/openmw/mwgui/keyboardnavigation.cpp | 4 +++- apps/openmw/mwgui/savegamedialog.cpp | 3 +++ apps/openmw/mwgui/spellcreationdialog.cpp | 3 +++ apps/openmw/mwgui/textinput.cpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + 11 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 564c66e757..b2b80cca38 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -351,6 +351,7 @@ namespace MWBase virtual const MWGui::TextColours& getTextColours() = 0; virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; + virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0; }; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 80284e9b2a..6fd3d3220c 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -65,6 +65,9 @@ namespace MWGui void AlchemyWindow::onAccept(MyGUI::EditBox* sender) { onCreateButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index cf058caac3..4292f2e045 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -73,8 +73,10 @@ namespace MWGui void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); - setVisible(false); + + // To do not spam onEnterKeyPressed() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void CountDialog::onEditValueChanged(int value) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 8fbfa65c68..039377dee3 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -288,6 +288,9 @@ namespace MWGui void EnchantingDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 361ab407a6..aaf369aa7f 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -193,8 +193,10 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, b case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: { + // We should disable repeating for activation keys + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::None); if (repeat) - return false; + return true; return accept(); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 45790cbf5f..3264e5e339 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -127,6 +127,9 @@ namespace MWGui void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { accept(); + + // To do not spam onEditSelectAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SaveGameDialog::onOpen() diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2e..f06f059cdc 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -420,6 +420,9 @@ namespace MWGui void SpellCreationDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SpellCreationDialog::onOpen() diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 169c9e742e..54f2d3be9b 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -65,6 +65,9 @@ namespace MWGui void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { onOkClicked(_sender); + + // To do not spam onTextAccepted() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } std::string TextInputDialog::getTextInput() const diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 3871baa09d..f461d5f50b 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -367,6 +367,9 @@ namespace MWGui void TradeWindow::onAccept(MyGUI::EditBox *sender) { onOfferButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6007846fad..e332f4f62a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2091,6 +2091,11 @@ namespace MWGui return true; } + bool WindowManager::injectKeyRelease(MyGUI::KeyCode key) + { + return MyGUI::InputManager::getInstance().injectKeyRelease(key); + } + void WindowManager::GuiModeState::update(bool visible) { for (unsigned int i=0; i Date: Mon, 10 Sep 2018 15:18:07 +0400 Subject: [PATCH 008/150] Use drag and drop for ActionTake when InventoryWindow is active (bug #4543) --- CHANGELOG.md | 1 + apps/openmw/mwworld/actiontake.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900b..7034202e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4531: Movement does not reset idle animations Bug #4539: Paper Doll is affected by GUI scaling + Bug #4543: Picking cursed items through inventory (menumode) makes it disappear Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index d858859a69..a173e87fbc 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -5,6 +5,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwgui/inventorywindow.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -14,6 +16,17 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + // When in GUI mode, we should use drag and drop + if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if (mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) + { + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(getTarget()); + return; + } + } + MWBase::Environment::get().getMechanicsManager()->itemTaken( actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount()); MWWorld::Ptr newitem = *actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); From e5a81b1f99bb6e23fb633c3fb03cb5145ba352aa Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 18:17:11 +0400 Subject: [PATCH 009/150] Fix some issues, found by Coverity Scan --- apps/openmw/mwmechanics/character.cpp | 4 +++- apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 12 +++++++++--- components/debug/debugging.hpp | 2 -- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fddd351fa6..69916b37af 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2145,9 +2145,11 @@ void CharacterController::update(float duration) if (isTurning()) { // Adjust animation speed from 1.0 to 1.5 multiplier - float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); if (duration > 0) + { + float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f)); + } } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1ace2f8b4b..7410dbf037 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -219,6 +219,7 @@ namespace RemoveFinishedCallbackVisitor(int effectId) : RemoveVisitor() + , mHasMagicEffects(false) , mEffectId(effectId) { } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 48cb1dda3c..9e08a25637 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1159,11 +1159,17 @@ namespace MWWorld osg::Vec3f vec(x, y, z); - CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + CellStore *currCell = ptr.isInCell() ? ptr.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell)); MWWorld::Ptr newPtr = ptr; + if (!isPlayer && !currCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: current cell is nullptr"); + + if (!newCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: new cell is nullptr"); + if (currCell != newCell) { removeContainerScripts(ptr); @@ -1185,10 +1191,10 @@ namespace MWWorld addContainerScripts (getPlayerPtr(), newCell); newPtr = getPlayerPtr(); } - else if (currCell) + else { bool currCellActive = mWorldScene->isCellActive(*currCell); - bool newCellActive = newCell && mWorldScene->isCellActive(*newCell); + bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { newPtr = currCell->moveTo(ptr, newCell); diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 59536d685c..f47f58e451 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -41,8 +41,6 @@ namespace Debug { return size; } - - char mDebugLevel; }; #if defined(_WIN32) && defined(_DEBUG) From d5bcc49079c6b4d229f403816bbacb1ddce9a64d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 19:54:22 +0400 Subject: [PATCH 010/150] Initialize missing struct fields --- apps/essimporter/converter.hpp | 5 ++++- apps/essimporter/importgame.hpp | 14 +++++++------- apps/openmw/mwsound/openal_output.cpp | 3 ++- apps/openmw/mwsound/openal_output.hpp | 4 ++-- components/esm/loadcell.hpp | 8 ++++---- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 1772e0e2d0..2694ea5707 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -526,7 +526,10 @@ public: class ConvertGAME : public Converter { public: - ConvertGAME() : mHasGame(false) {} + ConvertGAME() + : mHasGame(false) + { + } virtual void read(ESM::ESMReader &esm) { diff --git a/apps/essimporter/importgame.hpp b/apps/essimporter/importgame.hpp index fca7d72a0a..d8051a527a 100644 --- a/apps/essimporter/importgame.hpp +++ b/apps/essimporter/importgame.hpp @@ -14,13 +14,13 @@ namespace ESSImport { struct GMDT { - char mCellName[64]; - int mFogColour; - float mFogDensity; - int mCurrentWeather, mNextWeather; - int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage - float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition - int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage + char mCellName[64] {}; + int mFogColour {0}; + float mFogDensity {0.f}; + int mCurrentWeather {0}, mNextWeather {0}; + int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition + int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage }; GMDT mGMDT; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 42dd6b5fda..833b40e385 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1483,7 +1483,8 @@ void OpenAL_Output::resumeSounds(int types) OpenAL_Output::OpenAL_Output(SoundManager &mgr) - : Sound_Output(mgr), mDevice(0), mContext(0) + : Sound_Output(mgr) + , mDevice(0), mContext(0) , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) , mWaterFilter(0), mWaterEffect(0), mDefaultEffect(0), mEffectSlot(0) , mStreamThread(new StreamThread) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index afc8697331..b6a26c99a8 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -26,10 +26,10 @@ namespace MWSound struct { bool EXT_EFX : 1; bool SOFT_HRTF : 1; - } ALC; + } ALC = {false, false}; struct { bool SOFT_source_spatialize : 1; - } AL; + } AL = {false}; typedef std::deque IDDq; IDDq mFreeSources; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 249d812b10..bc50167189 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,14 +78,14 @@ struct Cell struct DATAstruct { - int mFlags; - int mX, mY; + int mFlags {0}; + int mX {0}, mY {0}; }; struct AMBIstruct { - Color mAmbient, mSunlight, mFog; - float mFogDensity; + Color mAmbient {0}, mSunlight {0}, mFog {0}; + float mFogDensity {0.f}; }; Cell() : mName(""), From a262e4b342a5c751311653c7a28950c4ae3472bb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 20:20:41 +0400 Subject: [PATCH 011/150] Print warning, if can not close or remove temporary file --- components/crashcatcher/crashcatcher.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 04b239a7fa..64824b6b35 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -175,17 +175,18 @@ static void gdb_info(pid_t pid) fflush(stdout); /* Clean up */ - remove(respfile); + if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } else { /* Error creating temp file */ if(fd >= 0) { - if (close(fd) == 0) - remove(respfile); - else - Log(Debug::Warning) << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno); + if (close(fd) != 0) + Log(Debug::Warning) << "Warning: can not close file '" << respfile << "': " << std::strerror(errno); + else if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } printf("!!! Could not create gdb command file\n"); } From aca6625af41957d4624944f15d2cf714b2bbde0d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 23:04:42 +0400 Subject: [PATCH 012/150] Avoid possible memory leak by using the unique_ptr --- components/files/constrainedfilestream.cpp | 15 +++++---------- components/files/constrainedfilestream.hpp | 8 +++++--- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/components/files/constrainedfilestream.cpp b/components/files/constrainedfilestream.cpp index b239ec6a17..419af0d6c2 100644 --- a/components/files/constrainedfilestream.cpp +++ b/components/files/constrainedfilestream.cpp @@ -103,21 +103,16 @@ namespace Files }; - ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length) - : std::istream(new ConstrainedFileStreamBuf(filename, start, length)) + ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr buf) + : std::istream(buf.get()) + , mBuf(std::move(buf)) { - } - ConstrainedFileStream::~ConstrainedFileStream() - { - delete rdbuf(); - } - - IStreamPtr openConstrainedFileStream(const char *filename, size_t start, size_t length) { - return IStreamPtr(new ConstrainedFileStream(filename, start, length)); + auto buf = std::unique_ptr(new ConstrainedFileStreamBuf(filename, start, length)); + return IStreamPtr(new ConstrainedFileStream(std::move(buf))); } } diff --git a/components/files/constrainedfilestream.hpp b/components/files/constrainedfilestream.hpp index 05ae0fbecc..bf67c7b973 100644 --- a/components/files/constrainedfilestream.hpp +++ b/components/files/constrainedfilestream.hpp @@ -11,9 +11,11 @@ namespace Files class ConstrainedFileStream : public std::istream { public: - ConstrainedFileStream(const char *filename, - size_t start=0, size_t length=0xFFFFFFFF); - virtual ~ConstrainedFileStream(); + ConstrainedFileStream(std::unique_ptr buf); + virtual ~ConstrainedFileStream() {}; + +private: + std::unique_ptr mBuf; }; typedef std::shared_ptr IStreamPtr; From c2c24a76a4914af9c5a857368fcc64df6d486e68 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 9 Sep 2018 16:06:25 +0400 Subject: [PATCH 013/150] Handle MyGUI exceptions inside destructors --- apps/openmw/mwgui/keyboardnavigation.cpp | 11 ++++- apps/openmw/mwgui/layout.hpp | 14 ++++++- apps/openmw/mwgui/screenfader.cpp | 9 ++++- apps/openmw/mwgui/windowmanagerimp.cpp | 51 ++++++++++++++---------- components/fontloader/fontloader.cpp | 12 +++++- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index cde8a17ccd..96970b39fe 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -49,7 +51,14 @@ KeyboardNavigation::KeyboardNavigation() KeyboardNavigation::~KeyboardNavigation() { - MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + try + { + MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void KeyboardNavigation::saveFocus(int mode) diff --git a/apps/openmw/mwgui/layout.hpp b/apps/openmw/mwgui/layout.hpp index 0e7cf3faa7..ea51bf541e 100644 --- a/apps/openmw/mwgui/layout.hpp +++ b/apps/openmw/mwgui/layout.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace MWGui { /** The Layout class is an utility class used to load MyGUI layouts @@ -16,7 +18,17 @@ namespace MWGui Layout(const std::string & _layout, MyGUI::Widget* _parent = nullptr) : mMainWidget(nullptr) { initialise(_layout, _parent); } - virtual ~Layout() { shutdown(); } + virtual ~Layout() + { + try + { + shutdown(); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } MyGUI::Widget* getWidget(const std::string& _name); diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index 9a9a1ae01a..e75e4954e4 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -99,7 +99,14 @@ namespace MWGui ScreenFader::~ScreenFader() { - MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + try + { + MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void ScreenFader::onFrameStart(float dt) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 45897b88cf..6917e2e8ad 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -518,35 +518,42 @@ namespace MWGui WindowManager::~WindowManager() { - mKeyboardNavigation.reset(); + try + { + mKeyboardNavigation.reset(); - MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); - MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); - MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); + MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); + MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); + MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); - for (WindowBase* window : mWindows) - delete window; - mWindows.clear(); + for (WindowBase* window : mWindows) + delete window; + mWindows.clear(); - delete mMessageBoxManager; - delete mLocalMapRender; - delete mCharGen; - delete mDragAndDrop; - delete mSoulgemDialog; - delete mCursorManager; - delete mToolTips; + delete mMessageBoxManager; + delete mLocalMapRender; + delete mCharGen; + delete mDragAndDrop; + delete mSoulgemDialog; + delete mCursorManager; + delete mToolTips; - cleanupGarbage(); + cleanupGarbage(); - mFontLoader.reset(); + mFontLoader.reset(); - mGui->shutdown(); - delete mGui; + mGui->shutdown(); + delete mGui; - mGuiPlatform->shutdown(); - delete mGuiPlatform; + mGuiPlatform->shutdown(); + delete mGuiPlatform; + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void WindowManager::setStore(const MWWorld::ESMStore &store) diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 790df7fa84..37214a038c 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -161,7 +161,17 @@ namespace Gui mTextures.clear(); for (std::vector::iterator it = mFonts.begin(); it != mFonts.end(); ++it) - MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + { + try + { + MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } + mFonts.clear(); } From 9918212a1e4f974f243e41c0026f67bbddf8b84e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 10 Sep 2018 10:51:36 +0400 Subject: [PATCH 014/150] Set focus to Close button when opening the container window (bug #4333) --- CHANGELOG.md | 1 + apps/openmw/mwgui/container.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900b..105ec9f821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching + Bug #4333: Keyboard navigation in containers is not intuitive Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default Bug #4378: On-self absorb spells restore stats diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 703ba309e5..99d0ea0d13 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -174,6 +174,8 @@ namespace MWGui if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) return; + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); @@ -219,6 +221,8 @@ namespace MWGui { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + onTakeAllButtonClicked(mTakeButton); if (mPtr.getClass().isPersistent(mPtr)) From f0919f51e93e7dd6811074c63188563c2b88bdaa Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 11 Sep 2018 14:05:44 +0400 Subject: [PATCH 015/150] Fix integer result formatting of scripting functions --- components/compiler/lineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 602bb826fb..c2c1dff9b0 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -38,7 +38,7 @@ namespace Compiler { case 'l': - Generator::report (mCode, mLiterals, "%g"); + Generator::report (mCode, mLiterals, "%d"); break; case 'f': From 0440c11ccd0dbf9e12fa67805acb595f8beaa883 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 17:31:56 +0400 Subject: [PATCH 016/150] Fix swim crossbow animations --- apps/openmw/mwmechanics/character.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fddd351fa6..b17b8b20cf 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -423,6 +423,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character movementAnimName = weap->shortgroup + movementAnimName; else movementAnimName += weap->shortgroup; + if(!mAnimation->hasAnimation(movementAnimName)) { movemask = MWRender::Animation::BlendMask_LowerBody; @@ -451,6 +452,10 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } else { + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; + movementAnimName.erase(swimpos, 4); if (weap != sWeaponTypeListEnd) { From 929d78d6a3195fb826e90acf670ea2198efc11c6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 21:52:05 +0400 Subject: [PATCH 017/150] Randomize attacks for non-bipedal creatures with Weapon flag --- apps/openmw/mwmechanics/character.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b17b8b20cf..f6b6a93e62 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1399,6 +1399,14 @@ bool CharacterController::updateWeaponState() MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; + // Randomize attacks for non-bipedal creatures with Weapon flag + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && + !mPtr.getClass().isBipedal(mPtr) && + (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) + { + mCurrentWeapon = chooseRandomAttackAnimation(); + } + if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would From 0136f0552bebf7773a4a1f9d82a37c357060d38e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 22:04:02 +0400 Subject: [PATCH 018/150] Do not update mIdleState directly --- apps/openmw/mwmechanics/character.cpp | 118 ++++++++++++++++---------- apps/openmw/mwmechanics/character.hpp | 8 +- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f6b6a93e62..6b689deaae 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -243,7 +243,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i return prefix + toString(roll); } -void CharacterController::refreshHitRecoilAnims() +void CharacterController::refreshHitRecoilAnims(CharacterState& idle) { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); @@ -348,14 +348,16 @@ void CharacterController::refreshHitRecoilAnims() mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); } if (mHitState != CharState_None) - mIdleState = CharState_None; + idle = CharState_None; } -void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force) +void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force) { if(force || jump != mJumpState) { - mIdleState = CharState_None; + if (jump != JumpState_None) + idle = CharState_None; + bool startAtLoop = (jump == mJumpState); mJumpState = jump; @@ -372,6 +374,11 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jumpmask = MWRender::Animation::BlendMask_LowerBody; jumpAnimName = "jump"; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; + // For crossbow animations use 1h ones as fallback if (mWeaponType == WeapType_Crossbow) jumpAnimName += "1h"; @@ -406,20 +413,21 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState } } -void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force) +void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force) { - if(force || movement != mMovementState) + std::string movementAnimName; + MWRender::Animation::BlendMask movemask; + const StateInfo *movestate; + if(force || movement != mMovementState || idle != mIdleState) { - mMovementState = movement; - std::string movementAnimName; - MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; - const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); + movemask = MWRender::Animation::BlendMask_All; + movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); if(movestate != sMovementListEnd) { movementAnimName = movestate->groupname; if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) { - if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case movementAnimName = weap->shortgroup + movementAnimName; else movementAnimName += weap->shortgroup; @@ -429,12 +437,24 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName = movestate->groupname; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; + // For crossbow animations use 1h ones as fallback if (mWeaponType == WeapType_Crossbow) movementAnimName += "1h"; } } + } + } + if(force || movement != mMovementState) + { + mMovementState = movement; + if(movestate != sMovementListEnd) + { if(!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type swimpos = movementAnimName.find("swim"); @@ -532,7 +552,15 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) { - if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) + // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), + // the idle animation should be displayed + if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) + || (mMovementState != CharState_None && !isTurning()) + || mHitState != CharState_None) + && !mPtr.getClass().isBipedal(mPtr)) + idle = CharState_None; + + if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) { mIdleState = idle; size_t numLoops = ~0ul; @@ -591,24 +619,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat return; if (mPtr.getClass().isActor()) - refreshHitRecoilAnims(); + refreshHitRecoilAnims(idle); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; - refreshJumpAnims(weap, jump, force); - refreshMovementAnims(weap, movement, force); + refreshJumpAnims(weap, jump, idle, force); + refreshMovementAnims(weap, movement, idle, force); // idle handled last as it can depend on the other states - // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), - // the idle animation should be displayed - if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) - || (mMovementState != CharState_None && !isTurning()) - || mHitState != CharState_None) - && !mPtr.getClass().isBipedal(mPtr)) - idle = CharState_None; - refreshIdleAnims(weap, idle, force); } @@ -1200,7 +1220,7 @@ bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const } } -bool CharacterController::updateWeaponState() +bool CharacterController::updateWeaponState(CharacterState& idle) { const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); @@ -1570,11 +1590,11 @@ bool CharacterController::updateWeaponState() // We should reset player's idle animation in the first-person mode. if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) - mIdleState = CharState_None; + idle = CharState_None; // In other cases we should not break swim and sneak animations if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) - mIdleState = CharState_None; + idle = CharState_None; animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) @@ -2085,12 +2105,18 @@ void CharacterController::update(float duration) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) + else if(rot.z() != 0.0f) { - if(rot.z() > rotationThreshold) - movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - else if(rot.z() < -rotationThreshold) - movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + // It seems only bipedal actors use turning animations. + // Also do not use turning animations in the first-person view and when sneaking. + bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson(); + if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) + { + if(rot.z() > rotationThreshold) + movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; + else if(rot.z() < -rotationThreshold) + movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + } } } @@ -2108,16 +2134,21 @@ void CharacterController::update(float duration) } else { - mTurnAnimationThreshold -= duration; - if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || - movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + if (mPtr.getClass().isBipedal(mPtr)) { - mTurnAnimationThreshold = 0.05f; - } - else if (movestate == CharState_None && isTurning() - && mTurnAnimationThreshold > 0) - { - movestate = mMovementState; + if (mTurnAnimationThreshold > 0) + mTurnAnimationThreshold -= duration; + + if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || + movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + { + mTurnAnimationThreshold = 0.05f; + } + else if (movestate == CharState_None && isTurning() + && mTurnAnimationThreshold > 0) + { + movestate = mMovementState; + } } } @@ -2126,13 +2157,12 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { - // Note: turning animations should not interrupt idle ones. - // Also movement should not stop idle animation for spellcasting stance. + // Note: turning animations should not interrupt idle ones if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell) + else if (movestate != CharState_None && !isTurning()) idlestate = CharState_None; else idlestate = CharState_Idle; @@ -2144,7 +2174,7 @@ void CharacterController::update(float duration) { // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used. if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr)) - forcestateupdate = updateWeaponState() || forcestateupdate; + forcestateupdate = updateWeaponState(idlestate) || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 43d26e52f3..8b3c68048d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -213,14 +213,14 @@ class CharacterController : public MWRender::Animation::TextKeyListener void setAttackTypeBasedOnMovement(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); - void refreshHitRecoilAnims(); - void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force=false); - void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force=false); + void refreshHitRecoilAnims(CharacterState& idle); + void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false); + void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); void clearAnimQueue(bool clearPersistAnims = false); - bool updateWeaponState(); + bool updateWeaponState(CharacterState& idle); bool updateCreatureState(); void updateIdleStormState(bool inwater); From adbaeb7ccada43631f580c504ba01a0f2f6a874a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 18 Jun 2018 13:43:39 +0400 Subject: [PATCH 019/150] Improve GUI scaling (bug #3288) --- CHANGELOG.md | 1 + apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/bookpage.cpp | 45 +++++++------- apps/openmw/mwgui/bookpage.hpp | 46 ++++++++++++++ apps/openmw/mwgui/console.cpp | 6 -- apps/openmw/mwgui/console.hpp | 2 - apps/openmw/mwgui/formatting.cpp | 13 +--- apps/openmw/mwgui/formatting.hpp | 4 +- apps/openmw/mwgui/journalbooks.cpp | 28 ++++++--- apps/openmw/mwgui/journalbooks.hpp | 6 +- apps/openmw/mwgui/journalwindow.cpp | 22 ++++++- apps/openmw/mwgui/merchantrepair.cpp | 11 ++-- apps/openmw/mwgui/merchantrepair.hpp | 2 - apps/openmw/mwgui/review.cpp | 23 +++---- apps/openmw/mwgui/review.hpp | 2 - apps/openmw/mwgui/spellbuyingwindow.cpp | 10 ++-- apps/openmw/mwgui/spellbuyingwindow.hpp | 2 - apps/openmw/mwgui/spellview.cpp | 5 +- apps/openmw/mwgui/statswindow.cpp | 18 +++--- apps/openmw/mwgui/statswindow.hpp | 2 - apps/openmw/mwgui/tooltips.cpp | 7 ++- apps/openmw/mwgui/travelwindow.cpp | 10 ++-- apps/openmw/mwgui/travelwindow.hpp | 2 - apps/openmw/mwgui/windowmanagerimp.cpp | 60 ++++++++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 5 ++ apps/openmw/mwrender/localmap.cpp | 5 ++ components/CMakeLists.txt | 2 +- components/widgets/box.cpp | 9 +-- components/widgets/box.hpp | 26 +++++++- components/widgets/fontwrapper.hpp | 45 ++++++++++++++ components/widgets/numericeditbox.hpp | 7 ++- components/widgets/sharedstatebutton.cpp | 1 - components/widgets/sharedstatebutton.hpp | 4 +- components/widgets/widgets.cpp | 3 + components/widgets/widgets.hpp | 3 +- .../source/reference/modding/settings/GUI.rst | 21 +++++++ files/mygui/core.skin | 1 - files/mygui/openmw_confirmation_dialog.layout | 2 +- files/mygui/openmw_enchanting_dialog.layout | 2 +- files/mygui/openmw_font.xml | 2 - files/mygui/openmw_inventory_window.layout | 2 +- files/mygui/openmw_journal.layout | 5 +- files/mygui/openmw_journal.skin.xml | 6 +- files/mygui/openmw_list.skin.xml | 4 -- files/mygui/openmw_windows.skin.xml | 8 +-- files/settings-default.cfg | 6 ++ 46 files changed, 352 insertions(+), 145 deletions(-) create mode 100644 components/widgets/fontwrapper.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cb90afd0..37bf5e660e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly + Bug #3288: TrueType fonts are handled incorrectly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work Bug #3533: GetSpellEffects should detect effects with zero duration diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39eed55379..b40f517f91 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -219,6 +219,7 @@ namespace MWBase virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual const MWWorld::Ptr& getSelectedWeapon() const = 0; + virtual int getFontHeight() const = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 6f16cf0769..8f2e8b5ed6 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1,6 +1,5 @@ #include "bookpage.hpp" -#include "MyGUI_FontManager.h" #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" @@ -8,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { struct TypesetBookImpl; @@ -497,9 +499,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - space_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -507,9 +509,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - word_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -530,6 +532,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mPartialWhitespace.empty() && mPartialWord.empty()) return; + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); int space_width = 0; int word_width = 0; @@ -549,9 +552,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + line_height); + append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -560,9 +562,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + line_height); + append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -756,32 +757,32 @@ namespace void emitGlyph (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (!gi) + if (info.codePoint < 0) return; MyGUI::FloatRect vr; - vr.left = mCursor.left + gi->bearingX; - vr.top = mCursor.top + gi->bearingY; - vr.right = vr.left + gi->width; - vr.bottom = vr.top + gi->height; + vr.left = mCursor.left + info.bearingX; + vr.top = mCursor.top + info.bearingY; + vr.right = vr.left + info.width; + vr.bottom = vr.top + info.height; - MyGUI::FloatRect tr = gi->uvRect; + MyGUI::FloatRect tr = info.uvRect; if (mRenderXform.clip (vr, tr)) quad (vr, tr); - mCursor.left += gi->bearingX + gi->advance; + mCursor.left += static_cast(info.bearingX + info.advance); } void emitSpace (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (gi) - mCursor.left += gi->bearingX + gi->advance; + if (info.codePoint >= 0) + mCursor.left += static_cast(info.bearingX + info.advance); } private: diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 66d1834c78..b8174bd3dc 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -3,10 +3,17 @@ #include "MyGUI_Colour.h" #include "MyGUI_Widget.h" +#include "MyGUI_FontManager.h" #include #include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { /// A formatted and paginated document to be used with @@ -28,6 +35,45 @@ namespace MWGui virtual std::pair getSize () const = 0; }; + struct GlyphInfo + { + char codePoint; + float width; + float height; + float advance; + float bearingX; + float bearingY; + MyGUI::FloatRect uvRect; + + GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) + { + static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch); + if (gi) + { + const float scale = font->getDefaultHeight() / (float) fontHeight; + + codePoint = gi->codePoint; + bearingX = (int) gi->bearingX / scale; + bearingY = (int) gi->bearingY / scale; + width = (int) gi->width / scale; + height = (int) gi->height / scale; + advance = (int) gi->advance / scale; + uvRect = gi->uvRect; + } + else + { + codePoint = -1; + bearingX = 0; + bearingY = 0; + width = 0; + height = 0; + advance = 0; + } + } + }; + /// A factory class for creating a typeset book instance. struct BookTypesetter { diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b367c6f492..dc22e4193b 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -152,12 +152,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::setFont(const std::string &fntName) - { - mHistory->setFontName(fntName); - mCommandLine->setFontName(fntName); - } - void Console::print(const std::string &msg, const std::string& color) { mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg)); diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index bbff34c8da..883bc8967d 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -41,8 +41,6 @@ namespace MWGui virtual void onOpen(); - void setFont(const std::string &fntName); - void onResChange(int width, int height); // Print a message to the console, in specified color. diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 663bd73380..157aeba26c 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -415,7 +415,7 @@ namespace MWGui : GraphicElement(parent, pag, blockStyle), mTextStyle(textStyle) { - MyGUI::EditBox* box = parent->createWidget("NormalText", + Gui::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); box->setEditStatic(true); @@ -432,15 +432,6 @@ namespace MWGui mEditBox = box; } - int TextElement::currentFontHeight() const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); - if (!font) - return 0; - return font->getDefaultHeight(); - } - int TextElement::getHeight() { return mEditBox->getTextSize().height; @@ -449,7 +440,7 @@ namespace MWGui int TextElement::pageSplit() { // split lines - const int lineHeight = currentFontHeight(); + const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()); if (lineHeight > 0) lastLine /= lineHeight; diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index d525baacec..7ccb0824e3 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace MWGui { namespace Formatting @@ -152,7 +154,7 @@ namespace MWGui private: int currentFontHeight() const; TextStyle mTextStyle; - MyGUI::EditBox * mEditBox; + Gui::EditBox * mEditBox; }; class ImageElement : public GraphicElement diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 1075239fa8..1956ee1c28 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -218,21 +218,24 @@ book JournalBooks::createQuestBook (const std::string& questName) return typesetter->complete (); } -book JournalBooks::createTopicIndexBook () +book JournalBooks::createTopicIndexBook (int& columnsCount) { bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); + BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex(columnsCount) : createLatinJournalIndex(columnsCount); return typesetter->complete (); } -BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () +BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + // Latin journal index always has two columns for now. + columnsCount = 2; + char ch = 'A'; BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); @@ -258,14 +261,25 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () return typesetter; } -BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () +BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters). + int sectionBreak = 10; + columnsCount = 3; + if (fontHeight < 18) + { + sectionBreak = 15; + columnsCount = 2; + } + unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 for (int i = 0; i < 32; ++i) @@ -287,7 +301,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () if (i == 26 || i == 28) continue; - if (i == 15) + if (i % sectionBreak == 0) typesetter->sectionBreak (); typesetter->write (style, to_utf8_span (buffer)); diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index aa36eecdff..cc6f42cc8b 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -22,14 +22,14 @@ namespace MWGui Book createTopicBook (uintptr_t topicId); Book createTopicBook (const std::string& topicId); Book createQuestBook (const std::string& questName); - Book createTopicIndexBook (); + Book createTopicIndexBook (int& columnsCount); ToUTF8::FromType mEncoding; private: BookTypesetter::Ptr createTypesetter (); - BookTypesetter::Ptr createLatinJournalIndex (); - BookTypesetter::Ptr createCyrillicJournalIndex (); + BookTypesetter::Ptr createLatinJournalIndex (int& columnsCount); + BookTypesetter::Ptr createCyrillicJournalIndex (int& columnsCount); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 0776706d8c..cef20159df 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -43,6 +43,7 @@ namespace static char const LeftBookPage [] = "LeftBookPage"; static char const RightBookPage [] = "RightBookPage"; static char const LeftTopicIndex [] = "LeftTopicIndex"; + static char const CenterTopicIndex [] = "CenterTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex"; struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow @@ -148,6 +149,7 @@ namespace callback = std::bind(&JournalWindowImpl::notifyIndexLinkClicked, this, std::placeholders::_1); getPage (LeftTopicIndex)->adviseLinkClicked (callback); + getPage (CenterTopicIndex)->adviseLinkClicked (callback); getPage (RightTopicIndex)->adviseLinkClicked (callback); } @@ -312,6 +314,7 @@ namespace setVisible (TopicsList, false); setVisible (QuestsList, mQuestMode); setVisible (LeftTopicIndex, !mQuestMode); + setVisible (CenterTopicIndex, !mQuestMode); setVisible (RightTopicIndex, !mQuestMode); setVisible (ShowAllBTN, mQuestMode && !mAllQuests); setVisible (ShowActiveBTN, mQuestMode && mAllQuests); @@ -462,11 +465,21 @@ namespace { setOptionsMode (); + int pagesCount; if (!mTopicIndexBook) - mTopicIndexBook = createTopicIndexBook (); + mTopicIndexBook = createTopicIndexBook (pagesCount); - getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); - getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + if (pagesCount == 3) + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 2); + } + else + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + } } void notifyJournal(MyGUI::Widget* _sender) @@ -480,6 +493,7 @@ namespace void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId index) { setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, false); setVisible (RightTopicIndex, false); setVisible (TopicsList, true); @@ -502,6 +516,7 @@ namespace mQuestMode = false; mTopicsMode = false; setVisible (LeftTopicIndex, true); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, true); setVisible (TopicsList, false); setVisible (QuestsList, false); @@ -540,6 +555,7 @@ namespace mQuestMode = true; setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, false); setVisible (TopicsList, false); setVisible (QuestsList, true); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 9974c6d16e..b9d4c80f4f 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -20,8 +20,6 @@ namespace MWGui { -const int MerchantRepair::sLineHeight = 18; - MerchantRepair::MerchantRepair() : WindowBase("openmw_merchantrepair.layout") { @@ -39,6 +37,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) while (mList->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -67,28 +66,26 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); - std::string name = iter->getClass().getName(*iter) + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() .find("sgp")->mValue.getString(); - MyGUI::Button* button = mList->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, currentY, 0, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - currentY += sLineHeight; + currentY += lineHeight; button->setUserString("Price", MyGUI::utility::toString(price)); button->setUserData(MWWorld::Ptr(*iter)); button->setCaptionWithReplacing(name); - button->setSize(mList->getWidth(),sLineHeight); + button->setSize(mList->getWidth(), lineHeight); button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); button->setUserString("ToolTipType", "ItemPtr"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 4e4e7164fd..26887ae2ca 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -27,8 +27,6 @@ protected: void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onRepairButtonClick(MyGUI::Widget* sender); void onOkButtonClick(MyGUI::Widget* sender); - - static const int sLineHeight; }; } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 61a7a2ac0a..b59b1582cf 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -26,9 +26,6 @@ namespace namespace MWGui { - - const int ReviewDialog::sLineHeight = 18; - ReviewDialog::ReviewDialog() : WindowModal("openmw_chargen_review.layout"), mUpdateSkillArea(false) @@ -261,8 +258,9 @@ namespace MWGui groupWidget->setCaption(label); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -282,8 +280,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillValueWidget; } @@ -298,8 +297,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) @@ -312,8 +312,9 @@ namespace MWGui mSkillWidgets.push_back(widget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 8e9ec0ec79..f46ad280d5 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -87,8 +87,6 @@ namespace MWGui void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void updateSkillArea(); - static const int sLineHeight; - MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; MyGUI::ScrollView* mSkillView; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9876013f13..bc5d161d83 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -18,8 +18,6 @@ namespace MWGui { - const int SpellBuyingWindow::sLineHeight = 18; - SpellBuyingWindow::SpellBuyingWindow() : WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) @@ -52,21 +50,23 @@ namespace MWGui // TODO: refactor to use MyGUI::ListBox + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + MyGUI::Button* toAdd = mSpellsView->createWidget( price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, mCurrentY, 200, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; toAdd->setUserData(price); toAdd->setCaptionWithReplacing(spell.mName+" - "+MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mSpellsView->getWidth(),sLineHeight); + toAdd->setSize(mSpellsView->getWidth(), lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); toAdd->setUserString("Spell", spell.mId); diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 3414c1b940..30727a545d 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -48,8 +48,6 @@ namespace MWGui void clearSpells(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 758e6b306a..702c2f8409 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "tooltips.hpp" @@ -240,7 +241,7 @@ namespace MWGui mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); } - MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget->setCaptionWithReplacing(label); @@ -249,7 +250,7 @@ namespace MWGui if (label2 != "") { - MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget2->setCaptionWithReplacing(label2); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index db73351bfd..dfa0294670 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -23,9 +23,6 @@ namespace MWGui { - - const int StatsWindow::sLineHeight = 18; - StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) @@ -376,8 +373,9 @@ namespace MWGui groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } std::pair StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -401,8 +399,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return std::make_pair(skillNameWidget, skillValueWidget); } @@ -421,8 +420,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillNameWidget; } diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 487953e002..8dab2f3d99 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -53,8 +53,6 @@ namespace MWGui void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - static const int sLineHeight; - MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 20814aac50..0bd0d191cc 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -421,7 +422,7 @@ namespace MWGui std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image); - MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); captionWidget->setEditStatic(true); captionWidget->setNeedKeyFocus(false); captionWidget->setCaptionWithReplacing(caption); @@ -429,7 +430,7 @@ namespace MWGui int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + Gui::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); textWidget->setEditStatic(true); textWidget->setEditMultiLine(true); textWidget->setEditWordWrap(info.wordWrap); @@ -447,7 +448,7 @@ namespace MWGui MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget("MarkerButton", MyGUI::IntCoord(padding.left, totalSize.height+padding.top, 8, 8), MyGUI::Align::Default); icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f)); - MyGUI::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", + Gui::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(padding.left+8+4, totalSize.height+padding.top, 300-padding.left-8-4, 300-totalSize.height), MyGUI::Align::Default); edit->setEditMultiLine(true); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 23a5b322fd..5f5430524f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -22,8 +22,6 @@ namespace MWGui { - const int TravelWindow::sLineHeight = 18; - TravelWindow::TravelWindow() : WindowBase("openmw_travel_window.layout") , mCurrentY(0) @@ -79,9 +77,11 @@ namespace MWGui else price *= std::max(1, static_cast(followers.size())); - MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + + MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, lineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; if(interior) toAdd->setUserString("interior","y"); else @@ -92,7 +92,7 @@ namespace MWGui toAdd->setUserString("price",oss.str()); toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mDestinationsView->getWidth(),sLineHeight); + toAdd->setSize(mDestinationsView->getWidth(),lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", name); toAdd->setUserData(pos); diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 5ae4660475..dcf0b77272 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -41,8 +41,6 @@ namespace MWGui void clearDestinations(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6917e2e8ad..493a14a1cb 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -44,7 +44,6 @@ #include -#include #include #include @@ -196,6 +195,7 @@ namespace MWGui , mFallbackMap(fallbackMap) , mShowOwned(0) , mEncoding(encoding) + , mFontHeight(16) , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -233,6 +233,13 @@ namespace MWGui SpellView::registerComponents(); Gui::registerAllWidgets(); + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontHeight = fontSize; + + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate); + MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); @@ -284,6 +291,51 @@ namespace MWGui mShowOwned = Settings::Manager::getInt("show owned", "Game"); } + void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + { + MyGUI::xml::ElementEnumerator root = _node->getElementEnumerator(); + while (root.next("Resource")) + { + std::string type, name; + root->findAttribute("type", type); + root->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML file. + // Also we should take UI scaling factor in account + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::max(0, resolution); + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + + if (uiScale > 1.0f) + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = root->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = root->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + } + else if (Misc::StringUtils::ciEqual(type, "ResourceSkin")) + { + // We should adjust line height for MyGUI widgets depending on font size + MyGUI::xml::ElementPtr heightNode = root->createChild("Property"); + heightNode->addAttribute("key", "HeightLine"); + heightNode->addAttribute("value", std::to_string(mFontHeight+2)); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + } + void WindowManager::initUI() { // Get size info from the Gui object @@ -504,6 +556,11 @@ namespace MWGui updateVisible(); } + int WindowManager::getFontHeight() const + { + return mFontHeight; + } + void WindowManager::setNewGame(bool newgame) { if (newgame) @@ -522,6 +579,7 @@ namespace MWGui { mKeyboardNavigation.reset(); + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 657548397c..f3bf6d00f3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -246,6 +246,7 @@ namespace MWGui virtual const MWWorld::Ptr& getSelectedEnchantItem() const; virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual const MWWorld::Ptr& getSelectedWeapon() const; + virtual int getFontHeight() const; virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -401,6 +402,8 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; + void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). @@ -513,6 +516,8 @@ namespace MWGui ToUTF8::FromType mEncoding; + int mFontHeight; + std::string mVersionDescription; MWGui::TextColours mTextColours; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5e501ecf86..8ed3441de6 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -76,6 +76,11 @@ LocalMap::LocalMap(osg::Group* root) , mAngle(0.f) , mInterior(false) { + // Increase map resolution, if use UI scaling + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale > 1.0) + mMapResolution *= uiScale; + SceneUtil::FindByNameVisitor find("Scene Root"); mRoot->accept(find); mSceneRoot = find.mFoundNode; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 551767f8f0..8e798455f5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -130,7 +130,7 @@ add_component_dir (myguiplatform ) add_component_dir (widgets - box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets + box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index bf18cef5b9..d38410a2c9 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -48,7 +48,7 @@ namespace Gui } else { - TextBox::setPropertyOverride (_key, _value); + Gui::TextBox::setPropertyOverride (_key, _value); } } @@ -81,11 +81,10 @@ namespace Gui } else { - EditBox::setPropertyOverride (_key, _value); + Gui::EditBox::setPropertyOverride (_key, _value); } } - MyGUI::IntSize AutoSizedButton::getRequestedSize() { MyGUI::IntSize padding(24, 8); @@ -111,16 +110,14 @@ namespace Gui } else { - Button::setPropertyOverride (_key, _value); + Gui::Button::setPropertyOverride (_key, _value); } } - Box::Box() : mSpacing(4) , mPadding(0) , mAutoResize(false) { - } void Box::notifyChildrenSizeChanged () diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 66be007191..9bab575c48 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -4,10 +4,27 @@ #include #include #include +#include #include +#include "fontwrapper.hpp" + namespace Gui { + class Button : public FontWrapper + { + MYGUI_RTTI_DERIVED( Button ) + }; + + class TextBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( TextBox ) + }; + + class EditBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( EditBox ) + }; class AutoSizedWidget { @@ -22,7 +39,7 @@ namespace Gui MyGUI::Align mExpandDirection; }; - class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox + class AutoSizedTextBox : public AutoSizedWidget, public TextBox { MYGUI_RTTI_DERIVED( AutoSizedTextBox ) @@ -32,9 +49,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox + class AutoSizedEditBox : public AutoSizedWidget, public EditBox { MYGUI_RTTI_DERIVED( AutoSizedEditBox ) @@ -47,9 +65,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button + class AutoSizedButton : public AutoSizedWidget, public Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) @@ -59,6 +78,7 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; /** diff --git a/components/widgets/fontwrapper.hpp b/components/widgets/fontwrapper.hpp new file mode 100644 index 0000000000..2424b22a7b --- /dev/null +++ b/components/widgets/fontwrapper.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_WIDGETS_WRAPPER_H +#define OPENMW_WIDGETS_WRAPPER_H + +#include "widgets.hpp" + +#include + +namespace Gui +{ + template + class FontWrapper : public T + { + public: + virtual void setFontName(const std::string& name) + { + T::setFontName(name); + T::setPropertyOverride ("FontHeight", mFontSize); + } + + protected: + FontWrapper() + { + // Note: we can not use the WindowManager here, so there is a code duplication a bit. + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontSize = std::to_string(fontSize); + } + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value) + { + T::setPropertyOverride (_key, _value); + + // There is a bug in MyGUI: when it initializes the FontName property, it reset the font height. + // We should restore it. + if (_key == "FontName") + { + T::setPropertyOverride ("FontHeight", mFontSize); + } + } + + std::string mFontSize; + }; +} + +#endif diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index ca7674f229..3edae2fc74 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -3,13 +3,15 @@ #include +#include "fontwrapper.hpp" + namespace Gui { /** * @brief A variant of the EditBox that only allows integer inputs */ - class NumericEditBox : public MyGUI::EditBox + class NumericEditBox : public FontWrapper { MYGUI_RTTI_DERIVED(NumericEditBox) @@ -17,7 +19,8 @@ namespace Gui NumericEditBox() : mValue(0), mMinValue(std::numeric_limits::min()), mMaxValue(std::numeric_limits::max()) - {} + { + } void initialiseOverride(); void shutdownOverride(); diff --git a/components/widgets/sharedstatebutton.cpp b/components/widgets/sharedstatebutton.cpp index 6859a3065a..f4456275bd 100644 --- a/components/widgets/sharedstatebutton.cpp +++ b/components/widgets/sharedstatebutton.cpp @@ -7,7 +7,6 @@ namespace Gui : mIsMousePressed(false) , mIsMouseFocus(false) { - } void SharedStateButton::shutdownOverride() diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 3d7fbc84e4..4143493967 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -3,6 +3,8 @@ #include +#include "fontwrapper.hpp" + namespace Gui { @@ -11,7 +13,7 @@ namespace Gui typedef std::vector ButtonGroup; /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup. - class SharedStateButton : public MyGUI::Button + class SharedStateButton : public FontWrapper { MYGUI_RTTI_DERIVED(SharedStateButton) diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index 92f2084dfa..c1a9a50531 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -18,9 +18,12 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/components/widgets/widgets.hpp b/components/widgets/widgets.hpp index d171321357..a5ba84b11e 100644 --- a/components/widgets/widgets.hpp +++ b/components/widgets/widgets.hpp @@ -1,9 +1,10 @@ #ifndef OPENMW_COMPONENTS_WIDGETS_H #define OPENMW_COMPONENTS_WIDGETS_H +extern int GuiFontHeight; + namespace Gui { - /// Register all widgets from this component with MyGUI's factory manager. void registerAllWidgets(); diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 3040fc48ba..3fe943b6f6 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -12,6 +12,27 @@ This setting scales the GUI interface windows. A value of 1.0 results in the normal scale. Larger values are useful to increase the scale of the GUI for high resolution displays. This setting can only be configured by editing the settings configuration file. +font size +--------- + +:Type: integer +:Range: 12 to 20 +:Default: 16 + +Allows to specify glyph size for in-game fonts. +Note: default bitmap fonts are supposed to work with 16px size, otherwise glyphs will be blurry. +TrueType fonts do not have this issue. + +ttf resolution +-------------- + +:Type: integer +:Range: > 0 +:Default: 96 + +Allows to specify resolution for in-game TrueType fonts. +Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 or lower scaling factor. + menu transparency ----------------- diff --git a/files/mygui/core.skin b/files/mygui/core.skin index 9aa5664510..ee9135554e 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -2,7 +2,6 @@ - diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index c5eb573a7c..246c8aa8ff 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index ed3cfb03e8..4738cdc13d 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -50,7 +50,7 @@ - + diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index e4037561d7..6ffe3017e5 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -2,8 +2,6 @@ - - diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 6221799b5e..e935e2f5cb 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -29,7 +29,7 @@ - + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 964b5ea955..0d3a71ab53 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -59,8 +59,9 @@ - - + + + diff --git a/files/mygui/openmw_journal.skin.xml b/files/mygui/openmw_journal.skin.xml index 42be2bb629..e9eecba792 100644 --- a/files/mygui/openmw_journal.skin.xml +++ b/files/mygui/openmw_journal.skin.xml @@ -13,11 +13,9 @@ + - - - - + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 5b6f14dd5c..9af0e79660 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -127,7 +127,6 @@ - @@ -140,8 +139,6 @@ - - @@ -153,7 +150,6 @@ - diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index a272ae84a0..ef618b3160 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -156,10 +156,10 @@ - + - + @@ -443,7 +443,6 @@ - @@ -458,7 +457,6 @@ ------------------------------------------------------ --> - @@ -593,7 +591,6 @@ - @@ -730,7 +727,6 @@ - diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c2ac2eb1ca..45cee05590 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -142,6 +142,12 @@ global = false # Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger). scaling factor = 1.0 +# Size of in-game fonts +font size = 16 + +# Resolution of TrueType fonts glyphs +ttf resolution = 96 + # Transparency of GUI windows (0.0 to 1.0, transparent to opaque). menu transparency = 0.84 From 8da099713e8c7a7535a2932dfe454013feb16f85 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 1 Sep 2018 22:02:43 +0400 Subject: [PATCH 020/150] Minor layout fixes to fit different font size --- files/mygui/openmw_alchemy_window.layout | 2 +- files/mygui/openmw_chargen_birth.layout | 2 +- files/mygui/openmw_chargen_class.layout | 6 ++-- .../mygui/openmw_chargen_create_class.layout | 6 ++-- ...penmw_chargen_generate_class_result.layout | 2 +- files/mygui/openmw_chargen_race.layout | 16 ++++----- .../openmw_chargen_select_attribute.layout | 31 +++++++++-------- .../mygui/openmw_chargen_select_skill.layout | 33 ++++++++++--------- ...penmw_chargen_select_specialization.layout | 11 ++++--- files/mygui/openmw_container_window.layout | 2 +- files/mygui/openmw_count_window.layout | 6 ++-- files/mygui/openmw_trade_window.layout | 4 +-- 12 files changed, 65 insertions(+), 56 deletions(-) diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index a8e5431fbf..edc9a4c3d9 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index a137d83d13..18d4123407 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -16,7 +16,7 @@ > - + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index 98c8e4fded..1402d9b5cf 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -27,11 +27,11 @@ - + - + @@ -73,7 +73,7 @@ - + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index c04cfe0646..fe491eb6d3 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -11,7 +11,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index d49f57dded..48a203b18f 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -24,7 +24,7 @@ - + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 31f0436d57..ea7ec6179f 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -1,7 +1,7 @@ - + @@ -54,28 +54,28 @@ - + - + - + - + - + - + - + diff --git a/files/mygui/openmw_chargen_select_attribute.layout b/files/mygui/openmw_chargen_select_attribute.layout index 0821d4a579..57bd4ebc65 100644 --- a/files/mygui/openmw_chargen_select_attribute.layout +++ b/files/mygui/openmw_chargen_select_attribute.layout @@ -1,28 +1,31 @@ - - + + - + - - - - - - - - + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_skill.layout b/files/mygui/openmw_chargen_select_skill.layout index d1061882d1..3737ea9043 100644 --- a/files/mygui/openmw_chargen_select_skill.layout +++ b/files/mygui/openmw_chargen_select_skill.layout @@ -1,10 +1,10 @@ - - + + - + @@ -44,20 +44,23 @@ - - - - - - - - - + + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_specialization.layout b/files/mygui/openmw_chargen_select_specialization.layout index c4fe6c6318..47dc32f629 100644 --- a/files/mygui/openmw_chargen_select_specialization.layout +++ b/files/mygui/openmw_chargen_select_specialization.layout @@ -2,7 +2,7 @@ - + @@ -22,9 +22,12 @@ - - - + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 6bb585e50e..5783779db9 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -9,7 +9,7 @@ - + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index 520d49fa3f..2e083dceac 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -16,12 +16,12 @@ - + - + - + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 0af9d59fe8..30e22302d3 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -48,7 +48,7 @@ - + @@ -62,7 +62,7 @@ - + From f89393fd6244e38ff4abed1f7a317d29599dc453 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 2 Sep 2018 18:08:49 +0400 Subject: [PATCH 021/150] Validate 'ttf resolution' option value --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- docs/source/reference/modding/settings/GUI.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 493a14a1cb..03cbc3ef2a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -309,7 +309,7 @@ namespace MWGui // to allow to configure font size via config file, without need to edit XML file. // Also we should take UI scaling factor in account int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::max(0, resolution); + resolution = std::min(960, std::max(48, resolution)); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 3fe943b6f6..2092096c93 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -27,7 +27,7 @@ ttf resolution -------------- :Type: integer -:Range: > 0 +:Range: 48 to 960 :Default: 96 Allows to specify resolution for in-game TrueType fonts. From c9c0230d2ac6a2fb8798608cf36c3ec487a9ab27 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 3 Sep 2018 20:45:04 +0400 Subject: [PATCH 022/150] Scale journal fonts separately from common ones --- apps/openmw/mwgui/bookpage.cpp | 23 +++++-- apps/openmw/mwgui/bookpage.hpp | 2 +- apps/openmw/mwgui/formatting.cpp | 2 +- apps/openmw/mwgui/formatting.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 67 +++++++++++++++---- components/fontloader/fontloader.cpp | 15 +++++ components/sdlutil/sdlvideowrapper.cpp | 5 ++ .../source/reference/modding/settings/GUI.rst | 2 +- files/mygui/openmw_book.layout | 2 + files/mygui/openmw_journal.layout | 2 + files/mygui/openmw_journal.skin.xml | 4 +- 11 files changed, 102 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 8f2e8b5ed6..8e264312eb 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -264,18 +264,29 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { } - Style * createStyle (char const * fontName, const Colour& fontColour) + Style * createStyle (const std::string& fontName, const Colour& fontColour) { - if (strcmp(fontName, "") == 0) - return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); + const std::string templateName = "Journalbook "; + std::string bookFont; + if (fontName.empty()) + { + bookFont = MyGUI::FontManager::getInstance().getDefaultFont(); + bookFont = templateName + bookFont; + return createStyle(bookFont, fontColour); + } + + if (fontName.compare(0, templateName.size(), templateName) == 0) + bookFont = fontName; + else + bookFont = templateName + bookFont; for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) - if (i->match (fontName, fontColour, fontColour, fontColour, 0)) + if (i->match (bookFont.c_str(), fontColour, fontColour, fontColour, 0)) return &*i; - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); + MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(bookFont); if (!font) - throw std::runtime_error(std::string("can't find font ") + fontName); + throw std::runtime_error(std::string("can't find font ") + bookFont); StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = font; diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index b8174bd3dc..c75f00ca41 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -102,7 +102,7 @@ namespace MWGui static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. - virtual Style* createStyle (char const * Font, const Colour& Colour) = 0; + virtual Style* createStyle (const std::string& fontName, const Colour& colour) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 157aeba26c..fd5ed4faae 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -377,7 +377,7 @@ namespace MWGui if (attr.find("face") != attr.end()) { std::string face = attr.at("face"); - mTextStyle.mFont = face; + mTextStyle.mFont = "Journalbook "+face; } if (attr.find("size") != attr.end()) { diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 7ccb0824e3..6d98d48b34 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -14,7 +14,7 @@ namespace MWGui { TextStyle() : mColour(0,0,0) - , mFont("Default") + , mFont("") , mTextSize(16) { } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 03cbc3ef2a..f4723da47b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -293,47 +293,90 @@ namespace MWGui void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) { - MyGUI::xml::ElementEnumerator root = _node->getElementEnumerator(); - while (root.next("Resource")) + const std::string templateName = "Journalbook "; + MyGUI::xml::ElementEnumerator font = _node->getElementEnumerator(); + bool createCopy = false; + while (font.next("Resource")) { std::string type, name; - root->findAttribute("type", type); - root->findAttribute("name", name); + font->findAttribute("type", type); + font->findAttribute("name", name); if (name.empty()) continue; if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) { + createCopy = true; + // For TrueType fonts we should override Size and Resolution properties - // to allow to configure font size via config file, without need to edit XML file. - // Also we should take UI scaling factor in account + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); resolution = std::min(960, std::max(48, resolution)); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + resolution *= uiScale; - if (uiScale > 1.0f) - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = root->createChild("Property"); + MyGUI::xml::ElementPtr resolutionNode = font->createChild("Property"); resolutionNode->addAttribute("key", "Resolution"); resolutionNode->addAttribute("value", std::to_string(resolution)); - MyGUI::xml::ElementPtr sizeNode = root->createChild("Property"); + MyGUI::xml::ElementPtr sizeNode = font->createChild("Property"); sizeNode->addAttribute("key", "Size"); sizeNode->addAttribute("value", std::to_string(mFontHeight)); } else if (Misc::StringUtils::ciEqual(type, "ResourceSkin")) { // We should adjust line height for MyGUI widgets depending on font size - MyGUI::xml::ElementPtr heightNode = root->createChild("Property"); + MyGUI::xml::ElementPtr heightNode = font->createChild("Property"); heightNode->addAttribute("key", "HeightLine"); heightNode->addAttribute("value", std::to_string(mFontHeight+2)); } } MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + + if (createCopy) + { + MyGUI::xml::ElementPtr copy = _node->createCopy(); + + MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); + while (copyFont.next("Resource")) + { + std::string type, name; + copyFont->findAttribute("type", type); + copyFont->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // Since the journal and books use the custom scaling factor depending on resolution, + // setup separate fonts with different Resolution to fit these windows. + // These fonts have an internal prefix. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml + float heightScale = (currentY / 520); + float widthScale = (currentX / 600); + float uiScale = std::min(widthScale, heightScale); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + copyFont->setAttribute("name", "Journalbook " + name); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); + } } void WindowManager::initUI() diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 37214a038c..adcdd2d14b 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -480,6 +480,14 @@ namespace Gui font->deserialization(root, MyGUI::Version(3,2,0)); + // Setup "book" version of font as fallback if we will not use TrueType fonts + MyGUI::ResourceManualFont* bookFont = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); + mFonts.push_back(bookFont); + bookFont->deserialization(root, MyGUI::Version(3,2,0)); + bookFont->setResourceName("Journalbook " + resourceName); + + // Remove automatically registered fonts for (std::vector::iterator it = mFonts.begin(); it != mFonts.end();) { if ((*it)->getResourceName() == font->getResourceName()) @@ -487,10 +495,17 @@ namespace Gui MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName()); it = mFonts.erase(it); } + else if ((*it)->getResourceName() == bookFont->getResourceName()) + { + MyGUI::ResourceManager::getInstance().removeByName(bookFont->getResourceName()); + it = mFonts.erase(it); + } else ++it; } + MyGUI::ResourceManager::getInstance().addResource(font); + MyGUI::ResourceManager::getInstance().addResource(bookFont); } } diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index c2963be861..d5c8833eaf 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -89,6 +91,9 @@ namespace SDLUtil SDL_SetWindowSize(mWindow, width, height); SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE); } + + // We should reload TrueType fonts for new resolution + MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); } } diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 2092096c93..c8f4e16f80 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -31,7 +31,7 @@ ttf resolution :Default: 96 Allows to specify resolution for in-game TrueType fonts. -Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 or lower scaling factor. +Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 scaling factor. menu transparency ----------------- diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index c83c4982b2..e6f0f858c0 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -32,11 +32,13 @@ + + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 0d3a71ab53..5d6e290662 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -25,10 +25,12 @@ + + diff --git a/files/mygui/openmw_journal.skin.xml b/files/mygui/openmw_journal.skin.xml index e9eecba792..0702d8c5eb 100644 --- a/files/mygui/openmw_journal.skin.xml +++ b/files/mygui/openmw_journal.skin.xml @@ -2,7 +2,7 @@ - + @@ -19,7 +19,7 @@ - + From 7a986f38da71544f1d9f6965942ba7463c1e7055 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 09:15:07 +0400 Subject: [PATCH 023/150] Support for user-defined TrueType fonts --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 15 +++++++++++---- apps/openmw/mwgui/windowmanagerimp.hpp | 5 +++-- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +++ components/fontloader/fontloader.cpp | 24 ++++++++++++++++++++++-- components/fontloader/fontloader.hpp | 9 +++++++-- components/sdlutil/sdlvideowrapper.cpp | 5 ----- 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2941ede1b2..057113ba35 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -516,7 +516,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, - Version::getOpenmwVersionDescription(mResDir.string())); + Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); mEnvironment.setWindowManager (window); // Create sound system diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index b40f517f91..fb0be30e4d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -290,6 +290,8 @@ namespace MWBase /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; + virtual void loadUserFonts() = 0; + virtual Loading::Listener* getLoadingScreen() = 0; /// Should the cursor be visible? diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f4723da47b..481a9ec003 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -131,8 +132,8 @@ namespace MWGui WindowManager::WindowManager( osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription) + const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& userDataPath) : mStore(NULL) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) @@ -210,8 +211,8 @@ namespace MWGui MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts - mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS())); - mFontLoader->loadAllFonts(exportFonts); + mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath)); + mFontLoader->loadBitmapFonts(exportFonts); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -245,6 +246,7 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); + loadUserFonts(); bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI"); mKeyboardNavigation.reset(new KeyboardNavigation()); @@ -379,6 +381,11 @@ namespace MWGui } } + void WindowManager::loadUserFonts() + { + mFontLoader->loadTrueTypeFonts(); + } + void WindowManager::initUI() { // Get size info from the Gui object diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f3bf6d00f3..5477014969 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -131,14 +131,15 @@ namespace MWGui typedef std::vector FactionList; WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription); + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& localPath); virtual ~WindowManager(); /// Set the ESMStore to use for retrieving of GUI-related strings. void setStore (const MWWorld::ESMStore& store); void initUI(); + virtual void loadUserFonts(); virtual Loading::Listener* getLoadingScreen(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b24c8cc149..ec9ead595a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -676,6 +676,9 @@ namespace MWInput Settings::Manager::getInt("resolution y", "Video"), Settings::Manager::getBool("fullscreen", "Video"), Settings::Manager::getBool("window border", "Video")); + + // We should reload TrueType fonts to fit new resolution + MWBase::Environment::get().getWindowManager()->loadUserFonts(); } } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index adcdd2d14b..0378e294eb 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -145,8 +145,9 @@ namespace namespace Gui { - FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs) + FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath) : mVFS(vfs) + , mUserDataPath(userDataPath) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; @@ -175,7 +176,7 @@ namespace Gui mFonts.clear(); } - void FontLoader::loadAllFonts(bool exportToFile) + void FontLoader::loadBitmapFonts(bool exportToFile) { const std::map& index = mVFS->getIndex(); @@ -198,6 +199,25 @@ namespace Gui } } + void FontLoader::loadTrueTypeFonts() + { + osgMyGUI::DataManager* dataManager = dynamic_cast(&osgMyGUI::DataManager::getInstance()); + if (!dataManager) + { + Log(Debug::Error) << "Can not load TrueType fonts: osgMyGUI::DataManager is not available."; + return; + } + + const std::string cfg = dataManager->getDataPath(""); + const std::string fontFile = mUserDataPath + "/" + "Fonts" + "/" + "openmw_font.xml"; + if (!boost::filesystem::exists(fontFile)) + return; + + dataManager->setResourcePath(mUserDataPath + "/" + "Fonts"); + MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); + dataManager->setResourcePath(cfg); + } + typedef struct { diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index b92815f136..39301f9f5f 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -1,6 +1,9 @@ #ifndef OPENMW_COMPONENTS_FONTLOADER_H #define OPENMW_COMPONENTS_FONTLOADER_H +#include "boost/filesystem/operations.hpp" + +#include #include namespace VFS @@ -23,15 +26,17 @@ namespace Gui class FontLoader { public: - FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs); + FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath); ~FontLoader(); /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files? - void loadAllFonts (bool exportToFile); + void loadBitmapFonts (bool exportToFile); + void loadTrueTypeFonts (); private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; + std::string mUserDataPath; std::vector mTextures; std::vector mFonts; diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index d5c8833eaf..c2963be861 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include @@ -91,9 +89,6 @@ namespace SDLUtil SDL_SetWindowSize(mWindow, width, height); SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE); } - - // We should reload TrueType fonts for new resolution - MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); } } From e300a16b24a0f8881549f4c97afb86466ef4612b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:19:50 +0400 Subject: [PATCH 024/150] Use field for columns count instead of out integer --- apps/openmw/mwgui/journalbooks.cpp | 16 ++++++++-------- apps/openmw/mwgui/journalbooks.hpp | 7 ++++--- apps/openmw/mwgui/journalwindow.cpp | 5 ++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 1956ee1c28..4302740f67 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -157,7 +157,7 @@ MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) typedef TypesetBook::Ptr book; JournalBooks::JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding) : - mModel (model), mEncoding(encoding) + mModel (model), mEncoding(encoding), mIndexPagesCount(0) { } @@ -218,23 +218,23 @@ book JournalBooks::createQuestBook (const std::string& questName) return typesetter->complete (); } -book JournalBooks::createTopicIndexBook (int& columnsCount) +book JournalBooks::createTopicIndexBook () { bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex(columnsCount) : createLatinJournalIndex(columnsCount); + BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); return typesetter->complete (); } -BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) +BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); // Latin journal index always has two columns for now. - columnsCount = 2; + mIndexPagesCount = 2; char ch = 'A'; @@ -261,7 +261,7 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) return typesetter; } -BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) +BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); @@ -273,11 +273,11 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters). int sectionBreak = 10; - columnsCount = 3; + mIndexPagesCount = 3; if (fontHeight < 18) { sectionBreak = 15; - columnsCount = 2; + mIndexPagesCount = 2; } unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index cc6f42cc8b..05eda6e222 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -22,14 +22,15 @@ namespace MWGui Book createTopicBook (uintptr_t topicId); Book createTopicBook (const std::string& topicId); Book createQuestBook (const std::string& questName); - Book createTopicIndexBook (int& columnsCount); + Book createTopicIndexBook (); ToUTF8::FromType mEncoding; + int mIndexPagesCount; private: BookTypesetter::Ptr createTypesetter (); - BookTypesetter::Ptr createLatinJournalIndex (int& columnsCount); - BookTypesetter::Ptr createCyrillicJournalIndex (int& columnsCount); + BookTypesetter::Ptr createLatinJournalIndex (); + BookTypesetter::Ptr createCyrillicJournalIndex (); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index cef20159df..a2f6ea1423 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -465,11 +465,10 @@ namespace { setOptionsMode (); - int pagesCount; if (!mTopicIndexBook) - mTopicIndexBook = createTopicIndexBook (pagesCount); + mTopicIndexBook = createTopicIndexBook (); - if (pagesCount == 3) + if (mIndexPagesCount == 3) { getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1); From 664c630ac023c5dc0b2d3cc313efb41bfeb2db14 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 12 Sep 2018 16:00:33 +0300 Subject: [PATCH 025/150] Don't make sTo strings static references --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index b4713291bb..f3f104e3cd 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -147,7 +147,7 @@ namespace MWGui mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); - static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); @@ -314,7 +314,7 @@ namespace MWGui } mEffect.mMagnMax = pos+1; - static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos+1)); From 275d10e1f724824adf293119a9e4fcc77c30245c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 27 Jul 2018 23:45:25 +0400 Subject: [PATCH 026/150] Add missing icons for UniversalId tables and use them in the editor menu (feature #912) --- CHANGELOG.md | 1 + apps/opencs/model/prefs/state.cpp | 1 + apps/opencs/model/world/universalid.cpp | 133 ++++----- apps/opencs/model/world/universalid.hpp | 21 +- apps/opencs/view/doc/view.cpp | 254 ++++++------------ apps/opencs/view/doc/view.hpp | 2 + files/opencs/cell.png | Bin 223 -> 1212 bytes files/opencs/debug-profile.png | Bin 0 -> 431 bytes files/opencs/edit-undo.png | Bin 650 -> 0 bytes files/opencs/error-log.png | Bin 0 -> 518 bytes files/opencs/{Info.png => info.png} | Bin files/opencs/instance.png | Bin 0 -> 618 bytes ...led-creature.png => levelled-creature.png} | Bin .../{leveled-item.png => levelled-item.png} | Bin files/opencs/magic-effect.png | Bin 325 -> 356 bytes files/opencs/menu-close.png | Bin 0 -> 438 bytes files/opencs/menu-exit.png | Bin 0 -> 257 bytes files/opencs/menu-merge.png | Bin 0 -> 219 bytes files/opencs/menu-new-addon.png | Bin 0 -> 282 bytes files/opencs/menu-new-game.png | Bin 0 -> 230 bytes files/opencs/menu-new-window.png | Bin 0 -> 213 bytes files/opencs/menu-open.png | Bin 0 -> 241 bytes files/opencs/menu-preferences.png | Bin 0 -> 385 bytes files/opencs/menu-redo.png | Bin 0 -> 323 bytes files/opencs/menu-reload.png | Bin 0 -> 447 bytes files/opencs/menu-save.png | Bin 0 -> 302 bytes files/opencs/menu-search.png | Bin 0 -> 408 bytes files/opencs/menu-status-bar.png | Bin 0 -> 188 bytes files/opencs/menu-undo.png | Bin 0 -> 323 bytes files/opencs/menu-verify.png | Bin 0 -> 487 bytes files/opencs/metadata.png | Bin 0 -> 389 bytes files/opencs/object.png | Bin 0 -> 447 bytes files/opencs/placeholder.png | Bin 2383 -> 2142 bytes files/opencs/region-map.png | Bin 0 -> 428 bytes files/opencs/region.png | Bin 320 -> 605 bytes files/opencs/resources.qrc | 36 ++- files/opencs/run-log.png | Bin 0 -> 294 bytes files/opencs/run-openmw.png | Bin 0 -> 388 bytes files/opencs/scene.png | Bin 0 -> 401 bytes files/opencs/start-script.png | Bin 0 -> 359 bytes files/opencs/stop-openmw.png | Bin 0 -> 252 bytes 41 files changed, 216 insertions(+), 232 deletions(-) create mode 100644 files/opencs/debug-profile.png delete mode 100644 files/opencs/edit-undo.png create mode 100644 files/opencs/error-log.png rename files/opencs/{Info.png => info.png} (100%) create mode 100644 files/opencs/instance.png rename files/opencs/{leveled-creature.png => levelled-creature.png} (100%) rename files/opencs/{leveled-item.png => levelled-item.png} (100%) create mode 100644 files/opencs/menu-close.png create mode 100644 files/opencs/menu-exit.png create mode 100644 files/opencs/menu-merge.png create mode 100644 files/opencs/menu-new-addon.png create mode 100644 files/opencs/menu-new-game.png create mode 100644 files/opencs/menu-new-window.png create mode 100644 files/opencs/menu-open.png create mode 100644 files/opencs/menu-preferences.png create mode 100644 files/opencs/menu-redo.png create mode 100644 files/opencs/menu-reload.png create mode 100644 files/opencs/menu-save.png create mode 100644 files/opencs/menu-search.png create mode 100644 files/opencs/menu-status-bar.png create mode 100644 files/opencs/menu-undo.png create mode 100644 files/opencs/menu-verify.png create mode 100644 files/opencs/metadata.png create mode 100644 files/opencs/object.png create mode 100644 files/opencs/region-map.png create mode 100644 files/opencs/run-log.png create mode 100644 files/opencs/run-openmw.png create mode 100644 files/opencs/scene.png create mode 100644 files/opencs/start-script.png create mode 100644 files/opencs/stop-openmw.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cb90afd0..2ae2a38a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index a704fb825d..b6a55ea862 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -305,6 +305,7 @@ void CSMPrefs::State::declare() declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence()); declareShortcut ("document-debug-run", "Run Debug", QKeySequence()); declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence()); + declareShortcut ("document-debug-profiles", "Debug Profiles", QKeySequence()); declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence()); declareSubcategory ("Table"); diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 8d7a7761e4..6f9eeb24fd 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -18,47 +18,60 @@ namespace static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Objects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "Instances", 0 }, - { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, - "Region Map", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, - + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", ":./global-variable.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunGame, "Run OpenMW", ":./run-openmw.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StopGame, "Stop OpenMW", ":./stop-openmw.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Redo, "Redo", ":./menu-redo.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Undo, "Undo", ":./menu-undo.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Preferences, "Preferences", ":./menu-preferences.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Reload, "Reload", ":./menu-reload.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewWindow, "New View", ":./menu-new-window.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StatusBar, "Toggle Status Bar", ":./menu-status-bar.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewGame, "New Game", ":./menu-new-game.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewAddon, "New Addon", ":./menu-new-addon.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Open, "Open", ":./menu-open.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Save, "Save", ":./menu-save.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Verify, "Verify", ":./menu-verify.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Merge, "Merge", ":./menu-merge.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_ErrorLog, "Error Log", ":./error-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Close, "Close", ":./menu-close.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Exit, "Exit", ":./menu-exit.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -81,7 +94,7 @@ namespace { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, @@ -93,9 +106,9 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, - "Creature Levelled List", ":./leveled-creature.png" }, + "Creature Levelled List", ":./levelled-creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List", ":./leveled-item.png" }, + "Item Levelled List", ":./levelled-item.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, @@ -105,35 +118,35 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, - { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":./instance.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":./record-preview.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"}, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":./resources-sound" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", ":./debug-profile.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", ":./land-texture.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", ":./menu-verify.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":./menu-search.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index accd1b78da..aa5276ba71 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -137,10 +137,27 @@ namespace CSMWorld Type_Search, Type_MetaDatas, Type_MetaData, - Type_RunLog + Type_RunLog, + Type_RunGame, + Type_StopGame, + Type_Undo, + Type_Redo, + Type_Preferences, + Type_NewWindow, + Type_StatusBar, + Type_NewGame, + Type_NewAddon, + Type_Open, + Type_Save, + Type_Verify, + Type_Merge, + Type_ErrorLog, + Type_Close, + Type_Exit, + Type_Reload }; - enum { NumberOfTypes = Type_RunLog+1 }; + enum { NumberOfTypes = Type_Reload+1 }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 3ebefd69a6..97aaac4d79 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("File")); - QAction *newGame = new QAction (tr ("New Game"), this); + QAction* newGame = createMenuEntry(CSMWorld::UniversalId::Type_NewGame, file, "document-file-newgame"); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); - setupShortcut("document-file-newgame", newGame); - file->addAction (newGame); - - QAction *newAddon = new QAction (tr ("New Addon"), this); + QAction* newAddon = createMenuEntry(CSMWorld::UniversalId::Type_NewAddon, file, "document-file-newaddon"); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); - setupShortcut("document-file-newaddon", newAddon); - file->addAction (newAddon); - QAction *open = new QAction (tr ("Open"), this); + QAction* open = createMenuEntry(CSMWorld::UniversalId::Type_Open, file, "document-file-open"); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); - setupShortcut("document-file-open", open); - file->addAction (open); - mSave = new QAction (tr ("Save"), this); - connect (mSave, SIGNAL (triggered()), this, SLOT (save())); - setupShortcut("document-file-save", mSave); - file->addAction (mSave); + QAction* save = createMenuEntry(CSMWorld::UniversalId::Type_Save, file, "document-file-save"); + connect (save, SIGNAL (triggered()), this, SLOT (save())); + mSave = save; - mVerify = new QAction (tr ("Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - setupShortcut("document-file-verify", mVerify); - file->addAction (mVerify); + QAction* verify = createMenuEntry(CSMWorld::UniversalId::Type_Verify, file, "document-file-verify"); + connect (verify, SIGNAL (triggered()), this, SLOT (verify())); + mVerify = verify; - mMerge = new QAction (tr ("Merge"), this); - connect (mMerge, SIGNAL (triggered()), this, SLOT (merge())); - setupShortcut("document-file-merge", mMerge); - file->addAction (mMerge); + QAction* merge = createMenuEntry(CSMWorld::UniversalId::Type_Merge, file, "document-file-merge"); + connect (merge, SIGNAL (triggered()), this, SLOT (merge())); + mMerge = merge; - QAction *loadErrors = new QAction (tr ("Open Load Error Log"), this); + QAction* loadErrors = createMenuEntry(CSMWorld::UniversalId::Type_ErrorLog, file, "document-file-errorlog"); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); - setupShortcut("document-file-errorlog", loadErrors); - file->addAction (loadErrors); - QAction *meta = new QAction (tr ("Meta Data"), this); + QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); - setupShortcut("document-file-metadata", meta); - file->addAction (meta); - QAction *close = new QAction (tr ("Close Document"), this); + QAction* close = createMenuEntry(CSMWorld::UniversalId::Type_Close, file, "document-file-close"); connect (close, SIGNAL (triggered()), this, SLOT (close())); - setupShortcut("document-file-close", close); - file->addAction(close); - QAction *exit = new QAction (tr ("Exit Application"), this); + QAction* exit = createMenuEntry(CSMWorld::UniversalId::Type_Exit, file, "document-file-exit"); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); - connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); - setupShortcut("document-file-exit", exit); - file->addAction(exit); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); } namespace @@ -130,251 +112,180 @@ void CSVDoc::View::setupEditMenu() mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); setupShortcut("document-edit-undo", mUndo); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); + std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Undo).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + mUndo->setIcon(QIcon(QString::fromStdString(iconName))); + edit->addAction (mUndo); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); setupShortcut("document-edit-redo", mRedo); + iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Redo).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + mRedo->setIcon(QIcon(QString::fromStdString(iconName))); + edit->addAction (mRedo); - QAction *userSettings = new QAction (tr ("Preferences"), this); + QAction* userSettings = createMenuEntry(CSMWorld::UniversalId::Type_Preferences, edit, "document-edit-preferences"); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); - setupShortcut("document-edit-preferences", userSettings); - edit->addAction (userSettings); - QAction *search = new QAction (tr ("Search"), this); + QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); - setupShortcut("document-edit-search", search); - edit->addAction (search); } void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("View")); - QAction *newWindow = new QAction (tr ("New View"), this); + QAction *newWindow = createMenuEntry(CSMWorld::UniversalId::Type_NewWindow, view, "document-view-newview"); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - setupShortcut("document-view-newview", newWindow); - view->addAction (newWindow); - mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this); - mShowStatusBar->setCheckable (true); + mShowStatusBar = createMenuEntry(CSMWorld::UniversalId::Type_StatusBar, view, "document-view-statusbar"); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); - setupShortcut("document-view-statusbar", mShowStatusBar); - + mShowStatusBar->setCheckable (true); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->addAction (mShowStatusBar); - QAction *filters = new QAction (tr ("Filters"), this); + QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, "document-mechanics-filters"); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); - setupShortcut("document-view-filters", filters); - view->addAction (filters); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("World")); - QAction *regions = new QAction (tr ("Regions"), this); + QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions"); connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); - setupShortcut("document-world-regions", regions); - world->addAction (regions); - QAction *cells = new QAction (tr ("Cells"), this); + QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells"); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); - setupShortcut("document-world-cells", cells); - world->addAction (cells); - QAction *referenceables = new QAction (tr ("Objects"), this); + QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables"); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); - setupShortcut("document-world-referencables", referenceables); - world->addAction (referenceables); - QAction *references = new QAction (tr ("Instances"), this); + QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references"); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); - setupShortcut("document-world-references", references); - world->addAction (references); - QAction *lands = new QAction (tr ("Lands"), this); + QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands"); connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView())); - setupShortcut("document-world-lands", lands); - world->addAction (lands); - QAction *landTextures = new QAction (tr ("Land Textures"), this); + QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, "document-world-landtextures"); connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView())); - setupShortcut("document-world-landtextures", landTextures); - world->addAction (landTextures); - QAction *grid = new QAction (tr ("Pathgrid"), this); + QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid"); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); - setupShortcut("document-world-pathgrid", grid); - world->addAction (grid); world->addSeparator(); // items that don't represent single record lists follow here - QAction *regionMap = new QAction (tr ("Region Map"), this); + QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap"); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); - setupShortcut("document-world-regionmap", regionMap); - world->addAction (regionMap); } void CSVDoc::View::setupMechanicsMenu() { QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics")); - QAction *globals = new QAction (tr ("Globals"), this); + QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals"); connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); - setupShortcut("document-mechanics-globals", globals); - mechanics->addAction (globals); - QAction *gmsts = new QAction (tr ("Game Settings"), this); + QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings"); connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); - setupShortcut("document-mechanics-gamesettings", gmsts); - mechanics->addAction (gmsts); - QAction *scripts = new QAction (tr ("Scripts"), this); + QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts"); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); - setupShortcut("document-mechanics-scripts", scripts); - mechanics->addAction (scripts); - QAction *spells = new QAction (tr ("Spells"), this); + QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells"); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); - setupShortcut("document-mechanics-spells", spells); - mechanics->addAction (spells); - QAction *enchantments = new QAction (tr ("Enchantments"), this); + QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, "document-mechanics-enchantments"); connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView())); - setupShortcut("document-mechanics-enchantments", enchantments); - mechanics->addAction (enchantments); - QAction *effects = new QAction (tr ("Magic Effects"), this); - connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - setupShortcut("document-mechanics-magiceffects", effects); - mechanics->addAction (effects); + QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects"); + connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - QAction *startScripts = new QAction (tr ("Start Scripts"), this); + QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts"); connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView())); - setupShortcut("document-mechanics-startscripts", startScripts); - mechanics->addAction (startScripts); } void CSVDoc::View::setupCharacterMenu() { QMenu *characters = menuBar()->addMenu (tr ("Characters")); - QAction *skills = new QAction (tr ("Skills"), this); + QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, "document-character-skills"); connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); - setupShortcut("document-character-skills", skills); - characters->addAction (skills); - QAction *classes = new QAction (tr ("Classes"), this); + QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, "document-character-classes"); connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); - setupShortcut("document-character-classes", classes); - characters->addAction (classes); - QAction *factions = new QAction (tr ("Factions"), this); + QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, "document-character-factions"); connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); - setupShortcut("document-character-factions", factions); - characters->addAction (factions); - QAction *races = new QAction (tr ("Races"), this); + QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, "document-character-races"); connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); - setupShortcut("document-character-races", races); - characters->addAction (races); - QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns"); connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); - setupShortcut("document-character-birthsigns", birthsigns); - characters->addAction (birthsigns); - QAction *topics = new QAction (tr ("Topics"), this); + QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics"); connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); - setupShortcut("document-character-topics", topics); - characters->addAction (topics); - QAction *journals = new QAction (tr ("Journals"), this); + QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals"); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); - setupShortcut("document-character-journals", journals); - characters->addAction (journals); - QAction *topicInfos = new QAction (tr ("Topic Infos"), this); + QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos"); connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView())); - setupShortcut("document-character-topicinfos", topicInfos); - characters->addAction (topicInfos); - QAction *journalInfos = new QAction (tr ("Journal Infos"), this); + QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos"); connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView())); - setupShortcut("document-character-journalinfos", journalInfos); - characters->addAction (journalInfos); - QAction *bodyParts = new QAction (tr ("Body Parts"), this); + QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts"); connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView())); - setupShortcut("document-character-bodyparts", bodyParts); - characters->addAction (bodyParts); } void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("Assets")); - QAction *reload = new QAction (tr ("Reload"), this); + QAction* reload = createMenuEntry(CSMWorld::UniversalId::Type_Reload, assets, "document-assets-reload"); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged())); - setupShortcut("document-assets-reload", reload); - assets->addAction (reload); assets->addSeparator(); - QAction *sounds = new QAction (tr ("Sounds"), this); + QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, "document-assets-sounds"); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); - setupShortcut("document-assets-sounds", sounds); - assets->addAction (sounds); - QAction *soundGens = new QAction (tr ("Sound Generators"), this); + QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, "document-assets-soundgens"); connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView())); - setupShortcut("document-assets-soundgens", soundGens); - assets->addAction (soundGens); assets->addSeparator(); // resources follow here - QAction *meshes = new QAction (tr ("Meshes"), this); + QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, "document-assets-meshes"); connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView())); - setupShortcut("document-assets-meshes", meshes); - assets->addAction (meshes); - QAction *icons = new QAction (tr ("Icons"), this); + QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, "document-assets-icons"); connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView())); - setupShortcut("document-assets-icons", icons); - assets->addAction (icons); - QAction *musics = new QAction (tr ("Music"), this); + QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, "document-assets-musics"); connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView())); - setupShortcut("document-assets-music", musics); - assets->addAction (musics); - QAction *soundsRes = new QAction (tr ("Sound Files"), this); - connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - setupShortcut("document-assets-soundres", soundsRes); - assets->addAction (soundsRes); + QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres"); + connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - QAction *textures = new QAction (tr ("Textures"), this); + QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, "document-assets-textures"); connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView())); - setupShortcut("document-assets-textures", textures); - assets->addAction (textures); - QAction *videos = new QAction (tr ("Videos"), this); + QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, "document-assets-videos"); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView())); - setupShortcut("document-assets-videos", videos); - assets->addAction (videos); } void CSVDoc::View::setupDebugMenu() { QMenu *debug = menuBar()->addMenu (tr ("Debug")); - QAction *profiles = new QAction (tr ("Debug Profiles"), this); + QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, "document-debug-profiles"); connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView())); - debug->addAction (profiles); debug->addSeparator(); @@ -387,18 +298,31 @@ void CSVDoc::View::setupDebugMenu() QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); - setupShortcut("document-debug-run", runDebug); + std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_RunGame).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + runDebug->setIcon(QIcon(QString::fromStdString(iconName))); - mStopDebug = new QAction (tr ("Shutdown OpenMW"), this); - connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop())); - setupShortcut("document-debug-shutdown", mStopDebug); - debug->addAction (mStopDebug); + QAction* stopDebug = createMenuEntry(CSMWorld::UniversalId::Type_StopGame, debug, "document-debug-shutdown"); + connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop())); + mStopDebug = stopDebug; - QAction *runLog = new QAction (tr ("Open Run Log"), this); + QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, "document-debug-runlog"); connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView())); - setupShortcut("document-debug-runlog", runLog); - debug->addAction (runLog); +} + +QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName) +{ + const std::string title = CSMWorld::UniversalId (type).getTypeName(); + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + const std::string iconName = CSMWorld::UniversalId (type).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; } void CSVDoc::View::setupUi() diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 5418b7720a..e767777d76 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -66,6 +66,8 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName); + void setupFileMenu(); void setupEditMenu(); diff --git a/files/opencs/cell.png b/files/opencs/cell.png index 9127dd5e5c63b41333de5a564aef7c6f77808bf0..4c3fbe25154dfc8dfb1e6cbe8422c1dec6d932ce 100644 GIT binary patch delta 1172 zcmV;F1Z(@>0lW#2BYy+HdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuva2`@ z{nsjb36|t>ImSa(@1U38$KXp6LT(b?!v!*mCZ8jlQ2X!SPXFK%F`ldo(I@W>E}LwU zj7c=xwMjO1ea;v2GkMU{{)0fJkh;D4HtjR??&c8e)Gv>E3V&tBi8IhoStRZ&et0la;7z|Q2U*GDMG_-Wt|7)?F9bL>`x4y^rTe)u6q6U_Z}hh6VA_TIMDW$S^I zr@5$~rK~d;E`NYhS2vFla62-Axt7A`2!N|OL7 zQbbo>)F4U9N{StWy95wZ2^@(Qu%=MUUJI{z)0(evMCQt{6h<(H$J3xQ<(07q>V32> zvxz%as7pozqKv`Zc)|*VaG#jE72kExUEi2HVigR!6@TW41!l8sAr{BC-qMw4g`G2& z%+~H4>I?vgklUdcRxn^ZnwdSDAQG5?V+21Lm5W+gzyLcZm8pXp#ZCwkW#ooW;MG=~ zY-$`WAi}BF0BW#xK$f)*{S$0N4`rv$l5@_x;G#<|d-c{k?|ty&jLJcS4KDZ)LJTS7 zsL@6jeSZuw#uRgsq&UJ{@+nY?DW#l|G$V9|cZNmIi!8d>#VvjbOI*^D3)-jH;)*Y! z#F9#`Qnh0HS6>6wSX0f78(OK^=9+Jz#g{qBUXv6pUleM*~Yx3*X8lA9kr1o%{*B%*hjP90C|uT^xCh~8!T6|+Ca=<7gT z?SGiaQW<@Kk3`9@P@^|6e=O;He$H#BH;&!5b#eGmPs#o?7Tpx~-~mMZpY~DzxXwKr zebIH#-ghbKTToxsO0SFku2#AN>{&Fr0_<6BbOqS6lyn8yvs&p2uxGW>wVm}`D_sHh z#a{a!*t1&c`tEwHiLURi$CPw^cRkih?_-aw=M!r;?7AOb)Kc8e_tLa$)#k(fd2bG< z-R^kwqYoY5N_v;+MWUnKQ=q?A(WmlQ>1D9LuIN+DE`$BY7`;qHM_TcBGtvLJv`ovx ze0Gum000JJOGiWiO#n>*O(_oqIgue4e+P6)O+^Rd2?h%(5uJd2d;kCdeMv+?R5;7+ zlhFylAPht=(sc?hVzzOLZljA794P&0g(wkh{q6fScQJ4Y@Zd`nUm|AETBi%=eqffW zC10Xog%ASOv1UD5yXBl4BQpyC5K&_ixn(J(rm$H*^rF7G#$l1a!5{@@7Awa7Ce3Ux mig3jGD%Y#}y2|d&1zKE!{xaOcA~d)F0000 delta 178 zcmV;j08RhA3Eu&bBa;CH7JrdRL_t(I5$#ez4!|G?gJs{-$La%_CBrmmmfd!l2LWj- z0YvUoYqe6!5%H7VVh7otNStG=LJdh(6*F_o;Oqw`tPC{;h*dKw$BaFD@3R5`xgsXw zN1WE$DAxb<`1Ctn0Hr4nL!5K3y3T~`{{kZ0DWKatDl~2`PU_+m;3qjJ%n}9`iSJkW g$g8~6yLsULT$nUR2>SeoVgLXD07*qoM6N<$g2eAn82|tP diff --git a/files/opencs/debug-profile.png b/files/opencs/debug-profile.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb01da4373c24e073eaa8780f2a1925d8e4c291 GIT binary patch literal 431 zcmV;g0Z{&lP)x>!fO`r}B#0U}7m4vO)L ze%-f*hFHKny78>*7F6o@9Wyw`w8k^_qV`xoD_*dn!6nV&UgIg6aviSFfm>|H0>X%5 zQd1npNnrg&n7}Hkw7k!d?}ls~p#o=dHZco_+K(su!yk}`Ij#M#W;5-=wLhRn(*6;! zg%6!v)B=)qB(|seE1ch2 z2}Yb(WEZv+3nAUa;B+8qvZK_S`?_ojxuzu|$ Zb6>EKPtoppBdGuY002ovPDHLkV1knC#qkSC&LBJ zfVRcM$?V1P3B6PcAX~#mZX7Dr0E-up6>u&Ai1M4+$6px*d6g)50zul{6fOZfD5 zx^(<(+NuITAZTdtF*`7sbB~Mx0Ae$D>fg&k;5u>{h0Ft(%sL2>YF!TkK|`BOGW-0q z$<55rD9F$#2!Mm7ygZedyZydz;2gN<0Fly!zty-=mBaVpcsEVu>hcW{d1#Z{npnGY zBY*fjmK0%?8^t(;YR@+XG=oH^Fxc%lEVS$r0NiT$nf{@pT|4*Ae2zV-96O_3`)`13 k>$j#pJbyuT?P=Bf1qBx3B`dxp6aWAK07*qoM6N<$g68)pi~s-t diff --git a/files/opencs/error-log.png b/files/opencs/error-log.png new file mode 100644 index 0000000000000000000000000000000000000000..df698d14531014feb9f4bd4b8f6cb7e0a6e79fe1 GIT binary patch literal 518 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_pf zf~Tj8V~B-+?_^)^!wv#%!mLx9az&?fami_EaEbY^tq9Ps@(eq|wP?W)Mx~~T&#%=v zUY(sRp0D)6-e}b$AYYtbYGHswzt4=u5fR32!1AmELWs z{eA3L=(WljtVdH$X{xm^V->5+IK=Sgkn)^^oHhsN-3>n1+t6rjvR{>XPL~|}@x%6T`X{OjY>=$&eym3zu zI(3&ND0F-5Ukg1WQH_M2m)$Dg|8G&?`1QNr_-kbx_wQRR0lw$cZUBRx!PC{xWt~$( F697;y!(jjb literal 0 HcmV?d00001 diff --git a/files/opencs/Info.png b/files/opencs/info.png similarity index 100% rename from files/opencs/Info.png rename to files/opencs/info.png diff --git a/files/opencs/instance.png b/files/opencs/instance.png new file mode 100644 index 0000000000000000000000000000000000000000..ce63e64ed66830118ed9c25e6b817be8d9f96909 GIT binary patch literal 618 zcmV-w0+s!VP)UGq2zJSnbXnj}@gMNyS!ybGM&f17qqb7rHS+D`Y8@40%+--0DA>#p^QU(d|223)fMBN$mC(w)k z4GvZmAEh}@Fb1OF2(ejk0HnZEnjQQCEEsFc_-E0<*y$JX(e8;4?*ff34gF>F=DUc>^9NrYBwnAHePvXegqyAk9fnD9R{FTuppn z(}j)$y~ah`(K!Qu0Ven-O30eiwe|+ZC-5(zmp~tw;HqP6`xZSy{wv%*KmnSxw(ghb z&ob;9JKJbI*%VEW;5KX9U~h&&@A0+jJdMG+d-I|G3k+|{pCe3}VE_OC07*qoM6N<$ Ef&wfNegFUf literal 0 HcmV?d00001 diff --git a/files/opencs/leveled-creature.png b/files/opencs/levelled-creature.png similarity index 100% rename from files/opencs/leveled-creature.png rename to files/opencs/levelled-creature.png diff --git a/files/opencs/leveled-item.png b/files/opencs/levelled-item.png similarity index 100% rename from files/opencs/leveled-item.png rename to files/opencs/levelled-item.png diff --git a/files/opencs/magic-effect.png b/files/opencs/magic-effect.png index 4901724c59a363d1ddffc137a17fa33a7c9712cc..44b682bf4ff359ce3c8076379cf27ccffa46f673 100644 GIT binary patch delta 309 zcmV-50m}Zx0^|aaIDY{aNklLCuHjy6z0As;Z-) zxq~wdyvXUgTfXz&J0^(G`?a`VoukVy{ww4o1>S55K4B{!_FZP zOGw}NUL@Hy`G@O!AGp451Pel(Q{==1D@~$!l)NT(Fs{x5aHKY|71*9DYA(}xN0D?ps*$%;ZIZLZ|NSq-Eppq5al4-Z1f{O$dr(g400000NkvXX Hu0mjf>~55q delta 278 zcmV+x0qOqa0>uK5IDY{5NklWd8N#j$ zZS9YnH2a)kVd}bK!!UHtxr9$jn?A;Pn&Cx7Hsrw&lT!zo6ze6RuLx`dC#}lJwe4Icz=Iua zkum>)5JKLdY*qaSg2^(1_1zL+KucCeDGFwB1!0C?$S6G_`HRp`7Miz+{jSojUE&u=k07*qoM6N<$f>i>0%K!iX diff --git a/files/opencs/menu-close.png b/files/opencs/menu-close.png new file mode 100644 index 0000000000000000000000000000000000000000..81bc986775089a2ab8b8113096c890a47785e84f GIT binary patch literal 438 zcmV;n0ZIOeP)_eP4v08QYouW`+TlKHqY~h g-$r@ga{kZ#4H#~PogGl%VgLXD07*qoM6N<$f?Wu@<^TWy literal 0 HcmV?d00001 diff --git a/files/opencs/menu-exit.png b/files/opencs/menu-exit.png new file mode 100644 index 0000000000000000000000000000000000000000..f583536fb695627bb749c8e742df32f37e845995 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;j%S zi(`m|;L)Iif`<%vyo1~x&RF8ZkbH=3ZcCYHl8VLdecLiWy^yWD(y6Q2cS+!%Y#@8Z zSM!a%V%(O;BsO`JOV0bbL}HSw`BWdBlPzbN0#X;7xgOJ;J>z9nzN|tI-)sxE$?|lw*K7*&LpUXO@geCy5 CkYic^ literal 0 HcmV?d00001 diff --git a/files/opencs/menu-merge.png b/files/opencs/menu-merge.png new file mode 100644 index 0000000000000000000000000000000000000000..c76288ae168580eb28605cca6c82876f8a467639 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_Wn2 z#WBP}@Mw@DUxNY<^VjMR;fMaatbV!a%-QCPCsLE&TJ4%}>Aw1B#;w;HZPGQD8ukf2 zx>Fs0vQ(K>N8;+q*)wc$cFN$M5XTG67GU&z$gXm3P*ne%c33&Br`ObTm(;sHX z2B&I1pA?od-|(3E`}>h>Z^Seb*T(MsKJ(4qc=OsMf!R|g86B&sI-#?mfc442-en9I zTI(~MChy4*O3V4?((D$wvpkC_sD5|Xw)DRo5Obi@&TU?oclyq6(>vU+-;`lY>2dcjb9C=}__=+$ cM*mmV)hqqBP323C1p14?)78&qol`;+0L{H`dH?_b literal 0 HcmV?d00001 diff --git a/files/opencs/menu-new-game.png b/files/opencs/menu-new-game.png new file mode 100644 index 0000000000000000000000000000000000000000..701daf34b179e4c2e507788fd38366f9d83c9c07 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;iE) zi(`m|;M?Fpz6J#j*K1#|%-&+5~5+}}`+Zp=FKa9hVk!j1d-CNBj$lkWtcHpe!-tP?^(cYK51uD5Gc}kTEZqVBl zDLgA>p=XQoO?FjSQZyelF{r5}E+txm0xk literal 0 HcmV?d00001 diff --git a/files/opencs/menu-new-window.png b/files/opencs/menu-new-window.png new file mode 100644 index 0000000000000000000000000000000000000000..4a42da0d1288678a5bc4bd01a4713834f3aba7a0 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_W(8 z#WBP}@a&{$p#}vGSHs)$^uoTgA7tmVnEGvoX1z%3>!%tAt}Cx-oFeqH%A{SOp(AOn zXt?jRv)ayGv32JvZ#ABcopUPXz_;0VuQ|n6A9K0&wD|l2aS6jfwst<34O1?yxy3DK zydi13!awf=jv^LL@`8(RT)7eT%${Ml{*m{uI=*idtt{Qkm|Cl~`N^Y5DWH=WJYD@< J);T3K0RY~_Pj&zR literal 0 HcmV?d00001 diff --git a/files/opencs/menu-open.png b/files/opencs/menu-open.png new file mode 100644 index 0000000000000000000000000000000000000000..3766e17549830ca741e42055f9de51faa0263226 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;jxQ zi(`m|;M-tFp#}vGm)F(5Ch65K@NVz>D)x6iTdze%m~rV@Zk-L%icB>&T|bV^hKmsy{PJL1`?c#)bIR|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;Q%fF~q`u=_FgeLk2vplYKfC7fe@ae<-uT$iT3~EtJho@4%^2p+9SuczMr`2zYsN z?VtZwt(GqNkg;~^)^E8zmPaJcE4CzmEOdCk@QL=zR*Oykm)1@{T=zVtY0vl0No6|^ z$lX7BeG^x*)07%TTaOBPkK~VA-{g21IGArL|M+i`-ckvvC!zkq>6wkDCt@O)RmGa8<0ehNE3bfxb Z|CL*E(BR zh$s~OY9M~d^p1;H?c&h7l=#PCS+ z#x9>|~QaJOhytpOOywJKnq=q3<%szi> TOZHS?05EvE`njxgN@xNAl(>K= literal 0 HcmV?d00001 diff --git a/files/opencs/menu-reload.png b/files/opencs/menu-reload.png new file mode 100644 index 0000000000000000000000000000000000000000..2b780598c4162eed952e978ae557e465278f0085 GIT binary patch literal 447 zcmV;w0YLtVP)PGS7Qaa1b3$&sEeJkZubf1&Oe51JYd%SmYmz3wBv^o(wk+$D21{nnZwrGZXc+SW z?9#EOUGQ_ZZSO}>RD<6Z{?7v9$co&fRtdfkP6Xa(l2fKC!{2Ih3qDMZ0v}@AU{6Bz z4(jNO1#)#r3uRV}v;g-K{YpLs<(!;BrWnu@NS^$@qVk*tsTRUcr#b)47VhEL^(1Fbv;Z*HtX0>tt#fj9#%sAm8G002ovPDHLkV1nW%zJve( literal 0 HcmV?d00001 diff --git a/files/opencs/menu-save.png b/files/opencs/menu-save.png new file mode 100644 index 0000000000000000000000000000000000000000..4be88765bef48fc5b571e98b8c942dcca95934f8 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkQ1F4L zi(`m|;Mw44p=Jf1Cl_Bo42|*+dhL7Uv*KOXLjgznjQ?h)nJCWA;Sg9_Cvx^rqJ+}c zYf(pM_~> zlD$gAKoo`NXTd`Jc>ocFP49~&C^lABR^qz&11?yp zm59X#wel4#Oav`$7oWhqf`y2}HYV|#T@ohljE!EHopaB*_vG$Pg!p4fyFU!WE2Y#{ zS(ZXdDT<=FNYnJduK9^oMbP{VUZ$y?B#Gn>$mii-sv7T2QOvn>6~I4mZd{t-TIPpw zJB&STup`#sJ~*2WZA?W`RC+~(-}KQA%+eTh#$^IOi~i-(3|B4j41DV;I)(3fG*Q1s zp7cu56K8{0HzZ#GpKGnx^E~gsU6M~kZD16dj<`-(sxp6z*yb!@vX4m@lh}}UGz@IP z#u)c8*>Ra@yU>TTEPGsR2Xj*`eH;YA8~LEBt(GW04_C@zZ*?@c3lHwoJtl~mq|ZdV z69!sIKBzJ6!tY4zpo_^h(CnreJhk434>vphDfrf1ZvFO7F0=P1S)EDdcINxOnVs8R&-+XBegA~lT!yPk5k(d9JH8IX@Flsj zIBh_&LUh9?6d%h9vbZUVjRqH`RNEx13Hbd+QS>C-(RgkVB$kpUSFj<%vlN1N$TxTo zt}D4*jQD|%as&YQ0bfYY9GYe9&;(c^tXo8qOj(u%xH}XCftKpr1d`0gOx4g@za{=t z0}A)JbraSt$j&lm=N9=E?{h{2$)yiTf+nWOyodOI8QVZJipM5!Hkvq1=-jYK_yOTy VK0Na~gXjPN002ovPDHLkV1lPsh4=sf literal 0 HcmV?d00001 diff --git a/files/opencs/menu-verify.png b/files/opencs/menu-verify.png new file mode 100644 index 0000000000000000000000000000000000000000..a7878ebb3f6c4c77230f8c080c0115dd7aefb765 GIT binary patch literal 487 zcmVQSK@^4eAtF{Hh@^M-7uZ;7z)DyvLBUEPpkRpF z2qM7;f{l%!2o|vj+KO8UHnC8_GL;ZWIxPf=g&`np*6&U-i&;|`yl`gbo_o%n$F5=g zGt9W(^SnX0ZPPT|9HtCNVdxbO(-+ev*uz{F?$1HP dUz_|ie*;IC(~hX{6SDvS002ovPDHLkV1m_N&5r;8 literal 0 HcmV?d00001 diff --git a/files/opencs/metadata.png b/files/opencs/metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..34c78ffd61e30acaef3cc30960d59fe30979cc50 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;8rdF~q`uYVbkc!wNh-S6BXe!Er$1XZhB5lilTFn|}$k$2?+bN|AK`_H##Y%0=Pi zGbfaqOl&Nte{=o3IEKSujtOtQv4#^z&}Z)2DEnZqRMCC6Ep$|)Sbu~y#5zhbWEi_K ztmey;%ICH@yCCoX4PA+OX$h@+&Mqi-oPY6NuH=Kdf`h@*dKZdWwa+U};AYTX&E{Hb z794Q)wJO7WhcEkXcD*$Ez~FEU%PZr9_qKJsy8l!9 b=5OW&4^?AKLb7;(eq!)+^>bP0l+XkK>c5Fr literal 0 HcmV?d00001 diff --git a/files/opencs/object.png b/files/opencs/object.png new file mode 100644 index 0000000000000000000000000000000000000000..4f552151dbd78992f83ed7939f25356e38fc4f79 GIT binary patch literal 447 zcmV;w0YLtVP)7PS= z*9Sq+2ez%0S|W<&BuVxuw!$#{C_oa&ED+Zm$9V#)q-nZ_$RkFGD%iN|y6VL5Km^w4^@K7r`^EyY56&bcZxpns_ca?oa) zMDuE1;wh>l8)eQ}WImHzx3JEu8KkGNoX~NCGXr-RLQP27?L|@a;`_ehVj7<3wW;ra zb3i-_M_%Iw$G;Iy@g+(Dp$=h({#uj=#D(CDDPn=OA=c&)A7?0!F9rf);xR1~RRmpv pA(9{HoY+O&6wG>H_+t}O{0UfliPDznHH!cM002ovPDHLkV1iEIw{idg literal 0 HcmV?d00001 diff --git a/files/opencs/placeholder.png b/files/opencs/placeholder.png index 1d3df3c47d76bd213e958a44e752e4693e537f32..b9db9f18886fbefe5068cadfd31409136f20af42 100644 GIT binary patch delta 2140 zcmV-i2&4DU65bGy8Gix*007uvZqNV#00v@9M??Vs0RI60puMM)00009a7bBm000XU z000XU0RWnu7ytkO2XskIMF-*q1`!-LIsNgF0000PbVXQnLvL+uWo~o;Lvm$dbY)~9 zcWHEJAV*0}P*;Ht7XSbU#z{m$R9M69m~Bi_T^zs<2wegR5PuY3P?r>%S(=$S$O@f^ zIYBcuo%W&8hrX13uvSZ(%k{DxYpbm-O?#V_OIvDbsl%a}D{YBdzNFckpr}b^aRKr9 zaJRer0P)h(p7pfn|LNZIKm7UKbN+AV;Igx`!S5t^@SDk>4s`VB(Ty87{w_dbu{ba= zaCmt5pBN#yFn^Um005XwCYxFaf|8Syo12^G=jW}pL?V&FV7R%txw*MbOiY-~=3fAm zk&&@s!-m6$58Kr4-Me>gZmy=L#$tT?_U+Np(ZRvNI2>+iX$b%T!*FkJZ$m@Fr%#{$ z5zyw%o4?4VF6OiN265{Z3%eSfD((t>wwye9 zvcA6l-o1N22_%t7yu7>y2M29xckSBM*Vp&@^=ot_D=SMR5)BOv$>nm3T~;cUN~JPB zK7RM^-7pM4eE9HZoSM()tJP|oD-;UeH9*-vyi53?ZZ{NP%(9rPt z^Jkq-XUDQyt!A^?t^uW|r$Z1_U0qG5)3sWyg?~0NF|o6=6A5(!fgqE~mMvS>*x1reJY zqtUnql%1VzG#YDaY9I(AlgVi4A`%xD*WTU^!!Yt16B7f&@QoWcoKGcYvl)-aI}1dm zQh#G(V;?eo^}=} zFE3B6RzH6H7y#hy?QIcad_KRwzaK@6jEn>TJbn7qrOL%*GG}IHoC4x-I8jkiXen5} ze7VtRM73lxIV21+MoC3+6f%L5BiCPaA06Suh*m6sHiBNPG>(UTgbkB`?OkZXJ@AaAP9mI5)xWlThZ)-2-?j6 z0764U+uPeMeABwB*pZx^oY2tFix)3CZejQA*`w3xtlJcHh4_5F$z*!|{5e)jB9ROR zgOfA~2?=p=ag~*oHZ#`Wfc*UYVt-;{YHMq)=NXMg2!ap?8XFt4(0=*yg-j+pLnD<+ zGcz+EJa}O7Nw$IV^73B2dezg@V?A$bYKl&$0|2;O?#Rf9bgnkj9v;rm&u1_gHh)WyOeQ;j z{yd#dKXKwjb91vDZXEU~ZINs&e5j(LLa9^+1qB^He%$5)6ciM2x!lpw(b?JAg@px! z!N6j%*lf0skI(q{cw1YWMJPBvb^M41Da#)|d=Ll()|mql7=}wqN&*4`ghC;eO7-#a zfgoshcDA>-x2&w}-Me?c{(oH7+}yl#=T7^b*ZcSHf3)dN3hw{gkN-N*xpU`2LPCBc zpziMO@$vD0?eB|YuCasTwh;5 zF);xEkjZ3Eo;(qWM00aP;^JZog;HK#UQ|@%AP-3- z5(0s6?AS4EOW)PiRexDo2>>{A=1h8edRbW+4u?}H6fG?+EEY>Hm%F>WqfJPEe}7n5 zSY2IRbab?vn_Eju%Y_RU6beOcZEav+;HgumNF>tPvuDf8%Vjdzw{PDZ05KR0i{6{b zWTM$tqtUQfEaZ7$U;qFxJw1)X;XFJ%P@u-f#^T~)5{a~F(|@MH!9hG8&tNbT6B9)u z5dgqoFmN~=WZ#|2)G!RYySrmY^m;v$$wZzh6w1QFg8gdq@#DvdiHVey6dsRv=gu9o z*$l(*-Me?OmDOg)VmlA@_4RpqdBw%W0RX(byZ`{BqoZrqtfA3p>(;H~a5zIlLk=TI zrBX#iMBs2Zq<>Ex8yky>i9wu?$KxR%xwyE<<#O!-nM|gorKJ}yUMLibyu7@!va*7L z0sz3(t5@-Oyj(8N&(Bw>RE>>|0D#5CMeGkPOx4xZd3$@Kw^$Vw6+|Mjw6yf{<;%Ib zxk$lQsZ@s!9XfL4h_kf}>!-Fdf!a=pS zXx*Kp!Q=5B9v*3FX_J$aSQ^BFg%NCsxz>I-FH|bEu&{7$ZVpKk|F<81Hqc+BEpsT* S7HYBp0000DsmHZ}0aX5fB&{$e=-k zc>etPZ|_^~=!;V6ckT7{^^Ff8m!s3^*uH%`b#-+%{bVv3EnBukDwQIYN_qG09cFXW z@x@^QTCJAxTrrc9YaRaF)F`T3&l{{H@q9Xl4SR*O_BrM|u%v)N2dO%2D7 zALrDmQ@nfk?)M9rI&~`V-@k9vr+xeOOrAWM`1p9MwqCt@k(88#i;D|-y`GsfXY%63 z3!&Z5&yR@{Cvy1kVUm-RxpwWE=ok+D14D)k!OzdHNdYrw&gA6DlNbyJ3;T!>BiOQK z3wQ6{B_<|@%*;#+@&Ki!rEJ)+0i8}qQc@BF2M+vw0;WxyMsaa5A3l7r>C>f47lscX z&Vd65EbOtdu_Pxa)3$9}#*7(5L_|cRV-*z@v1!vL7A;yN{CCX~5EvMUr>7@3Z{BQl z24>BgMOIc8A3uH+Y~kVIBqStITU*PbMT>}visI6xOHGcMpPx@{Z7o4TLCqJCkdVOc z-Mdk#RJ?liO4P^S-=BW{`jMTTE!aFgJ<;iOm`o;?EnCK|TeqmKt>x3FPj-!4TwIK| zw|DadgocKqR4Tc6@giPcUQ|_8iTcf%Gl$H~Od)JarBYU}UX4G zm6g$@OBcrlNTpH|5)#PB$e_Ny9xpF1s;jH5{DOjl@bdEF#EBDvO{>)s7#PUWqenS^ z{`@bK2PiKuZ=QhBqeoL$SI6bcmyyfmxVpMpMGk=3vuBf$k-^umUj^Ir>C>sHsUagH z!y)qUeZ!Q=WL9>&1Sk{=;^X5AO1(gd4}i;GoWyGjUzR!5io;@il zDza*8R6ys>ozZHwWM*bs*u`cbDk_STCr?`Sq0wlXEg(8Nn%vx6(QLL+0rTh2XWzbk zR_k4%P*`k0YPA|4A0Mtz2dh<#IXE(a{_|dQ`OCrT~pbgIcZT z$dMyfZ8DjRZ{NNN&ykUloH=s_lgVV^=k4u{!CN;5Bqk;@fByVN_YW%p?b@{?Ha51=lU7|_ z9W7h71YqdUp%fGpi2A*K`xcc-^{d>`Xf&)`xssHW6wwd!BdnIt?Afz9ckZ0%rqO_^ zsw&iKHSk@4t(kiH@+Ce#J}8w+yNJu>a^}vRi%zFw<;s;d`?V>cO`A3Z1qHEZ&z?qK zs;;g^tya^fO&ffDeYt)6wy0luc{zD`d8}HsN|-S*C=?1NO`62v!-w(m@**xSj{EoT zH%T5q@^fkN=LjSEl0?FpGiOLoPe-HCuypBCo4#_noE0lpFm&ip^7HelsHou0n>T#= z^a&pyAN>6M2nYz^#*G^sI&_Ffj~>}Y+@=6~v2o)@`u6S1u3fuq#^@iRQmGg=Y#1IM z9=N%=(W+G|Dl02_`0yb`MMb=R{n`;4jtlVf^CKxKiG>RnHY>|)E*uu{|AN28-9HUc z0cmMzbm-9GpXTBaLV-McrTp`jR!M)Z2UP=K8@X%bhiTw&a}aTp8+^78U5DtHQoLa2E*RB-ClDZyvJfB{G(5-YBvGY}USN9)$Dk;~7Gf986hR z8P3km00ajIvwr=0K7Ra2r%s(nPfr)pg~Y@}7B61RlP6E;-@iX=)~vDUyRNQ|QKLpN zXwV?RwqU^m;Ctn6!GZ;pmzNU~62h`&%K%78Nx{v{jojQ^)~#FD^b9m0BqRidLc!w2 zi!JQ2v9aviw@=u>cI?xqtzMk0|2$Hl8xuQFl61mS=3^z;P4WHMngneg!Nuus63FJCa5 z%~VuWkeZsxwr$%ucI+6NH*W@D@7}%0WHPd|vq?`+=fsHTL^Bsg#_Y z9OlfKV|9ZglgW_FlpYv1?F3dfp;iHt}kX!rUZtv1xl#xXO414!0&U0@qnv?^=M45CNIfjVPRmCG|D z=mLT^S;hHfLLl#18)`DqYXqKo*N)?uae^rU-w_jmXWp|$${Fe2eqvU#Q+!enb~CdV z>Fp&?Y0=FWStmpSrPGJYDWGKpkaKCBc{u%y41luSs=7vL&$5c%37?6htE z`_NyTN&o>+-#$39Yzk W4f$`6a&e*n0000004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00009a7bBm000XT000XT0n*)m z`~Uy|8gxZibW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE-^7j^M67&0004?Nkl3&4Kk!Qef*>(s(8?|rTKK3e^m73jt?xw zF<~}L_cB}AWHM{*B;>as2xgJYK|UiWQ%4(pe062$w|`jX1Ntsd8QEwNPW>q-ThMA@ zqmTLq(F!iVqw)or_E6>qCVq@_i?YJXrQ++{QXiZ(2C@H7z!q4}Z(BeGt8ojJpAHe1 zy(fo=bJ_4I{`!o0o1gD-!AjM?e@)4Pr~QUVBwIK3>F)vQsWb&$S&^r&QW?aV;lvYE zu4git@ja1!AU!?f+FT$mk}i{#TY$^d;msq#$xNIH8O8}C;8g*c1MByk2>1lNhz?s& St*b}?0000;*Roco)Imd;0Jd^?ycU1meGlDguJ39An%? z$E1+#>$+A%j*2)edm{Kr;UK|Hr3j1u^a!kSOSNF48I-`Xk(sQ$%3{~vEX$(A&clothing.png container.png creature.png + debug-profile.png dialogue-info.png dialogue-journal.png dialogue-regular.png @@ -27,34 +28,60 @@ journal-topics.png door.png enchantment.png + error-log.png faction.png filter.png global-variable.png gmst.png - Info.png + info.png ingredient.png + instance.png land-heightmap.png land-texture.png - leveled-creature.png - leveled-item.png + levelled-creature.png + levelled-item.png light.png lockpick.png magic-effect.png magicrabbit.png map.png + probe.png + menu-close.png + menu-exit.png + menu-new-addon.png + menu-new-game.png + menu-new-window.png + menu-merge.png + menu-open.png + menu-preferences.png + menu-reload.png + menu-redo.png + menu-save.png + menu-search.png + menu-status-bar.png + menu-undo.png + menu-verify.png + metadata.png miscellaneous.png list-modified.png npc.png + object.png pathgrid.png potion.png - probe.png race.png random-item.png random.png list-removed.png + region.png + region-map.png repair.png + run-log.png + run-openmw.png + scene.png script.png skill.png + start-script.png + stop-openmw.png sound-generator.png sound.png spell.png @@ -64,7 +91,6 @@ record-next.png record-previous.png record-delete.png - record-revert.png record-preview.png record-clone.png record-add.png diff --git a/files/opencs/run-log.png b/files/opencs/run-log.png new file mode 100644 index 0000000000000000000000000000000000000000..463ead176d4c5e367262864fcbcf4e5a19d23ca8 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=G_YAk0{w5XjGVuN& z)o`BQMRLN785QCkyc;B4)-e8`JbCheX5LKZB)*j94l{-of+-9<%MbD>o@Q8)?Qn0C z;^~GZOH?L!-(%d!ZprqCHS3JxO>Rw1O@-*`OorT+jTHhb0;j&oP_b4FoFlGb#4yG3 k&)>hDhYYqVtY>04?6v!2)}vJ`fv#onboFyt=akR{0N)K>X#fBK literal 0 HcmV?d00001 diff --git a/files/opencs/run-openmw.png b/files/opencs/run-openmw.png new file mode 100644 index 0000000000000000000000000000000000000000..1033d62baa120ce5c3ae4e2da94d122f88051a7d GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb==d%F~q_@`Op9V_RKs6Mn;Ejwn!AzDzNdKKKJL0mxr>C+%oock}3-3zi-zsP&|7? zqUkEbf@l9X2{_+l*vM(uTp-P0#M1HfSq=Ne^2QqsvmIG@8FsU!T*%qrnCoz+F-PDc zTLbT`uc8?z#3wjrRxk^uY3oV7lA5qR$D^3>j`ahEI`$)SOC%Y-9eTdsVUF`6@qK@$ z&;BjEq*7C0^8}HO3mG;XS5|mMnK%gvaU7p8dBQ)%-;-sSTRKm&E0{(kGg@W~CkuGk ZGJNsgqSE=Fa~{x344$rjF6*2UngEJofxiF% literal 0 HcmV?d00001 diff --git a/files/opencs/scene.png b/files/opencs/scene.png new file mode 100644 index 0000000000000000000000000000000000000000..a9fc03f7699a715592edd31db78ad47ac514f006 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;Z-gF~q_@IYEL|Lh-@t*V$Z0!W(|@3;h57`SWl7MU6jL1fn1C7%foY7O>~x;9$^< zV?46nan?dM8J17ifB*j7{6vn)P~L3ze@4}JjVcW#`~vyxj{+3ZSROGu*l=r^OTSW5 z+@a{usPC|cS&bod0`rpcLq{C+94D=3J@wbg?14$Z0;WjA3G&=u&!s9@b3Lg^NoPKq zx442?}xUyxj@&tLlBR3uPh;*bY?0g_T;e2NvgD_{~i9UU^lm<3m=10dDY-l)k mC|qH;aK}@(V7oL928Qm_H3#l%PWuYpUXO@geCwN2ZUMx literal 0 HcmV?d00001 diff --git a/files/opencs/start-script.png b/files/opencs/start-script.png new file mode 100644 index 0000000000000000000000000000000000000000..73ed157b9ed2c2366388723f8e5b25fff4bc3b97 GIT binary patch literal 359 zcmV-t0hs=YP)Q%iD#FbuV^VUy_zGW14pjAq*dWSMD~X{RfgUV_h! zasz4tfSa9O0ryxh=a?XB zf=o_JY^ti7sR{sWwoTKF2s#(|144HJfK!Jo%YqE1X-W|M{1Zd~{5ylcrur;`3~-I8 zuIr0ees$m;`MIkGG46U-GxZ99s|LS>;vs3-Lt1pt{7g}-u;fDPya%O?-LK1jXrETj zY5{PM3os7K82hm0O4G9UrzV_I}Un^?hcdO-#kvH8dF z#R=Wh@(Uh6k0j)d-_HyP1Xtj^h`ff5tb+0Uij%io@C{lR|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zW$)?Y7-Hd{{OA9FduAR3Bcnq%TO Date: Thu, 13 Sep 2018 22:15:59 +0400 Subject: [PATCH 027/150] Do not use universal IDs for menu items --- apps/opencs/model/world/universalid.cpp | 17 -------- apps/opencs/model/world/universalid.hpp | 21 +--------- apps/opencs/view/doc/view.cpp | 54 +++++++++++++------------ apps/opencs/view/doc/view.hpp | 1 + 4 files changed, 32 insertions(+), 61 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 6f9eeb24fd..486f3770aa 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -48,8 +48,6 @@ namespace { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunGame, "Run OpenMW", ":./run-openmw.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StopGame, "Stop OpenMW", ":./stop-openmw.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" }, @@ -57,21 +55,6 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Redo, "Redo", ":./menu-redo.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Undo, "Undo", ":./menu-undo.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Preferences, "Preferences", ":./menu-preferences.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Reload, "Reload", ":./menu-reload.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewWindow, "New View", ":./menu-new-window.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StatusBar, "Toggle Status Bar", ":./menu-status-bar.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewGame, "New Game", ":./menu-new-game.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewAddon, "New Addon", ":./menu-new-addon.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Open, "Open", ":./menu-open.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Save, "Save", ":./menu-save.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Verify, "Verify", ":./menu-verify.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Merge, "Merge", ":./menu-merge.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_ErrorLog, "Error Log", ":./error-log.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Close, "Close", ":./menu-close.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Exit, "Exit", ":./menu-exit.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index aa5276ba71..accd1b78da 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -137,27 +137,10 @@ namespace CSMWorld Type_Search, Type_MetaDatas, Type_MetaData, - Type_RunLog, - Type_RunGame, - Type_StopGame, - Type_Undo, - Type_Redo, - Type_Preferences, - Type_NewWindow, - Type_StatusBar, - Type_NewGame, - Type_NewAddon, - Type_Open, - Type_Save, - Type_Verify, - Type_Merge, - Type_ErrorLog, - Type_Close, - Type_Exit, - Type_Reload + Type_RunLog }; - enum { NumberOfTypes = Type_Reload+1 }; + enum { NumberOfTypes = Type_RunLog+1 }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 97aaac4d79..ca90b11ff4 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -47,37 +47,37 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("File")); - QAction* newGame = createMenuEntry(CSMWorld::UniversalId::Type_NewGame, file, "document-file-newgame"); + QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame"); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); - QAction* newAddon = createMenuEntry(CSMWorld::UniversalId::Type_NewAddon, file, "document-file-newaddon"); + QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon"); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); - QAction* open = createMenuEntry(CSMWorld::UniversalId::Type_Open, file, "document-file-open"); + QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open"); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); - QAction* save = createMenuEntry(CSMWorld::UniversalId::Type_Save, file, "document-file-save"); + QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save"); connect (save, SIGNAL (triggered()), this, SLOT (save())); mSave = save; - QAction* verify = createMenuEntry(CSMWorld::UniversalId::Type_Verify, file, "document-file-verify"); + QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify"); connect (verify, SIGNAL (triggered()), this, SLOT (verify())); mVerify = verify; - QAction* merge = createMenuEntry(CSMWorld::UniversalId::Type_Merge, file, "document-file-merge"); + QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge"); connect (merge, SIGNAL (triggered()), this, SLOT (merge())); mMerge = merge; - QAction* loadErrors = createMenuEntry(CSMWorld::UniversalId::Type_ErrorLog, file, "document-file-errorlog"); + QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog"); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); - QAction* close = createMenuEntry(CSMWorld::UniversalId::Type_Close, file, "document-file-close"); + QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close"); connect (close, SIGNAL (triggered()), this, SLOT (close())); - QAction* exit = createMenuEntry(CSMWorld::UniversalId::Type_Exit, file, "document-file-exit"); + QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit"); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); @@ -112,22 +112,16 @@ void CSVDoc::View::setupEditMenu() mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); setupShortcut("document-edit-undo", mUndo); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); - std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Undo).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - mUndo->setIcon(QIcon(QString::fromStdString(iconName))); - + mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png"))); edit->addAction (mUndo); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); setupShortcut("document-edit-redo", mRedo); - iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Redo).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - mRedo->setIcon(QIcon(QString::fromStdString(iconName))); - + mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png"))); edit->addAction (mRedo); - QAction* userSettings = createMenuEntry(CSMWorld::UniversalId::Type_Preferences, edit, "document-edit-preferences"); + QAction* userSettings = createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences"); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); @@ -138,10 +132,10 @@ void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("View")); - QAction *newWindow = createMenuEntry(CSMWorld::UniversalId::Type_NewWindow, view, "document-view-newview"); + QAction *newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview"); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - mShowStatusBar = createMenuEntry(CSMWorld::UniversalId::Type_StatusBar, view, "document-view-statusbar"); + mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar"); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); mShowStatusBar->setCheckable (true); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); @@ -248,7 +242,7 @@ void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("Assets")); - QAction* reload = createMenuEntry(CSMWorld::UniversalId::Type_Reload, assets, "document-assets-reload"); + QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload"); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged())); assets->addSeparator(); @@ -299,11 +293,9 @@ void CSVDoc::View::setupDebugMenu() QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); setupShortcut("document-debug-run", runDebug); - std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_RunGame).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - runDebug->setIcon(QIcon(QString::fromStdString(iconName))); + runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png"))); - QAction* stopDebug = createMenuEntry(CSMWorld::UniversalId::Type_StopGame, debug, "document-debug-shutdown"); + QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown"); connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop())); mStopDebug = stopDebug; @@ -325,6 +317,18 @@ QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* return entry; } +QAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName) +{ + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; +} + void CSVDoc::View::setupUi() { setupFileMenu(); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index e767777d76..76c81b964a 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -67,6 +67,7 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName); + QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName); void setupFileMenu(); From 363516049d449d21c98f161705894192e858a112 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 14 Sep 2018 18:37:04 +0300 Subject: [PATCH 028/150] Actually fix General number formatting --- components/interpreter/miscopcodes.hpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 03b7e186f5..77e5a00790 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -65,23 +65,18 @@ namespace Interpreter } else if (notation == ShortestNotation) { - std::string scientific; - std::string fixed; - - out << std::scientific << value; - - scientific = out.str(); + out << value; + std::string standard = out.str(); out.str(std::string()); out.clear(); - out << std::fixed << value; + out << std::scientific << value; + std::string scientific = out.str(); - fixed = out.str(); - - mFormattedMessage += fixed.length() < scientific.length() ? fixed : scientific; + mFormattedMessage += standard.length() < scientific.length() ? standard : scientific; } - else + else { out << std::scientific << value; mFormattedMessage += out.str(); From d3aa5840ec380fcb029e795f31f7158f46d195a7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 16:57:32 +0300 Subject: [PATCH 029/150] Refactor magic effect record verifying --- apps/opencs/model/tools/magiceffectcheck.cpp | 93 +++++++------------- apps/opencs/model/tools/magiceffectcheck.hpp | 11 +-- 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 531bd9e1d6..448e4d28c3 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -9,7 +9,7 @@ namespace { - void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) + void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) { if (!text.empty()) { @@ -18,42 +18,34 @@ namespace } } -bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const +std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { + if (texture.empty()) return (isIcon ? "Icon is not specified" : std::string()); + const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; - bool exists = false; + if (textures.searchId(texture) != -1) return std::string(); - if (textures.searchId(texture) != -1) - { - exists = true; - } - else - { - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) - { - exists = true; - } - } + std::string ddsTexture = texture; + if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) return std::string(); - return exists; + return (isIcon ? "Icon '" : "Particle '") + texture + "' does not exist"; } -std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id, +std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { std::string error; if (!id.empty()) { - CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); + CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); if (index.first == -1) { - error = "No such " + column + " '" + id + "'"; + error = column + " '" + id + "' " + "does not exist"; } else if (index.second != type.getType()) { - error = column + " is not of type " + type.getTypeName(); + error = column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"; } } return error; @@ -64,19 +56,19 @@ std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, c std::string error; if (!id.empty() && mSounds.searchId(id) == -1) { - error = "No such " + column + " '" + id + "'"; + error = column + " '" + id + "' " + "does not exist"; } return error; } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures) : mMagicEffects(effects), mSounds(sounds), - mReferenceables(referenceables), + mObjects(objects), mIcons(icons), mTextures(textures) { @@ -100,46 +92,25 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa ESM::MagicEffect effect = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); - - if (effect.mData.mBaseCost < 0.0f) - { - messages.push_back(std::make_pair(id, "Base Cost is negative")); - } - - if (effect.mIcon.empty()) - { - messages.push_back(std::make_pair(id, "Icon is not specified")); - } - else if (!isTextureExists(effect.mIcon, true)) - { - messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); - } - - if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) - { - messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); - } - - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object")); - - addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound")); if (effect.mDescription.empty()) { - messages.push_back(std::make_pair(id, "Description is empty")); + addMessage(messages, id, "Description is missing"); } + + if (effect.mData.mBaseCost < 0.0f) + { + addMessage(messages, id, "Base cost is negative"); + } + + addMessage(messages, id, checkTexture(effect.mIcon, true)); + addMessage(messages, id, checkTexture(effect.mParticle, false)); + addMessage(messages, id, checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object")); + addMessage(messages, id, checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object")); + addMessage(messages, id, checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object")); + addMessage(messages, id, checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object")); + addMessage(messages, id, checkSound(effect.mCastSound, "Casting sound")); + addMessage(messages, id, checkSound(effect.mHitSound, "Hit sound")); + addMessage(messages, id, checkSound(effect.mAreaSound, "Area sound")); + addMessage(messages, id, checkSound(effect.mBoltSound, "Bolt sound")); } diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 28a4062832..3e45495c18 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -21,23 +21,20 @@ namespace CSMTools { const CSMWorld::IdCollection &mMagicEffects; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; private: - bool isTextureExists(const std::string &texture, bool isIcon) const; - - std::string checkReferenceable(const std::string &id, - const CSMWorld::UniversalId &type, - const std::string &column) const; + std::string checkTexture(const std::string &texture, bool isIcon) const; + std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; std::string checkSound(const std::string &id, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures); From 51fdb94e34427ca01a70228c41791f7e2e49684b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 17:37:07 +0300 Subject: [PATCH 030/150] Add texture check to birthsign verifier --- apps/opencs/model/tools/birthsigncheck.cpp | 42 +++++++++++++++++----- apps/opencs/model/tools/birthsigncheck.hpp | 14 ++++++-- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index fc29893076..4ef264f657 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -4,13 +4,41 @@ #include #include +#include #include "../prefs/state.hpp" +#include "../world/data.hpp" +#include "../world/resources.hpp" #include "../world/universalid.hpp" -CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) -: mBirthsigns (birthsigns) +namespace +{ + void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) + { + if (!text.empty()) + { + messages.push_back(std::make_pair(id, text)); + } + } +} + + +std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const +{ + if (texture.empty()) return "Texture is missing"; + if (mTextures.searchId(texture) != -1) return std::string(); + + std::string ddsTexture = texture; + if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); + + return "Texture '" + texture + "' does not exist"; +} + +CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, + const CSMWorld::Resources &textures) +: mBirthsigns(birthsigns), + mTextures(textures) { mIgnoreBaseRecords = false; } @@ -34,17 +62,13 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); - // test for empty name, description and texture if (birthsign.mName.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); + addMessage(messages, id, "Name is missing"); if (birthsign.mDescription.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); + addMessage(messages, id, "Description is missing"); - if (birthsign.mTexture.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); - - /// \todo test if the texture exists + addMessage(messages, id, checkTexture(birthsign.mTexture)); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index a8a7a2c14a..cd22fb6d0f 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -7,17 +7,27 @@ #include "../doc/stage.hpp" +namespace CSMWorld +{ + class Resources; +} + namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent class BirthsignCheckStage : public CSMDoc::Stage { - const CSMWorld::IdCollection& mBirthsigns; + const CSMWorld::IdCollection &mBirthsigns; + const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; + private: + std::string checkTexture(const std::string &texture) const; + public: - BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); + BirthsignCheckStage (const CSMWorld::IdCollection &birthsigns, + const CSMWorld::Resources &textures); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 445db53af7..0946225cff 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -78,7 +78,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); - mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures))); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); From 5249492a31f31f04d2acd16c691af82df61f3f05 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 20:13:20 +0300 Subject: [PATCH 031/150] Update skill record verifier messages --- apps/opencs/model/tools/skillcheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index b34d18e2a9..a2bf3ff032 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -38,11 +38,11 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) { std::ostringstream stream; - stream << "Use value #" << i << " of " << skill.mId << " is negative"; + stream << "Usage experience value #" << i << " is negative"; messages.push_back (std::make_pair (id, stream.str())); } if (skill.mDescription.empty()) - messages.push_back (std::make_pair (id, skill.mId + " has an empty description")); + messages.push_back (std::make_pair (id, "Description is missing")); } From cf7a8c5775a6dbf29536b6a1c1e7a9c91b6dc9a9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 20:20:27 +0300 Subject: [PATCH 032/150] Update soundgen record verifier messages --- apps/opencs/model/tools/soundgencheck.cpp | 12 ++++++------ apps/opencs/model/tools/soundgencheck.hpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 3692259cee..99a8c184cc 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -9,10 +9,10 @@ CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables) + const CSMWorld::RefIdCollection &objects) : mSoundGens(soundGens), mSounds(sounds), - mReferenceables(referenceables) + mObjects(objects) { mIgnoreBaseRecords = false; } @@ -37,10 +37,10 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages if (!soundGen.mCreature.empty()) { - CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); + CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { - messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); + messages.push_back(std::make_pair(id, "Creature '" + soundGen.mCreature + "' doesn't exist")); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { @@ -50,10 +50,10 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages if (soundGen.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound is not specified")); + messages.push_back(std::make_pair(id, "Sound is missing")); } else if (mSounds.searchId(soundGen.mSound) == -1) { - messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); + messages.push_back(std::make_pair(id, "Sound '" + soundGen.mSound + "' doesn't exist")); } } diff --git a/apps/opencs/model/tools/soundgencheck.hpp b/apps/opencs/model/tools/soundgencheck.hpp index 19388cb915..3c2a7f071d 100644 --- a/apps/opencs/model/tools/soundgencheck.hpp +++ b/apps/opencs/model/tools/soundgencheck.hpp @@ -12,13 +12,13 @@ namespace CSMTools { const CSMWorld::IdCollection &mSoundGens; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; bool mIgnoreBaseRecords; public: SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables); + const CSMWorld::RefIdCollection &objects); virtual int setup(); ///< \return number of steps From fd1a3ad88d1922246e19e667a1a5aac789fff078 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:02:42 +0300 Subject: [PATCH 033/150] Update object and script record verifier messages --- .../opencs/model/tools/referenceablecheck.cpp | 164 ++++++++---------- apps/opencs/model/tools/scriptcheck.cpp | 9 +- apps/opencs/model/tools/soundcheck.cpp | 2 +- 3 files changed, 80 insertions(+), 95 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3e8dc11887..6d06cce121 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -272,7 +272,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, activator.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -340,11 +340,11 @@ void CSMTools::ReferenceableCheckStage::armorCheck( //checking for armor class, armor should have poistive armor class, but 0 is considered legal if (armor.mData.mArmor < 0) - messages.push_back (std::make_pair (id, armor.mId + " has negative armor class")); + messages.push_back (std::make_pair (id, "Armor class is negative")); //checking for health. Only positive numbers are allowed, or 0 is illegal if (armor.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); + messages.push_back (std::make_pair (id, "Durability is non-positive")); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); @@ -385,16 +385,15 @@ void CSMTools::ReferenceableCheckStage::containerCheck( //Checking for model, IIRC all containers should have a model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, container.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed - messages.push_back (std::make_pair (id, - container.mId + " has negative weight (capacity)")); + messages.push_back (std::make_pair (id, "Capacity is negative")); //checking for name if (container.mName.empty()) - messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); @@ -417,61 +416,60 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE if (creature.mName.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); //stats checks if (creature.mData.mLevel < 1) - messages.push_back (std::make_pair (id, creature.mId + " has non-positive level")); + messages.push_back (std::make_pair (id, "Level is non-positive")); if (creature.mData.mStrength < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); + messages.push_back (std::make_pair (id, "Strength is negative")); if (creature.mData.mIntelligence < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence")); + messages.push_back (std::make_pair (id, "Intelligence is negative")); if (creature.mData.mWillpower < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative willpower")); + messages.push_back (std::make_pair (id, "Willpower is negative")); if (creature.mData.mAgility < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative agility")); + messages.push_back (std::make_pair (id, "Agility is negative")); if (creature.mData.mSpeed < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative speed")); + messages.push_back (std::make_pair (id, "Speed is negative")); if (creature.mData.mEndurance < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative endurance")); + messages.push_back (std::make_pair (id, "Endurance is negative")); if (creature.mData.mPersonality < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative personality")); + messages.push_back (std::make_pair (id, "Personality is negative")); if (creature.mData.mLuck < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative luck")); + messages.push_back (std::make_pair (id, "Luck is negative")); if (creature.mData.mHealth < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative health")); + messages.push_back (std::make_pair (id, "Health is negative")); if (creature.mData.mSoul < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative soul value")); + messages.push_back (std::make_pair (id, "Soul value is negative")); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) { - messages.push_back (std::make_pair (id, - creature.mId + " has negative attack strength")); + messages.push_back (std::make_pair (id, "One of attacks has negative damage")); break; } } //TODO, find meaning of other values - if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures - messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); + if (creature.mData.mGold < 0) + messages.push_back (std::make_pair (id, "Gold count is negative")); if (creature.mScale == 0) - messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); + messages.push_back (std::make_pair (id, "Scale is equal to zero")); // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); @@ -495,10 +493,10 @@ void CSMTools::ReferenceableCheckStage::doorCheck( //usual, name or model if (door.mName.empty()) - messages.push_back (std::make_pair (id, door.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, door.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -572,7 +570,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) - messages.push_back (std::make_pair (id, light.mId + " has negative light radius")); + messages.push_back (std::make_pair (id, "Light radius is negative")); if (light.mData.mFlags & ESM::Light::Carry) inventoryItemCheck(light, messages, id.toString()); @@ -667,71 +665,69 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( else { if (npc.mNpdt.mAgility == 0) - messages.push_back (std::make_pair (id, npc.mId + " agility has zero value")); + messages.push_back (std::make_pair (id, "Agility is equal to zero")); if (npc.mNpdt.mEndurance == 0) - messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value")); + messages.push_back (std::make_pair (id, "Endurance is equal to zero")); if (npc.mNpdt.mIntelligence == 0) - messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value")); + messages.push_back (std::make_pair (id, "Intelligence is equal to zero")); if (npc.mNpdt.mLuck == 0) - messages.push_back (std::make_pair (id, npc.mId + " luck has zero value")); + messages.push_back (std::make_pair (id, "Luck is equal to zero")); if (npc.mNpdt.mPersonality == 0) - messages.push_back (std::make_pair (id, npc.mId + " personality has zero value")); + messages.push_back (std::make_pair (id, "Personality is equal to zero")); if (npc.mNpdt.mStrength == 0) - messages.push_back (std::make_pair (id, npc.mId + " strength has zero value")); + messages.push_back (std::make_pair (id, "Strength is equal to zero")); if (npc.mNpdt.mSpeed == 0) - messages.push_back (std::make_pair (id, npc.mId + " speed has zero value")); + messages.push_back (std::make_pair (id, "Speed is equal to zero")); if (npc.mNpdt.mWillpower == 0) - messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value")); + messages.push_back (std::make_pair (id, "Willpower is equal to zero")); } - if (level < 1) - messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); + if (level <= 0) + messages.push_back (std::make_pair (id, "Level is non-positive")); if (gold < 0) - messages.push_back (std::make_pair (id, npc.mId + " gold has negative value")); + messages.push_back (std::make_pair (id, "Gold count is negative")); if (npc.mName.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has any empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (npc.mClass.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty class")); + messages.push_back (std::make_pair (id, "Class is missing")); else if (mClasses.searchId (npc.mClass) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid class")); + messages.push_back (std::make_pair (id, "Class '" + npc.mClass + "' does not exist")); if (npc.mRace.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty race")); + messages.push_back (std::make_pair (id, "Race is missing")); else if (mRaces.searchId (npc.mRace) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid race")); + messages.push_back (std::make_pair (id, "Race '" + npc.mRace + "' does not exist")); if (disposition < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); + messages.push_back (std::make_pair (id, "Disposition is negative")); - if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid - { - messages.push_back (std::make_pair (id, npc.mId + " has negative reputation")); - } + if (reputation < 0) + messages.push_back (std::make_pair (id, "Reputation is negative")); if (!npc.mFaction.empty()) { if (rank < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative rank")); + messages.push_back (std::make_pair (id, "Faction rank is negative")); if (mFactions.searchId(npc.mFaction) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid faction")); + messages.push_back (std::make_pair (id, "Faction '" + npc.mFaction + "' does not exist")); } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no head")); + messages.push_back (std::make_pair (id, "Head is missing")); // ADD CHECK HERE if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no hair")); + messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE //TODO: reputation, Disposition, rank, everything else @@ -793,28 +789,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum slash damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum slash damage higher than maximum")); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum thrust damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum thrust damage is higher than maximum")); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum chop damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum chop damage is higher than maximum")); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health - if (weapon.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health")); + if (weapon.mData.mHealth == 0) + messages.push_back (std::make_pair (id, "Durability is equal to zero")); if (weapon.mData.mReach < 0) - messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); + messages.push_back (std::make_pair (id, "Reach is negative")); } } @@ -877,7 +870,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) - messages.push_back (std::make_pair (id, staticElement.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); } //final check @@ -886,7 +879,7 @@ void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, - "There is no player record")); + "Player record is missing")); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( @@ -900,8 +893,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) - messages.push_back (std::make_pair (id, - id + " contains non-existing item (" + itemName + ")")); + messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); else { // Needs to accommodate containers, creatures, and NPCs @@ -922,8 +914,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: - messages.push_back (std::make_pair(id, - id + " contains item of invalid type (" + itemName + ")")); + messages.push_back (std::make_pair(id, "'" + itemName + "' is not an item")); } } } @@ -935,67 +926,66 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.push_back (std::make_pair (someID, "Name is missing")); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.push_back (std::make_pair (someID, "Weight is negative")); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.push_back (std::make_pair (someID, "Value is negative")); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE if (enchantable && someItem.mData.mEnchant < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment")); + messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.push_back (std::make_pair (someID, "Name is missing")); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.push_back (std::make_pair (someID, "Weight is negative")); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.push_back (std::make_pair (someID, "Value is negative")); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.push_back (std::make_pair (someID, "Quality is non-positive")); if (canBeBroken && someTool.mData.mUses<=0) - messages.push_back (std::make_pair (someID, - someTool.mId + " has non-positive uses count")); + messages.push_back (std::make_pair (someID, "Number of uses is non-positive")); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.push_back (std::make_pair (someID, "Quality is non-positive")); } template void CSMTools::ReferenceableCheckStage::listCheck ( @@ -1004,12 +994,10 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mReferencables.searchId(someList.mList[i].mId).first == -1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item without referencable")); + messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); if (someList.mList[i].mLevel < 1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item with non-positive level")); + messages.push_back (std::make_pair (someID, "Level of item '" + someList.mList[i].mId + "' is non-positive")); } } @@ -1019,6 +1007,6 @@ template void CSMTools::ReferenceableCheckStage::scriptCheck ( if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) - messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); + messages.push_back (std::make_pair (someID, "Script '"+someTool.mScript+"' does not exist")); } } diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index d3c6221cd2..e62f9eb889 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - stream - << "script " << mFile - << ", line " << loc.mLine << ", column " << loc.mColumn - << " (" << loc.mLiteral << "): " << message; + stream << "Line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; std::ostringstream hintStream; @@ -47,7 +44,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << message; + stream << message; mMessages->add (id, stream.str(), "", getSeverity (type)); } @@ -128,7 +125,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << error.what(); + stream << error.what(); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index b84453b5cd..3fdbcc3bc3 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -36,5 +36,5 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); - /// \todo check, if the sound file exists + /// \todo check, if the sound file exists ADD CHECK HERE } From 7535daa94da38e1259c4aeb61b21bb98e9c4e786 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:15:43 +0300 Subject: [PATCH 034/150] Update class and race record verifier messages --- apps/opencs/model/tools/classcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/racecheck.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 89923a3984..157dfab7fa 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -37,11 +37,11 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name")); + messages.push_back (std::make_pair (id, "Name is missing")); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable")); + messages.push_back (std::make_pair (id, "Description of a playable class is missing")); // test for invalid attributes for (int i=0; i<2; ++i) @@ -49,14 +49,14 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) { std::ostringstream stream; - stream << "Attribute #" << i << " of " << class_.mId << " is not set"; + stream << "Attribute #" << i << " is not set"; messages.push_back (std::make_pair (id, stream.str())); } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Class lists same attribute twice")); + messages.push_back (std::make_pair (id, "Same attribute is listed twice")); } // test for non-unique skill @@ -66,10 +66,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) for (int i2=0; i2<2; ++i2) ++skills[class_.mData.mSkills[i][i2]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); } } diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 38abfef18b..7ce9a8869f 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -29,24 +29,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me // test for empty name and description if (race.mName.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (race.mDescription.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty description")); + messages.push_back (std::make_pair (id, "Description is missing")); // test for positive height if (race.mData.mHeight.mMale<=0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); + messages.push_back (std::make_pair (id, "Male height is non-positive")); if (race.mData.mHeight.mFemale<=0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); + messages.push_back (std::make_pair (id, "Female height is non-positive")); // test for non-negative weight if (race.mData.mWeight.mMale<0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); + messages.push_back (std::make_pair (id, "Male weight is negative")); if (race.mData.mWeight.mFemale<0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); + messages.push_back (std::make_pair (id, "Female weight is negative")); /// \todo check data members that can't be edited in the table view } From a9ce155a7bdacabf351e4a4ec394046a4f0a09ee Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:41:10 +0300 Subject: [PATCH 035/150] Update faction and body part record verifier messages --- apps/opencs/model/tools/bodypartcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/factioncheck.cpp | 11 +++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index b5bd78f6c7..d33705cf6c 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -34,25 +34,24 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message // Check BYDT if (bodyPart.mData.mPart > 14 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); + messages.push_back(std::make_pair(id, "Invalid mesh part")); if (bodyPart.mData.mFlags > 3 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); + messages.push_back(std::make_pair(id, "Invalid flags")); if (bodyPart.mData.mType > 2 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); + messages.push_back(std::make_pair(id, "Invalid type")); // Check MODL if ( bodyPart.mModel.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); + messages.push_back(std::make_pair(id, "Model is missing" )); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); + messages.push_back(std::make_pair(id, "Model '" + bodyPart.mModel + "' does not exist")); // Check FNAM - if ( bodyPart.mRace.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); + messages.push_back(std::make_pair(id, "Race is missing" )); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); + messages.push_back(std::make_pair(id, "Race '" + bodyPart.mRace + " does not exist")); } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 39073db5f0..d19c0ae527 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id , "Faction lists same attribute twice")); + messages.push_back (std::make_pair (id, "Same attribute is listed twice")); } // test for non-unique skill @@ -52,11 +52,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); } /// \todo check data members that can't be edited in the table view From 14ef145b3b83691e00c2d2c433ab76e069b5e4a2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 22:01:08 +0300 Subject: [PATCH 036/150] Update region and pathgrid record verifier messages --- apps/opencs/model/tools/pathgridcheck.cpp | 37 +++++++++++------------ apps/opencs/model/tools/regioncheck.cpp | 2 +- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index c25845885e..e63e9122f1 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message // check the number of pathgrid points if (pathgrid.mData.mS2 < static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error); else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error); std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; @@ -56,9 +56,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; - ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 - << " and " << pathgrid.mEdges[i].mV1; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Duplicate edge between points" + << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); break; } } @@ -70,8 +70,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else { std::ostringstream ss; - ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "An edge is connected to a non-existent point " << pathgrid.mEdges[i].mV0; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -93,8 +93,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (!foundReverse) { std::ostringstream ss; - ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -113,11 +113,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (it == duplList.end()) { std::ostringstream ss; - ss << " has a duplicated point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point " << i << " duplicates point " << j + << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); duplList.push_back(i); break; @@ -132,11 +130,12 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; - ss << " has an orphaned point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point " << i << " (" + << pathgrid.mPoints[i].mX << ", " + << pathgrid.mPoints[i].mY << ", " + << pathgrid.mPoints[i].mZ << ") " + << "is disconnected from other points"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index f21253090f..8375e3f0ac 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -36,7 +36,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for empty name if (region.mName.empty()) - messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); /// \todo test that the ID in mSleeplist exists From a6d3cd919016c060bc1d2735e85f5653d0db53d1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 23:12:11 +0300 Subject: [PATCH 037/150] Update topic info verifier messages --- apps/opencs/model/tools/magiceffectcheck.cpp | 4 +- apps/opencs/model/tools/topicinfocheck.cpp | 109 ++++++++----------- apps/opencs/model/tools/topicinfocheck.hpp | 11 -- 3 files changed, 48 insertions(+), 76 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 448e4d28c3..7e7a09aa5d 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -20,7 +20,7 @@ namespace std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { - if (texture.empty()) return (isIcon ? "Icon is not specified" : std::string()); + if (texture.empty()) return (isIcon ? "Icon is missing" : std::string()); const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; if (textures.searchId(texture) != -1) return std::string(); @@ -97,7 +97,7 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa { addMessage(messages, id, "Description is missing"); } - + if (effect.mData.mBaseCost < 0.0f) { addMessage(messages, id, "Base cost is negative"); diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index ac1f596aee..9a31db72b7 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -134,7 +134,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) { std::ostringstream stream; - messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error); } if (!topicInfo.mRace.empty()) @@ -166,23 +166,28 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Actor"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); if (index.first == -1) { - writeMissingIdError(specifier, actor, id, messages); + std::ostringstream stream; + stream << "Actor '" << actor << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, actor, id, messages); + std::ostringstream stream; + stream << "Deleted actor '" << actor << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) { - writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages); + CSMWorld::UniversalId tempId(index.second, actor); + std::ostringstream stream; + stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -192,11 +197,11 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Cell"; - if (mCellNames.find(cell) == mCellNames.end()) { - writeMissingIdError(specifier, cell, id, messages); + std::ostringstream stream; + stream << "Cell '" << cell << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -209,9 +214,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank < -1) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; } @@ -229,10 +233,10 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank >= limit) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 - << " for the " << factionName << " faction"; + stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1 + << " for the '" << factionName << "' faction"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; } @@ -242,18 +246,20 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Item"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); if (index.first == -1) { - writeMissingIdError(specifier, item, id, messages); + std::ostringstream stream; + stream << "Item '" << item << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, item, id, messages); + std::ostringstream stream; + stream << "Deleted item '" << item << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else @@ -276,8 +282,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS break; default: - writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages); + { + CSMWorld::UniversalId tempId(index.second, item); + std::ostringstream stream; + stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; + } } } @@ -291,13 +302,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) { - messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error); return false; } else if (!infoCondition.variantTypeIsValid()) { std::ostringstream stream; - stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of "; + stream << "Value of condition '" << infoCondition.toString() << "' has invalid "; switch (select.mValue.getType()) { @@ -307,8 +318,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele case ESM::VT_Long: stream << "Long"; break; case ESM::VT_Float: stream << "Float"; break; case ESM::VT_String: stream << "String"; break; - default: stream << "Unknown"; break; + default: stream << "unknown"; break; } + stream << " type"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; @@ -316,7 +328,7 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele else if (infoCondition.conditionIsAlwaysTrue()) { std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is always true"; + stream << "Condition '" << infoCondition.toString() << "' is always true"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; @@ -324,7 +336,7 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele else if (infoCondition.conditionIsNeverTrue()) { std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is never true"; + stream << "Condition '" << infoCondition.toString() << "' is never true"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; @@ -383,11 +395,11 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Sound File"; - if (mSoundFiles.searchId(sound) == -1) { - writeMissingIdError(specifier, sound, id, messages); + std::ostringstream stream; + stream << "Sound file '" << sound << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -402,47 +414,18 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW if (index == -1) { - writeMissingIdError(T::getRecordType(), name, id, messages); + std::ostringstream stream; + stream << T::getRecordType() + " '" << name << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (collection.getRecord(index).isDeleted()) { - writeDeletedRecordError(T::getRecordType(), name, id, messages); + std::ostringstream stream; + stream << "Deleted " << T::getRecordType() << " record '" << name << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } return true; } - -// Error functions - -void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": ID or name \"" << missingId << "\" could not be found"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id, - CSMDoc::Messages& messages) -{ - CSMWorld::UniversalId tempId(invalidType, invalidId); - - std::ostringstream stream; - stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \"" - << invalidId << "\" (can be of type " << expectedType << ")"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} diff --git a/apps/opencs/model/tools/topicinfocheck.hpp b/apps/opencs/model/tools/topicinfocheck.hpp index dbd5fe1c57..9575181b0a 100644 --- a/apps/opencs/model/tools/topicinfocheck.hpp +++ b/apps/opencs/model/tools/topicinfocheck.hpp @@ -80,17 +80,6 @@ namespace CSMTools template bool verifyId(const std::string& name, const CSMWorld::IdCollection& collection, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - // Common error messages - void writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); }; } From 1f717def35c32dfa78c356239dd0a32b83554e19 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 00:27:42 +0300 Subject: [PATCH 038/150] Update journal, start script and spell verifiers messages --- apps/opencs/model/tools/journalcheck.cpp | 18 +++++------------- apps/opencs/model/tools/spellcheck.cpp | 4 ++-- apps/opencs/model/tools/startscriptcheck.cpp | 3 +-- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index 4a7ab7d660..bd77e95b63 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -33,6 +33,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::set questIndices; CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it) { @@ -56,9 +57,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (journalInfo.mResponse.empty()) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - - messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning); } std::pair::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); @@ -66,25 +65,18 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) // Duplicate index if (result.second == false) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - std::ostringstream stream; - stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex; - + stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); } } if (totalInfoCount == 0) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning); } else if (statusNamedCount > 1) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 3e59f0d9a3..6c08aca37b 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -36,11 +36,11 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for empty name and description if (spell.mName.empty()) - messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); // test for invalid cost values if (spell.mData.mCost<0) - messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); + messages.push_back (std::make_pair (id, "Spell cost is negative")); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/startscriptcheck.cpp b/apps/opencs/model/tools/startscriptcheck.cpp index b1d92380b3..5a9f63dc43 100644 --- a/apps/opencs/model/tools/startscriptcheck.cpp +++ b/apps/opencs/model/tools/startscriptcheck.cpp @@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) - messages.push_back ( - std::make_pair (id, "Start script " + scriptId + " does not exist")); + messages.push_back (std::make_pair (id, "Start script " + scriptId + " does not exist")); } int CSMTools::StartScriptCheckStage::setup() From 38ea7928f54876755d30a732fd36ed2db547d039 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 00:47:38 +0300 Subject: [PATCH 039/150] Add sound file checks to sound record verifier --- apps/opencs/model/tools/soundcheck.cpp | 17 ++++++++++++++--- apps/opencs/model/tools/soundcheck.hpp | 9 ++++++++- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 3fdbcc3bc3..5708a59d30 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -6,10 +6,14 @@ #include "../prefs/state.hpp" +#include "../world/data.hpp" +#include "../world/resources.hpp" #include "../world/universalid.hpp" -CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) -: mSounds (sounds) +CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection &sounds, + const CSMWorld::Resources &soundfiles) + : mSounds (sounds), + mSoundFiles (soundfiles) { mIgnoreBaseRecords = false; } @@ -36,5 +40,12 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); - /// \todo check, if the sound file exists ADD CHECK HERE + if (sound.mSound.empty()) + { + messages.push_back(std::make_pair(id, "Sound file is missing")); + } + else if (mSoundFiles.searchId(sound.mSound) == -1) + { + messages.push_back(std::make_pair(id, "Sound file '" + sound.mSound + "' does not exist")); + } } diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index d6fff52638..47c3249ce7 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -7,17 +7,24 @@ #include "../doc/stage.hpp" +namespace CSMWorld +{ + class Resources; +} + namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; + const CSMWorld::Resources &mSoundFiles; bool mIgnoreBaseRecords; public: - SoundCheckStage (const CSMWorld::IdCollection& sounds); + SoundCheckStage (const CSMWorld::IdCollection& sounds, + const CSMWorld::Resources &soundfiles); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 0946225cff..dfa2ae202d 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -74,7 +74,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces())); - mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds())); + mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes))); mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); From d90940011f7c1bd5f25dedc9cae476b61b1f7b15 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:22:41 +0300 Subject: [PATCH 040/150] Add model checks to object record verifier --- .../opencs/model/tools/referenceablecheck.cpp | 123 ++++++++++-------- .../opencs/model/tools/referenceablecheck.hpp | 7 +- apps/opencs/model/tools/tools.cpp | 3 +- 3 files changed, 75 insertions(+), 58 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6d06cce121..8a1103c3d7 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -11,13 +11,14 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, - const CSMWorld::IdCollection& scripts) - : - mReferencables(referenceable), + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models) + :mObjects(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), + mModels(models), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -26,201 +27,201 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - const int bookSize(mReferencables.getBooks().getSize()); + const int bookSize(mObjects.getBooks().getSize()); if (stage < bookSize) { - bookCheck(stage, mReferencables.getBooks(), messages); + bookCheck(stage, mObjects.getBooks(), messages); return; } stage -= bookSize; - const int activatorSize(mReferencables.getActivators().getSize()); + const int activatorSize(mObjects.getActivators().getSize()); if (stage < activatorSize) { - activatorCheck(stage, mReferencables.getActivators(), messages); + activatorCheck(stage, mObjects.getActivators(), messages); return; } stage -= activatorSize; - const int potionSize(mReferencables.getPotions().getSize()); + const int potionSize(mObjects.getPotions().getSize()); if (stage < potionSize) { - potionCheck(stage, mReferencables.getPotions(), messages); + potionCheck(stage, mObjects.getPotions(), messages); return; } stage -= potionSize; - const int apparatusSize(mReferencables.getApparati().getSize()); + const int apparatusSize(mObjects.getApparati().getSize()); if (stage < apparatusSize) { - apparatusCheck(stage, mReferencables.getApparati(), messages); + apparatusCheck(stage, mObjects.getApparati(), messages); return; } stage -= apparatusSize; - const int armorSize(mReferencables.getArmors().getSize()); + const int armorSize(mObjects.getArmors().getSize()); if (stage < armorSize) { - armorCheck(stage, mReferencables.getArmors(), messages); + armorCheck(stage, mObjects.getArmors(), messages); return; } stage -= armorSize; - const int clothingSize(mReferencables.getClothing().getSize()); + const int clothingSize(mObjects.getClothing().getSize()); if (stage < clothingSize) { - clothingCheck(stage, mReferencables.getClothing(), messages); + clothingCheck(stage, mObjects.getClothing(), messages); return; } stage -= clothingSize; - const int containerSize(mReferencables.getContainers().getSize()); + const int containerSize(mObjects.getContainers().getSize()); if (stage < containerSize) { - containerCheck(stage, mReferencables.getContainers(), messages); + containerCheck(stage, mObjects.getContainers(), messages); return; } stage -= containerSize; - const int doorSize(mReferencables.getDoors().getSize()); + const int doorSize(mObjects.getDoors().getSize()); if (stage < doorSize) { - doorCheck(stage, mReferencables.getDoors(), messages); + doorCheck(stage, mObjects.getDoors(), messages); return; } stage -= doorSize; - const int ingredientSize(mReferencables.getIngredients().getSize()); + const int ingredientSize(mObjects.getIngredients().getSize()); if (stage < ingredientSize) { - ingredientCheck(stage, mReferencables.getIngredients(), messages); + ingredientCheck(stage, mObjects.getIngredients(), messages); return; } stage -= ingredientSize; - const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); + const int creatureLevListSize(mObjects.getCreatureLevelledLists().getSize()); if (stage < creatureLevListSize) { - creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); + creaturesLevListCheck(stage, mObjects.getCreatureLevelledLists(), messages); return; } stage -= creatureLevListSize; - const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); + const int itemLevelledListSize(mObjects.getItemLevelledList().getSize()); if (stage < itemLevelledListSize) { - itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + itemLevelledListCheck(stage, mObjects.getItemLevelledList(), messages); return; } stage -= itemLevelledListSize; - const int lightSize(mReferencables.getLights().getSize()); + const int lightSize(mObjects.getLights().getSize()); if (stage < lightSize) { - lightCheck(stage, mReferencables.getLights(), messages); + lightCheck(stage, mObjects.getLights(), messages); return; } stage -= lightSize; - const int lockpickSize(mReferencables.getLocpicks().getSize()); + const int lockpickSize(mObjects.getLocpicks().getSize()); if (stage < lockpickSize) { - lockpickCheck(stage, mReferencables.getLocpicks(), messages); + lockpickCheck(stage, mObjects.getLocpicks(), messages); return; } stage -= lockpickSize; - const int miscSize(mReferencables.getMiscellaneous().getSize()); + const int miscSize(mObjects.getMiscellaneous().getSize()); if (stage < miscSize) { - miscCheck(stage, mReferencables.getMiscellaneous(), messages); + miscCheck(stage, mObjects.getMiscellaneous(), messages); return; } stage -= miscSize; - const int npcSize(mReferencables.getNPCs().getSize()); + const int npcSize(mObjects.getNPCs().getSize()); if (stage < npcSize) { - npcCheck(stage, mReferencables.getNPCs(), messages); + npcCheck(stage, mObjects.getNPCs(), messages); return; } stage -= npcSize; - const int weaponSize(mReferencables.getWeapons().getSize()); + const int weaponSize(mObjects.getWeapons().getSize()); if (stage < weaponSize) { - weaponCheck(stage, mReferencables.getWeapons(), messages); + weaponCheck(stage, mObjects.getWeapons(), messages); return; } stage -= weaponSize; - const int probeSize(mReferencables.getProbes().getSize()); + const int probeSize(mObjects.getProbes().getSize()); if (stage < probeSize) { - probeCheck(stage, mReferencables.getProbes(), messages); + probeCheck(stage, mObjects.getProbes(), messages); return; } stage -= probeSize; - const int repairSize(mReferencables.getRepairs().getSize()); + const int repairSize(mObjects.getRepairs().getSize()); if (stage < repairSize) { - repairCheck(stage, mReferencables.getRepairs(), messages); + repairCheck(stage, mObjects.getRepairs(), messages); return; } stage -= repairSize; - const int staticSize(mReferencables.getStatics().getSize()); + const int staticSize(mObjects.getStatics().getSize()); if (stage < staticSize) { - staticCheck(stage, mReferencables.getStatics(), messages); + staticCheck(stage, mObjects.getStatics(), messages); return; } stage -= staticSize; - const int creatureSize(mReferencables.getCreatures().getSize()); + const int creatureSize(mObjects.getCreatures().getSize()); if (stage < creatureSize) { - creatureCheck(stage, mReferencables.getCreatures(), messages); + creatureCheck(stage, mObjects.getCreatures(), messages); return; } // if we come that far, we are about to perform our last, final check. @@ -233,7 +234,7 @@ int CSMTools::ReferenceableCheckStage::setup() mPlayerPresent = false; mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); - return mReferencables.getSize() + 1; + return mObjects.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( @@ -272,7 +273,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(activator.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + activator.mModel + "' does not exist")); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -383,9 +386,11 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); - //Checking for model, IIRC all containers should have a model + //Checking for model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(container.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + container.mModel + "' does not exist")); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed @@ -416,7 +421,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(creature.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + creature.mModel + "' does not exist")); if (creature.mName.empty()) messages.push_back (std::make_pair (id, "Name is missing")); @@ -496,7 +503,9 @@ void CSMTools::ReferenceableCheckStage::doorCheck( messages.push_back (std::make_pair (id, "Name is missing")); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(door.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + door.mModel + "' does not exist")); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -729,8 +738,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( if (npc.mHair.empty()) messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE - //TODO: reputation, Disposition, rank, everything else - // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); @@ -871,6 +878,8 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( if (staticElement.mModel.empty()) messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(staticElement.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + staticElement.mModel + "' does not exist")); } //final check @@ -890,7 +899,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( for (size_t i = 0; i < itemList.size(); ++i) { std::string itemName = itemList[i].mItem.toString(); - CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); + CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); if (localIndex.first == -1) messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); @@ -938,7 +947,9 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Model is missing")); + else if (mModels.searchId(someItem.mModel) == -1) + messages.push_back(std::make_pair(someID, "Model '" + someItem.mModel + "' does not exist")); //checking for icon if (someItem.mIcon.empty()) @@ -964,7 +975,9 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Model is missing")); + else if (mModels.searchId(someItem.mModel) == -1) + messages.push_back (std::make_pair (someID, "Model '" + someItem.mModel + "' does not exist")); //checking for icon if (someItem.mIcon.empty()) @@ -993,7 +1006,7 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( { for (unsigned i = 0; i < someList.mList.size(); ++i) { - if (mReferencables.searchId(someList.mList[i].mId).first == -1) + if (mObjects.searchId(someList.mList[i].mId).first == -1) messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); if (someList.mList[i].mLevel < 1) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index f9341bd9ce..24f73176c8 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -5,6 +5,7 @@ #include "../doc/stage.hpp" #include "../world/data.hpp" #include "../world/refiddata.hpp" +#include "../world/resources.hpp" namespace CSMTools { @@ -16,7 +17,8 @@ namespace CSMTools const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, - const CSMWorld::IdCollection& scripts); + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -76,11 +78,12 @@ namespace CSMTools CSMDoc::Messages& messages, const std::string& someID); - const CSMWorld::RefIdData& mReferencables; + const CSMWorld::RefIdData& mObjects; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; + const CSMWorld::Resources& mModels; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index dfa2ae202d..2e471d66f0 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -82,7 +82,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); + mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), + mData.getResources (CSMWorld::UniversalId::Type_Meshes))); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 34077a6987939791ffd5f62552eaf6b28194f781 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:27:10 +0300 Subject: [PATCH 041/150] Purge unnecessary namespaces --- apps/opencs/model/tools/birthsigncheck.cpp | 1 - apps/opencs/model/tools/birthsigncheck.hpp | 6 +----- apps/opencs/model/tools/magiceffectcheck.cpp | 1 - apps/opencs/model/tools/magiceffectcheck.hpp | 6 +----- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index 4ef264f657..b61bf74214 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -9,7 +9,6 @@ #include "../prefs/state.hpp" #include "../world/data.hpp" -#include "../world/resources.hpp" #include "../world/universalid.hpp" namespace diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index cd22fb6d0f..d032d3cefd 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -4,14 +4,10 @@ #include #include "../world/idcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 7e7a09aa5d..77a26c5968 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,7 +4,6 @@ #include "../prefs/state.hpp" -#include "../world/resources.hpp" #include "../world/data.hpp" namespace diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 3e45495c18..22dad3db96 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -6,14 +6,10 @@ #include "../world/idcollection.hpp" #include "../world/refidcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that magic effect records are internally consistent From dc847dce09aeddbe7df925d305dc96969220538d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:44:46 +0300 Subject: [PATCH 042/150] Add item icon checks to object record verifier --- .../opencs/model/tools/referenceablecheck.cpp | 21 ++++++++++++++++--- .../opencs/model/tools/referenceablecheck.hpp | 4 +++- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 8a1103c3d7..d3901ac883 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,6 +1,7 @@ #include "referenceablecheck.hpp" #include +#include #include "../prefs/state.hpp" @@ -12,13 +13,15 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, const CSMWorld::IdCollection& scripts, - const CSMWorld::Resources& models) + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons) :mObjects(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), mModels(models), + mIcons(icons), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -953,7 +956,13 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Icon is missing")); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + } if (enchantable && someItem.mData.mEnchant < 0) messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); @@ -981,7 +990,13 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Icon is missing")); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + } } template void CSMTools::ReferenceableCheckStage::toolCheck ( diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 24f73176c8..5da7378755 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -18,7 +18,8 @@ namespace CSMTools const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, const CSMWorld::IdCollection& scripts, - const CSMWorld::Resources& models); + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -84,6 +85,7 @@ namespace CSMTools const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; const CSMWorld::Resources& mModels; + const CSMWorld::Resources& mIcons; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 2e471d66f0..87b5041e07 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -83,7 +83,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), - mData.getResources (CSMWorld::UniversalId::Type_Meshes))); + mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons))); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 5b8880c6fa24b65f6899a9141a0f64fd991368e9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:03:55 +0300 Subject: [PATCH 043/150] Update cell reference record verifier messages --- apps/opencs/model/tools/referencecheck.cpp | 42 +++++++++++----------- apps/opencs/model/tools/referencecheck.hpp | 2 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index 447238be41..dd93b4e9cc 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage( const CSMWorld::IdCollection& factions) : mReferences(references), - mReferencables(referencables), + mObjects(referencables), mDataSet(referencables.getDataSet()), mCells(cells), mFactions(factions) @@ -30,75 +30,73 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message // Check for empty reference id if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); + messages.push_back(std::make_pair(id, "Instance is not based on an object")); } else { // Check for non existing referenced object - if (mReferencables.searchId(cellRef.mRefID) == -1) { - messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); + if (mObjects.searchId(cellRef.mRefID) == -1) { + messages.push_back(std::make_pair(id, "Instance of a non-existent object '" + cellRef.mRefID + "'")); } else { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { - std::string str = " has invalid charge "; + std::string str = "Invalid charge: "; if (localIndex.second == CSMWorld::UniversalId::Type_Light) str += std::to_string(cellRef.mChargeFloat); else str += std::to_string(cellRef.mChargeInt); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } } } // If object have owner, check if that owner reference is valid - if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) - messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); + if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1) + messages.push_back(std::make_pair(id, "Owner object '" + cellRef.mOwner + "' does not exist")); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) - if (mReferencables.searchId(cellRef.mSoul) == -1) - messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); + if (mObjects.searchId(cellRef.mSoul) == -1) + messages.push_back(std::make_pair(id, "Trapped soul object '" + cellRef.mSoul + "' does not exist")); bool hasFaction = !cellRef.mFaction.empty(); // If object have faction, check if that faction reference is valid if (hasFaction) if (mFactions.searchId(cellRef.mFaction) == -1) - messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction)); + messages.push_back(std::make_pair(id, "Faction '" + cellRef.mFaction + "' does not exist")); // Check item's faction rank - if (hasFaction && cellRef.mFactionRank < -1) - messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + std::to_string(cellRef.mFactionRank))); - else if (!hasFaction && cellRef.mFactionRank != -2) - messages.push_back(std::make_pair(id, " has invalid faction rank " + std::to_string(cellRef.mFactionRank))); + if ((hasFaction && cellRef.mFactionRank < -1) || (!hasFaction && cellRef.mFactionRank != -2)) + messages.push_back(std::make_pair(id, "Invalid faction rank " + std::to_string(cellRef.mFactionRank))); // If door have destination cell, check if that reference is valid if (!cellRef.mDestCell.empty()) if (mCells.searchId(cellRef.mDestCell) == -1) - messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell)); + messages.push_back(std::make_pair(id, "Destination cell '" + cellRef.mDestCell + "' does not exist")); // Check if scale isn't negative if (cellRef.mScale < 0) { - std::string str = " has negative scale "; + std::string str = "Negative scale: "; str += std::to_string(cellRef.mScale); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } // Check if enchantement points aren't negative or are at full (-1) if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) { - std::string str = " has negative enchantment points "; + std::string str = "Negative enchantment points: "; str += std::to_string(cellRef.mEnchantmentCharge); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } // Check if gold value isn't negative if (cellRef.mGoldValue < 0) { - std::string str = " has negative gold value "; + std::string str = "Negative gold value: "; str += cellRef.mGoldValue; - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } } diff --git a/apps/opencs/model/tools/referencecheck.hpp b/apps/opencs/model/tools/referencecheck.hpp index 5e25924f3a..7373903e66 100644 --- a/apps/opencs/model/tools/referencecheck.hpp +++ b/apps/opencs/model/tools/referencecheck.hpp @@ -19,7 +19,7 @@ namespace CSMTools private: const CSMWorld::RefCollection& mReferences; - const CSMWorld::RefIdCollection& mReferencables; + const CSMWorld::RefIdCollection& mObjects; const CSMWorld::RefIdData& mDataSet; const CSMWorld::IdCollection& mCells; const CSMWorld::IdCollection& mFactions; From 6b226eef8f53d06dfdb9bb0817614ee4db35077b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:38:02 +0300 Subject: [PATCH 044/150] Use Messages::add in magic effect record verifier --- apps/opencs/model/tools/magiceffectcheck.cpp | 128 ++++++++++++------- 1 file changed, 81 insertions(+), 47 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 77a26c5968..917e358c34 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,23 +4,8 @@ #include "../prefs/state.hpp" -#include "../world/data.hpp" - -namespace -{ - void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) - { - if (!text.empty()) - { - messages.push_back(std::make_pair(id, text)); - } - } -} - std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { - if (texture.empty()) return (isIcon ? "Icon is missing" : std::string()); - const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; if (textures.searchId(texture) != -1) return std::string(); @@ -34,30 +19,16 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { - std::string error; - if (!id.empty()) - { - CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); - if (index.first == -1) - { - error = column + " '" + id + "' " + "does not exist"; - } - else if (index.second != type.getType()) - { - error = column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"; - } - } - return error; + CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); + if (index.first == -1) return (column + " '" + id + "' " + "does not exist"); + else if (index.second != type.getType()) return (column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"); + return std::string(); } std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const { - std::string error; - if (!id.empty() && mSounds.searchId(id) == -1) - { - error = column + " '" + id + "' " + "does not exist"; - } - return error; + if (!id.empty() && mSounds.searchId(id) == -1) return (column + " '" + id + "' " + "does not exist"); + return std::string(); } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, @@ -94,22 +65,85 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa if (effect.mDescription.empty()) { - addMessage(messages, id, "Description is missing"); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); } if (effect.mData.mBaseCost < 0.0f) { - addMessage(messages, id, "Base cost is negative"); + messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error); } - addMessage(messages, id, checkTexture(effect.mIcon, true)); - addMessage(messages, id, checkTexture(effect.mParticle, false)); - addMessage(messages, id, checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object")); - addMessage(messages, id, checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object")); - addMessage(messages, id, checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object")); - addMessage(messages, id, checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object")); - addMessage(messages, id, checkSound(effect.mCastSound, "Casting sound")); - addMessage(messages, id, checkSound(effect.mHitSound, "Hit sound")); - addMessage(messages, id, checkSound(effect.mAreaSound, "Area sound")); - addMessage(messages, id, checkSound(effect.mBoltSound, "Bolt sound")); + if (effect.mIcon.empty()) + { + messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error); + } + else + { + const std::string error = checkTexture(effect.mIcon, true); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mParticle.empty()) + { + const std::string error = checkTexture(effect.mParticle, false); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mCasting.empty()) + { + const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mHit.empty()) + { + const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mArea.empty()) + { + const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mBolt.empty()) + { + const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Static, "Bolt object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mCastSound.empty()) + { + const std::string error = checkSound(effect.mCastSound, "Casting sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mHitSound.empty()) + { + const std::string error = checkSound(effect.mHitSound, "Hit sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mAreaSound.empty()) + { + const std::string error = checkSound(effect.mAreaSound, "Area sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mBoltSound.empty()) + { + const std::string error = checkSound(effect.mBoltSound, "Bolt sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } } From d433929194578c477343df7a09183b5ed83dde20 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:48:58 +0300 Subject: [PATCH 045/150] Use Messages::add in birthsign record verifier --- apps/opencs/model/tools/birthsigncheck.cpp | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index b61bf74214..c582da8e4b 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -1,37 +1,21 @@ #include "birthsigncheck.hpp" -#include -#include - #include #include #include "../prefs/state.hpp" -#include "../world/data.hpp" #include "../world/universalid.hpp" -namespace -{ - void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) - { - if (!text.empty()) - { - messages.push_back(std::make_pair(id, text)); - } - } -} - std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const { - if (texture.empty()) return "Texture is missing"; if (mTextures.searchId(texture) != -1) return std::string(); std::string ddsTexture = texture; if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); - return "Texture '" + texture + "' does not exist"; + return "Image '" + texture + "' does not exist"; } CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, @@ -62,12 +46,25 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); if (birthsign.mName.empty()) - addMessage(messages, id, "Name is missing"); + { + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + } if (birthsign.mDescription.empty()) - addMessage(messages, id, "Description is missing"); + { + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + } - addMessage(messages, id, checkTexture(birthsign.mTexture)); + if (birthsign.mTexture.empty()) + { + messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); + } + else + { + const std::string error = checkTexture(birthsign.mTexture); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } /// \todo check data members that can't be edited in the table view } From 8dcb58d745839d87272446cb318565cc08756484 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 03:49:10 +0300 Subject: [PATCH 046/150] Use Messages::add in Faction, Sound, Bodypart and Class record verifiers --- apps/opencs/model/tools/bodypartcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/classcheck.cpp | 19 +++++++++---------- apps/opencs/model/tools/factioncheck.cpp | 6 +++--- apps/opencs/model/tools/soundcheck.cpp | 11 +++++------ 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index d33705cf6c..16dd9891e9 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -34,24 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message // Check BYDT if (bodyPart.mData.mPart > 14 ) - messages.push_back(std::make_pair(id, "Invalid mesh part")); + messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mFlags > 3 ) - messages.push_back(std::make_pair(id, "Invalid flags")); + messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mType > 2 ) - messages.push_back(std::make_pair(id, "Invalid type")); + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); // Check MODL - if ( bodyPart.mModel.empty() ) - messages.push_back(std::make_pair(id, "Model is missing" )); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) - messages.push_back(std::make_pair(id, "Model '" + bodyPart.mModel + "' does not exist")); + messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check FNAM if ( bodyPart.mRace.empty() ) - messages.push_back(std::make_pair(id, "Race is missing" )); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.push_back(std::make_pair(id, "Race '" + bodyPart.mRace + " does not exist")); + messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 157dfab7fa..b409c4e064 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -1,6 +1,5 @@ #include "classcheck.hpp" -#include #include #include @@ -37,26 +36,24 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description of a playable class is missing")); + messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning); // test for invalid attributes for (int i=0; i<2; ++i) + { if (class_.mData.mAttribute[i]==-1) { - std::ostringstream stream; - - stream << "Attribute #" << i << " is not set"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error); } + } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Same attribute is listed twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); } // test for non-unique skill @@ -67,8 +64,10 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) ++skills[class_.mData.mSkills[i][i2]]; for (auto &skill : skills) + { if (skill.second>1) { - messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } + } } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index d19c0ae527..5bd613851a 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Same attribute is listed twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); } // test for non-unique skill @@ -55,7 +55,7 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages for (auto &skill : skills) if (skill.second>1) { - messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 5708a59d30..7bb27e9847 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -1,12 +1,9 @@ #include "soundcheck.hpp" -#include - #include #include "../prefs/state.hpp" -#include "../world/data.hpp" #include "../world/resources.hpp" #include "../world/universalid.hpp" @@ -38,14 +35,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) - messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); + { + messages.add(id, "Minimum range larger than maximum range", "", CSMDoc::Message::Severity_Warning); + } if (sound.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound file is missing")); + messages.add(id, "Sound file is missing", "", CSMDoc::Message::Severity_Error); } else if (mSoundFiles.searchId(sound.mSound) == -1) { - messages.push_back(std::make_pair(id, "Sound file '" + sound.mSound + "' does not exist")); + messages.add(id, "Sound file '" + sound.mSound + "' does not exist", "", CSMDoc::Message::Severity_Error); } } From b2b9bd94f09b817a20ac9cae3b54e7f174bd4f6d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 04:05:48 +0300 Subject: [PATCH 047/150] Fix bolt object type --- apps/opencs/model/tools/magiceffectcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 917e358c34..943d89cdc7 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -114,7 +114,7 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa if (!effect.mBolt.empty()) { - const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Static, "Bolt object"); + const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object"); if (!error.empty()) messages.add(id, error, "", CSMDoc::Message::Severity_Error); } From 35281d7c3844b9c3b1af100fd7317d8ca9eebc78 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 05:04:49 +0300 Subject: [PATCH 048/150] Use Messages::add in race, soundgen, spell and start script record verifiers --- apps/opencs/model/tools/racecheck.cpp | 18 ++++++++++-------- apps/opencs/model/tools/soundgencheck.cpp | 8 ++++---- apps/opencs/model/tools/spellcheck.cpp | 6 +++--- apps/opencs/model/tools/startscriptcheck.cpp | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 7ce9a8869f..3b487b9253 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -28,25 +28,27 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description - if (race.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + if (race.mName.empty() && race.mData.mFlags & 0x1) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + else if (race.mName.empty()) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); if (race.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description is missing")); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); // test for positive height if (race.mData.mHeight.mMale<=0) - messages.push_back (std::make_pair (id, "Male height is non-positive")); + messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error); if (race.mData.mHeight.mFemale<=0) - messages.push_back (std::make_pair (id, "Female height is non-positive")); + messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error); // test for non-negative weight if (race.mData.mWeight.mMale<0) - messages.push_back (std::make_pair (id, "Male weight is negative")); + messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error); if (race.mData.mWeight.mFemale<0) - messages.push_back (std::make_pair (id, "Female weight is negative")); + messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } @@ -56,7 +58,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); if (!mPlayable) - messages.push_back (std::make_pair (id, "No playable race")); + messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError); } CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 99a8c184cc..7b6cf7a4b3 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -40,20 +40,20 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { - messages.push_back(std::make_pair(id, "Creature '" + soundGen.mCreature + "' doesn't exist")); + messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { - messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); + messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error); } } if (soundGen.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound is missing")); + messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error); } else if (mSounds.searchId(soundGen.mSound) == -1) { - messages.push_back(std::make_pair(id, "Sound '" + soundGen.mSound + "' doesn't exist")); + messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 6c08aca37b..dc9ce65c0a 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); - // test for empty name and description + // test for empty name if (spell.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid cost values if (spell.mData.mCost<0) - messages.push_back (std::make_pair (id, "Spell cost is negative")); + messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/startscriptcheck.cpp b/apps/opencs/model/tools/startscriptcheck.cpp index 5a9f63dc43..deb7d384fc 100644 --- a/apps/opencs/model/tools/startscriptcheck.cpp +++ b/apps/opencs/model/tools/startscriptcheck.cpp @@ -25,7 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) - messages.push_back (std::make_pair (id, "Start script " + scriptId + " does not exist")); + messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error); } int CSMTools::StartScriptCheckStage::setup() From 1e5330d9da8577df523f7c51c9dda94bc036d27f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 05:16:49 +0300 Subject: [PATCH 049/150] Use Messages::add in skill record verifier --- apps/opencs/model/tools/skillcheck.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index a2bf3ff032..ab7df51cb5 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -1,7 +1,5 @@ #include "skillcheck.hpp" -#include - #include #include "../prefs/state.hpp" @@ -33,16 +31,14 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); + if (skill.mDescription.empty()) + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + for (int i=0; i<4; ++i) + { if (skill.mData.mUseValue[i]<0) { - std::ostringstream stream; - - stream << "Usage experience value #" << i << " is negative"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } - - if (skill.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description is missing")); + } } From e1ae7a9b0e964154cc0a205ad1bc14364d0f7a27 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 06:40:14 +0300 Subject: [PATCH 050/150] Avoid duplicate duplicate pathgrid point warnings --- apps/opencs/model/tools/pathgridcheck.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index e63e9122f1..88750ad7f9 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -100,11 +100,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message // check duplicate points // FIXME: how to do this efficiently? - for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) + for (unsigned int j = 0; j != i; ++j) { - if (j == i) - continue; - if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) From 85dc1e4ef2b43953fbade3294acb5a77a6242c28 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 10:45:02 +0300 Subject: [PATCH 051/150] Revert unnecessary universalid changes in Journal record verifier --- apps/opencs/model/tools/journalcheck.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index bd77e95b63..dc15b26ee8 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -33,7 +33,6 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::set questIndices; CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it) { @@ -57,6 +56,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (journalInfo.mResponse.empty()) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning); } @@ -65,6 +65,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) // Duplicate index if (result.second == false) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); std::ostringstream stream; stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); @@ -73,10 +74,12 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (totalInfoCount == 0) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning); } else if (statusNamedCount > 1) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error); } } From d1b2fc11ef58594bacafe48b3191e9bf150f8983 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 13:00:39 +0300 Subject: [PATCH 052/150] Use messages::add in object record verifiers Add NPC head and hair body part existence checks and expand creature record verifier, update playable class checks in class record verifier --- apps/opencs/model/tools/classcheck.cpp | 7 +- .../opencs/model/tools/referenceablecheck.cpp | 263 +++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 4 +- apps/opencs/model/tools/tools.cpp | 3 +- 4 files changed, 147 insertions(+), 130 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index b409c4e064..9984c32872 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -36,7 +36,12 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + { + if (class_.mData.mIsPlayable != 0) + messages.add(id, "Name of a playable class is missing", "", CSMDoc::Message::Severity_Error); + else + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + } // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d3901ac883..c61d84d514 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -14,7 +14,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::IdCollection& faction, const CSMWorld::IdCollection& scripts, const CSMWorld::Resources& models, - const CSMWorld::Resources& icons) + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts) :mObjects(referenceable), mRaces(races), mClasses(classes), @@ -22,6 +23,7 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( mScripts(scripts), mModels(models), mIcons(icons), + mBodyParts(bodyparts), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -274,11 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); - //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(activator.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + activator.mModel + "' does not exist")); + messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -299,7 +300,6 @@ void CSMTools::ReferenceableCheckStage::potionCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); - //IIRC potion can have empty effects list just fine. // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); @@ -344,13 +344,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck( inventoryItemCheck(armor, messages, id.toString(), true); - //checking for armor class, armor should have poistive armor class, but 0 is considered legal + // Armor should have positive armor class, but 0 class is not an error if (armor.mData.mArmor < 0) - messages.push_back (std::make_pair (id, "Armor class is negative")); + messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error); - //checking for health. Only positive numbers are allowed, or 0 is illegal + // Armor durability must be a positive number if (armor.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, "Durability is non-positive")); + messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); @@ -389,19 +389,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); + //checking for name + if (container.mName.empty()) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + //Checking for model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(container.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + container.mModel + "' does not exist")); + messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed - messages.push_back (std::make_pair (id, "Capacity is negative")); - - //checking for name - if (container.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); @@ -423,69 +423,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); - if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); - else if (mModels.searchId(creature.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + creature.mModel + "' does not exist")); - if (creature.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + + if (creature.mModel.empty()) + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(creature.mModel) == -1) + messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //stats checks - if (creature.mData.mLevel < 1) - messages.push_back (std::make_pair (id, "Level is non-positive")); + if (creature.mData.mLevel <= 0) + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mStrength < 0) - messages.push_back (std::make_pair (id, "Strength is negative")); - + messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mIntelligence < 0) - messages.push_back (std::make_pair (id, "Intelligence is negative")); - + messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mWillpower < 0) - messages.push_back (std::make_pair (id, "Willpower is negative")); - + messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mAgility < 0) - messages.push_back (std::make_pair (id, "Agility is negative")); - + messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mSpeed < 0) - messages.push_back (std::make_pair (id, "Speed is negative")); - + messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mEndurance < 0) - messages.push_back (std::make_pair (id, "Endurance is negative")); - + messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mPersonality < 0) - messages.push_back (std::make_pair (id, "Personality is negative")); - + messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mLuck < 0) - messages.push_back (std::make_pair (id, "Luck is negative")); + messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning); + + if (creature.mData.mCombat < 0) + messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mMagic < 0) + messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mStealth < 0) + messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mHealth < 0) - messages.push_back (std::make_pair (id, "Health is negative")); + messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mMana < 0) + messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mFatigue < 0) + messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error); if (creature.mData.mSoul < 0) - messages.push_back (std::make_pair (id, "Soul value is negative")); + messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) - { - messages.push_back (std::make_pair (id, "One of attacks has negative damage")); - break; - } + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error); + if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1]) + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Warning); } - //TODO, find meaning of other values if (creature.mData.mGold < 0) - messages.push_back (std::make_pair (id, "Gold count is negative")); + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (creature.mScale == 0) - messages.push_back (std::make_pair (id, "Scale is equal to zero")); + messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error); + + if (!creature.mOriginal.empty()) + { + CSMWorld::RefIdData::LocalIndex index = mObjects.searchId(creature.mOriginal); + if (index.first == -1) + messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (index.second != CSMWorld::UniversalId::Type_Creature) + messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error); + } // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(creature, messages, id.toString()); + /// \todo Check spells, teleport table, AI data and AI packages for validity } void CSMTools::ReferenceableCheckStage::doorCheck( @@ -503,12 +515,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck( //usual, name or model if (door.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(door.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + door.mModel + "' does not exist")); + messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -582,7 +594,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) - messages.push_back (std::make_pair (id, "Light radius is negative")); + messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error); if (light.mData.mFlags & ESM::Light::Carry) inventoryItemCheck(light, messages, id.toString()); @@ -657,89 +669,87 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( char disposition(npc.mNpdt.mDisposition); char reputation(npc.mNpdt.mReputation); char rank(npc.mNpdt.mRank); - //Don't know what unknown is for int gold(npc.mNpdt.mGold); if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { - messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen? + messages.add(id, "NPC with autocalculated stats doesn't have autocalc flag turned on", "", CSMDoc::Message::Severity_Error); //should not happen? return; } - - level = npc.mNpdt.mLevel; - disposition = npc.mNpdt.mDisposition; - reputation = npc.mNpdt.mReputation; - rank = npc.mNpdt.mRank; - gold = npc.mNpdt.mGold; } else { - if (npc.mNpdt.mAgility == 0) - messages.push_back (std::make_pair (id, "Agility is equal to zero")); - - if (npc.mNpdt.mEndurance == 0) - messages.push_back (std::make_pair (id, "Endurance is equal to zero")); - - if (npc.mNpdt.mIntelligence == 0) - messages.push_back (std::make_pair (id, "Intelligence is equal to zero")); - - if (npc.mNpdt.mLuck == 0) - messages.push_back (std::make_pair (id, "Luck is equal to zero")); - - if (npc.mNpdt.mPersonality == 0) - messages.push_back (std::make_pair (id, "Personality is equal to zero")); - if (npc.mNpdt.mStrength == 0) - messages.push_back (std::make_pair (id, "Strength is equal to zero")); - - if (npc.mNpdt.mSpeed == 0) - messages.push_back (std::make_pair (id, "Speed is equal to zero")); - + messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mIntelligence == 0) + messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning); if (npc.mNpdt.mWillpower == 0) - messages.push_back (std::make_pair (id, "Willpower is equal to zero")); + messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mAgility == 0) + messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mSpeed == 0) + messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mEndurance == 0) + messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mPersonality == 0) + messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mLuck == 0) + messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning); } if (level <= 0) - messages.push_back (std::make_pair (id, "Level is non-positive")); + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (gold < 0) - messages.push_back (std::make_pair (id, "Gold count is negative")); + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (npc.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (npc.mClass.empty()) - messages.push_back (std::make_pair (id, "Class is missing")); + messages.add(id, "Class is missing", "", CSMDoc::Message::Severity_Error); else if (mClasses.searchId (npc.mClass) == -1) - messages.push_back (std::make_pair (id, "Class '" + npc.mClass + "' does not exist")); + messages.add(id, "Class '" + npc.mClass + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mRace.empty()) - messages.push_back (std::make_pair (id, "Race is missing")); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if (mRaces.searchId (npc.mRace) == -1) - messages.push_back (std::make_pair (id, "Race '" + npc.mRace + "' does not exist")); + messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); if (disposition < 0) - messages.push_back (std::make_pair (id, "Disposition is negative")); + messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); if (reputation < 0) - messages.push_back (std::make_pair (id, "Reputation is negative")); + messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); if (!npc.mFaction.empty()) { if (rank < 0) - messages.push_back (std::make_pair (id, "Faction rank is negative")); + messages.add(id, "Faction rank is negative", "", CSMDoc::Message::Severity_Warning); if (mFactions.searchId(npc.mFaction) == -1) - messages.push_back (std::make_pair (id, "Faction '" + npc.mFaction + "' does not exist")); + messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, "Head is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Head is missing")); + else + { + if (mBodyParts.searchId(npc.mHead) == -1) + messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body parts stuff validity for the specific NPC + } if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Hair is missing")); + else + { + if (mBodyParts.searchId(npc.mHair) == -1) + messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body part stuff validity for the specific NPC + } // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); @@ -799,14 +809,14 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) - messages.push_back (std::make_pair (id, "Minimum slash damage higher than maximum")); + messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) - messages.push_back (std::make_pair (id, "Minimum thrust damage is higher than maximum")); + messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) - messages.push_back (std::make_pair (id, "Minimum chop damage is higher than maximum")); + messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || @@ -814,10 +824,10 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( { //checking of health if (weapon.mData.mHealth == 0) - messages.push_back (std::make_pair (id, "Durability is equal to zero")); + messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mReach < 0) - messages.push_back (std::make_pair (id, "Reach is negative")); + messages.add(id, "Reach is negative", "", CSMDoc::Message::Severity_Error); } } @@ -880,9 +890,9 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(staticElement.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + staticElement.mModel + "' does not exist")); + messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); } //final check @@ -890,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) - messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, - "Player record is missing")); + messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( @@ -905,7 +914,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); if (localIndex.first == -1) - messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); + messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error); else { // Needs to accommodate containers, creatures, and NPCs @@ -926,7 +935,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: - messages.push_back (std::make_pair(id, "'" + itemName + "' is not an item")); + messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error); } } } @@ -938,64 +947,64 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, "Name is missing")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, "Weight is negative")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, "Value is negative")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(someItem.mModel) == -1) - messages.push_back(std::make_pair(someID, "Model '" + someItem.mModel + "' does not exist")); + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); else if (mIcons.searchId(someItem.mIcon) == -1) { std::string ddsIcon = someItem.mIcon; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) - messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); } if (enchantable && someItem.mData.mEnchant < 0) - messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); + messages.add(someID, "Enchantment points number is negative", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, "Name is missing")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, "Weight is negative")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, "Value is negative")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(someItem.mModel) == -1) - messages.push_back (std::make_pair (someID, "Model '" + someItem.mModel + "' does not exist")); + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); else if (mIcons.searchId(someItem.mIcon) == -1) { std::string ddsIcon = someItem.mIcon; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) - messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); } } @@ -1003,17 +1012,17 @@ template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, "Quality is non-positive")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); if (canBeBroken && someTool.mData.mUses<=0) - messages.push_back (std::make_pair (someID, "Number of uses is non-positive")); + messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) -{ +{ if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, "Quality is non-positive")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::listCheck ( @@ -1022,10 +1031,10 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mObjects.searchId(someList.mList[i].mId).first == -1) - messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); + messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error); if (someList.mList[i].mLevel < 1) - messages.push_back (std::make_pair (someID, "Level of item '" + someList.mList[i].mId + "' is non-positive")); + messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error); } } @@ -1035,6 +1044,6 @@ template void CSMTools::ReferenceableCheckStage::scriptCheck ( if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) - messages.push_back (std::make_pair (someID, "Script '"+someTool.mScript+"' does not exist")); + messages.add(someID, "Script '" + someTool.mScript + "' does not exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 5da7378755..d8682c9072 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -19,7 +19,8 @@ namespace CSMTools const CSMWorld::IdCollection& factions, const CSMWorld::IdCollection& scripts, const CSMWorld::Resources& models, - const CSMWorld::Resources& icons); + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -86,6 +87,7 @@ namespace CSMTools const CSMWorld::IdCollection& mScripts; const CSMWorld::Resources& mModels; const CSMWorld::Resources& mIcons; + const CSMWorld::IdCollection& mBodyParts; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 87b5041e07..b9a0abb757 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -83,7 +83,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), - mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons))); + mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons), + mData.getBodyParts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 34ffaa2fe2cf6f85094289769cc788f55a9c4c03 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 21:18:17 +0300 Subject: [PATCH 053/150] Make finishing touches to object record verifiers --- .../opencs/model/tools/referenceablecheck.cpp | 95 ++++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 2 +- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index c61d84d514..60d4a6b589 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -16,7 +16,7 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::Resources& models, const CSMWorld::Resources& icons, const CSMWorld::IdCollection& bodyparts) - :mObjects(referenceable), + :mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), @@ -32,201 +32,201 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - const int bookSize(mObjects.getBooks().getSize()); + const int bookSize(mReferencables.getBooks().getSize()); if (stage < bookSize) { - bookCheck(stage, mObjects.getBooks(), messages); + bookCheck(stage, mReferencables.getBooks(), messages); return; } stage -= bookSize; - const int activatorSize(mObjects.getActivators().getSize()); + const int activatorSize(mReferencables.getActivators().getSize()); if (stage < activatorSize) { - activatorCheck(stage, mObjects.getActivators(), messages); + activatorCheck(stage, mReferencables.getActivators(), messages); return; } stage -= activatorSize; - const int potionSize(mObjects.getPotions().getSize()); + const int potionSize(mReferencables.getPotions().getSize()); if (stage < potionSize) { - potionCheck(stage, mObjects.getPotions(), messages); + potionCheck(stage, mReferencables.getPotions(), messages); return; } stage -= potionSize; - const int apparatusSize(mObjects.getApparati().getSize()); + const int apparatusSize(mReferencables.getApparati().getSize()); if (stage < apparatusSize) { - apparatusCheck(stage, mObjects.getApparati(), messages); + apparatusCheck(stage, mReferencables.getApparati(), messages); return; } stage -= apparatusSize; - const int armorSize(mObjects.getArmors().getSize()); + const int armorSize(mReferencables.getArmors().getSize()); if (stage < armorSize) { - armorCheck(stage, mObjects.getArmors(), messages); + armorCheck(stage, mReferencables.getArmors(), messages); return; } stage -= armorSize; - const int clothingSize(mObjects.getClothing().getSize()); + const int clothingSize(mReferencables.getClothing().getSize()); if (stage < clothingSize) { - clothingCheck(stage, mObjects.getClothing(), messages); + clothingCheck(stage, mReferencables.getClothing(), messages); return; } stage -= clothingSize; - const int containerSize(mObjects.getContainers().getSize()); + const int containerSize(mReferencables.getContainers().getSize()); if (stage < containerSize) { - containerCheck(stage, mObjects.getContainers(), messages); + containerCheck(stage, mReferencables.getContainers(), messages); return; } stage -= containerSize; - const int doorSize(mObjects.getDoors().getSize()); + const int doorSize(mReferencables.getDoors().getSize()); if (stage < doorSize) { - doorCheck(stage, mObjects.getDoors(), messages); + doorCheck(stage, mReferencables.getDoors(), messages); return; } stage -= doorSize; - const int ingredientSize(mObjects.getIngredients().getSize()); + const int ingredientSize(mReferencables.getIngredients().getSize()); if (stage < ingredientSize) { - ingredientCheck(stage, mObjects.getIngredients(), messages); + ingredientCheck(stage, mReferencables.getIngredients(), messages); return; } stage -= ingredientSize; - const int creatureLevListSize(mObjects.getCreatureLevelledLists().getSize()); + const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); if (stage < creatureLevListSize) { - creaturesLevListCheck(stage, mObjects.getCreatureLevelledLists(), messages); + creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); return; } stage -= creatureLevListSize; - const int itemLevelledListSize(mObjects.getItemLevelledList().getSize()); + const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); if (stage < itemLevelledListSize) { - itemLevelledListCheck(stage, mObjects.getItemLevelledList(), messages); + itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } stage -= itemLevelledListSize; - const int lightSize(mObjects.getLights().getSize()); + const int lightSize(mReferencables.getLights().getSize()); if (stage < lightSize) { - lightCheck(stage, mObjects.getLights(), messages); + lightCheck(stage, mReferencables.getLights(), messages); return; } stage -= lightSize; - const int lockpickSize(mObjects.getLocpicks().getSize()); + const int lockpickSize(mReferencables.getLocpicks().getSize()); if (stage < lockpickSize) { - lockpickCheck(stage, mObjects.getLocpicks(), messages); + lockpickCheck(stage, mReferencables.getLocpicks(), messages); return; } stage -= lockpickSize; - const int miscSize(mObjects.getMiscellaneous().getSize()); + const int miscSize(mReferencables.getMiscellaneous().getSize()); if (stage < miscSize) { - miscCheck(stage, mObjects.getMiscellaneous(), messages); + miscCheck(stage, mReferencables.getMiscellaneous(), messages); return; } stage -= miscSize; - const int npcSize(mObjects.getNPCs().getSize()); + const int npcSize(mReferencables.getNPCs().getSize()); if (stage < npcSize) { - npcCheck(stage, mObjects.getNPCs(), messages); + npcCheck(stage, mReferencables.getNPCs(), messages); return; } stage -= npcSize; - const int weaponSize(mObjects.getWeapons().getSize()); + const int weaponSize(mReferencables.getWeapons().getSize()); if (stage < weaponSize) { - weaponCheck(stage, mObjects.getWeapons(), messages); + weaponCheck(stage, mReferencables.getWeapons(), messages); return; } stage -= weaponSize; - const int probeSize(mObjects.getProbes().getSize()); + const int probeSize(mReferencables.getProbes().getSize()); if (stage < probeSize) { - probeCheck(stage, mObjects.getProbes(), messages); + probeCheck(stage, mReferencables.getProbes(), messages); return; } stage -= probeSize; - const int repairSize(mObjects.getRepairs().getSize()); + const int repairSize(mReferencables.getRepairs().getSize()); if (stage < repairSize) { - repairCheck(stage, mObjects.getRepairs(), messages); + repairCheck(stage, mReferencables.getRepairs(), messages); return; } stage -= repairSize; - const int staticSize(mObjects.getStatics().getSize()); + const int staticSize(mReferencables.getStatics().getSize()); if (stage < staticSize) { - staticCheck(stage, mObjects.getStatics(), messages); + staticCheck(stage, mReferencables.getStatics(), messages); return; } stage -= staticSize; - const int creatureSize(mObjects.getCreatures().getSize()); + const int creatureSize(mReferencables.getCreatures().getSize()); if (stage < creatureSize) { - creatureCheck(stage, mObjects.getCreatures(), messages); + creatureCheck(stage, mReferencables.getCreatures(), messages); return; } // if we come that far, we are about to perform our last, final check. @@ -239,7 +239,7 @@ int CSMTools::ReferenceableCheckStage::setup() mPlayerPresent = false; mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); - return mObjects.getSize() + 1; + return mReferencables.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( @@ -300,6 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); + /// \todo Check magic effects for validity // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); @@ -485,7 +486,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( if (!creature.mOriginal.empty()) { - CSMWorld::RefIdData::LocalIndex index = mObjects.searchId(creature.mOriginal); + CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal); if (index.first == -1) messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error); else if (index.second != CSMWorld::UniversalId::Type_Creature) @@ -892,7 +893,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( if (staticElement.mModel.empty()) messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(staticElement.mModel) == -1) - messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); } //final check @@ -911,7 +912,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( for (size_t i = 0; i < itemList.size(); ++i) { std::string itemName = itemList[i].mItem.toString(); - CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); + CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error); @@ -1020,7 +1021,7 @@ template void CSMTools::ReferenceableCheckStage::toolCheck ( template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) -{ +{ if (someTool.mData.mQuality <= 0) messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); } @@ -1030,7 +1031,7 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( { for (unsigned i = 0; i < someList.mList.size(); ++i) { - if (mObjects.searchId(someList.mList[i].mId).first == -1) + if (mReferencables.searchId(someList.mList[i].mId).first == -1) messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error); if (someList.mList[i].mLevel < 1) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index d8682c9072..e55e5fad97 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -80,7 +80,7 @@ namespace CSMTools CSMDoc::Messages& messages, const std::string& someID); - const CSMWorld::RefIdData& mObjects; + const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; From 0bdb7ea92f77c695f90e10b0eda0d581561515cb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 21:40:07 +0300 Subject: [PATCH 054/150] Cleanup --- apps/opencs/model/tools/birthsigncheck.cpp | 26 ++----- apps/opencs/model/tools/birthsigncheck.hpp | 3 - apps/opencs/model/tools/classcheck.cpp | 4 -- apps/opencs/model/tools/magiceffectcheck.cpp | 70 ++++++------------- .../opencs/model/tools/referenceablecheck.cpp | 2 + 5 files changed, 28 insertions(+), 77 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index c582da8e4b..a0ae4b11f1 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -7,17 +7,6 @@ #include "../world/universalid.hpp" - -std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const -{ - if (mTextures.searchId(texture) != -1) return std::string(); - - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); - - return "Image '" + texture + "' does not exist"; -} - CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, const CSMWorld::Resources &textures) : mBirthsigns(birthsigns), @@ -46,24 +35,21 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); if (birthsign.mName.empty()) - { messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); - } if (birthsign.mDescription.empty()) - { messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); - } if (birthsign.mTexture.empty()) - { messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); - } else { - const std::string error = checkTexture(birthsign.mTexture); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mTextures.searchId(birthsign.mTexture) != -1) + return; + + std::string ddsTexture = birthsign.mTexture; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1)) + messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index d032d3cefd..9001c524cd 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -17,9 +17,6 @@ namespace CSMTools const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; - private: - std::string checkTexture(const std::string &texture) const; - public: BirthsignCheckStage (const CSMWorld::IdCollection &birthsigns, diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 9984c32872..25cca8fcdf 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -49,12 +49,10 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for invalid attributes for (int i=0; i<2; ++i) - { if (class_.mData.mAttribute[i]==-1) { messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error); } - } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { @@ -69,10 +67,8 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) ++skills[class_.mData.mSkills[i][i2]]; for (auto &skill : skills) - { if (skill.second>1) { messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } - } } diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 943d89cdc7..b62e791a43 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,17 +4,6 @@ #include "../prefs/state.hpp" -std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const -{ - const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; - if (textures.searchId(texture) != -1) return std::string(); - - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) return std::string(); - - return (isIcon ? "Icon '" : "Particle '") + texture + "' does not exist"; -} - std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const @@ -25,12 +14,6 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, return std::string(); } -std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const -{ - if (!id.empty() && mSounds.searchId(id) == -1) return (column + " '" + id + "' " + "does not exist"); - return std::string(); -} - CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &objects, @@ -79,16 +62,22 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa } else { - const std::string error = checkTexture(effect.mIcon, true); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mIcons.searchId(effect.mIcon) == -1) + { + std::string ddsIcon = effect.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } if (!effect.mParticle.empty()) { - const std::string error = checkTexture(effect.mParticle, false); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mTextures.searchId(effect.mParticle) == -1) + { + std::string ddsParticle = effect.mParticle; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1)) + messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } if (!effect.mCasting.empty()) @@ -119,31 +108,12 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa messages.add(id, error, "", CSMDoc::Message::Severity_Error); } - if (!effect.mCastSound.empty()) - { - const std::string error = checkSound(effect.mCastSound, "Casting sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mHitSound.empty()) - { - const std::string error = checkSound(effect.mHitSound, "Hit sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mAreaSound.empty()) - { - const std::string error = checkSound(effect.mAreaSound, "Area sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mBoltSound.empty()) - { - const std::string error = checkSound(effect.mBoltSound, "Bolt sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } + if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1) + messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1) + messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1) + messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1) + messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 60d4a6b589..4d68c93cde 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -723,7 +723,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); if (reputation < 0) + { messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); + } if (!npc.mFaction.empty()) { From 47b9008743b9a2a4d824f0768035f2932f672dd7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 22:16:50 +0300 Subject: [PATCH 055/150] Renovate reference record verifier --- apps/opencs/model/tools/referencecheck.cpp | 81 +++++++++------------- apps/opencs/model/tools/skillcheck.cpp | 2 - apps/opencs/model/tools/soundcheck.cpp | 1 - apps/opencs/model/tools/soundcheck.hpp | 6 +- 4 files changed, 33 insertions(+), 57 deletions(-) diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index dd93b4e9cc..76bfeb3ba2 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -28,76 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); - // Check for empty reference id - if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, "Instance is not based on an object")); - } else { + // Check reference id + if (cellRef.mRefID.empty()) + messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error); + else + { // Check for non existing referenced object - if (mObjects.searchId(cellRef.mRefID) == -1) { - messages.push_back(std::make_pair(id, "Instance of a non-existent object '" + cellRef.mRefID + "'")); - } else { + if (mObjects.searchId(cellRef.mRefID) == -1) + messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error); + else + { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; - if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { - std::string str = "Invalid charge: "; - if (localIndex.second == CSMWorld::UniversalId::Type_Light) - str += std::to_string(cellRef.mChargeFloat); - else - str += std::to_string(cellRef.mChargeInt); - messages.push_back(std::make_pair(id, str)); - } + if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) + messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error); } } // If object have owner, check if that owner reference is valid if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1) - messages.push_back(std::make_pair(id, "Owner object '" + cellRef.mOwner + "' does not exist")); + messages.add(id, "Owner object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) if (mObjects.searchId(cellRef.mSoul) == -1) - messages.push_back(std::make_pair(id, "Trapped soul object '" + cellRef.mSoul + "' does not exist")); + messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); - bool hasFaction = !cellRef.mFaction.empty(); - - // If object have faction, check if that faction reference is valid - if (hasFaction) - if (mFactions.searchId(cellRef.mFaction) == -1) - messages.push_back(std::make_pair(id, "Faction '" + cellRef.mFaction + "' does not exist")); - - // Check item's faction rank - if ((hasFaction && cellRef.mFactionRank < -1) || (!hasFaction && cellRef.mFactionRank != -2)) - messages.push_back(std::make_pair(id, "Invalid faction rank " + std::to_string(cellRef.mFactionRank))); - - // If door have destination cell, check if that reference is valid - if (!cellRef.mDestCell.empty()) - if (mCells.searchId(cellRef.mDestCell) == -1) - messages.push_back(std::make_pair(id, "Destination cell '" + cellRef.mDestCell + "' does not exist")); - - // Check if scale isn't negative - if (cellRef.mScale < 0) + if (cellRef.mFaction.empty()) { - std::string str = "Negative scale: "; - str += std::to_string(cellRef.mScale); - messages.push_back(std::make_pair(id, str)); + if (cellRef.mFactionRank != -2) + messages.add(id, "Reference without a faction has a faction rank", "", CSMDoc::Message::Severity_Error); } + else + { + if (mFactions.searchId(cellRef.mFaction) == -1) + messages.add(id, "Faction '" + cellRef.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (cellRef.mFactionRank < -1) + messages.add(id, "Invalid faction rank", "", CSMDoc::Message::Severity_Error); + } + + if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1) + messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error); + + if (cellRef.mScale < 0) + messages.add(id, "Negative scale", "", CSMDoc::Message::Severity_Error); // Check if enchantement points aren't negative or are at full (-1) - if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) - { - std::string str = "Negative enchantment points: "; - str += std::to_string(cellRef.mEnchantmentCharge); - messages.push_back(std::make_pair(id, str)); - } + if (cellRef.mEnchantmentCharge < -1) + messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error); // Check if gold value isn't negative if (cellRef.mGoldValue < 0) - { - std::string str = "Negative gold value: "; - str += cellRef.mGoldValue; - messages.push_back(std::make_pair(id, str)); - } + messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error); } int CSMTools::ReferenceCheckStage::setup() diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index ab7df51cb5..89eba011a8 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -35,10 +35,8 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); for (int i=0; i<4; ++i) - { if (skill.mData.mUseValue[i]<0) { messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } - } } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 7bb27e9847..09e5ecdbfd 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -4,7 +4,6 @@ #include "../prefs/state.hpp" -#include "../world/resources.hpp" #include "../world/universalid.hpp" CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection &sounds, diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index 47c3249ce7..fc59257178 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -3,15 +3,11 @@ #include +#include "../world/resources.hpp" #include "../world/idcollection.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent From dabdb0bfaf63d55ec72c678332e5153c19cdcc4b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 18:53:02 +0300 Subject: [PATCH 056/150] Get rid of deprecated Messages::push_back() --- apps/opencs/model/doc/messages.cpp | 5 ----- apps/opencs/model/doc/messages.hpp | 3 --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/doc/messages.cpp b/apps/opencs/model/doc/messages.cpp index 76bbb6f227..b70d44eda3 100644 --- a/apps/opencs/model/doc/messages.cpp +++ b/apps/opencs/model/doc/messages.cpp @@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& mMessages.push_back (Message (id, message, hint, severity)); } -void CSMDoc::Messages::push_back (const std::pair& data) -{ - add (data.first, data.second); -} - CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const { return mMessages.begin(); diff --git a/apps/opencs/model/doc/messages.hpp b/apps/opencs/model/doc/messages.hpp index 4041e1a678..671ded82a0 100644 --- a/apps/opencs/model/doc/messages.hpp +++ b/apps/opencs/model/doc/messages.hpp @@ -56,9 +56,6 @@ namespace CSMDoc const std::string& hint = "", Message::Severity severity = Message::Severity_Default); - /// \deprecated Use add instead. - void push_back (const std::pair& data); - Iterator begin() const; Iterator end() const; diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 4d68c93cde..def6dece38 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -737,7 +737,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, "Head is missing")); + messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error); else { if (mBodyParts.searchId(npc.mHead) == -1) @@ -746,7 +746,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( } if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, "Hair is missing")); + messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error); else { if (mBodyParts.searchId(npc.mHair) == -1) From 015cd6064faa44fd4969de80722b2f9ebaa79dbc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 01:21:27 +0300 Subject: [PATCH 057/150] Implement basic enchantment record verifier (feature #1617) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/enchantmentcheck.cpp | 42 ++++++++++++++++++++ apps/opencs/model/tools/enchantmentcheck.hpp | 30 ++++++++++++++ apps/opencs/model/tools/magiceffectcheck.cpp | 6 ++- apps/opencs/model/tools/tools.cpp | 3 ++ 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/model/tools/enchantmentcheck.cpp create mode 100644 apps/opencs/model/tools/enchantmentcheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d1ebcde428..f2821f184a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -42,7 +42,7 @@ opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck - mergestages gmstcheck topicinfocheck journalcheck + mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck ) opencs_hdrs_noqt (model/tools diff --git a/apps/opencs/model/tools/enchantmentcheck.cpp b/apps/opencs/model/tools/enchantmentcheck.cpp new file mode 100644 index 0000000000..e1dca93312 --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.cpp @@ -0,0 +1,42 @@ +#include "enchantmentcheck.hpp" + +#include "../prefs/state.hpp" + +#include "../world/universalid.hpp" + +CSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments) + : mEnchantments (enchantments) +{ + mIgnoreBaseRecords = false; +} + +int CSMTools::EnchantmentCheckStage::setup() +{ + mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); + + return mEnchantments.getSize(); +} + +void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = mEnchantments.getRecord (stage); + + // Skip "Base" records (setting!) and "Deleted" records + if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) + return; + + const ESM::Enchantment& enchantment = record.get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Enchantment, enchantment.mId); + + if (enchantment.mData.mType < 0 || enchantment.mData.mType > 3) + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCost < 0) + messages.add(id, "Cost is negative", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCharge < 0) + messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error); + + /// \todo Check effects list for validity +} diff --git a/apps/opencs/model/tools/enchantmentcheck.hpp b/apps/opencs/model/tools/enchantmentcheck.hpp new file mode 100644 index 0000000000..ba8ae64aa8 --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.hpp @@ -0,0 +1,30 @@ +#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H +#define CSM_TOOLS_ENCHANTMENTCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "../doc/stage.hpp" + +namespace CSMTools +{ + /// \brief Make sure that enchantment records are correct + class EnchantmentCheckStage : public CSMDoc::Stage + { + const CSMWorld::IdCollection& mEnchantments; + bool mIgnoreBaseRecords; + + public: + + EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, CSMDoc::Messages& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index b62e791a43..f55fb14eea 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -9,8 +9,10 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const std::string &column) const { CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); - if (index.first == -1) return (column + " '" + id + "' " + "does not exist"); - else if (index.second != type.getType()) return (column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"); + if (index.first == -1) + return (column + " '" + id + "' does not exist"); + else if (index.second != type.getType()) + return (column + " '" + id + "' does not have " + type.getTypeName() + " type"); return std::string(); } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index b9a0abb757..07a721e8e2 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -32,6 +32,7 @@ #include "gmstcheck.hpp" #include "topicinfocheck.hpp" #include "journalcheck.hpp" +#include "enchantmentcheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -128,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos())); + mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments())); + mVerifier.setOperation (mVerifierOperation); } From 5d1c1f25f79ed21432503ebb3fdc6b4a935e3352 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 3 Sep 2018 16:57:01 +0300 Subject: [PATCH 058/150] Remove now redundant NPC fields checks --- .../opencs/model/tools/referenceablecheck.cpp | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index def6dece38..d31b8b7d0a 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -667,9 +667,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( return; short level(npc.mNpdt.mLevel); - char disposition(npc.mNpdt.mDisposition); - char reputation(npc.mNpdt.mReputation); - char rank(npc.mNpdt.mRank); int gold(npc.mNpdt.mGold); if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated @@ -719,22 +716,8 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( else if (mRaces.searchId (npc.mRace) == -1) messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); - if (disposition < 0) - messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); - - if (reputation < 0) - { - messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); - } - - if (!npc.mFaction.empty()) - { - if (rank < 0) - messages.add(id, "Faction rank is negative", "", CSMDoc::Message::Severity_Warning); - - if (mFactions.searchId(npc.mFaction) == -1) - messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); - } + if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1) + messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mHead.empty()) messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error); From d6560d3f20dc5dd889088bc796e5bf0032610508 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 4 Sep 2018 16:08:09 +0300 Subject: [PATCH 059/150] Make several messages more strict and clean up topic info verifier --- apps/opencs/model/tools/classcheck.cpp | 11 ++--- apps/opencs/model/tools/factioncheck.cpp | 6 +-- .../opencs/model/tools/referenceablecheck.cpp | 2 +- apps/opencs/model/tools/topicinfocheck.cpp | 45 +++++-------------- 4 files changed, 18 insertions(+), 46 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 25cca8fcdf..a82121597c 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -36,12 +36,7 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - { - if (class_.mData.mIsPlayable != 0) - messages.add(id, "Name of a playable class is missing", "", CSMDoc::Message::Severity_Error); - else - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); - } + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) @@ -56,7 +51,7 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -69,6 +64,6 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) for (auto &skill : skills) if (skill.second>1) { - messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 5bd613851a..0ee245ad41 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -55,7 +55,7 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages for (auto &skill : skills) if (skill.second>1) { - messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d31b8b7d0a..fdbab7fd09 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -475,7 +475,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( if (creature.mData.mAttack[i] < 0) messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error); if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1]) - messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error); } if (creature.mData.mGold < 0) diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index 9a31db72b7..fe9bc991d4 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -133,7 +133,6 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) { - std::ostringstream stream; messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error); } @@ -170,16 +169,12 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const if (index.first == -1) { - std::ostringstream stream; - stream << "Actor '" << actor << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted actor '" << actor << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) @@ -199,9 +194,7 @@ bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CS { if (mCellNames.find(cell) == mCellNames.end()) { - std::ostringstream stream; - stream << "Cell '" << cell << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -236,7 +229,7 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1 << " for the '" << factionName << "' faction"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -250,16 +243,12 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS if (index.first == -1) { - std::ostringstream stream; - stream << "Item '" << item << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted item '" << item << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error); return false; } else @@ -327,18 +316,12 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele } else if (infoCondition.conditionIsAlwaysTrue()) { - std::ostringstream stream; - stream << "Condition '" << infoCondition.toString() << "' is always true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning); return false; } else if (infoCondition.conditionIsNeverTrue()) { - std::ostringstream stream; - stream << "Condition '" << infoCondition.toString() << "' is never true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning); return false; } @@ -397,9 +380,7 @@ bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const { if (mSoundFiles.searchId(sound) == -1) { - std::ostringstream stream; - stream << "Sound file '" << sound << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -414,16 +395,12 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW if (index == -1) { - std::ostringstream stream; - stream << T::getRecordType() + " '" << name << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (collection.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted " << T::getRecordType() << " record '" << name << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } From c025427575e7279f2871a982f2a6213f3a4b9642 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 11 Sep 2018 21:40:41 +0300 Subject: [PATCH 060/150] Implement enchantment record effect list verification (feature #1617) --- apps/opencs/model/tools/enchantmentcheck.cpp | 42 +++++++++++++++++++- apps/opencs/model/tools/enchantmentcheck.hpp | 1 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/enchantmentcheck.cpp b/apps/opencs/model/tools/enchantmentcheck.cpp index e1dca93312..28f2b32cb9 100644 --- a/apps/opencs/model/tools/enchantmentcheck.cpp +++ b/apps/opencs/model/tools/enchantmentcheck.cpp @@ -38,5 +38,45 @@ void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& mess if (enchantment.mData.mCharge < 0) messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error); - /// \todo Check effects list for validity + if (enchantment.mData.mCost > enchantment.mData.mCharge) + messages.add(id, "Cost is higher than charge", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mEffects.mList.empty()) + { + messages.add(id, "Enchantment doesn't have any magic effects", "", CSMDoc::Message::Severity_Warning); + } + else + { + std::vector::const_iterator effect = enchantment.mEffects.mList.begin(); + + for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++) + { + const std::string number = std::to_string(i); + // At the time of writing this effects, attributes and skills are hardcoded + if (effect->mEffectID < 0 || effect->mEffectID > 142) + { + messages.add(id, "Effect #" + number + " is invalid", "", CSMDoc::Message::Severity_Error); + ++effect; + continue; + } + + if (effect->mSkill < -1 || effect->mSkill > 26) + messages.add(id, "Effect #" + number + " affected skill is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mAttribute < -1 || effect->mAttribute > 7) + messages.add(id, "Effect #" + number + " affected attribute is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mRange < 0 || effect->mRange > 2) + messages.add(id, "Effect #" + number + " range is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mArea < 0) + messages.add(id, "Effect #" + number + " area is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mDuration < 0) + messages.add(id, "Effect #" + number + " duration is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin < 0) + messages.add(id, "Effect #" + number + " minimum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMax < 0) + messages.add(id, "Effect #" + number + " maximum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin > effect->mMagnMax) + messages.add(id, "Effect #" + number + " minimum magnitude is higher than maximum magnitude", "", CSMDoc::Message::Severity_Error); + ++effect; + } + } } diff --git a/apps/opencs/model/tools/enchantmentcheck.hpp b/apps/opencs/model/tools/enchantmentcheck.hpp index ba8ae64aa8..3bd85326fb 100644 --- a/apps/opencs/model/tools/enchantmentcheck.hpp +++ b/apps/opencs/model/tools/enchantmentcheck.hpp @@ -24,6 +24,7 @@ namespace CSMTools virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. + }; } From 5a86554f97f87c52add041865a4fdcd2a5dee6bd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 14 Sep 2018 21:42:11 +0300 Subject: [PATCH 061/150] Cleanup --- apps/opencs/model/tools/birthsigncheck.cpp | 6 +----- apps/opencs/model/tools/factioncheck.cpp | 2 -- apps/opencs/model/tools/journalcheck.cpp | 7 ++----- apps/opencs/model/tools/magiceffectcheck.hpp | 2 -- apps/opencs/model/tools/racecheck.cpp | 10 ++-------- apps/opencs/model/tools/regioncheck.cpp | 5 ----- apps/opencs/model/tools/skillcheck.cpp | 4 +--- apps/opencs/model/tools/soundcheck.cpp | 4 +--- apps/opencs/model/tools/soundgencheck.cpp | 2 -- 9 files changed, 7 insertions(+), 35 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index a0ae4b11f1..f91fc22f60 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -1,6 +1,5 @@ #include "birthsigncheck.hpp" -#include #include #include "../prefs/state.hpp" @@ -42,11 +41,8 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag if (birthsign.mTexture.empty()) messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); - else + else if (mTextures.searchId(birthsign.mTexture) == -1) { - if (mTextures.searchId(birthsign.mTexture) != -1) - return; - std::string ddsTexture = birthsign.mTexture; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1)) messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error); diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 0ee245ad41..8a198e9535 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -1,9 +1,7 @@ #include "factioncheck.hpp" -#include #include -#include #include #include "../prefs/state.hpp" diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index dc15b26ee8..ae83abfa01 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -1,7 +1,6 @@ #include "journalcheck.hpp" #include -#include #include "../prefs/state.hpp" @@ -63,12 +62,10 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::pair::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); // Duplicate index - if (result.second == false) + if (!result.second) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - std::ostringstream stream; - stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 22dad3db96..a52723b0fe 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -23,9 +23,7 @@ namespace CSMTools bool mIgnoreBaseRecords; private: - std::string checkTexture(const std::string &texture, bool isIcon) const; std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; - std::string checkSound(const std::string &id, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 3b487b9253..6585a31ccb 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -1,9 +1,5 @@ #include "racecheck.hpp" -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -28,10 +24,8 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description - if (race.mName.empty() && race.mData.mFlags & 0x1) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); - else if (race.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + if (race.mName.empty()) + messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning); if (race.mDescription.empty()) messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index 8375e3f0ac..0b6537d7fd 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -1,10 +1,5 @@ #include "regioncheck.hpp" -#include -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index 89eba011a8..c5b38dc1e0 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -1,7 +1,5 @@ #include "skillcheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -37,6 +35,6 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) for (int i=0; i<4; ++i) if (skill.mData.mUseValue[i]<0) { - messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 09e5ecdbfd..c0d893f1a1 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -1,7 +1,5 @@ #include "soundcheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -35,7 +33,7 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) { - messages.add(id, "Minimum range larger than maximum range", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning); } if (sound.mSound.empty()) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 7b6cf7a4b3..ec29e23fef 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -1,7 +1,5 @@ #include "soundgencheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/refiddata.hpp" From 59092978091fba6e756408af80f9ad58de2a03b8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 15 Sep 2018 23:20:28 +0300 Subject: [PATCH 062/150] Make underwater SFX always apply based on camera position (bug #4532) --- CHANGELOG.md | 1 + apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb8..4c304ed9bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4531: Movement does not reset idle animations + Bug #4532: Underwater sfx isn't tied to 3rd person camera Bug #4539: Paper Doll is affected by GUI scaling Bug #4543: Picking cursed items through inventory (menumode) makes it disappear Bug #4545: Creatures flee from werewolves diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9e08a25637..d1f2747879 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1789,7 +1789,7 @@ namespace MWWorld osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0); osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1); - bool underwater = isUnderwater(getPlayerPtr().getCell(), listenerPos); + bool underwater = isUnderwater(getPlayerPtr().getCell(), mRendering->getCameraPosition()); MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater); } From ae1c054635b6268dc19aab85397ad884aa81a903 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 15 Sep 2018 19:38:21 +0400 Subject: [PATCH 063/150] Make GetPCJumping return true only when jumping (bug #4641) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 21 +++++++++++++-------- apps/openmw/mwmechanics/creaturestats.cpp | 6 +++++- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 3 +-- apps/openmw/mwworld/actionteleport.cpp | 2 +- apps/openmw/mwworld/player.cpp | 14 +++++++++++++- apps/openmw/mwworld/player.hpp | 4 ++++ 9 files changed, 41 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a01e2c7468..36c7e19c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Bug #4641: GetPCJumping is handled incorrectly Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 123f7bf594..af8ed706da 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1850,7 +1850,8 @@ void CharacterController::update(float duration) if (isKnockedOut()) mTimeUntilWake -= duration; - bool godmode = mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool isPlayer = mPtr == MWMechanics::getPlayer(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(!cls.isActor()) updateAnimQueue(); @@ -1915,7 +1916,7 @@ void CharacterController::update(float duration) isrunning = isrunning && mHasMovedInXY; // advance athletics - if(mHasMovedInXY && mPtr == getPlayer()) + if(mHasMovedInXY && isPlayer) { if(inwater) { @@ -2011,8 +2012,12 @@ void CharacterController::update(float duration) } // advance acrobatics - if (mPtr == getPlayer()) + // also set jumping flag to allow GetPCJumping works + if (isPlayer) + { cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); + MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + } // decrease fatigue const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); @@ -2035,7 +2040,7 @@ void CharacterController::update(float duration) jumpstate = JumpState_Landing; vec.z() = 0.0f; - float height = cls.getCreatureStats(mPtr).land(); + float height = cls.getCreatureStats(mPtr).land(isPlayer); float healthLost = getFallDamage(mPtr, height); if (healthLost > 0.0f) @@ -2058,7 +2063,7 @@ void CharacterController::update(float duration) else { // report acrobatics progression - if (mPtr == getPlayer()) + if (isPlayer) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } @@ -2080,7 +2085,7 @@ void CharacterController::update(float duration) // Do not play turning animation for player if rotation speed is very slow. // Actual threshold should take framerate in account. float rotationThreshold = 0; - if (mPtr == getPlayer()) + if (isPlayer) rotationThreshold = 0.015 * 60 * duration; if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) @@ -2109,7 +2114,7 @@ void CharacterController::update(float duration) { // It seems only bipedal actors use turning animations. // Also do not use turning animations in the first-person view and when sneaking. - bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson(); + bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) { if(rot.z() > rotationThreshold) @@ -2121,7 +2126,7 @@ void CharacterController::update(float duration) } // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering - if (mPtr == getPlayer()) + if (isPlayer) { float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f; float complete; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 2994eac289..7cc6ea7843 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -7,6 +7,7 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -387,8 +388,11 @@ namespace MWMechanics mFallHeight += height; } - float CreatureStats::land() + float CreatureStats::land(bool isPlayer) { + if (isPlayer) + MWBase::Environment::get().getWorld()->getPlayer().setJumping(false); + float height = mFallHeight; mFallHeight = 0; return height; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 057a6f6025..503ac7d68a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -95,7 +95,7 @@ namespace MWMechanics /// Reset the fall height /// @return total fall height - float land(); + float land(bool isPlayer=false); const AttributeValue & getAttribute(int index) const; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 90668914f2..90d25ee3e9 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1394,6 +1394,7 @@ namespace MWPhysics mStandingCollisions.clear(); } + const MWWorld::Ptr player = MWMechanics::getPlayer(); const MWBase::World *world = MWBase::Environment::get().getWorld(); PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) @@ -1451,7 +1452,7 @@ namespace MWPhysics MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); if ((wasOnGround && physicActor->getOnGround()) || flying || world->isSwimming(iter->first) || slowFall < 1) - stats.land(); + stats.land(iter->first == player); else if (heightDiff < 0) stats.addToFallHeight(-heightDiff); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8d592f29c6..7250e9fcfc 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -107,8 +107,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayerPtr(); - runtime.push (!world->isOnGround(player) && !world->isFlying(player)); + runtime.push (world->getPlayer().getJumping()); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 93705f0059..f54edc8cb2 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -36,7 +36,7 @@ namespace MWWorld void ActionTeleport::teleport(const Ptr &actor) { MWBase::World* world = MWBase::Environment::get().getWorld(); - actor.getClass().getCreatureStats(actor).land(); + actor.getClass().getCreatureStats(actor).land(actor == world->getPlayerPtr()); if(actor == world->getPlayerPtr()) { world->getPlayer().setTeleported(true); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 389f599832..49b7dd8bb3 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -36,7 +36,8 @@ namespace MWWorld mTeleported(false), mCurrentCrimeId(-1), mPaidCrimeId(-1), - mAttackingOrSpell(false) + mAttackingOrSpell(false), + mJumping(false) { ESM::CellRef cellRef; cellRef.blank(); @@ -255,6 +256,16 @@ namespace MWWorld return mAttackingOrSpell; } + void Player::setJumping(bool jumping) + { + mJumping = jumping; + } + + bool Player::getJumping() const + { + return mJumping; + } + bool Player::isInCombat() { return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; } @@ -286,6 +297,7 @@ namespace MWWorld mForwardBackward = 0; mTeleported = false; mAttackingOrSpell = false; + mJumping = false; mCurrentCrimeId = -1; mPaidCrimeId = -1; mPreviousItems.clear(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d4a9f6050c..4b92fa3962 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -56,6 +56,7 @@ namespace MWWorld MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; bool mAttackingOrSpell; + bool mJumping; public: @@ -111,6 +112,9 @@ namespace MWWorld void setAttackingOrSpell(bool attackingOrSpell); bool getAttackingOrSpell() const; + void setJumping(bool jumping); + bool getJumping() const; + ///Checks all nearby actors to see if anyone has an aipackage against you bool isInCombat(); From 09427d3f5ec05aaa00f31da272f725f1cdba6976 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 16 Sep 2018 14:38:58 +0400 Subject: [PATCH 064/150] Take in account transformations of NiTriShape and NiSkinData in skinning (bug #4437) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 6 +++++- components/sceneutil/riggeometry.cpp | 9 +++++++++ components/sceneutil/riggeometry.hpp | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb8..9e8f040921 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Bug #4431: "Lock 0" console command is a no-op Bug #4432: Guards behaviour is incorrect if they do not have AI packages Bug #4433: Guard behaviour is incorrect with Alarm = 0 + Bug #4437: Transformations for NiSkinInstance are ignored Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax Bug #4452: Default terrain texture bleeds through texture transitions Bug #4453: Quick keys behaviour is invalid for equipment diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3198e995c5..81898e4777 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1082,7 +1082,10 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + // We should take in account transformation of NiTriShape and NiSkinData const Nif::NiSkinData *data = skin->data.getPtr(); + osg::Matrixf shapeTransforms = triShape->trafo.toMatrix() * data->trafo.toMatrix(); + const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { @@ -1096,11 +1099,12 @@ namespace NifOsg influence.mWeights.insert(indexWeight); } influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); - influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); + influence.mBoundSphere = osg::BoundingSpheref(shapeTransforms * data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); map->mMap.insert(std::make_pair(boneName, influence)); } rig->setInfluenceMap(map); + rig->setRigTransforms(*&shapeTransforms); parentNode->addChild(rig); } diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index c409bcd5cc..94e4a3b3eb 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -49,6 +49,7 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) + , mRigTransforms(copy.mRigTransforms) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -214,6 +215,9 @@ void RigGeometry::cull(osg::NodeVisitor* nv) if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); + if (!mRigTransforms.isIdentity()) + resultMat *= mRigTransforms; + for (auto &vertex : pair.second) { (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); @@ -309,6 +313,11 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) mInfluenceMap = influenceMap; } +void RigGeometry::setRigTransforms(osg::Matrixf& transform) +{ + mRigTransforms = transform; +} + void RigGeometry::accept(osg::NodeVisitor &nv) { if (!nv.validNodeMask(*this)) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 60b3edc9d8..723455234d 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -40,6 +40,7 @@ namespace SceneUtil }; void setInfluenceMap(osg::ref_ptr influenceMap); + void setRigTransforms(osg::Matrixf& transform); /// Initialize this geometry from the source geometry. /// @note The source geometry will not be modified. @@ -65,6 +66,7 @@ namespace SceneUtil osg::ref_ptr mGeomToSkelMatrix; osg::ref_ptr mInfluenceMap; + osg::Matrixf mRigTransforms; typedef std::pair BoneBindMatrixPair; From 95aa05e41be646c7ce556789f4c75619cfcfa6b6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 16 Sep 2018 16:47:33 +0300 Subject: [PATCH 065/150] Tweaks to script parser messages and pathgrid warnings --- apps/opencs/model/tools/pathgridcheck.cpp | 18 ++++++++---------- apps/opencs/model/tools/scriptcheck.cpp | 2 +- components/compiler/declarationparser.cpp | 2 +- components/compiler/exprparser.cpp | 8 ++++---- components/compiler/junkparser.cpp | 4 ++-- components/compiler/lineparser.cpp | 18 +++++++++--------- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 88750ad7f9..febb79c641 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -56,8 +56,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; - ss << "Duplicate edge between points" - << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; + ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); break; } @@ -70,7 +69,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else { std::ostringstream ss; - ss << "An edge is connected to a non-existent point " << pathgrid.mEdges[i].mV0; + ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -93,7 +92,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (!foundReverse) { std::ostringstream ss; - ss << "Missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; + ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j]; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -110,7 +109,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (it == duplList.end()) { std::ostringstream ss; - ss << "Point " << i << " duplicates point " << j + ss << "Point #" << i << " duplicates point #" << j << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); @@ -127,11 +126,10 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; - ss << "Point " << i << " (" - << pathgrid.mPoints[i].mX << ", " - << pathgrid.mPoints[i].mY << ", " - << pathgrid.mPoints[i].mZ << ") " - << "is disconnected from other points"; + ss << "Point #" << i << " (" + << pathgrid.mPoints[i].mX << ", " + << pathgrid.mPoints[i].mY << ", " + << pathgrid.mPoints[i].mZ << ") is disconnected from other points"; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index e62f9eb889..d3d9d1503d 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -30,7 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - stream << "Line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; + stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")"; std::ostringstream hintStream; diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index ffac252d54..e85c8c3ec2 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -24,7 +24,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke if (type!=' ') { /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", + getErrorHandler().warning ("ignoring local variable re-declaration", loc); mState = State_End; diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 7cb0abfd11..6b849ec3aa 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -422,7 +422,7 @@ namespace Compiler { if (!hasExplicit) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -791,7 +791,7 @@ namespace Compiler ++optionalCount; } else - getErrorHandler().warning ("Ignoring extra argument", + getErrorHandler().warning ("ignoring extra argument", stringParser.getTokenLoc()); } else if (*iter=='X') @@ -805,7 +805,7 @@ namespace Compiler if (parser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", parser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc()); } else if (*iter=='z') { @@ -817,7 +817,7 @@ namespace Compiler if (discardParser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", discardParser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc()); } else if (*iter=='j') { diff --git a/components/compiler/junkparser.cpp b/components/compiler/junkparser.cpp index 7608e9bab4..5910cca43d 100644 --- a/components/compiler/junkparser.cpp +++ b/components/compiler/junkparser.cpp @@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==mIgnoreKeyword) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackKeyword (keyword, loc); @@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_member) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackSpecial (code, loc); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index c2c1dff9b0..3f9d2e7903 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -88,7 +88,7 @@ namespace Compiler { if (mState==PotentialEndState) { - getErrorHandler().warning ("stray string argument (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray string argument", loc); mState = EndState; return true; } @@ -233,7 +233,7 @@ namespace Compiler if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { - getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); + getErrorHandler().warning ("unknown variable, ignoring set instruction", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -286,7 +286,7 @@ namespace Compiler { if (!hasExplicit && mState==ExplicitState) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -344,7 +344,7 @@ namespace Compiler { if (!hasExplicit && !mExplicit.empty()) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -360,7 +360,7 @@ namespace Compiler if (mState==ExplicitState) { // drop stray explicit reference - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mState = BeginState; mExplicit.clear(); } @@ -412,19 +412,19 @@ namespace Compiler case Scanner::K_else: - getErrorHandler().warning ("stray else (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray else", loc); mState = EndState; return true; case Scanner::K_endif: - getErrorHandler().warning ("stray endif (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray endif", loc); mState = EndState; return true; case Scanner::K_begin: - getErrorHandler().warning ("stray begin (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray begin", loc); mState = EndState; return true; } @@ -491,7 +491,7 @@ namespace Compiler { if (mState==EndState && code==Scanner::S_open) { - getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", + getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line", loc); return true; } From 2f054cc7a8f1f2c8196522419cb32a985f4ad653 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 16 Sep 2018 17:09:47 +0300 Subject: [PATCH 066/150] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb8..af29a67e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Feature #912: Editor: Add missing icons to UniversalId tables + Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script @@ -155,6 +156,7 @@ Task #4606: Support Rapture3D's OpenAL driver Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4621: Optimize combat AI + Task #4643: Revise editor record verifying functionality 0.44.0 ------ From 2961f0d8109bb47d5ce3434988b0dcc97637f9b1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 16 Sep 2018 20:47:51 +0400 Subject: [PATCH 067/150] Allow to use the %Name for creatures (bug #4644) --- CHANGELOG.md | 1 + apps/openmw/mwscript/interpretercontext.cpp | 13 ++++++++++--- apps/openmw/mwscript/interpretercontext.hpp | 2 +- components/interpreter/context.hpp | 2 +- components/interpreter/defines.cpp | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb8..f37c5c4c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Bug #4644: %Name should be available for all actors, not just for NPCs Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index f4e729da18..07b7a3bffd 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -288,10 +288,17 @@ namespace MWScript return "None"; } - std::string InterpreterContext::getNPCName() const + std::string InterpreterContext::getActorName() const { - ESM::NPC npc = *getReferenceImp().get()->mBase; - return npc.mName; + const MWWorld::Ptr& ptr = getReferenceImp(); + if (ptr.getClass().isNpc()) + { + const ESM::NPC* npc = ptr.get()->mBase; + return npc->mName; + } + + const ESM::Creature* creature = ptr.get()->mBase; + return creature->mName; } std::string InterpreterContext::getNPCRace() const diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index ac8e1833b4..5f1f4f7ab8 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -93,7 +93,7 @@ namespace MWScript virtual std::string getActionBinding(const std::string& action) const; - virtual std::string getNPCName() const; + virtual std::string getActorName() const; virtual std::string getNPCRace() const; diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 881687366b..4c320879e6 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -55,7 +55,7 @@ namespace Interpreter virtual std::string getActionBinding(const std::string& action) const = 0; - virtual std::string getNPCName() const = 0; + virtual std::string getActorName() const = 0; virtual std::string getNPCRace() const = 0; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 3c6226d9ce..0ceed80d53 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -138,7 +138,7 @@ namespace Interpreter{ retval << context.getNPCRace(); } else if((found = check(temp, "name", &i, &start))){ - retval << context.getNPCName(); + retval << context.getActorName(); } } else { // In messagebox or book, not dialogue From 9c8fc0557a492b80dec644b07a9b1af101622fea Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 13:35:22 +0400 Subject: [PATCH 068/150] Fix MSVC warning about possibly uninitialized movestate variable --- apps/openmw/mwmechanics/character.cpp | 47 ++++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 123f7bf594..85e55ec38b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -415,37 +415,38 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force) { + if (movement == mMovementState && idle == mIdleState && !force) + return; + std::string movementAnimName; MWRender::Animation::BlendMask movemask; const StateInfo *movestate; - if(force || movement != mMovementState || idle != mIdleState) + + movemask = MWRender::Animation::BlendMask_All; + movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); + if(movestate != sMovementListEnd) { - movemask = MWRender::Animation::BlendMask_All; - movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); - if(movestate != sMovementListEnd) + movementAnimName = movestate->groupname; + if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) { - movementAnimName = movestate->groupname; - if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case + movementAnimName = weap->shortgroup + movementAnimName; + else + movementAnimName += weap->shortgroup; + + if(!mAnimation->hasAnimation(movementAnimName)) { - if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case - movementAnimName = weap->shortgroup + movementAnimName; - else - movementAnimName += weap->shortgroup; + movemask = MWRender::Animation::BlendMask_LowerBody; + movementAnimName = movestate->groupname; - if(!mAnimation->hasAnimation(movementAnimName)) - { - movemask = MWRender::Animation::BlendMask_LowerBody; - movementAnimName = movestate->groupname; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; - - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - movementAnimName += "1h"; - } + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; } } } From 70ed8fd1a97b93f360cb841cd1f276da0f632c42 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 14:52:43 +0400 Subject: [PATCH 069/150] Use constants instead of widely used magic numbers (task #4645) --- CHANGELOG.md | 1 + apps/essimporter/converter.cpp | 10 ++++--- apps/essimporter/convertplayer.cpp | 6 ++--- apps/essimporter/importer.cpp | 7 ++--- apps/opencs/model/world/cellcoordinates.cpp | 6 ++--- apps/opencs/model/world/commands.cpp | 6 ++--- apps/opencs/view/render/cellarrow.cpp | 4 ++- apps/opencs/view/render/cellmarker.cpp | 4 ++- apps/opencs/view/render/instancemode.cpp | 4 +-- apps/opencs/view/render/object.cpp | 6 ++--- .../view/render/pagedworldspacewidget.cpp | 20 ++++++-------- apps/openmw/mwclass/door.cpp | 4 +-- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwclass/weapon.cpp | 5 ++-- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 6 ++--- apps/openmw/mwmechanics/aiwander.cpp | 3 +-- apps/openmw/mwphysics/physicssystem.cpp | 3 ++- apps/openmw/mwrender/globalmap.cpp | 4 +-- apps/openmw/mwrender/localmap.cpp | 3 ++- apps/openmw/mwrender/water.cpp | 7 +++-- apps/openmw/mwrender/water.hpp | 2 -- apps/openmw/mwsound/openal_output.cpp | 14 ++++------ apps/openmw/mwworld/projectilemanager.cpp | 4 ++- apps/openmw/mwworld/scene.cpp | 4 +-- apps/openmw/mwworld/worldimp.cpp | 17 ++++++------ components/CMakeLists.txt | 2 +- components/esm/loadland.hpp | 4 ++- components/esmterrain/storage.cpp | 22 +++++++-------- components/misc/constants.hpp | 27 +++++++++++++++++++ components/terrain/quadtreeworld.cpp | 4 ++- 32 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 components/misc/constants.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb9837bd9..9e2a713419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -162,6 +162,7 @@ Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4621: Optimize combat AI Task #4643: Revise editor record verifying functionality + Task #4645: Use constants instead of widely used magic numbers 0.44.0 ------ diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 2473daf955..900fbb05cd 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "convertcrec.hpp" #include "convertcntc.hpp" #include "convertscri.hpp" @@ -288,12 +290,12 @@ namespace ESSImport notepos[1] += 31.f; notepos[0] += 0.5; notepos[1] += 0.5; - notepos[0] = 8192 * notepos[0] / 32.f; - notepos[1] = 8192 * notepos[1] / 32.f; + notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f; + notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f; if (cell.isExterior()) { - notepos[0] += 8192 * cell.mData.mX; - notepos[1] += 8192 * cell.mData.mY; + notepos[0] += Constants::CellSizeInUnits * cell.mData.mX; + notepos[1] += Constants::CellSizeInUnits * cell.mData.mY; } // TODO: what encoding is this in? std::string note = esm.getHNString("MPNT"); diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 4a4a9a573e..5e2da2b034 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -1,5 +1,6 @@ #include "convertplayer.hpp" +#include #include namespace ESSImport @@ -78,9 +79,8 @@ namespace ESSImport if (pcdt.mHasENAM) { - const int cellSize = 8192; - out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize; - out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize; + out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits; + out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits; out.mLastKnownExteriorPosition[2] = 0.0f; } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 4538d4e63c..a54c133340 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include "importercontext.hpp" @@ -413,9 +415,8 @@ namespace ESSImport if (context.mPlayer.mCellId.mPaged) { // exterior cell -> determine cell coordinates based on position - const int cellSize = 8192; - int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize)); - int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize)); + int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits)); + int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits)); context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mY = cellY; } diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index abb3bc82eb..dbe90b9061 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} @@ -61,9 +63,7 @@ std::pair CSMWorld::CellCoordinates::fromId ( std::pair CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y) { - const int cellSize = 8192; - - return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize)); + return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); } bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 79900c6c4c..4133b2050e 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -445,16 +445,14 @@ void CSMWorld::UpdateCellCommand::redo() int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); mIndex = mModel.index (mRow, cellColumn); - const int cellSize = 8192; - QModelIndex xIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); QModelIndex yIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); - int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); - int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); + int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits); + int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits); std::ostringstream stream; diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index b8c89c83d5..b6fee15459 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -10,6 +10,8 @@ #include "../../model/prefs/state.hpp" #include "../../model/prefs/shortcutmanager.hpp" +#include + #include "mask.hpp" CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) @@ -57,7 +59,7 @@ QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const void CSVRender::CellArrow::adjustTransform() { // position - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int offset = cellSize / 2 + 800; int x = mCoordinates.getX()*cellSize + cellSize/2; diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index d0521a7b75..3de96ab023 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) : TagBase(Mask_CellMarker), mMarker(marker) {} @@ -49,7 +51,7 @@ void CSVRender::CellMarker::buildMarker() void CSVRender::CellMarker::positionMarker() { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int markerHeight = 0; // Move marker to center of cell. diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4b14e29bfd..1cf8a56983 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -29,15 +29,13 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const { - const float Pi = 3.14159265f; - float x, y, z; float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z()); if (std::abs(test) >= 1.f) { x = atan2(rot.x(), rot.w()); - y = (test > 0) ? (Pi / 2) : (-Pi / 2); + y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2); z = 0; } else diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 8301f4e9ed..2b1e3addee 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -313,20 +313,18 @@ osg::ref_ptr CSVRender::Object::makeMoveOrScaleMarker (int axis) osg::ref_ptr CSVRender::Object::makeRotateMarker (int axis) { - const float Pi = 3.14159265f; - const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius()); const float OuterRadius = InnerRadius + MarkerShaftWidth; const float SegmentDistance = 100.f; - const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * Pi / SegmentDistance))); + const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance))); const size_t VerticesPerSegment = 4; const size_t IndicesPerSegment = 24; const size_t VertexCount = SegmentCount * VerticesPerSegment; const size_t IndexCount = SegmentCount * IndicesPerSegment; - const float Angle = 2 * Pi / SegmentCount; + const float Angle = 2 * osg::PI / SegmentCount; const unsigned short IndexPattern[IndicesPerSegment] = { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 1d1a7cd17d..ccea707610 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../../model/prefs/shortcut.hpp" #include "../../model/world/tablemimedata.hpp" @@ -506,13 +508,11 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y) void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY) { - const int CellSize = 8192; - osg::Vec3f eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); - int cellX = (int)std::floor(center.x() / CellSize) + offsetX; - int cellY = (int)std::floor(center.y() / CellSize) + offsetY; + int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX; + int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY; CSMWorld::CellCoordinates cellCoordinates(cellX, cellY); @@ -738,22 +738,18 @@ void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMas std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates cellCoordinates ( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); return cellCoordinates.getId (mWorldspace); } CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates coords( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); std::map::const_iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index d738974dd7..a261180298 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -213,7 +213,7 @@ namespace MWClass closeSound, 0.5f); // Doors rotate at 90 degrees per second, so start the sound at // where it would be at the current rotation. - float offset = doorRot/(3.14159265f * 0.5f); + float offset = doorRot/(osg::PI * 0.5f); action->setSoundOffset(offset); action->setSound(openSound); } @@ -221,7 +221,7 @@ namespace MWClass { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, openSound, 0.5f); - float offset = 1.0f - doorRot/(3.14159265f * 0.5f); + float offset = 1.0f - doorRot/(osg::PI * 0.5f); action->setSoundOffset(std::max(offset, 0.0f)); action->setSound(closeSound); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3d019ea90d..4979db2fd4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -1020,7 +1021,7 @@ namespace MWClass if(stats.getStance(MWMechanics::CreatureStats::Stance_Run)) x *= gmst.fJumpRunMultiplier->mValue.getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); - x -= -627.2f;/*gravity constant*/ + x -= -Constants::GravityConst * Constants::UnitsPerMeter; x /= 3.0f; return x; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 466ae47162..5e0f989a55 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -1,6 +1,7 @@ #include "weapon.hpp" #include +#include #include #include "../mwbase/environment.hpp" @@ -328,9 +329,9 @@ namespace MWClass // add reach and attack speed for melee weapon if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) { - // 64 game units = 1 yard = 3 ft, display value in feet + // display value in feet const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; - text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); + text += MWGui::ToolTips::getWeightString(combatDistance / Constants::UnitsPerFoot, "#{sRange}"); text += " #{sFeet}"; text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}"); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c1ff9510d3..2c8ad0565f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -33,7 +33,7 @@ namespace { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; enum LocalMapWidgetDepth { diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 96c0d7de4f..ee058e02e3 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -212,7 +212,7 @@ namespace MWGui void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { - float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * 3.14f * 2; + float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * osg::PI * 2; mPreview->setAngle (angle); mCurrentAngle = angle; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index bb03ff53b2..e7d1ecee12 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -33,9 +33,9 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont float distance = x * x + y * y + z * z; if(distance < 10 * 10) { //Got stuck, didn't move if(mAdjAngle == 0) //Try going in various directions - mAdjAngle = 1.57079632679f; //pi/2 - else if (mAdjAngle == 1.57079632679f) - mAdjAngle = -1.57079632679f; + mAdjAngle = osg::PI / 2; + else if (mAdjAngle == osg::PI / 2) + mAdjAngle = -osg::PI / 2; else mAdjAngle = 0; mDuration = 1; //reset timer diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d36c5930f8..caacf8cb64 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -300,9 +300,8 @@ namespace MWMechanics bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); do { // Determine a random location within radius of original position - const float pi = 3.14159265359f; const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance; - const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * pi; + const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI; const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection); const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection); const float destinationZ = mInitialActorPosition.z(); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 90d25ee3e9..31da729ea0 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -486,7 +487,7 @@ namespace MWPhysics physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { - inertia.z() += time * -627.2f; + inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter; if (inertia.z() < 0) inertia.z() *= slowFall; if (slowFall < 1.f) { diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fae524faa7..65820e577c 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -273,9 +273,9 @@ namespace MWRender void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) { - imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); + imageX = float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1); - imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + imageY = 1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 8ed3441de6..8ea4e39917 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +72,7 @@ namespace MWRender LocalMap::LocalMap(osg::Group* root) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) - , mMapWorldSize(8192.f) + , mMapWorldSize(Constants::CellSizeInUnits) , mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) , mAngle(0.f) , mInterior(false) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 52832ad874..8fd47d2c50 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -27,6 +27,8 @@ #include +#include + #include #include @@ -401,7 +403,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); - mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900); + mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); @@ -679,7 +681,8 @@ bool Water::isUnderwater(const osg::Vec3f &pos) const osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY) { - return osg::Vec3f(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); + return osg::Vec3f(static_cast(gridX * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), + static_cast(gridY * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), mTop); } void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index e2413cfa05..32a7977d2a 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -50,8 +50,6 @@ namespace MWRender /// Water rendering class Water { - static const int CELL_SIZE = 8192; - osg::ref_ptr mRainIntensityUniform; osg::ref_ptr mParent; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 833b40e385..cadcdc4abe 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -34,10 +35,6 @@ namespace { -// The game uses 64 units per yard, or approximately 69.99125109 units per meter. -// Should this be defined publically somewhere? -const float UnitsPerMeter = 69.99125109f; - const int sLoudnessFPS = 20; // loudness values per second of audio ALCenum checkALCError(ALCdevice *device, const char *func, int line) @@ -818,13 +815,13 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname LoadEffect(mWaterEffect, EFX_REVERB_PRESET_UNDERWATER); } - alListenerf(AL_METERS_PER_UNIT, 1.0f / UnitsPerMeter); + alListenerf(AL_METERS_PER_UNIT, 1.0f / Constants::UnitsPerMeter); } skip_efx: alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); - // Speed of sound is in units per second. Given the default speed of sound is 343.3 (assumed + // Speed of sound is in units per second. Take the sound speed in air (assumed // meters per second), multiply by the units per meter to get the speed in u/s. - alSpeedOfSound(343.3f * UnitsPerMeter); + alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter); alGetError(); mInitialized = true; @@ -1400,8 +1397,7 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi if(env != mListenerEnv) { - // Speed of sound in water is 1484m/s, and in air is 343.3m/s (roughly) - alSpeedOfSound(((env == Env_Underwater) ? 1484.0f : 343.3f) * UnitsPerMeter); + alSpeedOfSound(((env == Env_Underwater) ? Constants::SoundSpeedUnderwater : Constants::SoundSpeedInAir) * Constants::UnitsPerMeter); // Update active sources with the environment's direct filter if(mWaterFilter) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 21218fab01..4698ba0114 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -460,7 +462,7 @@ namespace MWWorld { // gravity constant - must be way lower than the gravity affecting actors, since we're not // simulating aerodynamics at all - it->mVelocity -= osg::Vec3f(0, 0, 627.2f * 0.1f) * duration; + it->mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration; osg::Vec3f pos(it->mNode->getPosition()); osg::Vec3f newPos = pos + it->mVelocity * duration; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ed6dde310c..dc3d4fd308 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -345,7 +345,7 @@ namespace MWWorld getGridCenter(cellX, cellY); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = 8192/2 + mCellLoadingThreshold; // 1/2 cell size + threshold + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { @@ -793,7 +793,7 @@ namespace MWWorld float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y()))); - float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; + float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold + mPreloadDistance; if (dist < loadDist) preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy)); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d1f2747879..2afcdffc8b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -260,7 +261,7 @@ namespace MWWorld if (!getPlayerPtr().isInCell()) { ESM::Position pos; - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; pos.pos[0] = cellSize/2; pos.pos[1] = cellSize/2; pos.pos[2] = 0; @@ -1434,7 +1435,7 @@ namespace MWWorld void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; x = static_cast(cellSize * cellX); y = static_cast(cellSize * cellY); @@ -1448,10 +1449,8 @@ namespace MWWorld void World::positionToIndex (float x, float y, int &cellX, int &cellY) const { - const int cellSize = 8192; - - cellX = static_cast(std::floor(x / cellSize)); - cellY = static_cast(std::floor(y / cellSize)); + cellX = static_cast(std::floor(x / Constants::CellSizeInUnits)); + cellY = static_cast(std::floor(y / Constants::CellSizeInUnits)); } void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) @@ -3245,9 +3244,9 @@ namespace MWWorld float World::feetToGameUnits(float feet) { - // Looks like there is no GMST for this. This factor was determined in experiments - // with the Telekinesis effect. - return feet * 22; + // Original engine rounds size upward + static const int unitsPerFoot = ceil(Constants::UnitsPerFoot); + return feet * unitsPerFoot; } float World::getActivationDistancePlusTelekinesis() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8e798455f5..2da7c80bfa 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,7 +85,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng messageformatparser + constants utf8stream stringops resourcehelpers rng messageformatparser ) add_component_dir (debug diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index cccb472de9..eaf766442a 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -3,6 +3,8 @@ #include +#include + #include "esmcommon.hpp" namespace ESM @@ -53,7 +55,7 @@ struct Land static const int LAND_SIZE = 65; // cell terrain size in world coords - static const int REAL_SIZE = 8192; + static const int REAL_SIZE = Constants::CellSizeInUnits; // total number of vertices static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index f77e662765..0fa28cf0b4 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -267,8 +267,8 @@ namespace ESMTerrain height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; (*positions)[static_cast(vertX*numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, - (vertY / float(numVerts - 1) - 0.5f) * size * 8192, + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, + (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height); if (normalData) @@ -477,8 +477,8 @@ namespace ESMTerrain float Storage::getHeightAt(const osg::Vec3f &worldPos) { - int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); - int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); + int cellX = static_cast(std::floor(worldPos.x() / float(Constants::CellSizeInUnits))); + int cellY = static_cast(std::floor(worldPos.y() / float(Constants::CellSizeInUnits))); osg::ref_ptr land = getLand(cellX, cellY); if (!land) @@ -491,8 +491,8 @@ namespace ESMTerrain // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x() - (cellX * 8192))/8192.f; - float nY = (worldPos.y() - (cellY * 8192))/8192.f; + float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); + float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; @@ -524,10 +524,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / 8192.f); - osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / 8192.f); - osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / 8192.f); - osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits)); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -555,7 +555,7 @@ namespace ESMTerrain // Solve plane equation for z return (-plane.getNormal().x() * nX -plane.getNormal().y() * nY - - plane[3]) / plane.getNormal().z() * 8192; + - plane[3]) / plane.getNormal().z() * Constants::CellSizeInUnits; } diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp new file mode 100644 index 0000000000..7174ae8887 --- /dev/null +++ b/components/misc/constants.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_CONSTANTS_H +#define OPENMW_CONSTANTS_H + +namespace Constants +{ + +// The game uses 64 units per yard +const float UnitsPerMeter = 69.99125109f; +const float UnitsPerFoot = 21.33333333f; + +// Sound speed in meters per second +const float SoundSpeedInAir = 343.3f; +const float SoundSpeedUnderwater = 1484.0f; + +// Gravity constant in m/sec^2 +// Note: 8.96 m/sec^2 = 9.8 yards/sec^2 +// Probaly original engine's developers just forgot +// that their engine uses yards instead of meters +// and used standart gravity value as it is +const float GravityConst = 8.96f; + +// Size of one exterior cell in game units +const int CellSizeInUnits = 8192; + +} + +#endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e75deb2f70..9b9bbccd61 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -4,6 +4,8 @@ #include +#include + #include "quadtreenode.hpp" #include "storage.hpp" #include "viewdata.hpp" @@ -81,7 +83,7 @@ public: { float dist = distanceToBox(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(8192*mMinSize))); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize))); return nativeLodLevel <= lodLevel; } From dda02bd69618fdf4e1d04a3e54c52c94718d7405 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 19:56:36 +0400 Subject: [PATCH 070/150] Do not use book fonts for dialogue window --- apps/openmw/mwgui/bookpage.cpp | 25 ++++++++++--------------- apps/openmw/mwgui/bookpage.hpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 10 +++++----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 8e264312eb..c3b8935e81 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -264,29 +264,24 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { } - Style * createStyle (const std::string& fontName, const Colour& fontColour) + Style * createStyle (const std::string& fontName, const Colour& fontColour, bool useBookFont) { - const std::string templateName = "Journalbook "; - std::string bookFont; + std::string fullFontName; if (fontName.empty()) - { - bookFont = MyGUI::FontManager::getInstance().getDefaultFont(); - bookFont = templateName + bookFont; - return createStyle(bookFont, fontColour); - } - - if (fontName.compare(0, templateName.size(), templateName) == 0) - bookFont = fontName; + fullFontName = MyGUI::FontManager::getInstance().getDefaultFont(); else - bookFont = templateName + bookFont; + fullFontName = fontName; + + if (useBookFont) + fullFontName = "Journalbook " + fullFontName; for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) - if (i->match (bookFont.c_str(), fontColour, fontColour, fontColour, 0)) + if (i->match (fullFontName.c_str(), fontColour, fontColour, fontColour, 0)) return &*i; - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(bookFont); + MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName); if (!font) - throw std::runtime_error(std::string("can't find font ") + bookFont); + throw std::runtime_error(std::string("can't find font ") + fullFontName); StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = font; diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index c75f00ca41..4cf99794d7 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -102,7 +102,7 @@ namespace MWGui static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. - virtual Style* createStyle (const std::string& fontName, const Colour& colour) = 0; + virtual Style* createStyle (const std::string& fontName, const Colour& colour, bool useBookFont=true) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6aceccaff5..3ac8839e27 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -137,7 +137,7 @@ namespace MWGui if (mTitle != "") { const MyGUI::Colour& headerColour = MWBase::Environment::get().getWindowManager()->getTextColours().header; - BookTypesetter::Style* title = typesetter->createStyle("", headerColour); + BookTypesetter::Style* title = typesetter->createStyle("", headerColour, false); typesetter->write(title, to_utf8_span(mTitle.c_str())); typesetter->sectionBreak(); } @@ -182,7 +182,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { @@ -223,7 +223,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); if (topicId) @@ -239,7 +239,7 @@ namespace MWGui void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify; - BookTypesetter::Style* title = typesetter->createStyle("", textColour); + BookTypesetter::Style* title = typesetter->createStyle("", textColour, false); typesetter->sectionBreak(9); typesetter->write(title, to_utf8_span(mText.c_str())); } @@ -567,7 +567,7 @@ namespace MWGui (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); - BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White, false); typesetter->sectionBreak(9); // choices From 6d0e6ab01220438a80bede2fb56dc5a4537da592 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 22:24:21 +0400 Subject: [PATCH 071/150] Use book font by default in the text formatter --- apps/openmw/mwgui/formatting.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 6d98d48b34..b16a4e57d3 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -14,7 +14,7 @@ namespace MWGui { TextStyle() : mColour(0,0,0) - , mFont("") + , mFont("Journalbook Magic Cards") , mTextSize(16) { } From 989de05f8046105c15cb6aa49280841a037ecde6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 15:23:16 +0400 Subject: [PATCH 072/150] Do not show duration for infinite light sources as -1 --- apps/openmw/mwclass/light.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 27a9f3f2b0..3ee2280137 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -161,7 +161,12 @@ namespace MWClass std::string text; if (Settings::Manager::getBool("show effect duration","Game")) - text += "\n#{sDuration}: " + MWGui::ToolTips::toString(ptr.getClass().getRemainingUsageTime(ptr)); + { + // -1 is infinite light source, so duration makes no sense here. Other negative values are treated as 0. + float remainingTime = ptr.getClass().getRemainingUsageTime(ptr); + if (remainingTime != -1.0f) + text += "\n#{sDuration}: " + MWGui::ToolTips::toString(std::max(0.f, remainingTime)); + } text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); From 62f6f174cf2a6648c6d0c0da71205b5a97295a44 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 15:36:37 +0400 Subject: [PATCH 073/150] Show attack speed of ranged weapons --- apps/openmw/mwclass/weapon.cpp | 13 +++++++++---- docs/source/reference/modding/settings/game.rst | 2 +- files/settings-default.cfg | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 466ae47162..6776434ec0 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -65,7 +65,7 @@ namespace MWClass { const MWWorld::LiveCellRef *ref = ptr.get(); - return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity + return (ref->mBase->mData.mType < ESM::Weapon::MarksmanThrown); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const @@ -318,21 +318,26 @@ namespace MWClass } } - if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity + if (hasItemHealth(ptr)) { int remainingHealth = getItemHealth(ptr); text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); } - // add reach and attack speed for melee weapon - if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) + const bool verbose = Settings::Manager::getBool("show melee info", "Game"); + // add reach for melee weapon + if (ref->mBase->mData.mType < ESM::Weapon::MarksmanBow && verbose) { // 64 game units = 1 yard = 3 ft, display value in feet const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); text += " #{sFeet}"; + } + // add attack speed for any weapon excepts arrows and bolts + if (ref->mBase->mData.mType < ESM::Weapon::Arrow && verbose) + { text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}"); } diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index b733cc7c49..baeac92b28 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -36,7 +36,7 @@ show melee info :Range: True/False :Default: False -If this setting is true, the reach and speed of melee weapons will show on their tooltip. +If this setting is true, the reach and speed of weapons will show on their tooltip. This setting can only be configured by editing the settings configuration file. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 45cee05590..3344e6bb4d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -188,7 +188,7 @@ show owned = 0 # Show damage bonus of arrow and bolts. show projectile damage = false -# Show additional melee weapon info: reach and attack speed +# Show additional weapon info: reach and attack speed show melee info = false # Show success probability in self-enchant dialog From 6f4f6a155fc5293a16fb55d945a3f01ce197db40 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 19 Sep 2018 12:04:58 +0400 Subject: [PATCH 074/150] Combat AI: take ranged weapon speed in account --- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 818065ae44..aaa06f6e44 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -119,7 +119,7 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; - if (weapon->mData.mType < ESM::Weapon::MarksmanBow) + if (weapon->mData.mType < ESM::Weapon::Arrow) rating *= weapon->mData.mSpeed; return rating * ratingMult; From 276b7830a9f03cea95f3a3382648b9e34046ed1f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 14:57:21 +0400 Subject: [PATCH 075/150] Mass potion creation (feature #4642) --- CHANGELOG.md | 1 + apps/openmw/mwgui/alchemywindow.cpp | 90 +++++++++++++++++++++++- apps/openmw/mwgui/alchemywindow.hpp | 24 +++++++ apps/openmw/mwmechanics/alchemy.cpp | 64 +++++++++++++++-- apps/openmw/mwmechanics/alchemy.hpp | 16 ++++- components/widgets/numericeditbox.cpp | 5 ++ components/widgets/numericeditbox.hpp | 1 + files/mygui/openmw_alchemy_window.layout | 15 +++- 8 files changed, 206 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb9837bd9..958ec0118e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ Feature #4626: Weapon priority: account for weapon speed Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu + Feature #4642: Batching potion creation Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 6fd3d3220c..0aa98dc4a3 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,5 +1,7 @@ #include "alchemywindow.hpp" +#include + #include #include #include @@ -25,6 +27,9 @@ namespace MWGui { + const float AlchemyWindow::sCountChangeInitialPause = 0.5f; + const float AlchemyWindow::sCountChangeInterval = 0.1f; + AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") , mSortModel(NULL) @@ -43,9 +48,21 @@ namespace MWGui getWidget(mApparatus[2], "Apparatus3"); getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); + getWidget(mBrewCountEdit, "BrewCount"); + getWidget(mIncreaseButton, "IncreaseButton"); + getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mNameEdit, "NameEdit"); getWidget(mItemView, "ItemView"); + mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged); + mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); + mBrewCountEdit->setMinValue(1); + mBrewCountEdit->setValue(1); + + mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onIncreaseButtonPressed); + mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); + mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onDecreaseButtonPressed); + mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); @@ -77,7 +94,15 @@ namespace MWGui void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { - MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); + mAlchemy->setPotionName(mNameEdit->getCaption()); + int count = mAlchemy->countPotionsToBrew(); + count = std::min(count, mBrewCountEdit->getValue()); + createPotions(count); + } + + void AlchemyWindow::createPotions(int count) + { + MWMechanics::Alchemy::Result result = mAlchemy->create(mNameEdit->getCaption(), count); MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); switch (result) @@ -92,8 +117,11 @@ namespace MWGui winMgr->messageBox("#{sNotifyMessage6a}"); break; case MWMechanics::Alchemy::Result_Success: - winMgr->messageBox("#{sPotionSuccess}"); winMgr->playSound("potion success"); + if (count == 1) + winMgr->messageBox("#{sPotionSuccess}"); + else + winMgr->messageBox("#{sPotionSuccess} "+mNameEdit->getCaption()+" ("+std::to_string(count)+")"); break; case MWMechanics::Alchemy::Result_NoEffects: case MWMechanics::Alchemy::Result_RandomFailure: @@ -126,6 +154,7 @@ namespace MWGui mItemView->resetScrollBars(); mNameEdit->setCaption(""); + mBrewCountEdit->setValue(1); int index = 0; for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); @@ -250,4 +279,61 @@ namespace MWGui update(); } + + void AlchemyWindow::addRepeatController(MyGUI::Widget *widget) + { + MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerRepeatEvent::getClassTypeName()); + Controllers::ControllerRepeatEvent* controller = item->castType(); + controller->eventRepeatClick += MyGUI::newDelegate(this, &AlchemyWindow::onRepeatClick); + controller->setRepeat(sCountChangeInitialPause, sCountChangeInterval); + MyGUI::ControllerManager::getInstance().addItem(widget, controller); + } + + void AlchemyWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onIncreaseButtonTriggered(); + } + + void AlchemyWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller) + { + if (widget == mIncreaseButton) + onIncreaseButtonTriggered(); + else if (widget == mDecreaseButton) + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onCountButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) + { + MyGUI::ControllerManager::getInstance().removeItem(_sender); + } + + void AlchemyWindow::onCountValueChanged(int value) + { + mBrewCountEdit->setValue(std::abs(value)); + } + + void AlchemyWindow::onIncreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + + // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined + if (currentCount == INT_MAX || currentCount == INT_MIN+1) + return; + + mBrewCountEdit->setValue(currentCount+1); + } + + void AlchemyWindow::onDecreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + if (currentCount > 1) + mBrewCountEdit->setValue(currentCount-1); + } } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index d1e54241a1..aa23fbaa50 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -3,8 +3,13 @@ #include +#include + #include "../mwmechanics/alchemy.hpp" +#include + +#include "controllers.hpp" #include "windowbase.hpp" namespace MWMechanics @@ -28,6 +33,10 @@ namespace MWGui void onResChange(int, int) { center(); } private: + + static const float sCountChangeInitialPause; // in seconds + static const float sCountChangeInterval; // in seconds + std::string mSuggestedPotionName; ItemView* mItemView; @@ -38,17 +47,32 @@ namespace MWGui MyGUI::Widget* mEffectsBox; + MyGUI::Button* mIncreaseButton; + MyGUI::Button* mDecreaseButton; MyGUI::EditBox* mNameEdit; + Gui::NumericEditBox* mBrewCountEdit; void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); void onAccept(MyGUI::EditBox*); + void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountValueChanged(int value); + void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller); + + void addRepeatController(MyGUI::Widget* widget); + + void onIncreaseButtonTriggered(); + void onDecreaseButtonTriggered(); void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); + void createPotions(int count); + void update(); std::unique_ptr mAlchemy; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b8f8203070..98860198bb 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -30,6 +30,7 @@ MWMechanics::Alchemy::Alchemy() : mValue(0) + , mPotionName("") { } @@ -336,6 +337,25 @@ int MWMechanics::Alchemy::countIngredients() const return ingredients; } +int MWMechanics::Alchemy::countPotionsToBrew() const +{ + Result readyStatus = getReadyStatus(); + if (readyStatus != Result_Success) + return 0; + + int toBrew = -1; + + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + { + int count = iter->getRefData().getCount(); + if ((count > 0 && count < toBrew) || toBrew < 0) + toBrew = count; + } + + return toBrew; +} + void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; @@ -396,6 +416,12 @@ void MWMechanics::Alchemy::clear() mTools.clear(); mIngredients.clear(); mEffects.clear(); + setPotionName(""); +} + +void MWMechanics::Alchemy::setPotionName(const std::string& name) +{ + mPotionName = name; } int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) @@ -456,7 +482,7 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW || (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4); } -MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) +MWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; @@ -464,15 +490,43 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na if (countIngredients()<2) return Result_LessThanTwoIngredients; - if (name.empty()) + if (mPotionName.empty()) return Result_NoName; if (listEffects().empty()) - { - removeIngredients(); return Result_NoEffects; + + return Result_Success; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name, int& count) +{ + setPotionName(name); + Result readyStatus = getReadyStatus(); + + if (readyStatus == Result_NoEffects) + removeIngredients(); + + if (readyStatus != Result_Success) + return readyStatus; + + Result result = Result_RandomFailure; + int brewedCount = 0; + for (int i = 0; i < count; ++i) + { + if (createSingle() == Result_Success) + { + result = Result_Success; + brewedCount++; + } } + count = brewedCount; + return result; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle () +{ if (beginEffects() == endEffects()) { // all effects were nullified due to insufficient skill @@ -486,7 +540,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na return Result_RandomFailure; } - addPotion (name); + addPotion(mPotionName); removeIngredients(); diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index f351881e0d..9f9f0b21c0 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -51,11 +51,14 @@ namespace MWMechanics TIngredientsContainer mIngredients; TEffectsContainer mEffects; int mValue; + std::string mPotionName; void applyTools (int flags, float& value) const; void updateEffects(); + Result getReadyStatus() const; + const ESM::Potion *getRecord(const ESM::Potion& toFind) const; ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found /// \note Does not account for record ID, model or icon @@ -70,6 +73,10 @@ namespace MWMechanics void increaseSkill(); ///< Increase alchemist's skill. + Result createSingle (); + ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + /// adjust the skills of the alchemist accordingly. + float getAlchemyFactor() const; int countIngredients() const; @@ -79,6 +86,8 @@ namespace MWMechanics TEffectsIterator endEffects() const; public: + int countPotionsToBrew() const; + ///< calculates maximum amount of potions, which you can make from selected ingredients static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc); ///< Does npc have sufficient alchemy skill to know about this potion effect? @@ -100,6 +109,9 @@ namespace MWMechanics void clear(); ///< Remove alchemist, tools and ingredients. + void setPotionName(const std::string& name); + ///< Set name of potion to create + std::set listEffects() const; ///< List all effects shared by at least two ingredients. @@ -115,8 +127,8 @@ namespace MWMechanics std::string suggestPotionName (); ///< Suggest a name for the potion, based on the current effects - Result create (const std::string& name); - ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + Result create (const std::string& name, int& count); + ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. /// \param name must not be an empty string, or Result_NoName is returned }; diff --git a/components/widgets/numericeditbox.cpp b/components/widgets/numericeditbox.cpp index efe4806536..e8ba226f70 100644 --- a/components/widgets/numericeditbox.cpp +++ b/components/widgets/numericeditbox.cpp @@ -59,6 +59,11 @@ namespace Gui } } + int NumericEditBox::getValue() + { + return mValue; + } + void NumericEditBox::setMinValue(int minValue) { mMinValue = minValue; diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index 3edae2fc74..137583d37e 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -30,6 +30,7 @@ namespace Gui /// @note Does not trigger eventValueChanged void setValue (int value); + int getValue(); void setMinValue(int minValue); void setMaxValue(int maxValue); diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index edc9a4c3d9..714872fc3f 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -74,7 +74,20 @@ - + + + + + + + + + + + + + + From bdd9eba2b822a913cf12bbaf53f543e6f5a71701 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 20 Sep 2018 16:02:26 +0400 Subject: [PATCH 076/150] Use C++ limits instead of C ones --- apps/opencs/view/world/util.cpp | 4 +--- apps/openmw/mwgui/alchemywindow.cpp | 6 ++---- apps/openmw/mwgui/tradewindow.cpp | 6 ++---- apps/openmw/mwmechanics/autocalcspell.cpp | 9 ++++----- apps/openmw/mwrender/globalmap.cpp | 2 -- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index eab37e1bf4..b194e460d3 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -1,8 +1,6 @@ #include "util.hpp" #include -#include -#include #include #include @@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Integer: { DialogueSpinBox *sb = new DialogueSpinBox(parent); - sb->setRange(INT_MIN, INT_MAX); + sb->setRange(std::numeric_limits::min(), std::numeric_limits::max()); return sb; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 0aa98dc4a3..a599e6c7b6 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,7 +1,5 @@ #include "alchemywindow.hpp" -#include - #include #include #include @@ -323,8 +321,8 @@ namespace MWGui { int currentCount = mBrewCountEdit->getValue(); - // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined - if (currentCount == INT_MAX || currentCount == INT_MIN+1) + // prevent overflows + if (currentCount == std::numeric_limits::max()) return; mBrewCountEdit->setValue(currentCount+1); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f461d5f50b..726c72b99e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -1,7 +1,5 @@ #include "tradewindow.hpp" -#include - #include #include #include @@ -96,7 +94,7 @@ namespace MWGui mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged); mTotalBalance->eventEditSelectAccept += MyGUI::newDelegate(this, &TradeWindow::onAccept); - mTotalBalance->setMinValue(INT_MIN+1); // disallow INT_MIN since abs(INT_MIN) is undefined + mTotalBalance->setMinValue(std::numeric_limits::min()+1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); } @@ -431,7 +429,7 @@ namespace MWGui void TradeWindow::onIncreaseButtonTriggered() { // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined - if (mCurrentBalance == INT_MAX || mCurrentBalance == INT_MIN+1) + if (mCurrentBalance == std::numeric_limits::max() || mCurrentBalance == std::numeric_limits::min()+1) return; if (mCurrentBalance < 0) mCurrentBalance -= 1; else mCurrentBalance += 1; diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index a52fcc059d..80ac14ddde 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -1,7 +1,6 @@ #include "autocalcspell.hpp" #include "spellcasting.hpp" -#include #include #include "../mwworld/esmstore.hpp" @@ -50,7 +49,7 @@ namespace MWMechanics caps.mCount = 0; caps.mLimit = iAutoSpellSchoolMax[i]; caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0; - caps.mMinCost = INT_MAX; + caps.mMinCost = std::numeric_limits::max(); caps.mWeakestSpell.clear(); schoolCaps[i] = caps; } @@ -101,7 +100,7 @@ namespace MWMechanics if (found != selectedSpells.end()) selectedSpells.erase(found); - cap.mMinCost = INT_MAX; + cap.mMinCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = spells.find(*weakIt); @@ -151,7 +150,7 @@ namespace MWMechanics float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; const ESM::Spell* weakestSpell = NULL; - int minCost = INT_MAX; + int minCost = std::numeric_limits::max(); std::vector selectedSpells; @@ -188,7 +187,7 @@ namespace MWMechanics if (it != selectedSpells.end()) selectedSpells.erase(it); - minCost = INT_MAX; + minCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = esmStore.get().find(*weakIt); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fae524faa7..520bd3cb74 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -1,7 +1,5 @@ #include "globalmap.hpp" -#include - #include #include #include From 01b3f92f7eac5d792bf910b966b441605c01ea07 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 13 Sep 2018 13:21:38 +0400 Subject: [PATCH 077/150] Make coc and fixme console commands close to vanilla (bug #4292, bug #4217) --- apps/openmw/mwscript/cellextensions.cpp | 12 +-- .../mwscript/transformationextensions.cpp | 6 +- apps/openmw/mwworld/worldimp.cpp | 77 +++++++++++++++---- components/compiler/extensions0.cpp | 2 +- components/compiler/opcodes.hpp | 1 - 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 2cb76ef884..f01fed2ce1 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -46,18 +46,19 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr playerPtr = world->getPlayerPtr(); if (world->findExteriorPosition(cell, pos)) { - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } else { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); - MWWorld::ActionTeleport(cell, pos, false).execute(world->getPlayerPtr()); + MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr); } } }; @@ -76,14 +77,15 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr playerPtr = world->getPlayerPtr(); world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 16d7e80364..9d7a5c6edc 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -732,14 +732,13 @@ namespace MWScript } }; - template class OpFixme : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); + const MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->fixPosition(ptr); } }; @@ -784,8 +783,7 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors); - interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); - interpreter.installSegment5(Compiler::Transformation::opcodeFixmeExplicit, new OpFixme); + interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2afcdffc8b..29161c2985 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -246,7 +246,7 @@ namespace MWWorld if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); - fixPosition(getPlayerPtr()); + adjustPosition(getPlayerPtr(), false); } else { @@ -1353,13 +1353,37 @@ namespace MWWorld void World::fixPosition(const Ptr &actor) { - const float dist = 8000; - osg::Vec3f pos (actor.getRefData().getPosition().asVec3()); - pos.z() += dist; + const float distance = 128.f; + ESM::Position esmPos = actor.getRefData().getPosition(); + osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1)); + osg::Vec3f pos (esmPos.asVec3()); - osg::Vec3f traced = mPhysics->traceDown(actor, pos, dist*1.1f); + int direction = 0; + int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4}; + + osg::Vec3f targetPos = pos; + for (int i=0; i<4; ++i) + { + direction = fallbackDirections[i]; + if (direction == 0) targetPos = pos + (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 1) targetPos = pos - (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 2) targetPos = pos - (orientation * osg::Vec3f(1,0,0)) * distance; + else if(direction == 3) targetPos = pos + (orientation * osg::Vec3f(1,0,0)) * distance; + + // destination is free + if (!castRay(pos.x(), pos.y(), pos.z(), targetPos.x(), targetPos.y(), targetPos.z())) + break; + } + + targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later + osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits); if (traced != pos) - moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); + { + esmPos.pos[0] = traced.x(); + esmPos.pos[1] = traced.y(); + esmPos.pos[2] = traced.z(); + MWWorld::ActionTeleport("", esmPos, false).execute(actor); + } } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1429,7 +1453,7 @@ namespace MWWorld ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2]; MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false); - placed.getClass().adjustPosition(placed, true); // snap to ground + adjustPosition(placed, true); // snap to ground return placed; } @@ -2604,31 +2628,52 @@ namespace MWWorld return false; } + std::vector sortedDoors; const DoorList &doors = cellStore->getReadOnlyDoors().mList; - for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { - if (!it->mRef.getTeleport()) { + for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) + { + if (!it->mRef.getTeleport()) + { continue; } + sortedDoors.push_back(&it->mRef); + } + + // Sort teleporting doors alphabetically, first by ID, then by destination cell to make search consistent + std::sort(sortedDoors.begin(), sortedDoors.end(), [] (const MWWorld::CellRef *lhs, const MWWorld::CellRef *rhs) + { + if (lhs->getRefId() != rhs->getRefId()) + return lhs->getRefId() < rhs->getRefId(); + + return lhs->getDestCell() < rhs->getDestCell(); + }); + + for (std::vector::const_iterator it = sortedDoors.begin(); it != sortedDoors.end(); ++it) + { MWWorld::CellStore *source = 0; // door to exterior - if (it->mRef.getDestCell().empty()) { + if ((*it)->getDestCell().empty()) + { int x, y; - ESM::Position doorDest = it->mRef.getDoorDest(); + ESM::Position doorDest = (*it)->getDoorDest(); positionToIndex(doorDest.pos[0], doorDest.pos[1], x, y); source = getExterior(x, y); } // door to interior - else { - source = getInterior(it->mRef.getDestCell()); + else + { + source = getInterior((*it)->getDestCell()); } - if (0 != source) { + if (0 != source) + { // Find door leading to our current teleport door // and use its destination to position inside cell. const DoorList &destinationDoors = source->getReadOnlyDoors().mList; - for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) { - if (it->mRef.getTeleport() && + for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) + { + if ((*it)->getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) { /// \note Using _any_ door pointed to the interior, diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7638d0f784..7979f06b91 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -546,7 +546,7 @@ namespace Compiler extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); extensions.registerInstruction("resetactors","",opcodeResetActors); - extensions.registerInstruction("fixme","",opcodeFixme, opcodeFixmeExplicit); + extensions.registerInstruction("fixme","",opcodeFixme); extensions.registerInstruction("ra","",opcodeResetActors); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index aef92b311b..c141eec682 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -504,7 +504,6 @@ namespace Compiler const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; const int opcodeFixme = 0x2000302; - const int opcodeFixmeExplicit = 0x2000303; } namespace User From fb484c6fde5b05bfaf2927bccd80caf037a53230 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 19:02:28 +0400 Subject: [PATCH 078/150] Optimize AI loop a bit --- apps/openmw/mwmechanics/actors.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0ee49ac45e..e3b1ec7911 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1303,6 +1303,16 @@ namespace MWMechanics std::map > cachedAllies; // will be filled as engageCombat iterates + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); + int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId(); + if (attackedByPlayerId != -1) + { + const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId); + + if (!playerHitAttemptActor.isInCell()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); + } + // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1328,11 +1338,6 @@ namespace MWMechanics player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } - const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); - - if (!playerHitAttemptActor.isInCell()) - player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); @@ -1343,7 +1348,7 @@ namespace MWMechanics return; // for now abort update of the old cell when cell changes by teleportation magic effect // a better solution might be to apply cell changes at the end of the frame } - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + if (aiActive && inProcessingRange) { if (timerUpdateAITargets == 0) { From b9346798c6bef7ec8b06d48853dd415d4e0dda91 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 22:34:18 +0400 Subject: [PATCH 079/150] Optimize combat music update --- apps/openmw/mwmechanics/actors.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e3b1ec7911..0cd9912d02 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1242,21 +1242,24 @@ namespace MWMechanics void Actors::updateCombatMusic () { MWWorld::Ptr player = getPlayer(); - int hostilesCount = 0; // need to know this to play Battle music + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + bool hasHostiles = false; // need to know this to play Battle music + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); - for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + if (aiActive) { - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - <= sqrAiProcessingDistance; + if (iter->first == player) continue; - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance; + if (inProcessingRange) { - if (iter->first != player) + MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + if (!stats.isDead() && stats.getAiSequence().isInCombat()) { - MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; + hasHostiles = true; + break; } } } @@ -1265,13 +1268,13 @@ namespace MWMechanics // check if we still have any player enemies to switch music static int currentMusic = 0; - if (currentMusic != 1 && hostilesCount == 0 && !(player.getClass().getCreatureStats(player).isDead() && + if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getSoundManager()->isMusicPlaying())) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); currentMusic = 1; } - else if (currentMusic != 2 && hostilesCount > 0) + else if (currentMusic != 2 && hasHostiles) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); currentMusic = 2; From 1634284739eb02715ca37c8c95f90695d3206dbb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 22:39:47 +0400 Subject: [PATCH 080/150] Cache player's position outside of loops --- apps/openmw/mwmechanics/actors.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0cd9912d02..79622180f0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1301,6 +1301,7 @@ namespace MWMechanics bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); MWWorld::Ptr player = getPlayer(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); /// \todo move update logic to Actor class where appropriate @@ -1321,7 +1322,7 @@ namespace MWMechanics { bool isPlayer = iter->first == player; - float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // This distance could be made configurable later, but the setting must be marked with a big warning: @@ -1431,7 +1432,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. - const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); bool isPlayer = iter->first == player; bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -1520,7 +1521,7 @@ namespace MWMechanics continue; // is the player in range and can they be detected - if ((observer.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= radius*radius + if ((observer.getRefData().getPosition().asVec3() - playerPos).length2() <= radius*radius && MWBase::Environment::get().getWorld()->getLOS(player, observer)) { if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) @@ -1664,7 +1665,8 @@ namespace MWMechanics void Actors::rest(bool sleep) { float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1674,7 +1676,7 @@ namespace MWMechanics restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || - (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) + (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) continue; adjustMagicEffects (iter->first); From b3fd173e007b62071baea8cc11e756adffcb1c68 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 06:51:56 +0400 Subject: [PATCH 081/150] Check if current weapon has health at all in HUD (bug #4648) --- CHANGELOG.md | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b457279391..22ff55bb2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4648: Hud thinks that throwing weapons have condition Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 98dbddaff3..7fca0058fd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1383,8 +1383,11 @@ namespace MWGui void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { mSelectedWeapon = item; - int durabilityPercent = - static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + int durabilityPercent = 100; + if (item.getClass().hasItemHealth(item)) + { + durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } From 90d35aaa8ff64cb9d3ef373fbcde56c5dce1f961 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 07:46:47 +0400 Subject: [PATCH 082/150] Handle current health level during levelup (bug #4649) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/npcstats.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b457279391..557848bb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4649: Levelup fully restores health Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index cb9ef54773..47d886e3fd 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -298,7 +298,12 @@ void MWMechanics::NpcStats::levelUp() // "When you gain a level, in addition to increasing three primary attributes, your Health // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, // the Health increase is calculated from the increased Endurance" - setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat()); + // Note: we should add bonus Health points to current level too. + float healthGain = endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat(); + MWMechanics::DynamicStat health(getHealth()); + health.setBase(getHealth().getBase() + healthGain); + health.setCurrent(std::max(1.f, getHealth().getCurrent() + healthGain)); + setHealth(health); setLevel(getLevel()+1); } From 2ac2d014322546ba3d6e86b27cb2ac41babde0cc Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 15:48:36 +0400 Subject: [PATCH 083/150] Optimize drowning state update --- apps/openmw/mwmechanics/actors.cpp | 27 +++++++++------------------ apps/openmw/mwmechanics/actors.hpp | 4 +--- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 79622180f0..9ea357f56f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -501,12 +501,6 @@ namespace MWMechanics } } - void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) - { - updateDrowning(ptr, duration); - calculateNpcStatModifiers(ptr, duration); - } - void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); @@ -926,13 +920,8 @@ namespace MWMechanics return ctrl->isSneaking(); } - void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer) { - PtrActorMap::iterator it = mActors.find(ptr); - if (it == mActors.end()) - return; - CharacterController* ctrl = it->second->getCharacterController(); - NpcStats &stats = ptr.getClass().getNpcStats(ptr); // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST @@ -940,9 +929,10 @@ namespace MWMechanics if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); - if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + if (stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { - if(ptr != MWMechanics::getPlayer() ) { + if(!isPlayer) + { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once seq.stack(MWMechanics::AiBreathe(), ptr); @@ -950,7 +940,7 @@ namespace MWMechanics } MWBase::World *world = MWBase::Environment::get().getWorld(); - bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); + bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { @@ -965,7 +955,7 @@ namespace MWMechanics stats.setTimeToStartDrowning(timeLeft); } - bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(timeLeft == 0.0f && !godmode) { @@ -980,7 +970,7 @@ namespace MWMechanics if(!sndmgr->getSoundPlaying(ptr, "drown")) sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f); - if(ptr == world->getPlayerPtr()) + if(isPlayer) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } @@ -1406,7 +1396,8 @@ namespace MWMechanics if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateNpc(iter->first, duration); + updateDrowning(iter->first, duration, iter->second->getCharacterController()->isKnockedOut(), isPlayer); + calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 8c94ce45f2..a1e0e511da 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -28,8 +28,6 @@ namespace MWMechanics void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); - void updateNpc(const MWWorld::Ptr &ptr, float duration); - void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); @@ -39,7 +37,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); - void updateDrowning (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip); From cd60d4fdf093b99e9ed30729e2e98c5949423ae4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 15:57:58 +0400 Subject: [PATCH 084/150] Store character controller in the variable make code less bulk --- apps/openmw/mwmechanics/actors.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9ea357f56f..92f2ba34dc 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1311,6 +1311,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { bool isPlayer = iter->first == player; + CharacterController* ctrl = iter->second->getCharacterController(); float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this @@ -1320,7 +1321,7 @@ namespace MWMechanics bool inProcessingRange = distSqr <= sqrAiProcessingDistance; if (isPlayer) - iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + ctrl->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead() @@ -1380,7 +1381,7 @@ namespace MWMechanics } } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); + ctrl->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) @@ -1390,13 +1391,13 @@ namespace MWMechanics { CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); if (isConscious(iter->first)) - stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration); + stats.getAiSequence().execute(iter->first, *ctrl, duration); } } if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateDrowning(iter->first, duration, iter->second->getCharacterController()->isKnockedOut(), isPlayer); + updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer); calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) @@ -1437,22 +1438,24 @@ namespace MWMechanics inAnimationRange = true; active = std::max(1, active); } - iter->second->getCharacterController()->setActive(active); + + CharacterController* ctrl = iter->second->getCharacterController(); + ctrl->setActive(active); if (!inAnimationRange) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) - iter->second->getCharacterController()->skipAnim(); + ctrl->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell // (would invalidate the iterator) if (iter->first == getPlayer()) { - playerCharacter = iter->second->getCharacterController(); + playerCharacter = ctrl; continue; } - iter->second->getCharacterController()->update(duration); + ctrl->update(duration); } if (playerCharacter) From c50ee22772cb7cb4e8229fc5e0e73ff46df85ad2 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 01:52:56 +0300 Subject: [PATCH 085/150] Wrap up World::canRest() implementation by moving enemiesNearby() from InputManager::rest() to World::canRest(). --- apps/openmw/mwbase/world.hpp | 16 ++++++++++------ apps/openmw/mwgui/waitdialog.cpp | 11 ++++++++--- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ---- apps/openmw/mwworld/worldimp.cpp | 11 +++++++---- apps/openmw/mwworld/worldimp.hpp | 2 +- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 72c4c3a2ab..4e9e3f4a98 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -438,12 +438,16 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - virtual int canRest() = 0; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + enum RestPermitted + { + Rest_Allowed = 0, + Rest_OnlyWaiting = 1, + Rest_PlayerIsUnderwater = 2, + Rest_EnemiesAreNearby = 3 + }; + + virtual RestPermitted canRest() = 0; + ///< check if the player is allowed to rest /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index c5a06a12c9..37cc9a59f6 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -83,7 +83,7 @@ namespace MWGui void WaitDialog::setPtr(const MWWorld::Ptr &ptr) { - setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == 0); + setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed); if (mUntilHealedButton->getVisible()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); @@ -120,9 +120,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - int canRest = MWBase::Environment::get().getWorld ()->canRest (); + MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); - if (canRest == 2) + if (canRest == MWBase::World::Rest_EnemiesAreNearby) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } + else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ba93701229..101b4f2f73 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1029,10 +1029,6 @@ namespace MWInput if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; - if(mPlayer->enemiesNearby()) {//Check if in combat - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, - return; - } MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2afcdffc8b..458ff3a01d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2297,7 +2297,7 @@ namespace MWWorld applyLoopingParticles(player); } - int World::canRest () + World::RestPermitted World::canRest () { CellStore *currentCell = mWorldScene->getCurrentCell(); @@ -2309,13 +2309,16 @@ namespace MWWorld if (!actor) throw std::runtime_error("can't find player"); + if(mPlayer->enemiesNearby()) + return Rest_EnemiesAreNearby; + if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) - return 2; + return Rest_PlayerIsUnderwater; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf()) - return 1; + return Rest_OnlyWaiting; - return 0; + return Rest_Allowed; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 536a40468a..92fb091bf5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - int canRest() override; + World::RestPermitted canRest() override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From 44bcd9b25fa8c94da732de933e2465185d58673c Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 02:46:29 +0300 Subject: [PATCH 086/150] Fix tab characters --- apps/openmw/mwgui/waitdialog.cpp | 8 ++++---- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 37cc9a59f6..db92b355ce 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -120,14 +120,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); + MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); - if (canRest == MWBase::World::Rest_EnemiesAreNearby) + if (canRest == MWBase::World::Rest_EnemiesAreNearby) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); MWBase::Environment::get().getWindowManager()->popGuiMode (); - } - else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) + } + else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 458ff3a01d..1b3e3c3ef9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2309,8 +2309,8 @@ namespace MWWorld if (!actor) throw std::runtime_error("can't find player"); - if(mPlayer->enemiesNearby()) - return Rest_EnemiesAreNearby; + if(mPlayer->enemiesNearby()) + return Rest_EnemiesAreNearby; if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) return Rest_PlayerIsUnderwater; From 52da65b776948f0119467add6072a41982993f1e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 03:28:30 +0300 Subject: [PATCH 087/150] Fix issues with sun specularity (bug #4527) --- CHANGELOG.md | 1 + apps/openmw/mwworld/weather.cpp | 11 +++++++---- files/shaders/water_fragment.glsl | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b457279391..c4c446d724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode + Bug #4527: Sun renders on water shader in some situations where it shouldn't Bug #4531: Movement does not reset idle animations Bug #4532: Underwater sfx isn't tied to 3rd person camera Bug #4539: Paper Doll is affected by GUI scaling diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2a9e8d7cc0..ac885f429c 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -752,12 +752,15 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog"); float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; + float glareFade = 1.f; if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) - mRendering.getSkyManager()->setGlareTimeOfDayFade(0); + glareFade = 0.f; else if (time.getHour() < peakHour) - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime)); + glareFade -= (peakHour - time.getHour()) / (peakHour - mSunriseTime); else - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour)); + glareFade -= (time.getHour() - peakHour) / (mSunsetTime - peakHour); + + mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade); mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); @@ -765,7 +768,7 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, mResult.mDLFogOffset/100.0f, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); - mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView); + mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade); mRendering.getSkyManager()->setWeather(mResult); diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index ee91df75fa..4c4a253f1f 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -286,6 +286,6 @@ void main(void) #if REFRACTION gl_FragData[0].w = 1.0; #else - gl_FragData[0].w = clamp(fresnel*6.0 + specular, 0.0, 1.0); //clamp(fresnel*2.0 + specular, 0.0, 1.0); + gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); #endif } From f2e11e6def966680380267ab84169b3670e66237 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 04:06:29 +0300 Subject: [PATCH 088/150] Fix MSVC C4596 illegal qualified name --- apps/openmw/mwworld/worldimp.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92fb091bf5..5c3eb30b3b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - World::RestPermitted canRest() override; + RestPermitted canRest() override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From f00f35ba86f719e2fa7615a3aab9e402acaa27b9 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 06:25:20 +0300 Subject: [PATCH 089/150] Add const to canRest() --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- apps/openmw/mwworld/worldimp.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 4e9e3f4a98..330babf097 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -446,7 +446,7 @@ namespace MWBase Rest_EnemiesAreNearby = 3 }; - virtual RestPermitted canRest() = 0; + virtual RestPermitted canRest() const = 0; ///< check if the player is allowed to rest /// \todo Probably shouldn't be here diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1b3e3c3ef9..9b701d17b2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2297,7 +2297,7 @@ namespace MWWorld applyLoopingParticles(player); } - World::RestPermitted World::canRest () + World::RestPermitted World::canRest () const { CellStore *currentCell = mWorldScene->getCurrentCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5c3eb30b3b..e694d9d84f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - RestPermitted canRest() override; + RestPermitted canRest() const override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From 92259c6cca4987d023de675b38781c98cd01db15 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 22:30:06 +0300 Subject: [PATCH 090/150] Discard count arguments for AddSoulGem and RemoveSpell (bug #3762) --- CHANGELOG.md | 1 + components/compiler/extensions0.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b457279391..41db3b0ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers + Bug #3762: AddSoulGem and RemoveSpell redundant count arguments break script execution Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7638d0f784..9b74377f29 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -276,7 +276,7 @@ namespace Compiler extensions.registerInstruction ("gotojail", "", opcodeGoToJail); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); - extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); + extensions.registerInstruction ("addsoulgem", "ccz", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); @@ -463,7 +463,7 @@ namespace Compiler extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); extensions.registerInstruction ("addspell", "cz", opcodeAddSpell, opcodeAddSpellExplicit); - extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, + extensions.registerInstruction ("removespell", "cz", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, opcodeRemoveSpellEffectsExplicit); From b2a52a5b6c1ba97ee3d5bab99cfb66a675e3ad78 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Mon, 24 Sep 2018 14:59:10 +0300 Subject: [PATCH 091/150] Change 'after function'-style doxygen comment to normal. --- apps/openmw/mwbase/world.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 330babf097..f44e6593bb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -446,8 +446,8 @@ namespace MWBase Rest_EnemiesAreNearby = 3 }; + /// check if the player is allowed to rest virtual RestPermitted canRest() const = 0; - ///< check if the player is allowed to rest /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; From 657d95a0bedc63e252139a28c6275e3b8ed86c27 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Mon, 24 Sep 2018 15:30:30 +0300 Subject: [PATCH 092/150] Paperwork for the pull request #1935 --- AUTHORS.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5320a24625..0740818763 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -148,6 +148,7 @@ Programmers Scott Howard scrawl Sebastian Wick (swick) + Sergey Fukanchik Sergey Shambir ShadowRadiance Siimacore diff --git a/CHANGELOG.md b/CHANGELOG.md index b457279391..7083fcdb77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -164,6 +164,7 @@ Task #4621: Optimize combat AI Task #4643: Revise editor record verifying functionality Task #4645: Use constants instead of widely used magic numbers + Task #4652: Move call to enemiesNearby() from InputManager::rest() to World::canRest() 0.44.0 ------ From 713330351bf1d8fa0867860266fce2de374e48d5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 00:08:16 +0300 Subject: [PATCH 093/150] Experimental animation regression fixes Don't unnecessarily start movement and jump animations from loop start Don't play movement animation until jumping animation finishes --- apps/openmw/mwmechanics/character.cpp | 104 +++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 749f908d28..8f14e0331b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -351,64 +351,69 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) idle = CharState_None; } -void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force) +void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force) { - if(force || jump != mJumpState) + if (!force && jump == mJumpState && movement == CharState_None) + return; + + if (jump != JumpState_None) { - if (jump != JumpState_None) - idle = CharState_None; + idle = CharState_None; + movement = CharState_None; + } - bool startAtLoop = (jump == mJumpState); - mJumpState = jump; - - std::string jumpAnimName; - MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; - if(mJumpState != JumpState_None) + std::string jumpAnimName; + MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; + if (jump != JumpState_None) + { + jumpAnimName = "jump"; + if(weap != sWeaponTypeListEnd) { - jumpAnimName = "jump"; - if(weap != sWeaponTypeListEnd) + jumpAnimName += weap->shortgroup; + if(!mAnimation->hasAnimation(jumpAnimName)) { - jumpAnimName += weap->shortgroup; - if(!mAnimation->hasAnimation(jumpAnimName)) - { - jumpmask = MWRender::Animation::BlendMask_LowerBody; - jumpAnimName = "jump"; + jumpmask = MWRender::Animation::BlendMask_LowerBody; + jumpAnimName = "jump"; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - jumpAnimName += "1h"; - } + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + jumpAnimName += "1h"; } } + } - if (!mCurrentJump.empty()) - { - mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); - } + if (!force && jump == mJumpState) + return; - if(mJumpState == JumpState_InAir) + mJumpState = jump; + + if (!mCurrentJump.empty()) + { + mAnimation->disable(mCurrentJump); + mCurrentJump.clear(); + } + + if(mJumpState == JumpState_InAir) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, - 1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, + 1.0f, "start", "stop", 0.f, ~0ul); + mCurrentJump = jumpAnimName; } - else if (mJumpState == JumpState_Landing) + } + else if (mJumpState == JumpState_Landing) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, - 1.0f, "loop stop", "stop", 0.0f, 0); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + mCurrentJump = jumpAnimName; } } } @@ -494,10 +499,9 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } // If we're playing the same animation, start it from the point it ended - bool sameAnim = (movementAnimName == mCurrentMovement); - float startPoint = 0.f; - if (sameAnim) - mAnimation->getInfo(mCurrentMovement, &startPoint); + float startpoint = 0.f; + if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement) + mAnimation->getInfo(mCurrentMovement, &startpoint); mMovementAnimationControlled = true; @@ -546,7 +550,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, - 1.f, (!sameAnim ? "start" : "loop start"), "stop", startPoint, ~0ul, true); + 1.f, "start", "stop", startpoint, ~0ul, true); } } } @@ -626,7 +630,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; - refreshJumpAnims(weap, jump, idle, force); + refreshJumpAnims(weap, jump, idle, movement, force); refreshMovementAnims(weap, movement, idle, force); // idle handled last as it can depend on the other states diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8b3c68048d..1db497828e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -214,7 +214,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshHitRecoilAnims(CharacterState& idle); - void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false); + void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force=false); void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); From 56ef11b023299b64e6177c31a3af832b3b274d8c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 13:55:49 +0300 Subject: [PATCH 094/150] Reset sneak and swim idle animations when moving --- apps/openmw/mwmechanics/character.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8f14e0331b..c437a21d51 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2168,12 +2168,12 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { // Note: turning animations should not interrupt idle ones - if (inwater) + if (movestate != CharState_None && !isTurning()) + idlestate = CharState_None; + else if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning()) - idlestate = CharState_None; else idlestate = CharState_Idle; } From 77fb4d6dd2d8ea589e8befe55a5075fdfbad2b0c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 14:06:04 +0300 Subject: [PATCH 095/150] Make sure the idle animations are reset while jumping --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c437a21d51..27170d6a89 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -353,7 +353,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force) { - if (!force && jump == mJumpState && movement == CharState_None) + if (!force && jump == mJumpState && idle == CharState_None && movement == CharState_None) return; if (jump != JumpState_None) From d2cad229f897cfb1657095ac362708360e6bc688 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 25 Sep 2018 17:09:54 +0300 Subject: [PATCH 096/150] Ugly hack: don't reset player idle and movement animations in first person view --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 27170d6a89..90f90e9e2a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -356,7 +356,7 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState if (!force && jump == mJumpState && idle == CharState_None && movement == CharState_None) return; - if (jump != JumpState_None) + if (jump != JumpState_None && !(mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) // FIXME { idle = CharState_None; movement = CharState_None; From bce8de5fe9332d54221349648a4d1a7788ac7cc2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 24 Sep 2018 14:09:24 +0400 Subject: [PATCH 097/150] Respect selected encoding in the content selector (bug #4467) --- CHANGELOG.md | 1 + apps/launcher/datafilespage.cpp | 4 +++- apps/opencs/editor.cpp | 5 +++-- apps/opencs/view/doc/filedialog.cpp | 5 +++++ apps/opencs/view/doc/filedialog.hpp | 1 + components/contentselector/view/contentselector.cpp | 7 ++++++- components/contentselector/view/contentselector.hpp | 1 + 7 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29c..392bcf0846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ Bug #4460: Script function "Equip" doesn't bypass beast restrictions Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages + Bug #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour Bug #4474: No fallback when getVampireHead fails diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7b703a9242..6f2389de3f 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config: ui.setupUi (this); setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + const QString encoding = mGameSettings.value("encoding", "win1252"); + mSelector->setEncoding(encoding); mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); @@ -357,4 +359,4 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles) QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles)); std::sort(cellNamesList.begin(), cellNamesList.end()); emit signalLoadedCellsChanged(cellNamesList); -} \ No newline at end of file +} diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index e733d9924b..f29c6aca81 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -108,8 +108,9 @@ std::pair > CS::Editor::readConfi mCfgMgr.readConfiguration(variables, desc, quiet); - mDocumentManager.setEncoding ( - ToUTF8::calculateEncoding (variables["encoding"].as().toStdString())); + const std::string encoding = variables["encoding"].as().toStdString(); + mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding)); + mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str())); mDocumentManager.setResourceDir (mResources = variables["resources"].as().toStdString()); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index b6f4aaec31..3c5f202021 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path) mSelector->addFiles(path); } +void CSVDoc::FileDialog::setEncoding(const QString &encoding) +{ + mSelector->setEncoding(encoding); +} + void CSVDoc::FileDialog::clearFiles() { mSelector->clearFiles(); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index ca6145b9c6..69acfac3d1 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -42,6 +42,7 @@ namespace CSVDoc void showDialog (ContentAction action); void addFiles (const QString &path); + void setEncoding (const QString &encoding); void clearFiles (); QString filename() const; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 5e16064f4b..595be9f44f 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -111,6 +111,11 @@ void ContentSelectorView::ContentSelector::clearCheckStates() mContentModel->uncheckAll(); } +void ContentSelectorView::ContentSelector::setEncoding(const QString &encoding) +{ + mContentModel->setEncoding(encoding); +} + void ContentSelectorView::ContentSelector::setContentList(const QStringList &list) { if (list.isEmpty()) @@ -239,4 +244,4 @@ void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems() void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); -} \ No newline at end of file +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 323f926ed6..9f9ad886cd 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -32,6 +32,7 @@ namespace ContentSelectorView void setProfileContent (const QStringList &fileList); void clearCheckStates(); + void setEncoding (const QString &encoding); void setContentList(const QStringList &list); ContentSelectorModel::ContentFileList selectedFiles() const; From 0176ee389e1af840c2c73c701a865bcf12beffbf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 25 Sep 2018 17:16:40 +0400 Subject: [PATCH 098/150] Handle length of UTF-8 strings properly in plugin metadata (bug #4653) --- CHANGELOG.md | 1 + apps/opencs/model/world/metadata.cpp | 4 ++-- components/esm/esmreader.hpp | 4 ++-- components/esm/loadtes3.cpp | 8 ++++---- components/esm/loadtes3.hpp | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392bcf0846..93592dec41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ Bug #4644: %Name should be available for all actors, not just for NPCs Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health + Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/opencs/model/world/metadata.cpp b/apps/opencs/model/world/metadata.cpp index 960fdc9e4e..b2fa3487cd 100644 --- a/apps/opencs/model/world/metadata.cpp +++ b/apps/opencs/model/world/metadata.cpp @@ -14,8 +14,8 @@ void CSMWorld::MetaData::blank() void CSMWorld::MetaData::load (ESM::ESMReader& esm) { mFormat = esm.getHeader().mFormat; - mAuthor = esm.getHeader().mData.author.toString(); - mDescription = esm.getHeader().mData.desc.toString(); + mAuthor = esm.getHeader().mData.author; + mDescription = esm.getHeader().mData.desc; } void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 4772aeb6fc..6e84fa7d4f 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -33,8 +33,8 @@ public: int getVer() const { return mHeader.mData.version; } int getRecordCount() const { return mHeader.mData.records; } float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; } - const std::string getAuthor() const { return mHeader.mData.author.toString(); } - const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::string getAuthor() const { return mHeader.mData.author; } + const std::string getDesc() const { return mHeader.mData.desc; } const std::vector &getGameFiles() const { return mHeader.mMaster; } const Header& getHeader() const { return mHeader; } int getFormat() const; diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index b16145467c..d953f1dc23 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -32,8 +32,8 @@ void ESM::Header::load (ESMReader &esm) esm.getSubHeader(); esm.getT(mData.version); esm.getT(mData.type); - mData.author.assign( esm.getString(mData.author.data_size()) ); - mData.desc.assign( esm.getString(mData.desc.data_size()) ); + mData.author.assign( esm.getString(32) ); + mData.desc.assign( esm.getString(256) ); esm.getT(mData.records); } @@ -73,8 +73,8 @@ void ESM::Header::save (ESMWriter &esm) esm.startSubRecord("HEDR"); esm.writeT(mData.version); esm.writeT(mData.type); - esm.writeFixedSizeString(mData.author.toString(), mData.author.data_size()); - esm.writeFixedSizeString(mData.desc.toString(), mData.desc.data_size()); + esm.writeFixedSizeString(mData.author, 32); + esm.writeFixedSizeString(mData.desc, 256); esm.writeT(mData.records); esm.endRecord("HEDR"); diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index 896936cf5c..5b26ac7d2d 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -21,8 +21,8 @@ namespace ESM */ unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) - NAME32 author; // Author's name - NAME256 desc; // File description + std::string author; // Author's name + std::string desc; // File description int records; // Number of records }; From 8af021d729de4b9d348a4aef5de7af6692a5143f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Sep 2018 22:03:43 +0400 Subject: [PATCH 099/150] Restore dynamic stats for actors in inactive cells (bug #1875) --- CHANGELOG.md | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 19 ++++++++------- .../mwmechanics/mechanicsmanagerimp.cpp | 8 +++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwworld/cells.cpp | 13 ++++++++++ apps/openmw/mwworld/cells.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 24 +++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 4 ++++ apps/openmw/mwworld/worldimp.hpp | 8 +++---- 12 files changed, 72 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29c..1ef5040033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.45.0 ------ + Bug #1875: Actors in inactive cells don't heal from resting Bug #1990: Sunrise/sunset not set correct Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index fe3fc5721b..8137bad959 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -90,6 +90,8 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0; + virtual void rest(bool sleep) = 0; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f44e6593bb..e17935abcd 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -571,6 +571,8 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void rest() = 0; + virtual void setPlayerTraveling(bool traveling) = 0; virtual bool isPlayerTraveling() const = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92f2ba34dc..7f7355eefe 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -545,10 +545,10 @@ namespace MWMechanics void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { - if (ptr.getClass().getCreatureStats(ptr).isDead()) + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + if (stats.isDead()) return; - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); if (sleep) @@ -565,12 +565,6 @@ namespace MWMechanics stats.setMagicka(stat); } - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - - float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - // Current fatigue can be above base value due to a fortify effect. // In that case stop here and don't try to restore. DynamicStat fatigue = stats.getFatigue(); @@ -582,6 +576,12 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; + float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; @@ -1667,7 +1667,8 @@ namespace MWMechanics if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue; - restoreDynamicStats(iter->first, sleep); + if (!sleep || iter->first == player) + restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 859b2e5229..33357b79a7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -453,9 +453,17 @@ namespace MWMechanics void MechanicsManager::rest(bool sleep) { + if (sleep) + MWBase::Environment::get().getWorld()->rest(); + mActors.rest(sleep); } + void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, bool sleep) + { + mActors.restoreDynamicStats(actor, sleep); + } + int MechanicsManager::getHoursToRest() const { return mActors.getHoursToRest(mWatched); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index af12d4d98f..3682e97cec 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -91,6 +91,8 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep); + virtual void rest(bool sleep); ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 5cac12b9c9..8506cefa37 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -151,6 +151,19 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } +void MWWorld::Cells::rest () +{ + for (auto &interior : mInteriors) + { + interior.second.rest(); + } + + for (auto &exterior : mExteriors) + { + exterior.second.rest(); + } +} + MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) { if (id.mPaged) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0b7d9444fd..bd730329bb 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -61,6 +61,7 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + void rest (); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 05ff1e3260..5cd8346a7c 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -17,6 +17,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -954,6 +955,29 @@ namespace MWWorld } } + void CellStore::rest() + { + if (mState == State_Loaded) + { + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + } + } + void CellStore::respawn() { if (mState == State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index dd54bdd6ac..bcf4b4ada0 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -183,6 +183,8 @@ namespace MWWorld /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + void rest(); + /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. /// The supported types are defined at the bottom of this file. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9b701d17b2..fe311f8fc8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3118,6 +3118,10 @@ namespace MWWorld return closestMarker; } + void World::rest() + { + mCells.rest(); + } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e694d9d84f..7df8d1af5b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -552,11 +552,9 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; RestPermitted canRest() const override; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + ///< check if the player is allowed to rest + + void rest() override; /// \todo Probably shouldn't be here MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; From ba2fd8b7959f13d1f3f548d98a8711b03744212f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 26 Sep 2018 21:16:57 +0300 Subject: [PATCH 100/150] Rename reflected absorb spells setting and add it to Advanced tab --- apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 2 +- .../reference/modding/settings/game.rst | 34 +++++++++---------- files/settings-default.cfg | 4 +-- files/ui/advancedpage.ui | 12 ++++++- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 102463085b..41f546af46 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -72,6 +72,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); @@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 14dfac283d..f1ee2520b2 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -582,7 +582,7 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game")) + if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); else diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index baeac92b28..4c3f4579f7 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -16,7 +16,7 @@ If the setting is 2, the crosshair is the color of the color crosshair owned set If the setting is 3, both the tool tip background and the crosshair are colored. The crosshair is not visible if crosshair is false. -This setting can only be configured by editing the settings configuration file. +This setting can be configured in Advanced tab of the launcher. show projectile damage ---------------------- @@ -27,7 +27,7 @@ show projectile damage If this setting is true, the damage bonus of arrows and bolts will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show melee info --------------- @@ -38,7 +38,7 @@ show melee info If this setting is true, the reach and speed of weapons will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show enchant chance ------------------- @@ -49,7 +49,7 @@ show enchant chance Whether or not the chance of success will be displayed in the enchanting menu. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. best attack ----------- @@ -73,13 +73,13 @@ can loot during death animation If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. -This is how original Morrowind behaves. +This is how Morrowind behaves. If this setting is false, player has to wait until end of death animation in all cases. This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. difficulty ---------- @@ -97,19 +97,19 @@ and values above 500 will result in the player inflicting no damage. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. -classic reflect absorb attribute behavior +classic reflected absorb spells behavior ----------------------------------------- :Type: boolean :Range: True/False :Default: True -If this setting is true, "Absorb Attribute" spells which were reflected by the target are not "mirrored", -and the caster will absorb their own attribute resulting in no effect on both the caster and the target. +If this setting is true, effects of Absorb spells which were reflected by the target are not mirrored, +and the caster will absorb their own stat resulting in no effect on either the caster and the target. This makes the gameplay as a mage easier, but these spells become imbalanced. -This is how the original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show effect duration -------------------- @@ -121,7 +121,7 @@ show effect duration Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. enchanted weapons are magical ----------------------------- @@ -131,9 +131,9 @@ enchanted weapons are magical :Default: True Make enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. -This is how original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. prevent merchant equipping -------------------------- @@ -144,7 +144,7 @@ prevent merchant equipping Prevent merchants from equipping items that are sold to them. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. followers attack on sight ------------------------- @@ -157,7 +157,7 @@ Make player followers and escorters start combat with enemies who have started c Otherwise they wait for the enemies or the player to do an attack first. Please note this setting has not been extensively tested and could have side effects with certain quests. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. use additional anim sources --------------------------- @@ -182,4 +182,4 @@ barter disposition change is permanent If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. This imitates the option Morrowind Code Patch offers. -This setting can be toggled with a checkbox in Advanced tab of the launcher. +This setting can be toggled in Advanced tab of the launcher. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 3344e6bb4d..ea6f6e7b66 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -200,8 +200,8 @@ best attack = false # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). difficulty = 0 -# Replicate how reflected "absorb attribute" spells do not have any effect in Morrowind engine. The caster absorbs the attribute from themselves. -classic reflect absorb attribute behavior = true +# Make reflected Absorb spells have no practical effect, like in Morrowind. +classic reflected absorb spells behavior = true # Show duration of magic effect and lights in the spells window. show effect duration = false diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8395d028f6..0793345c8d 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -73,7 +73,7 @@ Enchanted weapons are magical - + @@ -86,6 +86,16 @@ + + + + <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> + + + Classic reflected Absorb spells behavior + + + From ee4ca87352fd8731e9cb96cf42e5f82beec527ba Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 26 Sep 2018 23:13:18 +0400 Subject: [PATCH 101/150] Traverse child nodes of cell node in the editor (bug #4654) --- CHANGELOG.md | 1 + apps/opencs/view/render/cell.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29c..d5c7364b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,7 @@ Bug #4644: %Name should be available for all actors, not just for NPCs Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health + Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 552a54ac27..a0c408df0f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -47,6 +47,7 @@ namespace CSVRender virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { + traverse(node, nv); CellNodeContainer* container = static_cast(node->getUserData()); container->getCell()->updateLand(); } From 8444ee9981226e6f739b4df8ced94ef7bd9f7abe Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 13 Jul 2018 21:48:59 -0500 Subject: [PATCH 102/150] Start rendering npc's --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/actor.cpp | 196 +++++++++++++++++++++++++++++ apps/opencs/view/render/actor.hpp | 51 ++++++++ apps/opencs/view/render/object.cpp | 25 +++- apps/openmw/mwrender/animation.cpp | 34 +---- components/CMakeLists.txt | 2 +- components/esm/mappings.cpp | 134 ++++++++++++++++++++ components/esm/mappings.hpp | 16 +++ components/sceneutil/visitor.cpp | 19 ++- components/sceneutil/visitor.hpp | 30 +++++ 10 files changed, 471 insertions(+), 38 deletions(-) create mode 100644 apps/opencs/view/render/actor.cpp create mode 100644 apps/opencs/view/render/actor.hpp create mode 100644 components/esm/mappings.cpp create mode 100644 components/esm/mappings.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f2821f184a..26713f9256 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -93,7 +93,7 @@ opencs_units (view/render opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid + cellarrow cellmarker cellborder pathgrid actor ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp new file mode 100644 index 0000000000..1273b94c64 --- /dev/null +++ b/apps/opencs/view/render/actor.cpp @@ -0,0 +1,196 @@ +#include "actor.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) + : mId(id) + , mType(type) + , mData(data) + , mSkeleton(nullptr) + , mBaseNode(new osg::Group()) + { + } + + osg::Group* Actor::getBaseNode() + { + return mBaseNode; + } + + void Actor::update() + { + const std::string MeshPrefix = "meshes\\"; + const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; + + auto& bodyParts = mData.getBodyParts(); + auto& races = mData.getRaces(); + auto& referenceables = mData.getReferenceables(); + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + + // Remove children + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) + { + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + auto isBeast = [&](std::string race) -> bool { + int index = races.searchId(race); + if (index != -1 && !races.getRecord(index).isDeleted()) + return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; + else + return false; + }; + + // Load skeleton + std::string skeletonResource; + if (isBeast(npc.mRace)) { + std::cout << "is beast\n"; + skeletonResource = "base_animkna.nif"; + } + else if (npc.isMale()) { + std::cout << "is male\n"; + skeletonResource = "base_anim.nif"; + } + else { + std::cout << "is female\n"; + skeletonResource = "base_anim_female.nif"; + } + + std::string skeletonModel = MeshPrefix + skeletonResource; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + { + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) + { + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); + } + mBaseNode->addChild(mSkeleton); + } + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + if (!npc.isMale()) { + for (auto it : nodeMap) { + std::cout << it.first << "\n"; + } + } + + + // Female mesh has some drawables attached, get rid of them + SceneUtil::HideDrawablesVisitor hdVisitor; + mSkeleton->accept(hdVisitor); + + // Convenience method to retrieve the mesh name of a body part + auto getBodyPartMesh = [&](std::string bpName) -> std::string { + int index = bodyParts.searchId(bpName); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + }; + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) + { + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) + { + auto& bodyPart = record.get(); + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); + } + } + }; + + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); + + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace)); + if (meshResult != r2bpMap.end()) + { + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + if (!npc.isMale() && type == ESM::PRT_LHand) { + SceneUtil::NodeMapVisitor::NodeMap handNodeMap; + SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); + instance->accept(nmVisitor); + + std::cout << "Left hand\n"; + for (auto it : handNodeMap) { + std::cout << it.first << std::endl; + } + } + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + { + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); + } + } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + } +} diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp new file mode 100644 index 0000000000..10f4de558d --- /dev/null +++ b/apps/opencs/view/render/actor.hpp @@ -0,0 +1,51 @@ +#ifndef OPENCS_VIEW_RENDER_ACTOR_H +#define OPENCS_VIEW_RENDER_ACTOR_H + +#include + +#include + +namespace osg +{ + class Group; +} + +namespace CSMWorld +{ + class Data; +} + +namespace SceneUtil +{ + class Skeleton; +} + +namespace CSVRender +{ + /// Handles loading an npc or creature + class Actor + { + public: + /// Creates an actor. + /// \param id The referenceable id + /// \param type The record type + /// \param data The data store + Actor(const std::string& id, int type, CSMWorld::Data& data); + + /// Retrieves the base node that meshes are attached to + osg::Group* getBaseNode(); + + /// (Re)creates the npc or creature renderable + void update(); + + private: + std::string mId; + int mType; + CSMWorld::Data& mData; + + SceneUtil::Skeleton* mSkeleton; + osg::ref_ptr mBaseNode; + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 2b1e3addee..b8e1716504 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,6 +1,8 @@ #include "object.hpp" #include +#include +#include #include #include @@ -11,6 +13,7 @@ #include #include #include +#include #include @@ -28,7 +31,9 @@ #include #include #include +#include +#include "actor.hpp" #include "mask.hpp" @@ -84,6 +89,7 @@ void CSVRender::Object::update() const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); + int recordType = -1; const ESM::Light* light = NULL; if (index==-1) @@ -96,7 +102,7 @@ void CSVRender::Object::update() referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). toString().toUtf8().constData(); - int recordType = + recordType = referenceables.getData (index, referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); if (recordType == CSMWorld::UniversalId::Type_Light) @@ -112,7 +118,7 @@ void CSVRender::Object::update() model = "marker_creature.nif"; } - if (model.empty()) + if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty()) error = 2; } @@ -126,9 +132,18 @@ void CSVRender::Object::update() { try { - std::string path = "meshes\\" + model; - - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + if (recordType == CSMWorld::UniversalId::Type_Npc) + { + std::cout << "recordType: Npc\n"; + Actor actor(mReferenceableId, recordType, mData); + actor.update(); + mBaseNode->addChild(actor.getBaseNode()); + } + else + { + std::string path = "meshes\\" + model; + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + } } catch (std::exception& e) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7410dbf037..e4a7c94b3c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -91,32 +91,6 @@ namespace std::vector > mToRemove; }; - class NodeMapVisitor : public osg::NodeVisitor - { - public: - typedef std::map > NodeMap; - - NodeMapVisitor(NodeMap& map) - : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) - , mMap(map) - {} - - void apply(osg::MatrixTransform& trans) - { - // Take transformation for first found node in file - const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); - if (mMap.find(nodeName) == mMap.end()) - { - mMap[nodeName] = &trans; - } - - traverse(trans); - } - - private: - NodeMap& mMap; - }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); @@ -468,7 +442,7 @@ namespace MWRender } if (mDone) return; - + // Set the starting time to measure glow duration from if this is a temporary glow if ((mDuration >= 0) && mStartingTime == 0) mStartingTime = nv->getFrameStamp()->getSimulationTime(); @@ -1042,7 +1016,7 @@ namespace MWRender { if (!mNodeMapCreated && mObjectRoot) { - NodeMapVisitor visitor(mNodeMap); + SceneUtil::NodeMapVisitor visitor(mNodeMap); mObjectRoot->accept(visitor); mNodeMapCreated = true; } @@ -1313,7 +1287,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - } + } if(timepassed <= 0.0f) break; @@ -1528,7 +1502,7 @@ namespace MWRender osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); mGlowUpdater = glowUpdater; node->addUpdateCallback(glowUpdater); - + // set a texture now so that the ShaderVisitor can find it osg::ref_ptr writableStateSet = NULL; if (!node->getStateSet()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2da7c80bfa..e7e0ea0daa 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -77,7 +77,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate + aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate mappings ) add_component_dir (esmterrain diff --git a/components/esm/mappings.cpp b/components/esm/mappings.cpp new file mode 100644 index 0000000000..440e735739 --- /dev/null +++ b/components/esm/mappings.cpp @@ -0,0 +1,134 @@ +#include "mappings.hpp" + +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return ESM::BodyPart::MP_Head; + case ESM::PRT_Hair: + return ESM::BodyPart::MP_Hair; + case ESM::PRT_Neck: + return ESM::BodyPart::MP_Neck; + case ESM::PRT_Cuirass: + return ESM::BodyPart::MP_Chest; + case ESM::PRT_Groin: + return ESM::BodyPart::MP_Groin; + case ESM::PRT_RHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_LHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_RWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_LWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_RForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_LForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_RUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_LUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_RFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_LFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_RAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_LAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_RKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_LKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_RLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_LLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_Tail: + return ESM::BodyPart::MP_Tail; + default: + throw std::runtime_error("PartReferenceType " + + std::to_string(type) + " not associated with a mesh part"); + } + } + + std::string getBoneName(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return "head"; + case ESM::PRT_Hair: + return "head"; // This is purposeful. + case ESM::PRT_Neck: + return "neck"; + case ESM::PRT_Cuirass: + return "chest"; + case ESM::PRT_Groin: + return "groin"; + case ESM::PRT_Skirt: + return "groin"; + case ESM::PRT_RHand: + return "right hand"; + case ESM::PRT_LHand: + return "left hand"; + case ESM::PRT_RWrist: + return "right wrist"; + case ESM::PRT_LWrist: + return "left wrist"; + case ESM::PRT_Shield: + return "shield bone"; + case ESM::PRT_RForearm: + return "right forearm"; + case ESM::PRT_LForearm: + return "left forearm"; + case ESM::PRT_RUpperarm: + return "right upper arm"; + case ESM::PRT_LUpperarm: + return "left upper arm"; + case ESM::PRT_RFoot: + return "right foot"; + case ESM::PRT_LFoot: + return "left foot"; + case ESM::PRT_RAnkle: + return "right ankle"; + case ESM::PRT_LAnkle: + return "left ankle"; + case ESM::PRT_RKnee: + return "right knee"; + case ESM::PRT_LKnee: + return "left knee"; + case ESM::PRT_RLeg: + return "right upper leg"; + case ESM::PRT_LLeg: + return "left upper leg"; + case ESM::PRT_RPauldron: + return "right clavicle"; + case ESM::PRT_LPauldron: + return "left clavicle"; + case ESM::PRT_Weapon: + return "weapon bone"; + case ESM::PRT_Tail: + return "tail"; + default: + throw std::runtime_error("unknown PartReferenceType"); + } + } + + std::string getMeshFilter(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Hair: + return "hair"; + default: + return getBoneName(type); + } + } +} diff --git a/components/esm/mappings.hpp b/components/esm/mappings.hpp new file mode 100644 index 0000000000..f930fef152 --- /dev/null +++ b/components/esm/mappings.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESM_MAPPINGS_H +#define OPENMW_ESM_MAPPINGS_H + +#include + +#include +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type); + std::string getBoneName(ESM::PartReferenceType type); + std::string getMeshFilter(ESM::PartReferenceType type); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 2f6123e34d..5aaeb459ee 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,6 @@ #include "visitor.hpp" +#include #include #include @@ -23,7 +24,7 @@ namespace SceneUtil { if (Misc::StringUtils::ciEqual(node.className(), mNameToFind)) mFoundNodes.push_back(&node); - + traverse(node); } @@ -54,4 +55,20 @@ namespace SceneUtil partsys->setFreezeOnCull(false); } + void NodeMapVisitor::apply(osg::MatrixTransform& trans) + { + // Take transformation for first found node in file + const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); + if (mMap.find(nodeName) == mMap.end()) + { + mMap[nodeName] = &trans; + } + + traverse(trans); + } + + void HideDrawablesVisitor::apply(osg::Drawable& drawable) + { + drawable.setNodeMask(0); + } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 265fd6d02a..bd3945296d 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H +#include #include // Commonly used scene graph visitors @@ -58,6 +59,35 @@ namespace SceneUtil virtual void apply(osg::Drawable& drw); }; + /// Maps names to nodes + class NodeMapVisitor : public osg::NodeVisitor + { + public: + typedef std::map > NodeMap; + + NodeMapVisitor(NodeMap& map) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , mMap(map) + {} + + void apply(osg::MatrixTransform& trans); + + private: + NodeMap& mMap; + }; + + /// Hides all attached drawables + class HideDrawablesVisitor : public osg::NodeVisitor + { + public: + HideDrawablesVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + void apply(osg::Drawable& drawable) override; + }; + } #endif From e2ac392a4076c09aeb42d4a1f9213c83cb6d46ad Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 17 Jul 2018 21:28:05 -0500 Subject: [PATCH 103/150] Move common stuff to scene util, fix errors with 1st person meshes --- apps/opencs/view/render/actor.cpp | 247 ++++++++++++-------------- apps/openmw/mwrender/animation.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 33 +--- components/CMakeLists.txt | 1 + components/sceneutil/actorutil.cpp | 30 ++++ components/sceneutil/actorutil.hpp | 11 ++ components/sceneutil/visitor.cpp | 89 +++++++++- components/sceneutil/visitor.hpp | 40 ++++- 8 files changed, 290 insertions(+), 165 deletions(-) create mode 100644 components/sceneutil/actorutil.cpp create mode 100644 components/sceneutil/actorutil.hpp diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 1273b94c64..93fff270a0 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -45,152 +46,140 @@ namespace CSVRender // Remove children mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + try { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - - auto isBeast = [&](std::string race) -> bool { - int index = races.searchId(race); - if (index != -1 && !races.getRecord(index).isDeleted()) - return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; - else - return false; - }; - - // Load skeleton - std::string skeletonResource; - if (isBeast(npc.mRace)) { - std::cout << "is beast\n"; - skeletonResource = "base_animkna.nif"; - } - else if (npc.isMale()) { - std::cout << "is male\n"; - skeletonResource = "base_anim.nif"; - } - else { - std::cout << "is female\n"; - skeletonResource = "base_anim_female.nif"; - } - - std::string skeletonModel = MeshPrefix + skeletonResource; - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); + + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; + + // Load skeleton + std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); - } - - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); - - if (!npc.isMale()) { - for (auto it : nodeMap) { - std::cout << it.first << "\n"; - } - } - - - // Female mesh has some drawables attached, get rid of them - SceneUtil::HideDrawablesVisitor hdVisitor; - mSkeleton->accept(hdVisitor); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - auto& bodyPart = record.get(); - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); } - }; - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace)); - if (meshResult != r2bpMap.end()) + // Female mesh has some drawables attached, get rid of them + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Convenience method to retrieve the mesh name of a body part + auto getBodyPartMesh = [&](std::string bpName) -> std::string { + int index = bodyParts.searchId(bpName); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + }; + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - mesh = meshResult->second; - } - } + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) + { + // Method to check if 1st person part or not + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - if (!npc.isMale() && type == ESM::PRT_LHand) { - SceneUtil::NodeMapVisitor::NodeMap handNodeMap; - SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); - instance->accept(nmVisitor); + auto& bodyPart = record.get(); + if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) + continue; - std::cout << "Left hand\n"; - for (auto it : handNodeMap) { - std::cout << it.first << std::endl; + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); } } - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; + }; - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) - { - auto part = static_cast(i); - switch (part) + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); + + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + { + mesh = meshResult->second; + } + else if (isFemale){ + meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); + } } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e4a7c94b3c..484f90ab5c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1339,7 +1339,7 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - CleanObjectRootVisitor removeDrawableVisitor; + SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; created->accept(removeDrawableVisitor); removeDrawableVisitor.remove(); @@ -1408,7 +1408,7 @@ namespace MWRender if (isCreature) { - RemoveTriBipVisitor removeTriBipVisitor; + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; mObjectRoot->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 98f8ce892d..2716ea19ba 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -304,7 +305,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); - if(mViewMode == viewMode) + if(mViewMode == viewMode) return; mViewMode = viewMode; @@ -451,37 +452,15 @@ void NpcAnimation::updateNpcBase() } } + bool is1stPerson = mViewMode == VM_FirstPerson; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel; - if (mViewMode != VM_FirstPerson) - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.nif"; - else - smodel = "meshes\\base_anim.nif"; - } - else - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.1st.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.1st.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.1st.nif"; - else - smodel = "meshes\\base_anim.1st.nif"; - } - + std::string smodel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); - if(mViewMode != VM_FirstPerson) + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; if (smodel != base) @@ -675,7 +654,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st } osg::Vec3f NpcAnimation::runAnimation(float timepassed) -{ +{ osg::Vec3f ret = Animation::runAnimation(timepassed); mHeadAnimationTime->update(timepassed); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e7e0ea0daa..7af76137c5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,6 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer + actorutil ) add_component_dir (nif diff --git a/components/sceneutil/actorutil.cpp b/components/sceneutil/actorutil.cpp new file mode 100644 index 0000000000..988a61f60e --- /dev/null +++ b/components/sceneutil/actorutil.cpp @@ -0,0 +1,30 @@ +#include "actorutil.hpp" + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf) + { + if (!firstPerson) + { + if (isWerewolf) + return "meshes\\wolf\\skin.nif"; + else if (isBeast) + return "meshes\\base_animkna.nif"; + else if (isFemale) + return "meshes\\base_anim_female.nif"; + else + return "meshes\\base_anim.nif"; + } + else + { + if (isWerewolf) + return "meshes\\wolf\\skin.1st.nif"; + else if (isBeast) + return "meshes\\base_animkna.1st.nif"; + else if (isFemale) + return "meshes\\base_anim_female.1st.nif"; + else + return "meshes\\base_anim.1st.nif"; + } + } +} diff --git a/components/sceneutil/actorutil.hpp b/components/sceneutil/actorutil.hpp new file mode 100644 index 0000000000..7bdbbaa922 --- /dev/null +++ b/components/sceneutil/actorutil.hpp @@ -0,0 +1,11 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP +#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP + +#include + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 5aaeb459ee..07be1608e6 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,7 @@ #include "visitor.hpp" +#include + #include #include @@ -67,8 +69,91 @@ namespace SceneUtil traverse(trans); } - void HideDrawablesVisitor::apply(osg::Drawable& drawable) + void RemoveVisitor::remove() { - drawable.setNodeMask(0); + for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) + { + if (!it->second->removeChild(it->first)) + std::cerr << "error removing " << it->first->getName() << std::endl; + } + } + + void CleanObjectRootVisitor::apply(osg::Drawable& drw) + { + applyDrawable(drw); + } + + void CleanObjectRootVisitor::apply(osg::Group& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::MatrixTransform& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::Node& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::applyNode(osg::Node& node) + { + if (node.getStateSet()) + node.setStateSet(NULL); + + if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) + mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + else + traverse(node); + } + + void CleanObjectRootVisitor::applyDrawable(osg::Node& node) + { + osg::NodePath::iterator parent = getNodePath().end()-2; + // We know that the parent is a Group because only Groups can have children. + osg::Group* parentGroup = static_cast(*parent); + + // Try to prune nodes that would be empty after the removal + if (parent != getNodePath().begin()) + { + // This could be extended to remove the parent's parent, and so on if they are empty as well. + // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance. + osg::Group* parentParent = static_cast(*(parent - 1)); + if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) + { + mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + return; + } + } + + mToRemove.push_back(std::make_pair(&node, parentGroup)); + } + + void RemoveTriBipVisitor::apply(osg::Drawable& drw) + { + applyImpl(drw); + } + + void RemoveTriBipVisitor::apply(osg::Group& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::apply(osg::MatrixTransform& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::applyImpl(osg::Node& node) + { + const std::string toFind = "tri bip"; + if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) + { + osg::Group* parent = static_cast(*(getNodePath().end()-2)); + // Not safe to remove in apply(), since the visitor is still iterating the child list + mToRemove.push_back(std::make_pair(&node, parent)); + } } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index bd3945296d..3e9a565c03 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -68,7 +68,8 @@ namespace SceneUtil NodeMapVisitor(NodeMap& map) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) , mMap(map) - {} + { + } void apply(osg::MatrixTransform& trans); @@ -76,18 +77,47 @@ namespace SceneUtil NodeMap& mMap; }; - /// Hides all attached drawables - class HideDrawablesVisitor : public osg::NodeVisitor + /// @brief Base class for visitors that remove nodes from a scene graph. + /// Subclasses need to fill the mToRemove vector. + /// To use, node->accept(removeVisitor); removeVisitor.remove(); + class RemoveVisitor : public osg::NodeVisitor { public: - HideDrawablesVisitor() + RemoveVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } - void apply(osg::Drawable& drawable) override; + void remove(); + + protected: + // + typedef std::vector > RemoveVec; + std::vector > mToRemove; }; + // Removes all drawables from a graph. + class CleanObjectRootVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + virtual void apply(osg::Node& node); + + void applyNode(osg::Node& node); + void applyDrawable(osg::Node& node); + }; + + class RemoveTriBipVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + + void applyImpl(osg::Node& node); + }; } #endif From 6b42f37918271cdd41000e141e8f597b69b82f5b Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 22 Jul 2018 15:38:30 -0500 Subject: [PATCH 104/150] Handle creatures too --- apps/opencs/view/render/actor.cpp | 284 ++++++++++++++++------------- apps/opencs/view/render/actor.hpp | 8 + apps/opencs/view/render/object.cpp | 5 +- 3 files changed, 167 insertions(+), 130 deletions(-) diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 93fff270a0..3691085e3c 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -18,6 +18,8 @@ namespace CSVRender { + const std::string Actor::MeshPrefix = "meshes\\"; + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) , mType(type) @@ -34,7 +36,42 @@ namespace CSVRender void Actor::update() { - const std::string MeshPrefix = "meshes\\"; + try + { + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + if (mType == CSMWorld::UniversalId::Type_Npc) + updateNpc(); + else if (mType == CSMWorld::UniversalId::Type_Creature) + updateCreature(); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; + } + } + + void Actor::updateCreature() + { + auto& referenceables = mData.getReferenceables(); + + auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + std::string skeletonModel = MeshPrefix + creature.mModel; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::updateNpc() + { const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; auto& bodyParts = mData.getBodyParts(); @@ -42,144 +79,137 @@ namespace CSVRender auto& referenceables = mData.getReferenceables(); auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - // Remove children - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; - try - { - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + // Load skeleton + std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + // Get rid of the extra attachments + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - - bool is1stPerson = false; - bool isFemale = !npc.isMale(); - bool isBeast = race.mData.mFlags & ESM::Race::Beast; - bool isWerewolf = false; - - // Load skeleton - std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) - { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); + // Method to check if 1st person part or not + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; + + auto& bodyPart = record.get(); + if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) + continue; + + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); } + } + }; - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); - // Female mesh has some drawables attached, get rid of them - SceneUtil::CleanObjectRootVisitor cleanVisitor; - mSkeleton->accept(cleanVisitor); - cleanVisitor.remove(); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) - { - // Method to check if 1st person part or not - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; - - auto& bodyPart = record.get(); - if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) - continue; - - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); - } - } - }; - - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); - - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - { - mesh = meshResult->second; - } - else if (isFemale){ - meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - mesh = meshResult->second; - } - } - - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; - - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); + if (meshResult != r2bpMap.end()) { - auto part = static_cast(i); - switch (part) - { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); - } + mesh = meshResult->second; } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + else if (isFemale){ + meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + { + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); } } - catch (std::exception& e) + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::loadSkeleton(const std::string& model) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + osg::ref_ptr temp = sceneMgr->getInstance(model); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - std::cout << "Caught exception: " << e.what() << std::endl; + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); + } + + std::string Actor::getBodyPartMesh(const std::string& bodyPartId) + { + const auto& bodyParts = mData.getBodyParts(); + + int index = bodyParts.searchId(bodyPartId); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 10f4de558d..b4e65ff2b6 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -39,6 +39,14 @@ namespace CSVRender void update(); private: + void loadSkeleton(const std::string& model); + void updateCreature(); + void updateNpc(); + + std::string getBodyPartMesh(const std::string& bodyPartId); + + static const std::string MeshPrefix; + std::string mId; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index b8e1716504..dbfd595b6a 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -132,9 +132,8 @@ void CSVRender::Object::update() { try { - if (recordType == CSMWorld::UniversalId::Type_Npc) + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - std::cout << "recordType: Npc\n"; Actor actor(mReferenceableId, recordType, mData); actor.update(); mBaseNode->addChild(actor.getBaseNode()); @@ -147,8 +146,8 @@ void CSVRender::Object::update() } catch (std::exception& e) { - // TODO: use error marker mesh Log(Debug::Error) << e.what(); + mBaseNode->addChild(createErrorCube()); } } From 97ac0a92dd430c6a7b17a2b812fde52ecbc5b010 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 28 Jul 2018 12:23:43 -0500 Subject: [PATCH 105/150] Move data handling out of rendering code, equip armor/clothes --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/model/world/actoradapter.cpp | 242 +++++++++++++++++++++++ apps/opencs/model/world/actoradapter.hpp | 80 ++++++++ apps/opencs/model/world/data.cpp | 12 ++ apps/opencs/model/world/data.hpp | 7 + apps/opencs/view/render/actor.cpp | 129 ++++-------- apps/opencs/view/render/actor.hpp | 11 +- 7 files changed, 389 insertions(+), 93 deletions(-) create mode 100644 apps/opencs/model/world/actoradapter.cpp create mode 100644 apps/opencs/model/world/actoradapter.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 26713f9256..999324c51c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel + actoradapter ) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp new file mode 100644 index 0000000000..249908bb09 --- /dev/null +++ b/apps/opencs/model/world/actoradapter.cpp @@ -0,0 +1,242 @@ +#include "actoradapter.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "data.hpp" + +namespace CSMWorld +{ + ActorAdapter::ActorAdapter(CSMWorld::Data& data) + : mReferenceables(data.getReferenceables()) + , mRaces(data.getRaces()) + , mBodyParts(data.getBodyParts()) + { + connect(data.getTableModel(UniversalId::Type_Referenceable), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&))); + connect(data.getTableModel(UniversalId::Type_Race), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&))); + connect(data.getTableModel(UniversalId::Type_BodyPart), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); + } + + const ActorAdapter::ActorPartMap* ActorAdapter::getActorPartMap(const std::string& refId) + { + auto it = mActorPartMaps.find(refId); + if (it != mActorPartMaps.end()) + { + return &it->second; + } + else + { + updateActor(refId); + it = mActorPartMaps.find(refId); + if (it != mActorPartMaps.end()) + return &it->second; + } + + return nullptr; + } + + void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) + { + auto key = std::make_pair(raceId, isFemale); + auto it = mRacePartMaps.find(key); + if (it != mRacePartMaps.end()) + { + return it->second; + } + else + { + // Create and find result + updateRaceParts(raceId); + return mRacePartMaps.find(key)->second; + } + } + + void ActorAdapter::updateRaceParts(const std::string& raceId) + { + // Convenience function to determine if part is for 1st person view + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; + + RacePartMap maleMap, femaleMap; + for (int i = 0; i < mBodyParts.getSize(); ++i) + { + auto& record = mBodyParts.getRecord(i); + if (!record.isDeleted() && record.get().mRace == raceId && record.get().mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(record.get().mId)) + { + auto& part = record.get(); + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite + if (part.mData.mFlags & ESM::BodyPart::BPF_Female) + femaleMap.emplace(type, part.mId); + else + maleMap.emplace(type, part.mId); + } + } + + mRacePartMaps[std::make_pair(raceId, true)] = femaleMap; + mRacePartMaps[std::make_pair(raceId, false)] = maleMap; + } + + void ActorAdapter::updateActor(const std::string& refId) + { + int index = mReferenceables.searchId(refId); + if (index != -1) + { + int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int recordType = mReferenceables.getData(index, typeColumn).toInt(); + if (recordType == CSMWorld::UniversalId::Type_Creature) + updateCreature(refId); + else if (recordType == CSMWorld::UniversalId::Type_Npc) + updateNpc(refId); + } + } + + void ActorAdapter::updateNpc(const std::string& refId) + { + auto& record = mReferenceables.getRecord(refId); + if (record.isDeleted()) + { + mActorPartMaps.erase(refId); + return; + } + + auto& npc = dynamic_cast&>(record).get(); + auto& femaleRacePartMap = getOrCreateRacePartMap(npc.mRace, true); + auto& maleRacePartMap = getOrCreateRacePartMap(npc.mRace, false); + + ActorPartMap npcMap; + + // Look at the npc's inventory first + for (auto& item : npc.mInventory.mList) + { + if (item.mCount > 0) + { + std::string itemId = item.mItem.toString(); + // Handle armor, weapons, and clothing + int index = mReferenceables.searchId(itemId); + if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) + { + auto& itemRecord = mReferenceables.getRecord(index); + + int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int recordType = mReferenceables.getData(index, typeColumn).toInt(); + if (recordType == CSMWorld::UniversalId::Type_Armor) + { + auto& armor = dynamic_cast&>(itemRecord).get(); + for (auto& part : armor.mParts.mParts) + { + std::string bodyPartId; + if (!npc.isMale()) + bodyPartId = part.mFemale; + if (bodyPartId.empty()) + bodyPartId = part.mMale; + + if (!bodyPartId.empty()) + npcMap.emplace(static_cast(part.mPart), bodyPartId); + } + } + else if (recordType == CSMWorld::UniversalId::Type_Clothing) + { + auto& clothing = dynamic_cast&>(itemRecord).get(); + for (auto& part : clothing.mParts.mParts) + { + std::string bodyPartId; + if (!npc.isMale()) + bodyPartId = part.mFemale; + if (bodyPartId.empty()) + bodyPartId = part.mMale; + + if (!bodyPartId.empty()) + npcMap.emplace(static_cast(part.mPart), bodyPartId); + } + } + else if (recordType == CSMWorld::UniversalId::Type_Weapon) + { + // TODO + } + } + } + } + + // Fill in the rest with body parts + for (int i = 0; i < ESM::PRT_Count; ++i) + { + auto type = static_cast(i); + if (npcMap.find(type) == npcMap.end()) + { + switch (type) + { + case ESM::PRT_Head: + npcMap.emplace(type, npc.mHead); + break; + case ESM::PRT_Hair: + npcMap.emplace(type, npc.mHair); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part associated + break; + default: + { + std::string bodyPartId; + // Check female map if applicable + if (!npc.isMale()) + { + auto partIt = femaleRacePartMap.find(ESM::getMeshPart(type)); + if (partIt != femaleRacePartMap.end()) + bodyPartId = partIt->second; + } + + // Check male map next + if (bodyPartId.empty() || npc.isMale()) + { + auto partIt = maleRacePartMap.find(ESM::getMeshPart(type)); + if (partIt != maleRacePartMap.end()) + bodyPartId = partIt->second; + } + + // Add to map + if (!bodyPartId.empty()) + { + npcMap.emplace(type, bodyPartId); + } + } + } + } + } + + mActorPartMaps[refId] = npcMap; + } + + void ActorAdapter::updateCreature(const std::string& refId) + { + // TODO + } +} diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp new file mode 100644 index 0000000000..2c94f9c2ca --- /dev/null +++ b/apps/opencs/model/world/actoradapter.hpp @@ -0,0 +1,80 @@ +#ifndef CSM_WOLRD_ACTORADAPTER_H +#define CSM_WOLRD_ACTORADAPTER_H + +#include +#include +#include + +#include + +#include +#include + +#include "refidcollection.hpp" +#include "idcollection.hpp" + +namespace ESM +{ + struct Race; + enum PartReferenceType; +} + +namespace CSMWorld +{ + class Data; + + /// Quick and dirty hashing functor. + struct StringBoolPairHash + { + size_t operator()(const std::pair& value) const noexcept + { + auto stringHash = std::hash(); + return stringHash(value.first) + value.second; + } + }; + + class ActorAdapter : public QObject + { + Q_OBJECT + public: + + // Maps body part type to 'body part' id + using ActorPartMap = std::unordered_map; + // Maps mesh part type to 'body part' id + using RacePartMap = std::unordered_map; + + ActorAdapter(CSMWorld::Data& data); + + const ActorPartMap* getActorPartMap(const std::string& refId); + + signals: + + void actorChanged(const std::string& refId); + + public slots: + + void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); + void handleRaceChanged(const QModelIndex&, const QModelIndex&); + void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + + private: + + RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); + + void updateRaceParts(const std::string& raceId); + void updateActor(const std::string& refId); + void updateNpc(const std::string& refId); + void updateCreature(const std::string& refId); + + RefIdCollection& mReferenceables; + IdCollection& mRaces; + IdCollection& mBodyParts; + + // Key: referenceable id + std::unordered_map mActorPartMaps; + // Key: race id, is female + std::unordered_map, RacePartMap, StringBoolPairHash> mRacePartMaps; + }; +} + +#endif diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 053754943e..f3f897a292 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat UniversalId::Type_Video); addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); + mActorAdapter.reset(new ActorAdapter(*this)); + mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } @@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& return iter->second; } +const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const +{ + return mActorAdapter.get(); +} + +CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() +{ + return mActorAdapter.get(); +} + void CSMWorld::Data::merge() { mGlobals.merge(); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 1b975f4308..7c4d8885ad 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -36,6 +36,7 @@ #include "../doc/stage.hpp" +#include "actoradapter.hpp" #include "idcollection.hpp" #include "nestedidcollection.hpp" #include "universalid.hpp" @@ -73,6 +74,7 @@ namespace ESM namespace CSMWorld { + class ActorAdapter; class ResourcesManager; class Resources; @@ -110,6 +112,7 @@ namespace CSMWorld RefCollection mRefs; IdCollection mFilters; Collection mMetaData; + std::unique_ptr mActorAdapter; const Fallback::Map* mFallbackMap; std::vector mModels; std::map mModelIndex; @@ -287,6 +290,10 @@ namespace CSMWorld /// \note The returned table may either be the model for the ID itself or the model that /// contains the record specified by the ID. + const ActorAdapter* getActorAdapter() const; + + ActorAdapter* getActorAdapter(); + void merge(); ///< Merge modified into base. diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 3691085e3c..b816c15040 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include "../../model/world/data.hpp" @@ -24,8 +23,8 @@ namespace CSVRender : mId(id) , mType(type) , mData(data) - , mSkeleton(nullptr) , mBaseNode(new osg::Group()) + , mSkeleton(nullptr) { } @@ -57,6 +56,7 @@ namespace CSVRender auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); + // Load skeleton with meshes std::string skeletonModel = MeshPrefix + creature.mModel; skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); loadSkeleton(skeletonModel); @@ -65,6 +65,9 @@ namespace CSVRender mSkeleton->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); + // Attach weapons + loadBodyParts(creature.mId); + // Post setup mSkeleton->markDirty(); mSkeleton->setActive(SceneUtil::Skeleton::Active); @@ -72,12 +75,8 @@ namespace CSVRender void Actor::updateNpc() { - const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; - - auto& bodyParts = mData.getBodyParts(); auto& races = mData.getRaces(); auto& referenceables = mData.getReferenceables(); - auto sceneMgr = mData.getResourceSystem()->getSceneManager(); auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); @@ -97,91 +96,8 @@ namespace CSVRender mSkeleton->accept(cleanVisitor); cleanVisitor.remove(); - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) - { - // Method to check if 1st person part or not - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; - - auto& bodyPart = record.get(); - if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) - continue; - - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); - } - } - }; - - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); - - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - { - mesh = meshResult->second; - } - else if (isFemale){ - meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - mesh = meshResult->second; - } - } - - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; - - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) - { - auto part = static_cast(i); - switch (part) - { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); - } - } + // Attach parts to skeleton + loadBodyParts(npc.mId); // Post setup mSkeleton->markDirty(); @@ -200,6 +116,37 @@ namespace CSVRender mSkeleton->addChild(temp); } mBaseNode->addChild(mSkeleton); + + // Map bone names to bones + mNodeMap.clear(); + SceneUtil::NodeMapVisitor nmVisitor(mNodeMap); + mSkeleton->accept(nmVisitor); + + } + + void Actor::loadBodyParts(const std::string& actorId) + { + auto actorAdapter = mData.getActorAdapter(); + auto partMap = actorAdapter->getActorPartMap(actorId); + if (partMap) + { + for (auto& pair : *partMap) + attachBodyPart(pair.first, getBodyPartMesh(pair.second)); + } + } + + void Actor::attachBodyPart(ESM::PartReferenceType type, const std::string& mesh) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = mNodeMap.find(boneName); + if (!mesh.empty() && node != mNodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } } std::string Actor::getBodyPartMesh(const std::string& bodyPartId) diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index b4e65ff2b6..4f809c1720 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -5,6 +5,9 @@ #include +#include +#include + namespace osg { class Group; @@ -39,10 +42,13 @@ namespace CSVRender void update(); private: - void loadSkeleton(const std::string& model); void updateCreature(); void updateNpc(); + void loadSkeleton(const std::string& model); + void loadBodyParts(const std::string& actorId); + void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); + std::string getBodyPartMesh(const std::string& bodyPartId); static const std::string MeshPrefix; @@ -51,8 +57,9 @@ namespace CSVRender int mType; CSMWorld::Data& mData; - SceneUtil::Skeleton* mSkeleton; osg::ref_ptr mBaseNode; + SceneUtil::Skeleton* mSkeleton; + SceneUtil::NodeMapVisitor::NodeMap mNodeMap; }; } From 6bece13a32ff40b69ed4d9acf380c210d00a70e8 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 21 Aug 2018 15:41:05 -0400 Subject: [PATCH 106/150] Use new Log class for error message --- apps/opencs/view/render/actor.cpp | 5 ++--- components/sceneutil/visitor.cpp | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index b816c15040..a61d31de67 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -1,10 +1,9 @@ #include "actor.hpp" -#include - #include #include +#include #include #include #include @@ -46,7 +45,7 @@ namespace CSVRender } catch (std::exception& e) { - std::cout << "Caught exception: " << e.what() << std::endl; + Log(Debug::Error) << "Exception in Actor::update(): " << e.what(); } } diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 07be1608e6..536331132d 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,12 +1,12 @@ #include "visitor.hpp" -#include - #include #include #include +#include + #include namespace SceneUtil @@ -74,7 +74,7 @@ namespace SceneUtil for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) { if (!it->second->removeChild(it->first)) - std::cerr << "error removing " << it->first->getName() << std::endl; + Log(Debug::Error) << "error removing " << it->first->getName(); } } From b2115b60e6710c07b6209e241036fc2e8e006748 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 21 Aug 2018 15:54:21 -0400 Subject: [PATCH 107/150] Fix qt4 build --- apps/opencs/model/world/actoradapter.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 2c94f9c2ca..09bd2682ba 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include From 1276e0fa9b2e2f7ac5e1a67d288c79479e536fc8 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Thu, 23 Aug 2018 22:40:43 -0400 Subject: [PATCH 108/150] Handle changes to race record when rendering actors --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/world/actoradapter.cpp | 66 ++++++++++++++++++++---- apps/opencs/model/world/actoradapter.hpp | 7 +++ apps/opencs/view/render/actor.cpp | 18 ++++++- apps/opencs/view/render/actor.hpp | 9 +++- apps/opencs/view/render/object.cpp | 7 +-- apps/opencs/view/render/object.hpp | 3 ++ 7 files changed, 98 insertions(+), 16 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 999324c51c..b0bd95eb95 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,12 +89,12 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode + cellwater terraintexturemode actor ) opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid actor + cellarrow cellmarker cellborder pathgrid ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 249908bb09..9c397879e9 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -1,7 +1,6 @@ #include "actoradapter.hpp" -#include - +#include #include #include #include @@ -45,17 +44,56 @@ namespace CSMWorld void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + // Setup + const int TypeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + + // Handle each record + for (int row = rowStart; row <= rowEnd; ++row) + { + int type = mReferenceables.getData(row, TypeColumn).toInt(); + if (type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Npc) + { + // Update the cached npc or creature + std::string refId = mReferenceables.getId(row); + if (mActorPartMaps.find(refId) != mActorPartMaps.end()) + updateActor(refId); + } + else if (type == CSMWorld::UniversalId::Type_Armor) + { + // TODO update everything? + // store all items referenced when creating map and check against that here + } + else if (type == CSMWorld::UniversalId::Type_Clothing) + { + // TODO update everything? + } + } } void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + for (int row = rowStart; row <= rowEnd; ++row) + { + std::string raceId = mRaces.getId(row); + updateNpcsWithRace(raceId); + } } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { // TODO + Log(Debug::Info) << "Body Part Changed (" << topLeft.row() << ", " << topLeft.column() << ") (" << botRight.row() << ", " << botRight.column() << ")"; + } + + QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const + { + while (index.parent().isValid()) + index = index.parent(); + return index; } ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) @@ -174,10 +212,6 @@ namespace CSMWorld npcMap.emplace(static_cast(part.mPart), bodyPartId); } } - else if (recordType == CSMWorld::UniversalId::Type_Weapon) - { - // TODO - } } } } @@ -233,10 +267,24 @@ namespace CSMWorld } mActorPartMaps[refId] = npcMap; + emit actorChanged(refId); } void ActorAdapter::updateCreature(const std::string& refId) { - // TODO + emit actorChanged(refId); + } + + void ActorAdapter::updateNpcsWithRace(const std::string& raceId) + { + for (auto it : mActorPartMaps) + { + auto& refId = it.first; + auto& npc = dynamic_cast&>(mReferenceables.getRecord(refId)).get(); + if (npc.mRace == raceId) + { + updateNpc(refId); + } + } } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 09bd2682ba..ad308fc022 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -60,6 +60,11 @@ namespace CSMWorld private: + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); void updateRaceParts(const std::string& raceId); @@ -67,6 +72,8 @@ namespace CSMWorld void updateNpc(const std::string& refId); void updateCreature(const std::string& refId); + void updateNpcsWithRace(const std::string& raceId); + RefIdCollection& mReferenceables; IdCollection& mRaces; IdCollection& mBodyParts; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index a61d31de67..6d663ad5e6 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -20,6 +20,7 @@ namespace CSVRender Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) + , mInitialized(false) , mType(type) , mData(data) , mBaseNode(new osg::Group()) @@ -45,7 +46,22 @@ namespace CSVRender } catch (std::exception& e) { - Log(Debug::Error) << "Exception in Actor::update(): " << e.what(); + Log(Debug::Info) << "Exception in Actor::update(): " << e.what(); + } + + if (!mInitialized) + { + mInitialized = true; + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&))); + } + } + + void Actor::handleActorChanged(const std::string& refId) + { + if (mId == refId) + { + Log(Debug::Info) << "Actor::actorChanged " << mId; + update(); } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 4f809c1720..42a6019ed7 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -5,6 +5,8 @@ #include +#include + #include #include @@ -26,8 +28,9 @@ namespace SceneUtil namespace CSVRender { /// Handles loading an npc or creature - class Actor + class Actor : public QObject { + Q_OBJECT public: /// Creates an actor. /// \param id The referenceable id @@ -41,6 +44,9 @@ namespace CSVRender /// (Re)creates the npc or creature renderable void update(); + private slots: + void handleActorChanged(const std::string& refId); + private: void updateCreature(); void updateNpc(); @@ -54,6 +60,7 @@ namespace CSVRender static const std::string MeshPrefix; std::string mId; + bool mInitialized; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index dbfd595b6a..63f1bc8a82 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -134,9 +134,10 @@ void CSVRender::Object::update() { if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - Actor actor(mReferenceableId, recordType, mData); - actor.update(); - mBaseNode->addChild(actor.getBaseNode()); + if (!mActor) + mActor.reset(new Actor(mReferenceableId, recordType, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); } else { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 3e54093d3e..10a46fc103 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -1,6 +1,7 @@ #ifndef OPENCS_VIEW_OBJECT_H #define OPENCS_VIEW_OBJECT_H +#include #include #include @@ -41,6 +42,7 @@ namespace CSMWorld namespace CSVRender { + class Actor; class Object; // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query @@ -98,6 +100,7 @@ namespace CSVRender osg::ref_ptr mMarker[3]; int mSubMode; float mMarkerTransparency; + std::unique_ptr mActor; /// Not implemented Object (const Object&); From 2a9ebac57225407d3ff991d81964d9e7ceeb0941 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 24 Aug 2018 02:44:02 -0400 Subject: [PATCH 109/150] Simplify update logic, update when race parts are changed. --- apps/opencs/model/world/actoradapter.cpp | 207 +++++++++++++++-------- apps/opencs/model/world/actoradapter.hpp | 35 +++- apps/opencs/view/render/actor.cpp | 6 +- 3 files changed, 168 insertions(+), 80 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 9c397879e9..4626d16424 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -24,22 +24,22 @@ namespace CSMWorld this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); } - const ActorAdapter::ActorPartMap* ActorAdapter::getActorPartMap(const std::string& refId) + const ActorAdapter::ActorPartMap* ActorAdapter::getActorParts(const std::string& refId, bool create) { - auto it = mActorPartMaps.find(refId); - if (it != mActorPartMaps.end()) + auto it = mCachedActors.find(refId); + if (it != mCachedActors.end()) { - return &it->second; + return &it->second.parts; + } + else if (create) + { + updateActor(refId); + return getActorParts(refId, false); } else { - updateActor(refId); - it = mActorPartMaps.find(refId); - if (it != mActorPartMaps.end()) - return &it->second; + return nullptr; } - - return nullptr; } void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) @@ -57,17 +57,13 @@ namespace CSMWorld { // Update the cached npc or creature std::string refId = mReferenceables.getId(row); - if (mActorPartMaps.find(refId) != mActorPartMaps.end()) + if (mCachedActors.find(refId) != mCachedActors.end()) updateActor(refId); } - else if (type == CSMWorld::UniversalId::Type_Armor) + else if (type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Clothing) { - // TODO update everything? - // store all items referenced when creating map and check against that here - } - else if (type == CSMWorld::UniversalId::Type_Clothing) - { - // TODO update everything? + std::string refId = mReferenceables.getId(row); + updateActorsWithDependency(refId); } } } @@ -79,14 +75,28 @@ namespace CSMWorld for (int row = rowStart; row <= rowEnd; ++row) { std::string raceId = mRaces.getId(row); - updateNpcsWithRace(raceId); + updateActorsWithDependency(raceId); } } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO - Log(Debug::Info) << "Body Part Changed (" << topLeft.row() << ", " << topLeft.column() << ") (" << botRight.row() << ", " << botRight.column() << ")"; + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + for (int row = rowStart; row <= rowEnd; ++row) + { + // Manually update race specified by part + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + updateRace(record.get().mRace); + } + + // Update entries with a tracked dependency + std::string partId = mBodyParts.getId(row); + updateRacesWithDependency(partId); + updateActorsWithDependency(partId); + } } QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const @@ -96,47 +106,66 @@ namespace CSMWorld return index; } - ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) + bool ActorAdapter::is1stPersonPart(const std::string& name) const { - auto key = std::make_pair(raceId, isFemale); - auto it = mRacePartMaps.find(key); - if (it != mRacePartMaps.end()) + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + } + + ActorAdapter::RaceData& ActorAdapter::getRaceData(const std::string& raceId) + { + auto it = mCachedRaces.find(raceId); + if (it != mCachedRaces.end()) { return it->second; } else { // Create and find result - updateRaceParts(raceId); - return mRacePartMaps.find(key)->second; + updateRace(raceId); + return mCachedRaces.find(raceId)->second; } } - void ActorAdapter::updateRaceParts(const std::string& raceId) + void ActorAdapter::updateRace(const std::string& raceId) { - // Convenience function to determine if part is for 1st person view - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; + // Retrieve or create cache entry + auto raceDataIt = mCachedRaces.find(raceId); + if (raceDataIt == mCachedRaces.end()) + { + auto result = mCachedRaces.emplace(raceId, RaceData()); + raceDataIt = result.first; + } - RacePartMap maleMap, femaleMap; + auto& raceData = raceDataIt->second; + raceData.femaleParts.clear(); + raceData.maleParts.clear(); + raceData.dependencies.clear(); + + // Construct entry for (int i = 0; i < mBodyParts.getSize(); ++i) { auto& record = mBodyParts.getRecord(i); - if (!record.isDeleted() && record.get().mRace == raceId && record.get().mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(record.get().mId)) + if (!record.isDeleted() && record.get().mRace == raceId) { auto& part = record.get(); - auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; - // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite - if (part.mData.mFlags & ESM::BodyPart::BPF_Female) - femaleMap.emplace(type, part.mId); - else - maleMap.emplace(type, part.mId); + + // Part could affect race data + raceData.dependencies.emplace(part.mId, true); + + // Add base types + if (part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) + { + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite + if (part.mData.mFlags & ESM::BodyPart::BPF_Female) + raceData.femaleParts.emplace(type, part.mId); + else + raceData.maleParts.emplace(type, part.mId); + } } } - mRacePartMaps[std::make_pair(raceId, true)] = femaleMap; - mRacePartMaps[std::make_pair(raceId, false)] = maleMap; + updateActorsWithDependency(raceId); } void ActorAdapter::updateActor(const std::string& refId) @@ -156,17 +185,28 @@ namespace CSMWorld void ActorAdapter::updateNpc(const std::string& refId) { auto& record = mReferenceables.getRecord(refId); + + // Retrieve record if possible if (record.isDeleted()) { - mActorPartMaps.erase(refId); + mCachedActors.erase(refId); + emit actorChanged(refId); return; } - auto& npc = dynamic_cast&>(record).get(); - auto& femaleRacePartMap = getOrCreateRacePartMap(npc.mRace, true); - auto& maleRacePartMap = getOrCreateRacePartMap(npc.mRace, false); - ActorPartMap npcMap; + // Create holder for cached data + auto actorIt = mCachedActors.find(refId); + if (actorIt == mCachedActors.end()) + { + auto result = mCachedActors.emplace(refId, ActorData()); + actorIt = result.first; + } + auto& actorData = actorIt->second; + + // Reset old data + actorData.parts.clear(); + actorData.dependencies.clear(); // Look at the npc's inventory first for (auto& item : npc.mInventory.mList) @@ -174,7 +214,7 @@ namespace CSMWorld if (item.mCount > 0) { std::string itemId = item.mItem.toString(); - // Handle armor, weapons, and clothing + // Handle armor and clothing int index = mReferenceables.searchId(itemId); if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) { @@ -184,6 +224,10 @@ namespace CSMWorld int recordType = mReferenceables.getData(index, typeColumn).toInt(); if (recordType == CSMWorld::UniversalId::Type_Armor) { + // Changes here could affect the actor + actorData.dependencies.emplace(itemId, true); + + // Add any parts if there is room auto& armor = dynamic_cast&>(itemRecord).get(); for (auto& part : armor.mParts.mParts) { @@ -194,11 +238,18 @@ namespace CSMWorld bodyPartId = part.mMale; if (!bodyPartId.empty()) - npcMap.emplace(static_cast(part.mPart), bodyPartId); + { + actorData.parts.emplace(static_cast(part.mPart), bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); + } } } else if (recordType == CSMWorld::UniversalId::Type_Clothing) { + // Changes here could affect the actor + actorData.dependencies.emplace(itemId, true); + + // Add any parts if there is room auto& clothing = dynamic_cast&>(itemRecord).get(); for (auto& part : clothing.mParts.mParts) { @@ -209,26 +260,37 @@ namespace CSMWorld bodyPartId = part.mMale; if (!bodyPartId.empty()) - npcMap.emplace(static_cast(part.mPart), bodyPartId); + { + actorData.parts.emplace(static_cast(part.mPart), bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); + } } } } } } - // Fill in the rest with body parts + // Lookup cached race parts + auto& raceData = getRaceData(npc.mRace); + + // Changes to race could affect the actor + actorData.dependencies.emplace(npc.mRace, true); + + // Fill in the rest with race specific body parts for (int i = 0; i < ESM::PRT_Count; ++i) { auto type = static_cast(i); - if (npcMap.find(type) == npcMap.end()) + if (actorData.parts.find(type) == actorData.parts.end()) { switch (type) { case ESM::PRT_Head: - npcMap.emplace(type, npc.mHead); + actorData.parts.emplace(type, npc.mHead); + actorData.dependencies.emplace(npc.mHead, true); break; case ESM::PRT_Hair: - npcMap.emplace(type, npc.mHair); + actorData.parts.emplace(type, npc.mHair); + actorData.dependencies.emplace(npc.mHair, true); break; case ESM::PRT_Skirt: case ESM::PRT_Shield: @@ -243,48 +305,57 @@ namespace CSMWorld // Check female map if applicable if (!npc.isMale()) { - auto partIt = femaleRacePartMap.find(ESM::getMeshPart(type)); - if (partIt != femaleRacePartMap.end()) + auto partIt = raceData.femaleParts.find(ESM::getMeshPart(type)); + if (partIt != raceData.femaleParts.end()) bodyPartId = partIt->second; } // Check male map next if (bodyPartId.empty() || npc.isMale()) { - auto partIt = maleRacePartMap.find(ESM::getMeshPart(type)); - if (partIt != maleRacePartMap.end()) + auto partIt = raceData.maleParts.find(ESM::getMeshPart(type)); + if (partIt != raceData.maleParts.end()) bodyPartId = partIt->second; } // Add to map if (!bodyPartId.empty()) { - npcMap.emplace(type, bodyPartId); + actorData.parts.emplace(type, bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); } } } } } - mActorPartMaps[refId] = npcMap; + // Signal change to actor emit actorChanged(refId); } void ActorAdapter::updateCreature(const std::string& refId) { + // Signal change to actor emit actorChanged(refId); } - void ActorAdapter::updateNpcsWithRace(const std::string& raceId) + void ActorAdapter::updateActorsWithDependency(const std::string& id) { - for (auto it : mActorPartMaps) + for (auto it : mCachedActors) { - auto& refId = it.first; - auto& npc = dynamic_cast&>(mReferenceables.getRecord(refId)).get(); - if (npc.mRace == raceId) - { - updateNpc(refId); - } + auto& deps = it.second.dependencies; + if (deps.find(id) != deps.end()) + updateActor(it.first); + } + } + + void ActorAdapter::updateRacesWithDependency(const std::string& id) + { + for (auto it : mCachedRaces) + { + auto& deps = it.second.dependencies; + if (deps.find(id) != deps.end()) + updateRace(it.first); } } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index ad308fc022..f1148199c0 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -41,12 +41,10 @@ namespace CSMWorld // Maps body part type to 'body part' id using ActorPartMap = std::unordered_map; - // Maps mesh part type to 'body part' id - using RacePartMap = std::unordered_map; ActorAdapter(CSMWorld::Data& data); - const ActorPartMap* getActorPartMap(const std::string& refId); + const ActorPartMap* getActorParts(const std::string& refId, bool create=true); signals: @@ -59,29 +57,48 @@ namespace CSMWorld void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); private: + // Maps mesh part type to 'body part' id + using RacePartMap = std::unordered_map; + // Stores ids that are referenced by the actor. Data part is meaningless. + using DependencyMap = std::unordered_map; + + struct ActorData + { + ActorPartMap parts; + DependencyMap dependencies; + }; + + struct RaceData + { + RacePartMap femaleParts; + RacePartMap maleParts; + DependencyMap dependencies; + }; ActorAdapter(const ActorAdapter&) = delete; ActorAdapter& operator=(const ActorAdapter&) = delete; QModelIndex getHighestIndex(QModelIndex) const; + bool is1stPersonPart(const std::string& id) const; - RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); + RaceData& getRaceData(const std::string& raceId); - void updateRaceParts(const std::string& raceId); + void updateRace(const std::string& raceId); void updateActor(const std::string& refId); void updateNpc(const std::string& refId); void updateCreature(const std::string& refId); - void updateNpcsWithRace(const std::string& raceId); + void updateActorsWithDependency(const std::string& id); + void updateRacesWithDependency(const std::string& id); RefIdCollection& mReferenceables; IdCollection& mRaces; IdCollection& mBodyParts; // Key: referenceable id - std::unordered_map mActorPartMaps; - // Key: race id, is female - std::unordered_map, RacePartMap, StringBoolPairHash> mRacePartMaps; + std::unordered_map mCachedActors; + // Key: race id + std::unordered_map mCachedRaces; }; } diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 6d663ad5e6..b9979ae2c6 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -142,10 +142,10 @@ namespace CSVRender void Actor::loadBodyParts(const std::string& actorId) { auto actorAdapter = mData.getActorAdapter(); - auto partMap = actorAdapter->getActorPartMap(actorId); - if (partMap) + auto parts = actorAdapter->getActorParts(actorId); + if (parts) { - for (auto& pair : *partMap) + for (auto& pair : *parts) attachBodyPart(pair.first, getBodyPartMesh(pair.second)); } } From 031502b2ab80216d9dc4ef475a1ad6cb9ad2092e Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 26 Aug 2018 01:13:50 -0400 Subject: [PATCH 110/150] Reorganize ActorAdapter data, use weak cache for sharing --- apps/opencs/model/world/actoradapter.cpp | 715 +++++++++++++++-------- apps/opencs/model/world/actoradapter.hpp | 187 ++++-- apps/opencs/view/render/actor.cpp | 14 +- apps/opencs/view/render/actor.hpp | 3 + components/CMakeLists.txt | 4 + components/cache/weakcache.hpp | 116 ++++ 6 files changed, 714 insertions(+), 325 deletions(-) create mode 100644 components/cache/weakcache.hpp diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 4626d16424..b3a1216f75 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -11,72 +11,290 @@ namespace CSMWorld { - ActorAdapter::ActorAdapter(CSMWorld::Data& data) + const std::string& ActorAdapter::RaceData::getId() const + { + return mId; + } + + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const + { + switch (type) + { + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + return false; + default: + return true; + } + } + + const std::string& ActorAdapter::RaceData::getFemalePart(ESM::PartReferenceType index) const + { + return mFemaleParts[ESM::getMeshPart(index)]; + } + + const std::string& ActorAdapter::RaceData::getMalePart(ESM::PartReferenceType index) const + { + return mMaleParts[ESM::getMeshPart(index)]; + } + + bool ActorAdapter::RaceData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::RaceData::setFemalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mFemaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::setMalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mMaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::RaceData::reset(const std::string& id) + { + mId = id; + for (auto& str : mFemaleParts) + str.clear(); + for (auto& str : mMaleParts) + str.clear(); + mDependencies.clear(); + + // Mark self as a dependency + addOtherDependency(id); + } + + + const std::string& ActorAdapter::ActorData::getId() const + { + return mId; + } + + bool ActorAdapter::ActorData::isFemale() const + { + return mFemale; + } + + const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + { + if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) + { + return mFemale ? mRaceData->getFemalePart(index) : mRaceData->getMalePart(index); + } + return mParts[index]; + } + + bool ActorAdapter::ActorData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId) + { + mParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::ActorData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::ActorData::reset(const std::string& id, bool isFemale, RaceDataPtr raceData) + { + mId = id; + mFemale = isFemale; + mRaceData = raceData; + for (auto& str : mParts) + str.clear(); + mDependencies.clear(); + + // Mark self and race as a dependency + addOtherDependency(id); + if (raceData) addOtherDependency(raceData->getId()); + } + + + ActorAdapter::ActorAdapter(Data& data) : mReferenceables(data.getReferenceables()) , mRaces(data.getRaces()) , mBodyParts(data.getBodyParts()) { - connect(data.getTableModel(UniversalId::Type_Referenceable), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + // Setup qt slots and signals + QAbstractItemModel* refModel = data.getTableModel(UniversalId::Type_Referenceable); + connect(refModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesInserted(const QModelIndex&, int, int))); + connect(refModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&))); - connect(data.getTableModel(UniversalId::Type_Race), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + connect(refModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* raceModel = data.getTableModel(UniversalId::Type_Race); + connect(raceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + connect(raceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&))); - connect(data.getTableModel(UniversalId::Type_BodyPart), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + connect(raceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* partModel = data.getTableModel(UniversalId::Type_BodyPart); + connect(partModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsInserted(const QModelIndex&, int, int))); + connect(partModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); + connect(partModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int))); } - const ActorAdapter::ActorPartMap* ActorAdapter::getActorParts(const std::string& refId, bool create) + ActorAdapter::ActorDataPtr ActorAdapter::getActorData(const std::string& id) { - auto it = mCachedActors.find(refId); - if (it != mCachedActors.end()) + // Return cached actor data if it exists + ActorDataPtr data = mCachedActors.get(id); + if (data) { - return &it->second.parts; + return data; } - else if (create) + + // Create the actor data + data.reset(new ActorData()); + setupActor(id, data); + mCachedActors.insert(id, data); + return data; + } + + void ActorAdapter::handleReferenceablesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top level are pertinent. Others are caught by dataChanged handler. + if (!parent.isValid()) { - updateActor(refId); - return getActorParts(refId, false); - } - else - { - return nullptr; + for (int row = start; row <= end; ++row) + { + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } } + + // Update affected + updateDirty(); } void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // Setup - const int TypeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); // Handle each record - for (int row = rowStart; row <= rowEnd; ++row) + for (int row = start; row <= end; ++row) { - int type = mReferenceables.getData(row, TypeColumn).toInt(); - if (type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Npc) - { - // Update the cached npc or creature - std::string refId = mReferenceables.getId(row); - if (mCachedActors.find(refId) != mCachedActors.end()) - updateActor(refId); - } - else if (type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Clothing) + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleReferenceablesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only rows at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) { std::string refId = mReferenceables.getId(row); - updateActorsWithDependency(refId); + markDirtyDependency(refId); } } } + void ActorAdapter::handleReferenceablesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleReferenceablesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleRacesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mReferenceables.getId(row); + markDirtyDependency(raceId); + } + } + + // Update affected + updateDirty(); + } + void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); - for (int row = rowStart; row <= rowEnd; ++row) + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + for (int row = start; row <= end; ++row) { std::string raceId = mRaces.getId(row); - updateActorsWithDependency(raceId); + markDirtyDependency(raceId); } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleRacesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mRaces.getId(row); + markDirtyDependency(raceId); + } + } + } + + void ActorAdapter::handleRacesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleRacesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleBodyPartsInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + // Race specified by part may need update + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + markDirtyDependency(record.get().mRace); + } + + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + + // Update affected + updateDirty(); } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) @@ -85,18 +303,39 @@ namespace CSMWorld int rowEnd = getHighestIndex(botRight).row(); for (int row = rowStart; row <= rowEnd; ++row) { - // Manually update race specified by part + // Race specified by part may need update auto& record = mBodyParts.getRecord(row); if (!record.isDeleted()) { - updateRace(record.get().mRace); + markDirtyDependency(record.get().mRace); } // Update entries with a tracked dependency std::string partId = mBodyParts.getId(row); - updateRacesWithDependency(partId); - updateActorsWithDependency(partId); + markDirtyDependency(partId); } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleBodyPartsAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + } + + void ActorAdapter::handleBodyPartsRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleBodyPartsAboutToBeRemoved + updateDirty(); } QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const @@ -111,251 +350,223 @@ namespace CSMWorld return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; } - ActorAdapter::RaceData& ActorAdapter::getRaceData(const std::string& raceId) + ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const std::string& id) { - auto it = mCachedRaces.find(raceId); - if (it != mCachedRaces.end()) + // Return cached race data if it exists + RaceDataPtr data = mCachedRaces.get(id); + if (data) return data; + + // Create the race data + data.reset(new RaceData()); + setupRace(id, data); + mCachedRaces.insert(id, data); + return data; + } + + void ActorAdapter::setupActor(const std::string& id, ActorDataPtr data) + { + int index = mReferenceables.searchId(id); + if (index == -1) { - return it->second; + // Record does not exist + data->reset(id); + emit actorChanged(id); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Record is deleted and therefore not accessible + data->reset(id); + emit actorChanged(id); + return; + } + + const int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Creature) + { + // Valid creature record + setupCreature(id, data); + emit actorChanged(id); + } + else if (type == UniversalId::Type_Npc) + { + // Valid npc record + setupNpc(id, data); + emit actorChanged(id); } else { - // Create and find result - updateRace(raceId); - return mCachedRaces.find(raceId)->second; + // Wrong record type + data->reset(id); + emit actorChanged(id); } } - void ActorAdapter::updateRace(const std::string& raceId) + void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data) { - // Retrieve or create cache entry - auto raceDataIt = mCachedRaces.find(raceId); - if (raceDataIt == mCachedRaces.end()) + // Common setup + data->reset(id); + + int index = mRaces.searchId(id); + if (index == -1) { - auto result = mCachedRaces.emplace(raceId, RaceData()); - raceDataIt = result.first; - } - - auto& raceData = raceDataIt->second; - raceData.femaleParts.clear(); - raceData.maleParts.clear(); - raceData.dependencies.clear(); - - // Construct entry - for (int i = 0; i < mBodyParts.getSize(); ++i) - { - auto& record = mBodyParts.getRecord(i); - if (!record.isDeleted() && record.get().mRace == raceId) - { - auto& part = record.get(); - - // Part could affect race data - raceData.dependencies.emplace(part.mId, true); - - // Add base types - if (part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) - { - auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; - // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite - if (part.mData.mFlags & ESM::BodyPart::BPF_Female) - raceData.femaleParts.emplace(type, part.mId); - else - raceData.maleParts.emplace(type, part.mId); - } - } - } - - updateActorsWithDependency(raceId); - } - - void ActorAdapter::updateActor(const std::string& refId) - { - int index = mReferenceables.searchId(refId); - if (index != -1) - { - int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int recordType = mReferenceables.getData(index, typeColumn).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Creature) - updateCreature(refId); - else if (recordType == CSMWorld::UniversalId::Type_Npc) - updateNpc(refId); - } - } - - void ActorAdapter::updateNpc(const std::string& refId) - { - auto& record = mReferenceables.getRecord(refId); - - // Retrieve record if possible - if (record.isDeleted()) - { - mCachedActors.erase(refId); - emit actorChanged(refId); + // Record does not exist return; } - auto& npc = dynamic_cast&>(record).get(); - // Create holder for cached data - auto actorIt = mCachedActors.find(refId); - if (actorIt == mCachedActors.end()) + auto& raceRecord = mRaces.getRecord(index); + if (raceRecord.isDeleted()) { - auto result = mCachedActors.emplace(refId, ActorData()); - actorIt = result.first; + // Record is deleted, so not accessible + return; } - auto& actorData = actorIt->second; - // Reset old data - actorData.parts.clear(); - actorData.dependencies.clear(); + // TODO move stuff in actor related to race here - // Look at the npc's inventory first + // Setup body parts + for (int i = 0; i < mBodyParts.getSize(); ++i) + { + std::string partId = mBodyParts.getId(i); + auto& partRecord = mBodyParts.getRecord(i); + + if (partRecord.isDeleted()) + { + // Record is deleted, so not accessible. + continue; + } + + auto& part = partRecord.get(); + if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) + { + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female; + if (female) data->setFemalePart(type, part.mId); + else data->setMalePart(type, part.mId); + } + } + } + + void ActorAdapter::setupNpc(const std::string& id, ActorDataPtr data) + { + // Common setup, record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& npc = dynamic_cast&>(mReferenceables.getRecord(index)).get(); + + RaceDataPtr raceData = getRaceData(npc.mRace); + data->reset(id, !npc.isMale(), raceData); + + // Add inventory items for (auto& item : npc.mInventory.mList) { - if (item.mCount > 0) + if (item.mCount <= 0) continue; + std::string itemId = item.mItem.toString(); + addNpcItem(itemId, data); + } + + // Add head and hair + data->setPart(ESM::PRT_Head, npc.mHead); + data->setPart(ESM::PRT_Hair, npc.mHair); + } + + void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data) + { + int index = mReferenceables.searchId(itemId); + if (index == -1) + { + // Item does not exist yet + data->addOtherDependency(itemId); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Item cannot be accessed yet + data->addOtherDependency(itemId); + return; + } + + // Convenience function to add a parts list to actor data + auto addParts = [&](const ESM::PartReferenceList& list) { + for (auto& part : list.mParts) { - std::string itemId = item.mItem.toString(); - // Handle armor and clothing - int index = mReferenceables.searchId(itemId); - if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) - { - auto& itemRecord = mReferenceables.getRecord(index); + std::string partId; + auto partType = (ESM::PartReferenceType) part.mPart; - int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int recordType = mReferenceables.getData(index, typeColumn).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Armor) - { - // Changes here could affect the actor - actorData.dependencies.emplace(itemId, true); + if (data->isFemale()) + partId = part.mFemale; + if (partId.empty()) + partId = part.mMale; - // Add any parts if there is room - auto& armor = dynamic_cast&>(itemRecord).get(); - for (auto& part : armor.mParts.mParts) - { - std::string bodyPartId; - if (!npc.isMale()) - bodyPartId = part.mFemale; - if (bodyPartId.empty()) - bodyPartId = part.mMale; + if (!partId.empty()) data->setPart(partType, partId); + } + }; - if (!bodyPartId.empty()) - { - actorData.parts.emplace(static_cast(part.mPart), bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } - else if (recordType == CSMWorld::UniversalId::Type_Clothing) - { - // Changes here could affect the actor - actorData.dependencies.emplace(itemId, true); + int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Armor) + { + auto& armor = dynamic_cast&>(record).get(); + addParts(armor.mParts); - // Add any parts if there is room - auto& clothing = dynamic_cast&>(itemRecord).get(); - for (auto& part : clothing.mParts.mParts) - { - std::string bodyPartId; - if (!npc.isMale()) - bodyPartId = part.mFemale; - if (bodyPartId.empty()) - bodyPartId = part.mMale; + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + else if (type == UniversalId::Type_Clothing) + { + auto& clothing = dynamic_cast&>(record).get(); + addParts(clothing.mParts); - if (!bodyPartId.empty()) - { - actorData.parts.emplace(static_cast(part.mPart), bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } - } + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + } + + void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data) + { + data->reset(id); + + // TODO move stuff from Actor here + } + + void ActorAdapter::markDirtyDependency(const std::string& dep) + { + for (auto raceIt : mCachedRaces) + { + if (raceIt->hasDependency(dep)) + mDirtyRaces.emplace(raceIt->getId()); + } + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(dep)) + mDirtyActors.emplace(actorIt->getId()); + } + } + + void ActorAdapter::updateDirty() + { + // Handle races before actors, since actors are dependent on race + for (auto& race : mDirtyRaces) + { + RaceDataPtr data = mCachedRaces.get(race); + if (data) + { + setupRace(race, data); } } + mDirtyRaces.clear(); - // Lookup cached race parts - auto& raceData = getRaceData(npc.mRace); - - // Changes to race could affect the actor - actorData.dependencies.emplace(npc.mRace, true); - - // Fill in the rest with race specific body parts - for (int i = 0; i < ESM::PRT_Count; ++i) + for (auto& actor : mDirtyActors) { - auto type = static_cast(i); - if (actorData.parts.find(type) == actorData.parts.end()) + ActorDataPtr data = mCachedActors.get(actor); + if (data) { - switch (type) - { - case ESM::PRT_Head: - actorData.parts.emplace(type, npc.mHead); - actorData.dependencies.emplace(npc.mHead, true); - break; - case ESM::PRT_Hair: - actorData.parts.emplace(type, npc.mHair); - actorData.dependencies.emplace(npc.mHair, true); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part associated - break; - default: - { - std::string bodyPartId; - // Check female map if applicable - if (!npc.isMale()) - { - auto partIt = raceData.femaleParts.find(ESM::getMeshPart(type)); - if (partIt != raceData.femaleParts.end()) - bodyPartId = partIt->second; - } - - // Check male map next - if (bodyPartId.empty() || npc.isMale()) - { - auto partIt = raceData.maleParts.find(ESM::getMeshPart(type)); - if (partIt != raceData.maleParts.end()) - bodyPartId = partIt->second; - } - - // Add to map - if (!bodyPartId.empty()) - { - actorData.parts.emplace(type, bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } + setupActor(actor, data); } } - - // Signal change to actor - emit actorChanged(refId); - } - - void ActorAdapter::updateCreature(const std::string& refId) - { - // Signal change to actor - emit actorChanged(refId); - } - - void ActorAdapter::updateActorsWithDependency(const std::string& id) - { - for (auto it : mCachedActors) - { - auto& deps = it.second.dependencies; - if (deps.find(id) != deps.end()) - updateActor(it.first); - } - } - - void ActorAdapter::updateRacesWithDependency(const std::string& id) - { - for (auto it : mCachedRaces) - { - auto& deps = it.second.dependencies; - if (deps.find(id) != deps.end()) - updateRace(it.first); - } + mDirtyActors.clear(); } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index f1148199c0..0e8b0c9fed 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -1,13 +1,13 @@ #ifndef CSM_WOLRD_ACTORADAPTER_H #define CSM_WOLRD_ACTORADAPTER_H -#include -#include -#include +#include +#include #include #include +#include #include #include @@ -17,88 +17,147 @@ namespace ESM { struct Race; - enum PartReferenceType; } namespace CSMWorld { class Data; - /// Quick and dirty hashing functor. - struct StringBoolPairHash - { - size_t operator()(const std::pair& value) const noexcept - { - auto stringHash = std::hash(); - return stringHash(value.first) + value.second; - } - }; - + /// Adapts multiple collections to provide the data needed to render + /// an npc or creature. class ActorAdapter : public QObject { - Q_OBJECT + Q_OBJECT + public: + + /// A list indexed by ESM::PartReferenceType + using ActorPartList = std::array; + /// A list indexed by ESM::BodyPart::MeshPart + using RacePartList = std::array; + /// Tracks unique strings + using StringSet = std::unordered_set; + + + /// Contains base race data shared between actors + class RaceData + { public: + /// Retrieves the id of the race represented + const std::string& getId() const; + /// Checks if a part could exist for the given type + bool handlesPart(ESM::PartReferenceType type) const; + /// Retrieves the associated body part + const std::string& getFemalePart(ESM::PartReferenceType index) const; + /// Retrieves the associated body part + const std::string& getMalePart(ESM::PartReferenceType index) const; + /// Checks if the race has a data dependency + bool hasDependency(const std::string& id) const; - // Maps body part type to 'body part' id - using ActorPartMap = std::unordered_map; - - ActorAdapter(CSMWorld::Data& data); - - const ActorPartMap* getActorParts(const std::string& refId, bool create=true); - - signals: - - void actorChanged(const std::string& refId); - - public slots: - - void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); - void handleRaceChanged(const QModelIndex&, const QModelIndex&); - void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + /// Sets the associated part if it's empty and marks a dependency + void setFemalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Sets the associated part if it's empty and marks a dependency + void setMalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Marks an additional dependency + void addOtherDependency(const std::string& id); + /// Clears parts and dependencies + void reset(const std::string& raceId); private: - // Maps mesh part type to 'body part' id - using RacePartMap = std::unordered_map; - // Stores ids that are referenced by the actor. Data part is meaningless. - using DependencyMap = std::unordered_map; + bool handles(ESM::PartReferenceType type) const; + std::string mId; + RacePartList mFemaleParts; + RacePartList mMaleParts; + StringSet mDependencies; + }; + using RaceDataPtr = std::shared_ptr; - struct ActorData - { - ActorPartMap parts; - DependencyMap dependencies; - }; + /// Contains all the data needed to render an actor. Tracks dependencies + /// so that pertinent data changes can be checked. + class ActorData + { + public: + /// Retrieves the id of the actor represented + const std::string& getId() const; + /// Checks if the actor is female + bool isFemale() const; + /// Retrieves the associated actor part + const std::string& getPart(ESM::PartReferenceType index) const; + /// Checks if the actor has a data dependency + bool hasDependency(const std::string& id) const; - struct RaceData - { - RacePartMap femaleParts; - RacePartMap maleParts; - DependencyMap dependencies; - }; + /// Sets the actor part used and marks a dependency + void setPart(ESM::PartReferenceType partIndex, const std::string& partId); + /// Marks an additional dependency for the actor + void addOtherDependency(const std::string& id); + /// Clears race, parts, and dependencies + void reset(const std::string& actorId, bool female=true, RaceDataPtr raceData=nullptr); - ActorAdapter(const ActorAdapter&) = delete; - ActorAdapter& operator=(const ActorAdapter&) = delete; + private: + std::string mId; + bool mFemale; + RaceDataPtr mRaceData; + ActorPartList mParts; + StringSet mDependencies; + }; + using ActorDataPtr = std::shared_ptr; - QModelIndex getHighestIndex(QModelIndex) const; - bool is1stPersonPart(const std::string& id) const; - RaceData& getRaceData(const std::string& raceId); + ActorAdapter(Data& data); - void updateRace(const std::string& raceId); - void updateActor(const std::string& refId); - void updateNpc(const std::string& refId); - void updateCreature(const std::string& refId); + /// Obtains the shared data for a given actor + ActorDataPtr getActorData(const std::string& refId); - void updateActorsWithDependency(const std::string& id); - void updateRacesWithDependency(const std::string& id); + signals: - RefIdCollection& mReferenceables; - IdCollection& mRaces; - IdCollection& mBodyParts; + void actorChanged(const std::string& refId); - // Key: referenceable id - std::unordered_map mCachedActors; - // Key: race id - std::unordered_map mCachedRaces; + public slots: + + void handleReferenceablesInserted(const QModelIndex&, int, int); + void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); + void handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int); + void handleReferenceablesRemoved(const QModelIndex&, int, int); + + void handleRacesInserted(const QModelIndex&, int, int); + void handleRaceChanged(const QModelIndex&, const QModelIndex&); + void handleRacesAboutToBeRemoved(const QModelIndex&, int, int); + void handleRacesRemoved(const QModelIndex&, int, int); + + void handleBodyPartsInserted(const QModelIndex&, int, int); + void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + void handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int); + void handleBodyPartsRemoved(const QModelIndex&, int, int); + + private: + + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + bool is1stPersonPart(const std::string& id) const; + + RaceDataPtr getRaceData(const std::string& raceId); + + void setupActor(const std::string& id, ActorDataPtr data); + void setupRace(const std::string& id, RaceDataPtr data); + + void setupNpc(const std::string& id, ActorDataPtr data); + void addNpcItem(const std::string& itemId, ActorDataPtr data); + + void setupCreature(const std::string& id, ActorDataPtr data); + + void markDirtyDependency(const std::string& dependency); + void updateDirty(); + + RefIdCollection& mReferenceables; + IdCollection& mRaces; + IdCollection& mBodyParts; + + cache::WeakCache mCachedActors; // Key: referenceable id + cache::WeakCache mCachedRaces; // Key: race id + + StringSet mDirtyActors; // Actors that need updating + StringSet mDirtyRaces; // Races that need updating }; } diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index b9979ae2c6..0a2519d0dd 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -26,6 +26,7 @@ namespace CSVRender , mBaseNode(new osg::Group()) , mSkeleton(nullptr) { + mActorData = mData.getActorAdapter()->getActorData(mId); } osg::Group* Actor::getBaseNode() @@ -60,7 +61,6 @@ namespace CSVRender { if (mId == refId) { - Log(Debug::Info) << "Actor::actorChanged " << mId; update(); } } @@ -80,9 +80,6 @@ namespace CSVRender mSkeleton->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); - // Attach weapons - loadBodyParts(creature.mId); - // Post setup mSkeleton->markDirty(); mSkeleton->setActive(SceneUtil::Skeleton::Active); @@ -141,12 +138,11 @@ namespace CSVRender void Actor::loadBodyParts(const std::string& actorId) { - auto actorAdapter = mData.getActorAdapter(); - auto parts = actorAdapter->getActorParts(actorId); - if (parts) + for (int i = 0; i < ESM::PRT_Count; ++i) { - for (auto& pair : *parts) - attachBodyPart(pair.first, getBodyPartMesh(pair.second)); + auto type = (ESM::PartReferenceType) i; + std::string partId = mActorData->getPart(type); + attachBodyPart(type, getBodyPartMesh(partId)); } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 42a6019ed7..d7cbb30672 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -10,6 +10,8 @@ #include #include +#include "../../model/world/actoradapter.hpp" + namespace osg { class Group; @@ -63,6 +65,7 @@ namespace CSVRender bool mInitialized; int mType; CSMWorld::Data& mData; + CSMWorld::ActorAdapter::ActorDataPtr mActorData; osg::ref_ptr mBaseNode; SceneUtil::Skeleton* mSkeleton; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7af76137c5..494e1c5ce8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -150,6 +150,10 @@ add_component_dir (fallback fallback validate ) +add_component_dir(cache + weakcache + ) + if(NOT WIN32 AND NOT ANDROID) add_component_dir (crashcatcher crashcatcher diff --git a/components/cache/weakcache.hpp b/components/cache/weakcache.hpp new file mode 100644 index 0000000000..9d940cd746 --- /dev/null +++ b/components/cache/weakcache.hpp @@ -0,0 +1,116 @@ +#ifndef OPENMW_COMPONENTS_WEAKCACHE_HPP +#define OPENMW_COMPONENTS_WEAKCACHE_HPP + +#include +#include + +namespace cache +{ + /// \class WeakCache + /// Provides a container to weakly store pointers to shared data. + template + class WeakCache + { + public: + using WeakPtr = std::weak_ptr; + using StrongPtr = std::shared_ptr; + using Map = std::unordered_map; + + class iterator + { + public: + iterator(typename Map::iterator current, typename Map::iterator end); + iterator& operator++(); + bool operator==(const iterator& other); + bool operator!=(const iterator& other); + StrongPtr operator*(); + private: + typename Map::iterator mCurrent, mEnd; + StrongPtr mPtr; + }; + + /// Stores a weak pointer to the item. + void insert(Key key, StrongPtr value); + + /// Retrieves the item associated with the key. + /// \return An item or null. + StrongPtr get(Key key); + + iterator begin(); + iterator end(); + + private: + Map mData; + }; + + + template + WeakCache::iterator::iterator(typename Map::iterator current, typename Map::iterator end) + : mCurrent(current) + , mEnd(end) + { + // Move to 1st available valid item + for ( ; mCurrent != mEnd; ++mCurrent) + { + mPtr = mCurrent->second.lock(); + if (mPtr) break; + } + } + + template + typename WeakCache::iterator& WeakCache::iterator::operator++() + { + auto next = mCurrent; + ++next; + return *this = iterator(next, mEnd); + } + + template + bool WeakCache::iterator::operator==(const iterator& other) + { + return mCurrent == other.mCurrent; + } + + template + bool WeakCache::iterator::operator!=(const iterator& other) + { + return !(*this == other); + } + + template + typename WeakCache::StrongPtr WeakCache::iterator::operator*() + { + return mPtr; + } + + + template + void WeakCache::insert(Key key, StrongPtr value) + { + mData[key] = WeakPtr(value); + } + + template + typename WeakCache::StrongPtr WeakCache::get(Key key) + { + auto searchIt = mData.find(key); + if (searchIt != mData.end()) + return searchIt->second.lock(); + else + return StrongPtr(); + } + + template + typename WeakCache::iterator WeakCache::begin() + { + return iterator(mData.begin(), mData.end()); + } + + template + typename WeakCache::iterator WeakCache::end() + { + return iterator(mData.end(), mData.end()); + } +} + +#endif From 1518d630caa7068e2cf405120a65680bd08ac3e5 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 7 Sep 2018 22:00:02 -0400 Subject: [PATCH 111/150] Fix issue with body part events not propogating to actors --- apps/opencs/model/world/actoradapter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index b3a1216f75..6f564bac53 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -555,6 +555,14 @@ namespace CSMWorld if (data) { setupRace(race, data); + // Race was changed. Need to mark actor dependencies as dirty. + // Cannot use markDirtyDependency because that would invalidate + // the current iterator. + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(race)) + mDirtyActors.emplace(actorIt->getId()); + } } } mDirtyRaces.clear(); From c1ec926f4327c46dc98003ff90ecaf9205c665bf Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 7 Sep 2018 23:38:11 -0400 Subject: [PATCH 112/150] Workaround inconsistencies with record status changes --- apps/opencs/model/world/actoradapter.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 6f564bac53..d081f8a3ef 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -195,6 +195,10 @@ namespace CSMWorld int start = getHighestIndex(topLeft).row(); int end = getHighestIndex(botRight).row(); + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + // Handle each record for (int row = start; row <= end; ++row) { @@ -245,6 +249,11 @@ namespace CSMWorld { int start = getHighestIndex(topLeft).row(); int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + for (int row = start; row <= end; ++row) { std::string raceId = mRaces.getId(row); @@ -299,9 +308,14 @@ namespace CSMWorld void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); - for (int row = rowStart; row <= rowEnd; ++row) + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + + for (int row = start; row <= end; ++row) { // Race specified by part may need update auto& record = mBodyParts.getRecord(row); From f43b70d77be6a21015c93db5076edcc118594c53 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:06:48 -0400 Subject: [PATCH 113/150] Centralize actor data, simplify logic --- apps/opencs/model/world/actoradapter.cpp | 52 ++++++++++--- apps/opencs/model/world/actoradapter.hpp | 13 +++- apps/opencs/view/render/actor.cpp | 97 +++++++----------------- apps/opencs/view/render/actor.hpp | 9 +-- apps/opencs/view/render/object.cpp | 88 +++++++++------------ 5 files changed, 116 insertions(+), 143 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index d081f8a3ef..47bc5f4da4 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "data.hpp" @@ -16,6 +17,11 @@ namespace CSMWorld return mId; } + bool ActorAdapter::RaceData::isBeast() const + { + return mIsBeast; + } + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const { switch (type) @@ -63,9 +69,10 @@ namespace CSMWorld if (!id.empty()) mDependencies.emplace(id); } - void ActorAdapter::RaceData::reset(const std::string& id) + void ActorAdapter::RaceData::reset_data(const std::string& id, bool isBeast) { mId = id; + mIsBeast = isBeast; for (auto& str : mFemaleParts) str.clear(); for (auto& str : mMaleParts) @@ -82,11 +89,28 @@ namespace CSMWorld return mId; } + bool ActorAdapter::ActorData::isCreature() const + { + return mCreature; + } + bool ActorAdapter::ActorData::isFemale() const { return mFemale; } + std::string ActorAdapter::ActorData::getSkeleton() const + { + if (mCreature || !mSkeletonOverride.empty()) + return "meshes\\" + mSkeletonOverride; + + bool firstPerson = false; + bool beast = mRaceData ? mRaceData->isBeast() : false; + bool werewolf = false; + + return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); + } + const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) @@ -112,10 +136,12 @@ namespace CSMWorld if (!id.empty()) mDependencies.emplace(id); } - void ActorAdapter::ActorData::reset(const std::string& id, bool isFemale, RaceDataPtr raceData) + void ActorAdapter::ActorData::reset_data(const std::string& id, const std::string& skeleton, bool isCreature, bool isFemale, RaceDataPtr raceData) { mId = id; + mCreature = isCreature; mFemale = isFemale; + mSkeletonOverride = skeleton; mRaceData = raceData; for (auto& str : mParts) str.clear(); @@ -383,7 +409,7 @@ namespace CSMWorld if (index == -1) { // Record does not exist - data->reset(id); + data->reset_data(id); emit actorChanged(id); return; } @@ -392,7 +418,7 @@ namespace CSMWorld if (record.isDeleted()) { // Record is deleted and therefore not accessible - data->reset(id); + data->reset_data(id); emit actorChanged(id); return; } @@ -414,20 +440,18 @@ namespace CSMWorld else { // Wrong record type - data->reset(id); + data->reset_data(id); emit actorChanged(id); } } void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data) { - // Common setup - data->reset(id); - int index = mRaces.searchId(id); if (index == -1) { // Record does not exist + data->reset_data(id); return; } @@ -435,10 +459,12 @@ namespace CSMWorld if (raceRecord.isDeleted()) { // Record is deleted, so not accessible + data->reset_data(id); return; } - // TODO move stuff in actor related to race here + auto& race = raceRecord.get(); + data->reset_data(id, race.mData.mFlags & ESM::Race::Beast); // Setup body parts for (int i = 0; i < mBodyParts.getSize(); ++i) @@ -470,7 +496,7 @@ namespace CSMWorld auto& npc = dynamic_cast&>(mReferenceables.getRecord(index)).get(); RaceDataPtr raceData = getRaceData(npc.mRace); - data->reset(id, !npc.isMale(), raceData); + data->reset_data(id, "", false, !npc.isMale(), raceData); // Add inventory items for (auto& item : npc.mInventory.mList) @@ -541,9 +567,11 @@ namespace CSMWorld void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data) { - data->reset(id); + // Record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& creature = dynamic_cast&>(mReferenceables.getRecord(index)).get(); - // TODO move stuff from Actor here + data->reset_data(id, creature.mModel, true); } void ActorAdapter::markDirtyDependency(const std::string& dep) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 0e8b0c9fed..ba714e8402 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -44,6 +44,8 @@ namespace CSMWorld public: /// Retrieves the id of the race represented const std::string& getId() const; + /// Checks if it's a beast race + bool isBeast() const; /// Checks if a part could exist for the given type bool handlesPart(ESM::PartReferenceType type) const; /// Retrieves the associated body part @@ -60,11 +62,12 @@ namespace CSMWorld /// Marks an additional dependency void addOtherDependency(const std::string& id); /// Clears parts and dependencies - void reset(const std::string& raceId); + void reset_data(const std::string& raceId, bool isBeast=false); private: bool handles(ESM::PartReferenceType type) const; std::string mId; + bool mIsBeast; RacePartList mFemaleParts; RacePartList mMaleParts; StringSet mDependencies; @@ -78,8 +81,12 @@ namespace CSMWorld public: /// Retrieves the id of the actor represented const std::string& getId() const; + /// Checks if the actor is a creature + bool isCreature() const; /// Checks if the actor is female bool isFemale() const; + /// Returns the skeleton the actor should use for attaching parts to + std::string getSkeleton() const; /// Retrieves the associated actor part const std::string& getPart(ESM::PartReferenceType index) const; /// Checks if the actor has a data dependency @@ -90,11 +97,13 @@ namespace CSMWorld /// Marks an additional dependency for the actor void addOtherDependency(const std::string& id); /// Clears race, parts, and dependencies - void reset(const std::string& actorId, bool female=true, RaceDataPtr raceData=nullptr); + void reset_data(const std::string& actorId, const std::string& skeleton="", bool isCreature=false, bool female=true, RaceDataPtr raceData=nullptr); private: std::string mId; + bool mCreature; bool mFemale; + std::string mSkeletonOverride; RaceDataPtr mRaceData; ActorPartList mParts; StringSet mDependencies; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 0a2519d0dd..7238166fc1 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -18,15 +18,15 @@ namespace CSVRender { const std::string Actor::MeshPrefix = "meshes\\"; - Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) + Actor::Actor(const std::string& id, CSMWorld::Data& data) : mId(id) - , mInitialized(false) - , mType(type) , mData(data) , mBaseNode(new osg::Group()) , mSkeleton(nullptr) { mActorData = mData.getActorAdapter()->getActorData(mId); + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), + this, SLOT(handleActorChanged(const std::string&))); } osg::Group* Actor::getBaseNode() @@ -36,25 +36,33 @@ namespace CSVRender void Actor::update() { - try - { - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - if (mType == CSMWorld::UniversalId::Type_Npc) - updateNpc(); - else if (mType == CSMWorld::UniversalId::Type_Creature) - updateCreature(); - } - catch (std::exception& e) + // Load skeleton + std::string skeletonModel = mActorData->getSkeleton(); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + if (!mActorData->isCreature()) { - Log(Debug::Info) << "Exception in Actor::update(): " << e.what(); + // Get rid of the extra attachments + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Attach parts to skeleton + loadBodyParts(); + } + else + { + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); } - if (!mInitialized) - { - mInitialized = true; - connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&))); - } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); } void Actor::handleActorChanged(const std::string& refId) @@ -65,57 +73,6 @@ namespace CSVRender } } - void Actor::updateCreature() - { - auto& referenceables = mData.getReferenceables(); - - auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); - - // Load skeleton with meshes - std::string skeletonModel = MeshPrefix + creature.mModel; - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); - loadSkeleton(skeletonModel); - - SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; - mSkeleton->accept(removeTriBipVisitor); - removeTriBipVisitor.remove(); - - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); - } - - void Actor::updateNpc() - { - auto& races = mData.getRaces(); - auto& referenceables = mData.getReferenceables(); - - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - - bool is1stPerson = false; - bool isFemale = !npc.isMale(); - bool isBeast = race.mData.mFlags & ESM::Race::Beast; - bool isWerewolf = false; - - // Load skeleton - std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); - loadSkeleton(skeletonModel); - - // Get rid of the extra attachments - SceneUtil::CleanObjectRootVisitor cleanVisitor; - mSkeleton->accept(cleanVisitor); - cleanVisitor.remove(); - - // Attach parts to skeleton - loadBodyParts(npc.mId); - - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); - } - void Actor::loadSkeleton(const std::string& model) { auto sceneMgr = mData.getResourceSystem()->getSceneManager(); @@ -136,7 +93,7 @@ namespace CSVRender } - void Actor::loadBodyParts(const std::string& actorId) + void Actor::loadBodyParts() { for (int i = 0; i < ESM::PRT_Count; ++i) { diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index d7cbb30672..2f19454f78 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -38,7 +38,7 @@ namespace CSVRender /// \param id The referenceable id /// \param type The record type /// \param data The data store - Actor(const std::string& id, int type, CSMWorld::Data& data); + Actor(const std::string& id, CSMWorld::Data& data); /// Retrieves the base node that meshes are attached to osg::Group* getBaseNode(); @@ -50,11 +50,8 @@ namespace CSVRender void handleActorChanged(const std::string& refId); private: - void updateCreature(); - void updateNpc(); - void loadSkeleton(const std::string& model); - void loadBodyParts(const std::string& actorId); + void loadBodyParts(); void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); std::string getBodyPartMesh(const std::string& bodyPartId); @@ -62,8 +59,6 @@ namespace CSVRender static const std::string MeshPrefix; std::string mId; - bool mInitialized; - int mType; CSMWorld::Data& mData; CSMWorld::ActorAdapter::ActorDataPtr mActorData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 63f1bc8a82..6f0fb66068 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -83,74 +83,58 @@ void CSVRender::Object::update() { clear(); - std::string model; - int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh - const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model); int index = referenceables.searchId (mReferenceableId); - int recordType = -1; const ESM::Light* light = NULL; - if (index==-1) - error = 1; - else - { - /// \todo check for Deleted state (error 1) - - model = referenceables.getData (index, - referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). - toString().toUtf8().constData(); - - recordType = - referenceables.getData (index, - referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Light) - { - light = &dynamic_cast& >(referenceables.getRecord(index)).get(); - if (model.empty()) - model = "marker_light.nif"; - } - - if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) - { - if (model.empty()) - model = "marker_creature.nif"; - } - - if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty()) - error = 2; - } - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - if (error) + if (index == -1) { mBaseNode->addChild(createErrorCube()); + return; } - else + + /// \todo check for Deleted state (error 1) + + int recordType = referenceables.getData(index, TypeIndex).toInt(); + std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData(); + + if (recordType == CSMWorld::UniversalId::Type_Light) { - try + light = &dynamic_cast& >(referenceables.getRecord(index)).get(); + if (model.empty()) + model = "marker_light.nif"; + } + + if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) + { + if (model.empty()) + model = "marker_creature.nif"; + } + + try + { + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) - { - if (!mActor) - mActor.reset(new Actor(mReferenceableId, recordType, mData)); - mActor->update(); - mBaseNode->addChild(mActor->getBaseNode()); - } - else - { - std::string path = "meshes\\" + model; - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); - } + if (!mActor) mActor.reset(new Actor(mReferenceableId, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); } - catch (std::exception& e) + else { - Log(Debug::Error) << e.what(); - mBaseNode->addChild(createErrorCube()); + std::string path = "meshes\\" + model; + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } } + catch (std::exception& e) + { + // TODO: use error marker mesh + Log(Debug::Error) << e.what(); + } if (light) { From 676fc488557453f1b49c2d0441221ac573de2efb Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:24:33 -0400 Subject: [PATCH 114/150] Re-add logic for empty model --- apps/opencs/view/render/object.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 6f0fb66068..8b3b5ca255 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -124,11 +124,15 @@ void CSVRender::Object::update() mActor->update(); mBaseNode->addChild(mActor->getBaseNode()); } - else + else if (!model.empty()) { std::string path = "meshes\\" + model; mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } + else + { + throw std::runtime_error(mReferenceableId + " has no model"); + } } catch (std::exception& e) { From 0096951f2598df434e436000c994c1a3320179d7 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:32:47 -0400 Subject: [PATCH 115/150] cleanup --- apps/opencs/model/world/actoradapter.cpp | 1 - apps/opencs/model/world/data.hpp | 1 - apps/opencs/view/render/actor.cpp | 2 -- apps/opencs/view/render/object.cpp | 2 -- 4 files changed, 6 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 47bc5f4da4..8c9e5bd1f0 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -1,6 +1,5 @@ #include "actoradapter.hpp" -#include #include #include #include diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 7c4d8885ad..e50780f507 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -74,7 +74,6 @@ namespace ESM namespace CSMWorld { - class ActorAdapter; class ResourcesManager; class Resources; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 7238166fc1..d6077a65a5 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -3,12 +3,10 @@ #include #include -#include #include #include #include #include -#include #include #include diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 8b3b5ca255..36d80fa4b7 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include @@ -31,7 +30,6 @@ #include #include #include -#include #include "actor.hpp" #include "mask.hpp" From 7eb1b14b21ba0122b1aac63c1b5a2be38249c6f7 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 03:13:03 -0400 Subject: [PATCH 116/150] Periodically prune empty elements in weak cache --- components/cache/weakcache.hpp | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/components/cache/weakcache.hpp b/components/cache/weakcache.hpp index 9d940cd746..976818cc21 100644 --- a/components/cache/weakcache.hpp +++ b/components/cache/weakcache.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace cache { @@ -19,18 +20,19 @@ namespace cache class iterator { public: - iterator(typename Map::iterator current, typename Map::iterator end); + iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end); iterator& operator++(); bool operator==(const iterator& other); bool operator!=(const iterator& other); StrongPtr operator*(); private: + WeakCache* mCache; typename Map::iterator mCurrent, mEnd; StrongPtr mPtr; }; /// Stores a weak pointer to the item. - void insert(Key key, StrongPtr value); + void insert(Key key, StrongPtr value, bool prune=true); /// Retrieves the item associated with the key. /// \return An item or null. @@ -39,14 +41,19 @@ namespace cache iterator begin(); iterator end(); + /// Removes known invalid entries + void prune(); + private: Map mData; + std::vector mDirty; }; template - WeakCache::iterator::iterator(typename Map::iterator current, typename Map::iterator end) - : mCurrent(current) + WeakCache::iterator::iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end) + : mCache(cache) + , mCurrent(current) , mEnd(end) { // Move to 1st available valid item @@ -54,6 +61,7 @@ namespace cache { mPtr = mCurrent->second.lock(); if (mPtr) break; + else mCache->mDirty.push_back(mCurrent->first); } } @@ -62,7 +70,7 @@ namespace cache { auto next = mCurrent; ++next; - return *this = iterator(next, mEnd); + return *this = iterator(mCache, next, mEnd); } template @@ -85,9 +93,10 @@ namespace cache template - void WeakCache::insert(Key key, StrongPtr value) + void WeakCache::insert(Key key, StrongPtr value, bool shouldPrune) { mData[key] = WeakPtr(value); + if (shouldPrune) prune(); } template @@ -103,13 +112,26 @@ namespace cache template typename WeakCache::iterator WeakCache::begin() { - return iterator(mData.begin(), mData.end()); + return iterator(this, mData.begin(), mData.end()); } template typename WeakCache::iterator WeakCache::end() { - return iterator(mData.end(), mData.end()); + return iterator(this, mData.end(), mData.end()); + } + + template + void WeakCache::prune() + { + // Remove empty entries + for (auto& key : mDirty) + { + auto it = mData.find(key); + if (it != mData.end() && it->second.use_count() == 0) + mData.erase(it); + } + mDirty.clear(); } } From ac848b09028315850916bf644a22c965bd98581a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 27 Sep 2018 13:14:34 +0400 Subject: [PATCH 117/150] Use male bodyparts as fallback for females in the editor --- apps/opencs/model/world/actoradapter.cpp | 12 ++++++++++-- apps/opencs/model/world/actoradapter.hpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 8c9e5bd1f0..425c4c8044 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -110,11 +110,19 @@ namespace CSMWorld return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); } - const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) { - return mFemale ? mRaceData->getFemalePart(index) : mRaceData->getMalePart(index); + if (mFemale) + { + // Note: we should use male parts for females as fallback + const std::string femalePart = mRaceData->getFemalePart(index); + if (!femalePart.empty()) + return femalePart; + } + + return mRaceData->getMalePart(index); } return mParts[index]; } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index ba714e8402..d842bca354 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -88,7 +88,7 @@ namespace CSMWorld /// Returns the skeleton the actor should use for attaching parts to std::string getSkeleton() const; /// Retrieves the associated actor part - const std::string& getPart(ESM::PartReferenceType index) const; + const std::string getPart(ESM::PartReferenceType index) const; /// Checks if the actor has a data dependency bool hasDependency(const std::string& id) const; From 35abf7367cf85220c0117c270906661f73e8af76 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 27 Sep 2018 18:22:17 +0400 Subject: [PATCH 118/150] Implement wearing priority for editor --- CHANGELOG.md | 1 + apps/opencs/model/world/actoradapter.cpp | 83 ++++++++++++++++++++---- apps/opencs/model/world/actoradapter.hpp | 5 +- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04547e2496..c72ac7b9e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,6 +132,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Feature #912: Editor: Add missing icons to UniversalId tables + Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 425c4c8044..ea40a1f8dd 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -112,7 +112,8 @@ namespace CSMWorld const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { - if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) + auto it = mParts.find(index); + if (it == mParts.end() && mRaceData && mRaceData->handlesPart(index)) { if (mFemale) { @@ -124,7 +125,9 @@ namespace CSMWorld return mRaceData->getMalePart(index); } - return mParts[index]; + + const std::string& partName = it->second.first; + return partName; } bool ActorAdapter::ActorData::hasDependency(const std::string& id) const @@ -132,9 +135,16 @@ namespace CSMWorld return mDependencies.find(id) != mDependencies.end(); } - void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId) + void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId, int priority) { - mParts[index] = partId; + auto it = mParts.find(index); + if (it != mParts.end()) + { + if (it->second.second >= priority) + return; + } + + mParts[index] = std::make_pair(partId, priority); addOtherDependency(partId); } @@ -150,8 +160,7 @@ namespace CSMWorld mFemale = isFemale; mSkeletonOverride = skeleton; mRaceData = raceData; - for (auto& str : mParts) - str.clear(); + mParts.clear(); mDependencies.clear(); // Mark self and race as a dependency @@ -514,8 +523,8 @@ namespace CSMWorld } // Add head and hair - data->setPart(ESM::PRT_Head, npc.mHead); - data->setPart(ESM::PRT_Hair, npc.mHair); + data->setPart(ESM::PRT_Head, npc.mHead, 0); + data->setPart(ESM::PRT_Hair, npc.mHair, 0); } void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data) @@ -537,8 +546,8 @@ namespace CSMWorld } // Convenience function to add a parts list to actor data - auto addParts = [&](const ESM::PartReferenceList& list) { - for (auto& part : list.mParts) + auto addParts = [&](const std::vector& list, int priority) { + for (auto& part : list) { std::string partId; auto partType = (ESM::PartReferenceType) part.mPart; @@ -548,7 +557,7 @@ namespace CSMWorld if (partId.empty()) partId = part.mMale; - if (!partId.empty()) data->setPart(partType, partId); + data->setPart(partType, partId, priority); } }; @@ -557,15 +566,63 @@ namespace CSMWorld if (type == UniversalId::Type_Armor) { auto& armor = dynamic_cast&>(record).get(); - addParts(armor.mParts); + addParts(armor.mParts.mParts, 1); // Changing parts could affect what is picked for rendering data->addOtherDependency(itemId); } else if (type == UniversalId::Type_Clothing) { + int priority = 0; + // TODO: reserve bodyparts for robes and skirts auto& clothing = dynamic_cast&>(record).get(); - addParts(clothing.mParts); + + if (clothing.mData.mType == ESM::Clothing::Robe) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + else if (clothing.mData.mType == ESM::Clothing::Skirt) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + + addParts(clothing.mParts.mParts, priority); // Changing parts could affect what is picked for rendering data->addOtherDependency(itemId); diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index d842bca354..b97c652a83 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_ACTORADAPTER_H #include +#include #include #include @@ -31,7 +32,7 @@ namespace CSMWorld public: /// A list indexed by ESM::PartReferenceType - using ActorPartList = std::array; + using ActorPartList = std::map>; /// A list indexed by ESM::BodyPart::MeshPart using RacePartList = std::array; /// Tracks unique strings @@ -93,7 +94,7 @@ namespace CSMWorld bool hasDependency(const std::string& id) const; /// Sets the actor part used and marks a dependency - void setPart(ESM::PartReferenceType partIndex, const std::string& partId); + void setPart(ESM::PartReferenceType partIndex, const std::string& partId, int priority); /// Marks an additional dependency for the actor void addOtherDependency(const std::string& id); /// Clears race, parts, and dependencies From 2e98cad895f48c30c03382a98f09b1d88af19ba7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 27 Sep 2018 17:34:46 +0300 Subject: [PATCH 119/150] Fade out sun glare and specularity completely at night start, not sunset start --- apps/openmw/mwworld/weather.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index ac885f429c..7d3f41894d 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -751,14 +751,14 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog"); - float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; + float peakHour = mSunriseTime + (mTimeSettings.mNightStart - mSunriseTime) / 2; float glareFade = 1.f; - if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) + if (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart) glareFade = 0.f; else if (time.getHour() < peakHour) - glareFade -= (peakHour - time.getHour()) / (peakHour - mSunriseTime); + glareFade = 1.f - (peakHour - time.getHour()) / (peakHour - mSunriseTime); else - glareFade -= (time.getHour() - peakHour) / (mSunsetTime - peakHour); + glareFade = 1.f - (time.getHour() - peakHour) / (mTimeSettings.mNightStart - peakHour); mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade); From 43c7438e8e9907cfc0e895f11a7176cd03837d61 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Sep 2018 16:47:47 +0400 Subject: [PATCH 120/150] Move WeakCache to components/misc --- apps/opencs/model/world/actoradapter.hpp | 6 +++--- components/CMakeLists.txt | 6 +----- components/{cache => misc}/weakcache.hpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) rename components/{cache => misc}/weakcache.hpp (99%) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index b97c652a83..1c9265be42 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -8,9 +8,9 @@ #include #include -#include #include #include +#include #include "refidcollection.hpp" #include "idcollection.hpp" @@ -163,8 +163,8 @@ namespace CSMWorld IdCollection& mRaces; IdCollection& mBodyParts; - cache::WeakCache mCachedActors; // Key: referenceable id - cache::WeakCache mCachedRaces; // Key: race id + Misc::WeakCache mCachedActors; // Key: referenceable id + Misc::WeakCache mCachedRaces; // Key: race id StringSet mDirtyActors; // Actors that need updating StringSet mDirtyRaces; // Races that need updating diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 494e1c5ce8..5c245afd08 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - constants utf8stream stringops resourcehelpers rng messageformatparser + constants utf8stream stringops resourcehelpers rng messageformatparser weakcache ) add_component_dir (debug @@ -150,10 +150,6 @@ add_component_dir (fallback fallback validate ) -add_component_dir(cache - weakcache - ) - if(NOT WIN32 AND NOT ANDROID) add_component_dir (crashcatcher crashcatcher diff --git a/components/cache/weakcache.hpp b/components/misc/weakcache.hpp similarity index 99% rename from components/cache/weakcache.hpp rename to components/misc/weakcache.hpp index 976818cc21..022a722dba 100644 --- a/components/cache/weakcache.hpp +++ b/components/misc/weakcache.hpp @@ -5,7 +5,7 @@ #include #include -namespace cache +namespace Misc { /// \class WeakCache /// Provides a container to weakly store pointers to shared data. From 2073218fc68e6a3799576aecdf624d76a4a1a284 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 28 Sep 2018 22:28:54 +0300 Subject: [PATCH 121/150] Use specific googletest version --- CI/build_googletest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh index cd61379b59..ee89ebda6e 100755 --- a/CI/build_googletest.sh +++ b/CI/build_googletest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -git clone https://github.com/google/googletest.git +git clone -b release-1.8.1 https://github.com/google/googletest.git cd googletest mkdir build cd build From f20f49daa6fb0f2c5ccc2f7ae5ad141e914ba332 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 29 Sep 2018 01:54:00 +0300 Subject: [PATCH 122/150] Content selector: allow to copy paths to clipboard (feature #2847) --- CHANGELOG.md | 1 + .../contentselector/model/contentmodel.hpp | 4 ++-- .../contentselector/view/contentselector.cpp | 19 +++++++++++++++++++ .../contentselector/view/contentselector.hpp | 1 + files/ui/datafilespage.ui | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04547e2496..7539dcf17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,6 +135,7 @@ Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search + Feature #2847: Content selector: allow to copy the path to a file by using the context menu Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index bc785a2767..80cd6e4c3b 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -48,6 +48,8 @@ namespace ContentSelectorModel QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; + const EsmFile *item(int row) const; + EsmFile *item(int row); QStringList gameFiles() const; bool isEnabled (QModelIndex index) const; @@ -65,8 +67,6 @@ namespace ContentSelectorModel private: void addFile(EsmFile *file); - const EsmFile *item(int row) const; - EsmFile *item(int row); void sortFiles(); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 595be9f44f..e26f9a9421 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,7 @@ void ContentSelectorView::ContentSelector::buildContextMenu() mContextMenu = new QMenu(ui.addonView); mContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); mContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); + mContextMenu->addAction(tr("&Copy Path(s) to Clipboard"), this, SLOT(slotCopySelectedItemsPaths())); } void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) @@ -245,3 +247,20 @@ void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); } + +void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString filepaths; + foreach (const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) + { + int row = mAddonProxyModel->mapToSource(index).row(); + const ContentSelectorModel::EsmFile *file = mContentModel->item(row); + filepaths += file->filePath() + "\n"; + } + + if (!filepaths.isEmpty()) + { + clipboard->setText(filepaths); + } +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 9f9ad886cd..bc47224e4c 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -70,6 +70,7 @@ namespace ContentSelectorView void slotShowContextMenu(const QPoint& pos); void slotCheckMultiSelectedItems(); void slotUncheckMultiSelectedItems(); + void slotCopySelectedItemsPaths(); }; } diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5a6b7265af..5c26e1044a 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -62,7 +62,7 @@ - Select a profiile + Select a content list From 87394f2ebbfdfa932b4a651fc0b39eb9ca2f2875 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 29 Sep 2018 16:21:40 +0400 Subject: [PATCH 123/150] Revert "Take in account transformations of NiTriShape and NiSkinData in skinning (bug #4437)" This reverts commit 09427d3f5ec05aaa00f31da272f725f1cdba6976 since the fix is not entirely correct. --- CHANGELOG.md | 1 - components/nifosg/nifloader.cpp | 6 +----- components/sceneutil/riggeometry.cpp | 9 --------- components/sceneutil/riggeometry.hpp | 2 -- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1e9e5e0e..d83d38fd78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,6 @@ Bug #4431: "Lock 0" console command is a no-op Bug #4432: Guards behaviour is incorrect if they do not have AI packages Bug #4433: Guard behaviour is incorrect with Alarm = 0 - Bug #4437: Transformations for NiSkinInstance are ignored Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax Bug #4452: Default terrain texture bleeds through texture transitions Bug #4453: Quick keys behaviour is invalid for equipment diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 81898e4777..3198e995c5 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1082,10 +1082,7 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); - // We should take in account transformation of NiTriShape and NiSkinData const Nif::NiSkinData *data = skin->data.getPtr(); - osg::Matrixf shapeTransforms = triShape->trafo.toMatrix() * data->trafo.toMatrix(); - const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { @@ -1099,12 +1096,11 @@ namespace NifOsg influence.mWeights.insert(indexWeight); } influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); - influence.mBoundSphere = osg::BoundingSpheref(shapeTransforms * data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); + influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); map->mMap.insert(std::make_pair(boneName, influence)); } rig->setInfluenceMap(map); - rig->setRigTransforms(*&shapeTransforms); parentNode->addChild(rig); } diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 94e4a3b3eb..c409bcd5cc 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -49,7 +49,6 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) - , mRigTransforms(copy.mRigTransforms) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -215,9 +214,6 @@ void RigGeometry::cull(osg::NodeVisitor* nv) if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - if (!mRigTransforms.isIdentity()) - resultMat *= mRigTransforms; - for (auto &vertex : pair.second) { (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); @@ -313,11 +309,6 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) mInfluenceMap = influenceMap; } -void RigGeometry::setRigTransforms(osg::Matrixf& transform) -{ - mRigTransforms = transform; -} - void RigGeometry::accept(osg::NodeVisitor &nv) { if (!nv.validNodeMask(*this)) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 723455234d..60b3edc9d8 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -40,7 +40,6 @@ namespace SceneUtil }; void setInfluenceMap(osg::ref_ptr influenceMap); - void setRigTransforms(osg::Matrixf& transform); /// Initialize this geometry from the source geometry. /// @note The source geometry will not be modified. @@ -66,7 +65,6 @@ namespace SceneUtil osg::ref_ptr mGeomToSkelMatrix; osg::ref_ptr mInfluenceMap; - osg::Matrixf mRigTransforms; typedef std::pair BoneBindMatrixPair; From 674e33170bcd8a5f725d727d37ad48729a6da8a8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 30 Sep 2018 21:16:02 +0300 Subject: [PATCH 124/150] Fix quick key system regressions (bug #4662) --- apps/openmw/mwgui/quickkeysmenu.cpp | 35 +++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8041c50c59..23ad87fd68 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -254,6 +254,8 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); mSelected->type = Type_MagicItem; + mSelected->id = item.getCellRef().getRefId(); + mSelected->name = item.getClass().getName(item); mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); mSelected->button->setIcon(item); @@ -271,20 +273,19 @@ namespace MWGui while (mSelected->button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); + const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Spell* spell = esmStore.get().find(spellId); + mSelected->type = Type_Magic; mSelected->id = spellId; + mSelected->name = spell->mName; mSelected->button->setItem(MWWorld::Ptr()); mSelected->button->setUserString("ToolTipType", "Spell"); mSelected->button->setUserString("Spell", spellId); - const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - // use the icon of the first effect - const ESM::Spell* spell = esmStore.get().find(spellId); - - const ESM::MagicEffect* effect = - esmStore.get().find(spell->mEffects.mList.front().mEffectID); + const ESM::MagicEffect* effect = esmStore.get().find(spell->mEffects.mList.front().mEffectID); std::string path = effect->mIcon; int slashPos = path.rfind('\\'); @@ -423,9 +424,7 @@ namespace MWGui if (!spells.hasSpell(spellId)) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name); return; } @@ -547,28 +546,26 @@ namespace MWGui return; mSelected = &mKey[i]; - mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType; - mSelected->id = it->mId; - switch (mSelected->type) + switch (it->mType) { case Type_Magic: - if (MWBase::Environment::get().getWorld()->getStore().get().search(mSelected->id)) - onAssignMagic(mSelected->id); + if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mId)) + onAssignMagic(it->mId); break; case Type_Item: case Type_MagicItem: { // Find the item by id - MWWorld::Ptr item = store.findReplacement(mSelected->id); + MWWorld::Ptr item = store.findReplacement(it->mId); if (item.isEmpty()) - unassign(&mKey[i]); + unassign(mSelected); else { - if (mSelected->type == Type_Item) + if (it->mType == Type_Item) onAssignItem(item); - else if (mSelected->type == Type_MagicItem) + else // if (it->mType == Type_MagicItem) onAssignMagicItem(item); } @@ -576,7 +573,7 @@ namespace MWGui } case Type_Unassigned: case Type_HandToHand: - unassign(&mKey[i]); + unassign(mSelected); break; } From 4dc424036f218dda617b98d4ff4218e072da31a9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Sep 2018 13:57:13 +0400 Subject: [PATCH 125/150] Cleanup magic effects, when create a new ActorAnimation --- apps/openmw/mwrender/actoranimation.cpp | 3 ++ apps/openmw/mwrender/animation.cpp | 69 ++++++++++++++++++++++--- apps/openmw/mwrender/animation.hpp | 1 + 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index d92b95a71c..e6aff8d19e 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -40,6 +40,9 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr addHiddenItemLight(*iter, light); } } + + // Make sure we cleaned object from effects, just in cast if we re-use node + removeEffects(); } ActorAnimation::~ActorAnimation() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7410dbf037..e7c3f22004 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -213,14 +213,12 @@ namespace RemoveFinishedCallbackVisitor() : RemoveVisitor() , mHasMagicEffects(false) - , mEffectId(-1) { } RemoveFinishedCallbackVisitor(int effectId) : RemoveVisitor() , mHasMagicEffects(false) - , mEffectId(effectId) { } @@ -240,9 +238,63 @@ namespace MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); if (vfxCallback) { - bool finished = vfxCallback->mFinished; - bool toRemove = mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; - if (finished || toRemove) + if (vfxCallback->mFinished) + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + else + mHasMagicEffects = true; + } + } + } + + virtual void apply(osg::MatrixTransform &node) + { + traverse(node); + } + + virtual void apply(osg::Geometry&) + { + } + + private: + int mEffectId; + }; + + class RemoveCallbackVisitor : public RemoveVisitor + { + public: + bool mHasMagicEffects; + + RemoveCallbackVisitor() + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(-1) + { + } + + RemoveCallbackVisitor(int effectId) + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(effectId) + { + } + + virtual void apply(osg::Node &node) + { + traverse(node); + } + + virtual void apply(osg::Group &group) + { + traverse(group); + + osg::Callback* callback = group.getUpdateCallback(); + if (callback) + { + MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); + if (vfxCallback) + { + bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId; + if (toRemove) mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); else mHasMagicEffects = true; @@ -1649,12 +1701,17 @@ namespace MWRender void Animation::removeEffect(int effectId) { - RemoveFinishedCallbackVisitor visitor(effectId); + RemoveCallbackVisitor visitor(effectId); mInsert->accept(visitor); visitor.remove(); mHasMagicEffects = visitor.mHasMagicEffects; } + void Animation::removeEffects() + { + removeEffect(-1); + } + void Animation::getLoopingEffects(std::vector &out) const { if (!mHasMagicEffects) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index e10d2c995a..5828bd52f7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -370,6 +370,7 @@ public: */ void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f); void removeEffect (int effectId); + void removeEffects (); void getLoopingEffects (std::vector& out) const; // Add a spell casting glow to an object. From measuring video taken from the original engine, From 632045e145c4162a5cc095a77db3ee498791b14b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 10:35:01 +0400 Subject: [PATCH 126/150] Improve the 'part has no parents' warning --- apps/openmw/mwrender/animation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e7c3f22004..85d4aae9d9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1975,12 +1975,12 @@ namespace MWRender PartHolder::~PartHolder() { if (mNode.get() && !mNode->getNumParents()) - Log(Debug::Verbose) << "Part has no parents" ; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has no parents" ; if (mNode.get() && mNode->getNumParents()) { if (mNode->getNumParents() > 1) - Log(Debug::Verbose) << "Part has multiple (" << mNode->getNumParents() << ") parents"; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has multiple (" << mNode->getNumParents() << ") parents"; mNode->getParent(0)->removeChild(mNode); } } From 3896a2eba6da63a227094b1cc38fd3a3e22c4c26 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 11:12:28 +0400 Subject: [PATCH 127/150] Do not use a PartHolder for spell effect node --- apps/openmw/mwrender/animation.cpp | 5 ++--- apps/openmw/mwrender/animation.hpp | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 85d4aae9d9..840e59290b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -624,8 +624,8 @@ namespace MWRender } else { - // Remove effect immediately - mParams.mObjects.reset(); + // Hide effect immediately + node->setNodeMask(0); mFinished = true; } } @@ -1686,7 +1686,6 @@ namespace MWRender params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; - params.mObjects = PartHolderPtr(new PartHolder(node)); params.mAnimTime = std::shared_ptr(new EffectAnimationTime); trans->addUpdateCallback(new UpdateVfxCallback(params)); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 5828bd52f7..2d8ac152f0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -74,7 +74,6 @@ typedef std::shared_ptr PartHolderPtr; struct EffectParams { std::string mModelName; // Just here so we don't add the same effect twice - PartHolderPtr mObjects; std::shared_ptr mAnimTime; float mMaxControllerLength; int mEffectId; From 07ccc5abdb3f18d946a57d34ddd9161ef297bee2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 1 Oct 2018 21:57:13 +0400 Subject: [PATCH 128/150] Remove non-looping effects after rest --- apps/openmw/mwmechanics/actors.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92f2ba34dc..4f051f40a7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1683,7 +1683,10 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); if (animation) - animation->updateEffects(); + { + animation->removeEffects(); + MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first); + } } From 34e45efac3968850e1db2e3c88fcb0bffe78fcd3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 5 Oct 2018 16:29:57 +0300 Subject: [PATCH 129/150] Fix first person swimming animations --- apps/openmw/mwmechanics/character.cpp | 35 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 90f90e9e2a..c4975b308b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -432,26 +432,37 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character if(movestate != sMovementListEnd) { movementAnimName = movestate->groupname; - if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) + if(weap != sWeaponTypeListEnd) { - if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case - movementAnimName = weap->shortgroup + movementAnimName; - else - movementAnimName += weap->shortgroup; + std::string::size_type swimpos = movementAnimName.find("swim"); + if (swimpos == std::string::npos) + { + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case + movementAnimName = weap->shortgroup + movementAnimName; + else + movementAnimName += weap->shortgroup; + } if(!mAnimation->hasAnimation(movementAnimName)) { movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName = movestate->groupname; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; + if (swimpos == std::string::npos) + { + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - movementAnimName += "1h"; + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; + } + else if (idle == CharState_None) + { + idle = CharState_IdleSwim; + } } } } From 03788edd6399dc86ff5eee50f990c49ef1333260 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 5 Oct 2018 18:48:17 +0300 Subject: [PATCH 130/150] Fix redundant drag call in instance dragging (bug #4593) --- CHANGELOG.md | 1 + apps/opencs/view/render/worldspacewidget.cpp | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1e9e5e0e..2214a4f20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button + Bug #4593: Editor: Instance dragging is broken Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 5b825fab1f..7e405266ee 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -648,11 +648,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif - - if (mDragMode == InteractionType_PrimaryEdit) - { - editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos - } } } else From 768c532b8f41bf9e98d5136edc11550bc58616ad Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 7 Oct 2018 19:41:59 +0300 Subject: [PATCH 131/150] Editor: display light source color as a color (bug #4668) --- CHANGELOG.md | 1 + apps/opencs/model/world/refidcollection.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c835a58a..226f093742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects + Bug #4668: Editor: Light source color is displayed as an integer Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 6716c72408..272722f649 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); lightColumns.mRadius = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour)); lightColumns.mColor = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); From e627f49df438b1cc7792f6d7fe080d991e5e0a57 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 7 Oct 2018 20:22:52 +0300 Subject: [PATCH 132/150] Fix AppVeyor build --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2c4fe6312e..8ee4b7fd99 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then # OpenAL download "OpenAL-Soft 1.17.2" \ - "http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \ "OpenAL-Soft-1.17.2.zip" # OSG From cae2e84ab45b70e5690666de9649fe3e08b5f888 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 8 Oct 2018 15:50:54 +0300 Subject: [PATCH 133/150] Trace the player down after enabling collision with TCL (bug #4669) --- CHANGELOG.md | 2 ++ apps/openmw/mwworld/worldimp.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f093742..448009926c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip + Bug #3219: NPC and creature initial position tracing down limit is too small Bug #3249: Fixed revert function not updating views properly Bug #3288: TrueType fonts are handled incorrectly Bug #3374: Touch spells not hitting kwama foragers @@ -134,6 +135,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4669: ToggleCollision should trace the player down after collision being enabled Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3cb6b3a594..78eddf28fe 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1343,7 +1343,7 @@ namespace MWWorld if (force || !isFlying(ptr)) { - osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); + osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits); if (traced.z() < pos.z()) pos.z() = traced.z(); } @@ -1581,7 +1581,13 @@ namespace MWWorld bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode(); + if (mPhysics->toggleCollisionMode()) + { + adjustPosition(getPlayerPtr(), true); + return true; + } + + return false; } bool World::toggleRenderMode (MWRender::RenderMode mode) From 09aecb955c5b526936f9102d792e0b189f9048c3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 8 Oct 2018 17:06:30 +0300 Subject: [PATCH 134/150] getSkill usage cleanup (bug #4671) --- CHANGELOG.md | 1 + apps/openmw/mwclass/ingredient.cpp | 5 +---- apps/openmw/mwclass/npc.cpp | 8 ++++---- apps/openmw/mwgui/recharge.cpp | 4 +--- apps/openmw/mwgui/trainingwindow.cpp | 8 +++----- apps/openmw/mwmechanics/alchemy.cpp | 7 ++----- apps/openmw/mwmechanics/enchanting.cpp | 9 ++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/repair.cpp | 4 +--- apps/openmw/mwmechanics/security.cpp | 4 +--- apps/openmw/mwmechanics/spellcasting.cpp | 3 +-- apps/openmw/mwmechanics/spellpriority.cpp | 4 ++-- apps/openmw/mwworld/inventorystore.cpp | 5 ++--- 13 files changed, 25 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f093742..8d8f539a7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4671: knownEffect functions should use modified Alchemy skill Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c133b6a266..70339564c6 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -14,8 +14,6 @@ #include "../mwworld/actioneat.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwmechanics/npcstats.hpp" - #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" @@ -134,8 +132,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats (player); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4979db2fd4..c0f6eb64fe 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -949,7 +949,7 @@ namespace MWClass if(sneaking) walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); - float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * + float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); float moveSpeed; @@ -971,7 +971,7 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } @@ -1004,7 +1004,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); + float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); float b = 0.0f; if(a > 50.0f) { @@ -1129,7 +1129,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index a6ed16bd40..672db3e32d 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -18,7 +18,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemwidget.hpp" @@ -139,7 +138,6 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); if (luckTerm < 1|| luckTerm > 10) @@ -152,7 +150,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) if (intelligenceTerm < 1) intelligenceTerm = 1; - float x = (npcStats.getSkill(ESM::Skill::Enchant).getModified() + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); + float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); int roll = Misc::Rng::roll0to99(); if (roll < x) { diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index b309da27d0..60e6584c7a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -73,14 +73,12 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); - MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); - // NPC can train you in his best 3 skills std::vector< std::pair > skills; for (int i=0; i player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; - MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr); - if (npcStats.getSkill (skillId).getModified () <= pcStats.getSkill (skillId).getBase ()) + if (mPtr.getClass().getSkill(mPtr, skillId) <= pcStats.getSkill (skillId).getBase ()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; @@ -168,6 +165,7 @@ namespace MWGui player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool + MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); // advance time diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 98860198bb..c199bfb3f8 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -26,7 +26,6 @@ #include "magiceffects.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" MWMechanics::Alchemy::Alchemy() : mValue(0) @@ -318,10 +317,9 @@ void MWMechanics::Alchemy::increaseSkill() float MWMechanics::Alchemy::getAlchemyFactor() const { const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist); - const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist); return - (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + (mAlchemist.getClass().getSkill(mAlchemist, ESM::Skill::Alchemy) + 0.1f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()); } @@ -472,8 +470,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index c0bffc4cb5..e4bb0cf3f0 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -10,7 +10,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "spellcasting.hpp" #include "actorutil.hpp" @@ -280,11 +279,11 @@ namespace MWMechanics float Enchanting::getEnchantChance() const { - const NpcStats& npcStats = mEnchanter.getClass().getNpcStats (mEnchanter); + const CreatureStats& stats = mEnchanter.getClass().getCreatureStats(mEnchanter); - float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + - (0.25f * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) - + (0.125f * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); + float chance1 = (mEnchanter.getClass().getSkill(mEnchanter, ESM::Skill::Enchant) + + (0.25f * stats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125f * stats.getAttribute (ESM::Attribute::Luck).getModified())); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 33357b79a7..1ea40023bb 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -639,10 +639,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 3ebef36bf1..8c5e698960 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -14,7 +14,6 @@ #include "../mwworld/esmstore.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "actorutil.hpp" namespace MWMechanics @@ -34,12 +33,11 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) mTool.getCellRef().setCharge(uses-1); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float fatigueTerm = stats.getFatigueTerm(); int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); + int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index c8a4dd7706..18d4ec2e5a 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -11,7 +11,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "npcstats.hpp" #include "creaturestats.hpp" namespace MWMechanics @@ -21,10 +20,9 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - NpcStats& npcStats = actor.getClass().getNpcStats(actor); mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); - mSecuritySkill = static_cast(npcStats.getSkill(ESM::Skill::Security).getModified()); + mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f1ee2520b2..f6eb497e71 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -964,10 +964,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effect.mEffectID); - const MWMechanics::NpcStats& npcStats = mCaster.getClass().getNpcStats(mCaster); const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster); - float x = (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + float x = (mCaster.getClass().getSkill(mCaster, ESM::Skill::Alchemy) + 0.2f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()) * creatureStats.getFatigueTerm(); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 807b56608e..3b242266c2 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -14,7 +14,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/cellstore.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "spellcasting.hpp" #include "combat.hpp" @@ -538,7 +538,7 @@ namespace MWMechanics case ESM::MagicEffect::DrainSkill: if (enemy.isEmpty() || !enemy.getClass().isNpc()) return 0.f; - if (enemy.getClass().getNpcStats(enemy).getSkill(effect.mSkill).getModified() <= 0) + if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0) return 0.f; break; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 7f3018c559..59ec6ab3ef 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -250,11 +250,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); @@ -384,7 +383,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = stats.getSkill(static_cast(weaponSkills[j])).getModified(); + int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { From e06f0b797a596c2773018ecf3ddf0a7981767ede Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Oct 2018 10:21:12 +0400 Subject: [PATCH 135/150] Replace all NULLs to nullptr --- apps/opencs/editor.cpp | 4 +- apps/opencs/model/doc/operationholder.cpp | 2 +- apps/opencs/model/world/columnimp.cpp | 2 +- apps/opencs/model/world/commands.cpp | 2 +- .../model/world/idcompletionmanager.cpp | 2 +- apps/opencs/model/world/idtableproxymodel.cpp | 10 +-- .../model/world/infotableproxymodel.cpp | 6 +- apps/opencs/model/world/refidadapterimp.cpp | 34 ++++----- apps/opencs/model/world/resourcesmanager.cpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 4 +- apps/opencs/view/doc/subview.hpp | 2 +- apps/opencs/view/doc/view.cpp | 10 +-- apps/opencs/view/doc/view.hpp | 2 +- apps/opencs/view/render/cameracontroller.cpp | 4 +- apps/opencs/view/render/object.cpp | 2 +- .../view/render/pagedworldspacewidget.cpp | 2 +- apps/opencs/view/render/scenewidget.cpp | 8 +- apps/opencs/view/render/terrainstorage.cpp | 2 +- apps/opencs/view/widget/colorpickerpopup.cpp | 2 +- apps/opencs/view/widget/completerpopup.cpp | 2 +- apps/opencs/view/world/cellcreator.cpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 28 +++---- apps/opencs/view/world/dragdroputils.cpp | 2 +- apps/opencs/view/world/dragrecordtable.cpp | 2 +- apps/opencs/view/world/dragrecordtable.hpp | 2 +- .../world/extendedcommandconfigurator.cpp | 2 +- .../view/world/idcompletiondelegate.cpp | 2 +- apps/opencs/view/world/nestedtable.cpp | 6 +- apps/opencs/view/world/nestedtable.hpp | 2 +- apps/opencs/view/world/scenesubview.cpp | 10 +-- apps/opencs/view/world/util.cpp | 4 +- apps/openmw/android_main.c | 4 +- apps/openmw/engine.cpp | 12 +-- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 4 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwdialogue/journalentry.cpp | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/backgroundimage.cpp | 2 +- apps/openmw/mwgui/backgroundimage.hpp | 2 +- apps/openmw/mwgui/bookpage.cpp | 68 ++++++++--------- apps/openmw/mwgui/class.cpp | 12 +-- apps/openmw/mwgui/companionwindow.cpp | 12 +-- apps/openmw/mwgui/container.cpp | 16 ++-- apps/openmw/mwgui/countdialog.cpp | 4 +- apps/openmw/mwgui/cursor.cpp | 4 +- apps/openmw/mwgui/draganddrop.cpp | 8 +- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/exposedwindow.cpp | 2 +- apps/openmw/mwgui/formatting.cpp | 2 +- apps/openmw/mwgui/hud.cpp | 30 ++++---- apps/openmw/mwgui/inventorywindow.cpp | 14 ++-- apps/openmw/mwgui/itemchargeview.cpp | 4 +- apps/openmw/mwgui/itemmodel.cpp | 6 +- apps/openmw/mwgui/itemselection.cpp | 4 +- apps/openmw/mwgui/itemview.cpp | 6 +- apps/openmw/mwgui/itemwidget.cpp | 8 +- apps/openmw/mwgui/journalviewmodel.cpp | 2 +- apps/openmw/mwgui/loadingscreen.cpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 14 ++-- apps/openmw/mwgui/mapwindow.cpp | 18 ++--- apps/openmw/mwgui/messagebox.cpp | 22 +++--- apps/openmw/mwgui/race.cpp | 12 +-- apps/openmw/mwgui/recharge.cpp | 4 +- apps/openmw/mwgui/repair.cpp | 4 +- apps/openmw/mwgui/review.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 24 +++--- apps/openmw/mwgui/spellcreationdialog.cpp | 12 +-- apps/openmw/mwgui/spellmodel.cpp | 2 +- apps/openmw/mwgui/spellview.cpp | 12 +-- apps/openmw/mwgui/spellwindow.cpp | 4 +- apps/openmw/mwgui/statswindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.cpp | 12 +-- apps/openmw/mwgui/videowidget.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwgui/widgets.cpp | 22 +++--- apps/openmw/mwgui/windowbase.cpp | 4 +- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 74 +++++++++---------- apps/openmw/mwgui/windowpinnablebase.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aicombataction.cpp | 2 +- apps/openmw/mwmechanics/aicombataction.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 2 +- apps/openmw/mwmechanics/aipackage.hpp | 2 +- apps/openmw/mwmechanics/aistate.hpp | 8 +- apps/openmw/mwmechanics/aiwander.hpp | 2 +- apps/openmw/mwmechanics/autocalcspell.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 14 ++-- apps/openmw/mwmechanics/character.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/objects.cpp | 2 +- apps/openmw/mwmechanics/obstacle.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 4 +- apps/openmw/mwmechanics/pathgrid.cpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 6 +- apps/openmw/mwmechanics/spellcasting.hpp | 8 +- apps/openmw/mwphysics/physicssystem.cpp | 16 ++-- apps/openmw/mwphysics/trace.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 32 ++++---- apps/openmw/mwrender/animation.hpp | 4 +- apps/openmw/mwrender/bulletdebugdraw.cpp | 6 +- apps/openmw/mwrender/camera.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 6 +- apps/openmw/mwrender/creatureanimation.cpp | 4 +- apps/openmw/mwrender/globalmap.cpp | 2 +- apps/openmw/mwrender/landmanager.cpp | 4 +- apps/openmw/mwrender/landmanager.hpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 16 ++-- apps/openmw/mwrender/npcanimation.hpp | 8 +- apps/openmw/mwrender/objects.cpp | 14 ++-- apps/openmw/mwrender/pathgrid.cpp | 10 +-- apps/openmw/mwrender/renderingmanager.cpp | 10 +-- apps/openmw/mwrender/ripplesimulation.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 30 ++++---- apps/openmw/mwrender/water.cpp | 10 +-- apps/openmw/mwrender/weaponanimation.cpp | 4 +- apps/openmw/mwscript/statsextensions.cpp | 2 +- .../mwscript/transformationextensions.cpp | 2 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 54 +++++++------- apps/openmw/mwsound/movieaudiofactory.cpp | 2 +- apps/openmw/mwsound/openal_output.cpp | 6 +- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- apps/openmw/mwstate/charactermanager.cpp | 4 +- apps/openmw/mwstate/quicksavemanager.cpp | 4 +- apps/openmw/mwstate/quicksavemanager.hpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 4 +- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 6 +- apps/openmw/mwworld/cellstore.hpp | 4 +- apps/openmw/mwworld/cellvisitors.hpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 4 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- apps/openmw/mwworld/livecellref.hpp | 4 +- apps/openmw/mwworld/player.cpp | 4 +- apps/openmw/mwworld/player.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 6 +- apps/openmw/mwworld/store.cpp | 14 ++-- apps/openmw/mwworld/store.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 +-- apps/openmw_test_suite/mwworld/test_store.cpp | 4 +- apps/wizard/existinginstallationpage.cpp | 2 +- .../contentselector/view/contentselector.cpp | 2 +- components/crashcatcher/crashcatcher.cpp | 24 +++--- components/debug/debugging.cpp | 2 +- components/esm/esmreader.cpp | 4 +- components/esm/esmwriter.cpp | 4 +- components/esm/loadland.cpp | 6 +- components/esm/loadland.hpp | 4 +- components/esmterrain/storage.cpp | 2 +- components/files/androidpath.cpp | 6 +- components/files/linuxpath.cpp | 6 +- components/files/lowlevelfile.cpp | 26 +++---- components/files/macospath.cpp | 6 +- components/files/windowspath.cpp | 6 +- components/myguiplatform/additivelayer.cpp | 2 +- components/myguiplatform/myguidatamanager.cpp | 2 +- .../myguiplatform/myguirendermanager.cpp | 14 ++-- .../myguiplatform/myguirendermanager.hpp | 2 +- components/myguiplatform/myguitexture.cpp | 2 +- components/nif/niffile.cpp | 6 +- components/nif/node.hpp | 4 +- components/nif/recordptr.hpp | 10 +-- components/nifbullet/bulletnifloader.cpp | 14 ++-- components/nifosg/nifloader.cpp | 28 +++---- components/nifosg/particle.cpp | 4 +- components/resource/bulletshape.cpp | 4 +- components/resource/bulletshapemanager.cpp | 2 +- components/resource/multiobjectcache.hpp | 2 +- components/sceneutil/clone.cpp | 2 +- components/sceneutil/controller.cpp | 2 +- components/sceneutil/lightmanager.cpp | 16 ++-- components/sceneutil/lightmanager.hpp | 2 +- components/sceneutil/lightutil.cpp | 2 +- components/sceneutil/optimizer.cpp | 8 +- components/sceneutil/riggeometry.cpp | 8 +- components/sceneutil/skeleton.cpp | 8 +- components/sceneutil/statesetupdater.cpp | 4 +- components/sceneutil/visitor.cpp | 2 +- components/sceneutil/visitor.hpp | 4 +- components/sceneutil/workqueue.cpp | 2 +- components/sceneutil/workqueue.hpp | 2 +- components/sdlutil/sdlcursormanager.cpp | 2 +- components/sdlutil/sdlgraphicswindow.cpp | 8 +- components/sdlutil/sdlinputwrapper.cpp | 10 +-- components/shader/shadermanager.cpp | 6 +- components/shader/shadermanager.hpp | 2 +- components/shader/shadervisitor.cpp | 22 +++--- components/terrain/chunkmanager.cpp | 2 +- components/terrain/compositemaprenderer.cpp | 2 +- components/terrain/quadtreenode.cpp | 4 +- components/terrain/quadtreeworld.cpp | 10 +-- components/terrain/terraindrawable.hpp | 2 +- components/terrain/terraingrid.cpp | 6 +- components/terrain/viewdata.cpp | 10 +-- components/terrain/world.hpp | 2 +- components/translation/translation.cpp | 2 +- components/widgets/windowcaption.cpp | 6 +- 205 files changed, 717 insertions(+), 721 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index f29c6aca81..450b434e66 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv) : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mPid(""), mLock(), mMerge (mDocumentManager), - mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) + mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr) { std::pair > config = readConfig(); @@ -339,7 +339,7 @@ bool CS::Editor::makeIPCServer() } mServer->close(); - mServer = NULL; + mServer = nullptr; return false; } diff --git a/apps/opencs/model/doc/operationholder.cpp b/apps/opencs/model/doc/operationholder.cpp index ccbed6c8ba..0fd2bef957 100644 --- a/apps/opencs/model/doc/operationholder.cpp +++ b/apps/opencs/model/doc/operationholder.cpp @@ -3,7 +3,7 @@ #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) - : mOperation(NULL) + : mOperation(nullptr) , mRunning (false) { if (operation) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 49e4bebe6a..b202a97d93 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -318,7 +318,7 @@ namespace CSMWorld QVariant BodyPartRaceColumn::get(const Record &record) const { - if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin) { return QString::fromUtf8(record.get().mRace.c_str()); } diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 4133b2050e..4c70964791 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications() if (!mNestedValues.empty()) { CSMWorld::IdTree *tree = dynamic_cast(&mModel); - if (tree == NULL) + if (tree == nullptr) { throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model"); } diff --git a/apps/opencs/model/world/idcompletionmanager.cpp b/apps/opencs/model/world/idcompletionmanager.cpp index 9fa6e3addf..649a960387 100644 --- a/apps/opencs/model/world/idcompletionmanager.cpp +++ b/apps/opencs/model/world/idcompletionmanager.cpp @@ -92,7 +92,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data) { QAbstractItemModel *model = data.getTableModel(current->second); CSMWorld::IdTableBase *table = dynamic_cast(model); - if (table != NULL) + if (table != nullptr) { int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id); if (idColumn != -1) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 6d50e9edb0..534e5dd63b 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -18,7 +18,7 @@ namespace void CSMWorld::IdTableProxyModel::updateColumnMap() { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); mColumnMap.clear(); if (mFilter) @@ -33,7 +33,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap() bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); // It is not possible to use filterAcceptsColumn() and check for // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) @@ -51,14 +51,14 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent), - mSourceModel(NULL) + mSourceModel(nullptr) { setSortCaseSensitivity (Qt::CaseInsensitive); } QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); return mapFromSource(mSourceModel->getModelIndex (id, column)); } @@ -113,7 +113,7 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp index 4f0b2e7528..7c6a9c323e 100644 --- a/apps/opencs/model/world/infotableproxymodel.cpp +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -28,7 +28,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod { IdTableProxyModel::setSourceModel(sourceModel); - if (mSourceModel != NULL) + if (mSourceModel != nullptr) { mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mFirstRowCache.clear(); @@ -37,7 +37,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); @@ -52,7 +52,7 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int row = currentRow; int column = mInfoColumnIndex; diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 5ac9ecb188..1948a95cbf 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -453,13 +453,13 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mType(NULL), - mScale(NULL), - mOriginal(NULL), - mAttributes(NULL), - mAttacks(NULL), - mMisc(NULL), - mBloodType(NULL) + mType(nullptr), + mScale(nullptr), + mOriginal(nullptr), + mAttributes(nullptr), + mAttacks(nullptr), + mMisc(nullptr), + mBloodType(nullptr) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) @@ -748,16 +748,16 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mRace(NULL), - mClass(NULL), - mFaction(NULL), - mHair(NULL), - mHead(NULL), - mAttributes(NULL), - mSkills(NULL), - mMisc(NULL), - mBloodType(NULL), - mGender(NULL) + mRace(nullptr), + mClass(nullptr), + mFaction(nullptr), + mHair(nullptr), + mHead(nullptr), + mAttributes(nullptr), + mSkills(nullptr), + mMisc(nullptr), + mBloodType(nullptr), + mGender(nullptr) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) diff --git a/apps/opencs/model/world/resourcesmanager.cpp b/apps/opencs/model/world/resourcesmanager.cpp index 62dfe53a9e..1af9c5e9b1 100644 --- a/apps/opencs/model/world/resourcesmanager.cpp +++ b/apps/opencs/model/world/resourcesmanager.cpp @@ -3,7 +3,7 @@ #include CSMWorld::ResourcesManager::ResourcesManager() - : mVFS(NULL) + : mVFS(nullptr) { } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 3c5f202021..7693276c63 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -189,7 +189,7 @@ void CSVDoc::FileDialog::slotRejected() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } close(); } @@ -200,7 +200,7 @@ void CSVDoc::FileDialog::slotNewFile() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); close(); diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 8402bf79a6..44b81743f9 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -64,7 +64,7 @@ namespace CSVDoc void updateTitle(); - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); void universalIdChanged (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index ca90b11ff4..ed4dcff76f 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -400,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view) else { delete subView->titleBarWidget(); - subView->setTitleBarWidget (NULL); + subView->setTitleBarWidget (nullptr); } } } @@ -429,7 +429,7 @@ void CSVDoc::View::updateActions() CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), - mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false) + mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; @@ -563,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin return; } - SubView *view = NULL; + SubView *view = nullptr; if(isReferenceable) { view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); @@ -631,7 +631,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max) void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Windows/hide-subview") - updateSubViewIndices (NULL); + updateSubViewIndices (nullptr); else if (*setting=="Windows/mainwindow-scrollbar") { if (setting->toString()!="Grow Only") @@ -659,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) mScroll->takeWidget(); setCentralWidget (&mSubViewWindow); mScroll->deleteLater(); - mScroll = NULL; + mScroll = nullptr; } } } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 76c81b964a..7a9a48b0f0 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -149,7 +149,7 @@ namespace CSVDoc void updateTitle(); // called when subviews are added or removed - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); private slots: diff --git a/apps/opencs/view/render/cameracontroller.cpp b/apps/opencs/view/render/cameracontroller.cpp index 37f439fd3b..524a798213 100644 --- a/apps/opencs/view/render/cameracontroller.cpp +++ b/apps/opencs/view/render/cameracontroller.cpp @@ -38,7 +38,7 @@ namespace CSVRender , mCameraSensitivity(1/650.f) , mSecondaryMoveMult(50) , mWheelMoveMult(8) - , mCamera(NULL) + , mCamera(nullptr) { } @@ -81,7 +81,7 @@ namespace CSVRender bool wasActive = mActive; mCamera = camera; - mActive = (mCamera != NULL); + mActive = (mCamera != nullptr); if (mActive != wasActive) { diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 36d80fa4b7..0266257959 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -86,7 +86,7 @@ void CSVRender::Object::update() const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model); int index = referenceables.searchId (mReferenceableId); - const ESM::Light* light = NULL; + const ESM::Light* light = nullptr; mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ccea707610..ff69656a29 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -527,7 +527,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, in CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), - mControlElements(NULL), mDisplayCellCoord(true) + mControlElements(nullptr), mDisplayCellCoord(true) { QAbstractItemModel *cells = document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 0d1780d574..6cc64f6537 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste bool retrieveInput) : RenderWidget(parent, f) , mResourceSystem(resourceSystem) - , mLighting(NULL) + , mLighting(nullptr) , mHasDefaultAmbient(false) , mPrevMouseX(0) , mPrevMouseY(0) @@ -425,21 +425,21 @@ void SceneWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->fixUpAxis(CameraController::WorldUp); } else if (mode=="free") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->unfixUpAxis(); } else if (mode=="orbit") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mOrbitCamControl; mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->reset(); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 51c9dd0093..e0edae7743 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -18,7 +18,7 @@ namespace CSVRender // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)); if (index == -1) - return NULL; + return nullptr; const ESM::Land& land = mData.getLand().getRecord(index).get(); return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp index 47aab89817..a38728ef3b 100644 --- a/apps/opencs/view/widget/colorpickerpopup.cpp +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -45,7 +45,7 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) { QPushButton *button = qobject_cast(parentWidget()); - if (button != NULL) + if (button != nullptr) { QStyleOptionButton option; option.init(button); diff --git a/apps/opencs/view/widget/completerpopup.cpp b/apps/opencs/view/widget/completerpopup.cpp index 5777325c83..be509bcb93 100644 --- a/apps/opencs/view/widget/completerpopup.cpp +++ b/apps/opencs/view/widget/completerpopup.cpp @@ -11,7 +11,7 @@ CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent) int CSVWidget::CompleterPopup::sizeHintForRow(int row) const { - if (model() == NULL) + if (model() == nullptr) { return -1; } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 2a710a9400..a42e7ead4e 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - Q_ASSERT(model != NULL); + Q_ASSERT(model != nullptr); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 7b198056c4..b32e2c7a14 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent) CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { - CommandDelegate *delegate = NULL; + CommandDelegate *delegate = nullptr; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt == mDelegates.end()) { @@ -251,11 +251,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { - return NULL; + return nullptr; } } - QWidget* editor = NULL; + QWidget* editor = nullptr; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { return mNotEditableDelegate.createEditor(qobject_cast(mParent), @@ -325,7 +325,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di mWidget(widget), mIdType(CSMWorld::TableMimeData::convertEnums(display)) { - Q_ASSERT(mWidget != NULL); + Q_ASSERT(mWidget != nullptr); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); @@ -339,7 +339,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest())); QLineEdit *lineEdit = qobject_cast(mWidget); - if (lineEdit != NULL) + if (lineEdit != nullptr) { mContextMenu = lineEdit->createStandardContextMenu(); } @@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const QLabel *label = qobject_cast(mWidget); QString value = ""; - if (lineEdit != NULL) + if (lineEdit != nullptr) { value = lineEdit->text(); } - else if (label != NULL) + else if (label != nullptr) { value = label->text(); } @@ -436,7 +436,7 @@ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor, CSMWorld::ColumnBase::Display display, int currentRow) const { - Q_ASSERT(editor != NULL); + Q_ASSERT(editor != nullptr); if (CSMWorld::ColumnBase::isId(display) && CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) @@ -470,11 +470,11 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : QScrollArea(parent), -mWidgetMapper(NULL), -mNestedTableMapper(NULL), -mDispatcher(NULL), -mNestedTableDispatcher(NULL), -mMainWidget(NULL), +mWidgetMapper(nullptr), +mNestedTableMapper(nullptr), +mDispatcher(nullptr), +mNestedTableDispatcher(nullptr), +mMainWidget(nullptr), mTable(table), mCommandDispatcher (commandDispatcher), mDocument (document) @@ -733,7 +733,7 @@ bool CSVWorld::SimpleDialogueSubView::isLocked() const CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mEditWidget(0), - mMainLayout(NULL), + mMainLayout(nullptr), mTable(dynamic_cast(document.getData().getTableModel(id))), mLocked(false), mDocument(document), diff --git a/apps/opencs/view/world/dragdroputils.cpp b/apps/opencs/view/world/dragdroputils.cpp index 7f3974e539..789d4f33dc 100644 --- a/apps/opencs/view/world/dragdroputils.cpp +++ b/apps/opencs/view/world/dragdroputils.cpp @@ -12,7 +12,7 @@ const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const Q bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) { const CSMWorld::TableMimeData *data = getTableMimeData(event); - return data != NULL && data->holdsType(type); + return data != nullptr && data->holdsType(type); } CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event, diff --git a/apps/opencs/view/world/dragrecordtable.cpp b/apps/opencs/view/world/dragrecordtable.cpp index 6d980e1712..d795bd5de1 100644 --- a/apps/opencs/view/world/dragrecordtable.cpp +++ b/apps/opencs/view/world/dragrecordtable.cpp @@ -83,7 +83,7 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const { - Q_ASSERT(model() != NULL); + Q_ASSERT(model() != nullptr); if (index.isValid()) { diff --git a/apps/opencs/view/world/dragrecordtable.hpp b/apps/opencs/view/world/dragrecordtable.hpp index 560864ba5a..9e29b61451 100644 --- a/apps/opencs/view/world/dragrecordtable.hpp +++ b/apps/opencs/view/world/dragrecordtable.hpp @@ -28,7 +28,7 @@ namespace CSVWorld bool mEditLock; public: - DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL); + DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr); virtual std::vector getDraggedRecords() const = 0; diff --git a/apps/opencs/view/world/extendedcommandconfigurator.cpp b/apps/opencs/view/world/extendedcommandconfigurator.cpp index 2cf6222a6e..8947420242 100644 --- a/apps/opencs/view/world/extendedcommandconfigurator.cpp +++ b/apps/opencs/view/world/extendedcommandconfigurator.cpp @@ -95,7 +95,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout() int divider = 1; do { - while (layout->itemAt(0) != NULL) + while (layout->itemAt(0) != nullptr) { layout->removeItem(layout->itemAt(0)); } diff --git a/apps/opencs/view/world/idcompletiondelegate.cpp b/apps/opencs/view/world/idcompletiondelegate.cpp index 7f0f4ae460..4ff850b9f4 100644 --- a/apps/opencs/view/world/idcompletiondelegate.cpp +++ b/apps/opencs/view/world/idcompletiondelegate.cpp @@ -25,7 +25,7 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, { if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) { - return NULL; + return nullptr; } // The completer for InfoCondVar needs to return a completer based on the first column diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 02bd93920c..1b72211e8e 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -23,9 +23,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, bool editable, bool fixedRows) : DragRecordTable(document, parent), - mAddNewRowAction(NULL), - mRemoveRowAction(NULL), - mEditIdAction(NULL), + mAddNewRowAction(nullptr), + mRemoveRowAction(nullptr), + mEditIdAction(nullptr), mModel(model) { mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp index 765060ea5b..b39c7e560a 100644 --- a/apps/opencs/view/world/nestedtable.hpp +++ b/apps/opencs/view/world/nestedtable.hpp @@ -38,7 +38,7 @@ namespace CSVWorld NestedTable(CSMDoc::Document& document, CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, - QWidget* parent = NULL, + QWidget* parent = nullptr, bool editable = true, bool fixedRows = false); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index b14d708dae..b03cf8fdb9 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -27,7 +27,7 @@ #include "creator.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id), mScene(NULL), mLayout(new QHBoxLayout), mDocument(document), mToolbar(NULL) +: SubView (id), mScene(nullptr), mLayout(new QHBoxLayout), mDocument(document), mToolbar(nullptr) { QVBoxLayout *layout = new QVBoxLayout; @@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); - CSVRender::WorldspaceWidget* worldspaceWidget = NULL; + CSVRender::WorldspaceWidget* worldspaceWidget = nullptr; widgetType whatWidget; if (id.getId()==ESM::CellId::sDefaultWorldspace) @@ -189,9 +189,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData) { - CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; - CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; - CSVWidget::SceneToolbar* toolbar = NULL; + CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr; + CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr; + CSVWidget::SceneToolbar* toolbar = nullptr; CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index b194e460d3..8cdb2d2dbe 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -129,7 +129,7 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM // Color columns use a custom editor, so we need to fetch selected color from it. CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { variant = colorEditor->colorInt(); } @@ -322,7 +322,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde // Color columns use a custom editor, so we need explicitly set a data for it CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { colorEditor->setColor(variant.toInt()); return; diff --git a/apps/openmw/android_main.c b/apps/openmw/android_main.c index d234a369dc..9a7b6bc501 100644 --- a/apps/openmw/android_main.c +++ b/apps/openmw/android_main.c @@ -20,14 +20,14 @@ void releaseArgv(); int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(&ret, NULL); + SDL_GetMouseState(&ret, nullptr); return ret; } int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(NULL, &ret); + SDL_GetMouseState(nullptr, &ret); return ret; } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 057113ba35..f5b4171d4f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -197,9 +197,9 @@ bool OMW::Engine::frame(float frametime) } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) - : mWindow(NULL) + : mWindow(nullptr) , mEncoding(ToUTF8::WINDOWS_1252) - , mEncoder(NULL) + , mEncoder(nullptr) , mScreenCaptureOperation(nullptr) , mSkipMenu (false) , mUseSound (true) @@ -237,18 +237,18 @@ OMW::Engine::~Engine() mEnvironment.cleanup(); delete mScriptContext; - mScriptContext = NULL; + mScriptContext = nullptr; - mWorkQueue = NULL; + mWorkQueue = nullptr; mResourceSystem.reset(); - mViewer = NULL; + mViewer = nullptr; if (mWindow) { SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; } SDL_Quit(); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index efe460b00c..e1784d5937 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -74,7 +74,7 @@ namespace MWClass if (ref->mBase->mFlags & ESM::Container::Respawn) { MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6999558ae9..6d0b42bfeb 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -298,7 +298,7 @@ namespace MWClass bool healthdmg = true; if (!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -828,7 +828,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 3ee2280137..ea0abd6f64 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -41,7 +41,7 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->mBase != NULL); + assert (ref->mBase != nullptr); // TODO: add option somewhere to enable collision for placeable objects if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4979db2fd4..1daf285429 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -610,7 +610,7 @@ namespace MWClass float damage = 0.0f; if(!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -1382,7 +1382,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index dcc33d1c32..17f69d69b7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -572,7 +572,7 @@ namespace MWDialogue const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); Filter filter(actor, 0, creatureStats.hasTalkedToPlayer()); const ESM::DialInfo *info = filter.search(*dial, false); - if(info != NULL) + if(info != nullptr) { MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 15020898fb..042ccb019a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -621,7 +621,7 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); if (suitableInfos.empty()) - return NULL; + return nullptr; else return suitableInfos[0]; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 9f74d0733d..5eab6d5cae 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -30,7 +30,7 @@ namespace MWDialogue { if (actor.isEmpty()) { - MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr()); + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); } else diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index a599e6c7b6..fa8a96185f 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -30,7 +30,7 @@ namespace MWGui AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") - , mSortModel(NULL) + , mSortModel(nullptr) , mAlchemy(new MWMechanics::Alchemy()) , mApparatus (4) , mIngredients (4) diff --git a/apps/openmw/mwgui/backgroundimage.cpp b/apps/openmw/mwgui/backgroundimage.cpp index 98828a0411..55c825ebbd 100644 --- a/apps/openmw/mwgui/backgroundimage.cpp +++ b/apps/openmw/mwgui/backgroundimage.cpp @@ -10,7 +10,7 @@ void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRa if (mChild) { MyGUI::Gui::getInstance().destroyWidget(mChild); - mChild = NULL; + mChild = nullptr; } if (!stretch) { diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 8c963b7620..3db5bab16a 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -14,7 +14,7 @@ namespace MWGui MYGUI_RTTI_DERIVED(BackgroundImage) public: - BackgroundImage() : mChild(NULL), mAspect(0) {} + BackgroundImage() : mChild(nullptr), mAspect(0) {} /** * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index c3b8935e81..29dfe7f3a6 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -110,7 +110,7 @@ struct TypesetBookImpl : TypesetBook Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); if (i->empty()) - return Range (Utf8Point (NULL), Utf8Point (NULL)); + return Range (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point begin = &i->front (); Utf8Point end = &i->front () + i->size (); @@ -148,7 +148,7 @@ struct TypesetBookImpl : TypesetBook template void visitRuns (int top, int bottom, Visitor const & visitor) const { - visitRuns (top, bottom, NULL, visitor); + visitRuns (top, bottom, nullptr, visitor); } /// hit test with a margin for error. only hits on interactive text fragments are reported. @@ -176,7 +176,7 @@ struct TypesetBookImpl : TypesetBook return hit; } } - return NULL; + return nullptr; } StyleImpl * hitTest (int left, int top) const @@ -213,7 +213,7 @@ struct TypesetBookImpl : TypesetBook for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) if (&*i == style) return i->mFont; - return NULL; + return nullptr; } struct Typesetter; @@ -253,8 +253,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Typesetter (size_t width, size_t height) : mPageWidth (width), mPageHeight(height), - mSection (NULL), mLine (NULL), mRun (NULL), - mCurrentContent (NULL), + mSection (nullptr), mLine (nullptr), mRun (nullptr), + mCurrentContent (nullptr), mCurrentAlignment (AlignLeft) { mBook = std::make_shared (); @@ -342,7 +342,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void write (Style * style, size_t begin, size_t end) { - assert (mCurrentContent != NULL); + assert (mCurrentContent != nullptr); assert (end <= mCurrentContent->size ()); assert (begin <= mCurrentContent->size ()); @@ -358,8 +358,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter add_partial_text(); - mRun = NULL; - mLine = NULL; + mRun = nullptr; + mLine = nullptr; } void sectionBreak (int margin) @@ -368,9 +368,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mBook->mSections.size () > 0) { - mRun = NULL; - mLine = NULL; - mSection = NULL; + mRun = nullptr; + mLine = nullptr; + mSection = nullptr; if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); @@ -381,7 +381,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); - if (mSection != NULL) + if (mSection != nullptr) mSectionAlignment.back () = sectionAlignment; mCurrentAlignment = sectionAlignment; } @@ -491,7 +491,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); stream.consume (); - mLine = NULL, mRun = NULL; + mLine = nullptr, mRun = nullptr; continue; } @@ -551,7 +551,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (left + space_width + word_width > mPageWidth) { - mLine = NULL, mRun = NULL, left = 0; + mLine = nullptr, mRun = nullptr, left = 0; } else { @@ -580,7 +580,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) { - if (mSection == NULL) + if (mSection == nullptr) { mBook->mSections.push_back (Section ()); mSection = &mBook->mSections.back (); @@ -588,7 +588,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter mSectionAlignment.push_back (mCurrentAlignment); } - if (mLine == NULL) + if (mLine == nullptr) { mSection->mLines.push_back (Line ()); mLine = &mSection->mLines.back (); @@ -613,7 +613,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mLine->mRect.bottom < bottom) mLine->mRect.bottom = bottom; - if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) + if (mRun == nullptr || mRun->mStyle != style || mRun->mRange.second != begin) { int left = mRun ? mRun->mRight : mLine->mRect.left; @@ -843,17 +843,17 @@ protected: TextFormat (MyGUI::IFont* id, PageDisplay * display) : mFont (id), mCountVertex (0), - mTexture (NULL), - mRenderItem (NULL), + mTexture (nullptr), + mRenderItem (nullptr), mDisplay (display) { } void createDrawItem (MyGUI::ILayerNode* node) { - assert (mRenderItem == NULL); + assert (mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem = node->addToRenderItem(mTexture, false, false); mRenderItem->addDrawItem(this, mCountVertex); @@ -862,12 +862,12 @@ protected: void destroyDrawItem (MyGUI::ILayerNode* node) { - assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); + assert (mTexture != nullptr ? mRenderItem != nullptr : mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem->removeDrawItem (this); - mRenderItem = NULL; + mRenderItem = nullptr; } } @@ -920,9 +920,9 @@ public: resetPage (); mViewTop = 0; mViewBottom = 0; - mFocusItem = NULL; + mFocusItem = nullptr; mItemActive = false; - mNode = NULL; + mNode = nullptr; } void dirtyFocusItem () @@ -1053,7 +1053,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) { - if (mNode != NULL) + if (mNode != nullptr) i->second->destroyDrawItem (mNode); i->second.reset(); } @@ -1089,7 +1089,7 @@ public: else if (mBook && isPageDifferent (newPage)) { - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); @@ -1137,7 +1137,7 @@ public: { newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (mNode); } @@ -1238,7 +1238,7 @@ public: { _checkMargin(); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); } @@ -1247,7 +1247,7 @@ public: { _checkMargin (); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); @@ -1258,7 +1258,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); - mNode = NULL; + mNode = nullptr; } }; @@ -1269,7 +1269,7 @@ MYGUI_RTTI_DERIVED(BookPage) public: BookPageImpl() - : mPageDisplay(NULL) + : mPageDisplay(nullptr) { } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6ed7a44915..45abe889ed 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -399,12 +399,12 @@ namespace MWGui CreateClassDialog::CreateClassDialog() : WindowModal("openmw_chargen_create_class.layout") - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) - , mAffectedAttribute(NULL) - , mAffectedSkill(NULL) + , mSpecDialog(nullptr) + , mAttribDialog(nullptr) + , mSkillDialog(nullptr) + , mDescDialog(nullptr) + , mAffectedAttribute(nullptr) + , mAffectedSkill(nullptr) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index c6ef2890d1..b2639e938a 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -37,8 +37,8 @@ namespace MWGui CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) : WindowBase("openmw_companion_window.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) , mDragAndDrop(dragAndDrop) , mMessageBoxManager(manager) @@ -89,7 +89,7 @@ void CompanionWindow::onItemSelected(int index) dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) @@ -179,9 +179,9 @@ void CompanionWindow::onReferenceUnavailable() void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 99d0ea0d13..2f9643f740 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -31,8 +31,8 @@ namespace MWGui ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); @@ -83,7 +83,7 @@ namespace MWGui dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) @@ -151,9 +151,9 @@ namespace MWGui void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } void ContainerWindow::onClose() @@ -171,7 +171,7 @@ namespace MWGui void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop) return; MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); @@ -219,7 +219,7 @@ namespace MWGui void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop) { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 4292f2e045..baf3a43abd 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -63,7 +63,7 @@ namespace MWGui void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); } @@ -72,7 +72,7 @@ namespace MWGui // Enter key void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); // To do not spam onEnterKeyPressed() again and again diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp index 82e0b9699b..ed8a76eb87 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -10,7 +10,7 @@ namespace MWGui ResourceImageSetPointerFix::ResourceImageSetPointerFix() - : mImageSet(NULL) + : mImageSet(nullptr) , mRotation(0) { } @@ -47,7 +47,7 @@ namespace MWGui void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) { - if (mImageSet != NULL) + if (mImageSet != nullptr) _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); } diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index d81b2ed006..daf9f66367 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -20,10 +20,10 @@ namespace MWGui DragAndDrop::DragAndDrop() : mIsOnDragAndDrop(false) - , mDraggedWidget(NULL) - , mSourceModel(NULL) - , mSourceView(NULL) - , mSourceSortModel(NULL) + , mDraggedWidget(nullptr) + , mSourceModel(nullptr) + , mSourceView(nullptr) + , mSourceSortModel(nullptr) , mDraggedCount(0) { } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 039377dee3..98980e3397 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -28,7 +28,7 @@ namespace MWGui EnchantingDialog::EnchantingDialog() : WindowBase("openmw_enchanting_dialog.layout") , EffectEditorBase(EffectEditorBase::Enchanting) - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); diff --git a/apps/openmw/mwgui/exposedwindow.cpp b/apps/openmw/mwgui/exposedwindow.cpp index 150a8c893a..1a0484e729 100644 --- a/apps/openmw/mwgui/exposedwindow.cpp +++ b/apps/openmw/mwgui/exposedwindow.cpp @@ -14,7 +14,7 @@ namespace MWGui if (widgets.empty()) { MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' not found in skin of layout '" << getName() << "'"); - return NULL; + return nullptr; } else { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index fd5ed4faae..78359608ec 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -27,7 +27,7 @@ namespace MWGui BookTextParser::BookTextParser(const std::string & text) : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false) { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); // empty arguments, because there is no locals or actor mText = Interpreter::fixDefinesBook(mText, interpreterContext); boost::algorithm::replace_all(mText, "\r", ""); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 91467d490e..6076c1c10d 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -70,20 +70,20 @@ namespace MWGui HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) : WindowBase("openmw_hud.layout") , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map")) - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mDrowning(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mMinimap(NULL) - , mCrosshair(NULL) - , mCellNameBox(NULL) - , mDrowningFrame(NULL) - , mDrowningFlash(NULL) + , mHealth(nullptr) + , mMagicka(nullptr) + , mStamina(nullptr) + , mDrowning(nullptr) + , mWeapImage(nullptr) + , mSpellImage(nullptr) + , mWeapStatus(nullptr) + , mSpellStatus(nullptr) + , mEffectBox(nullptr) + , mMinimap(nullptr) + , mCrosshair(nullptr) + , mCellNameBox(nullptr) + , mDrowningFrame(nullptr) + , mDrowningFlash(nullptr) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) @@ -247,7 +247,7 @@ namespace MWGui float mouseY = cursorPosition.top / float(viewSize.height); WorldItemModel drop (mouseX, mouseY); - mDragAndDrop->drop(&drop, NULL); + mDragAndDrop->drop(&drop, nullptr); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0d37bbff3b..1877ef97d8 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -60,8 +60,8 @@ namespace MWGui : WindowPinnableBase("openmw_inventory_window.layout") , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mGuiMode(GM_Inventory) , mLastXSize(0) , mLastYSize(0) @@ -157,9 +157,9 @@ namespace MWGui void InventoryWindow::clear() { mPtr = MWWorld::Ptr(); - mTradeModel = NULL; - mSortModel = NULL; - mItemView->setModel(NULL); + mTradeModel = nullptr; + mSortModel = nullptr; + mItemView->setModel(nullptr); } void InventoryWindow::setGuiMode(GuiMode mode) @@ -296,9 +296,9 @@ namespace MWGui { mSelectedItem = index; if (mTrading) - sellItem (NULL, count); + sellItem (nullptr, count); else - dragItem (NULL, count); + dragItem (nullptr, count); } } diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 3001b9ef2f..10c36c73f8 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -21,7 +21,7 @@ namespace MWGui { ItemChargeView::ItemChargeView() - : mScrollView(NULL), + : mScrollView(nullptr), mDisplayMode(DisplayMode_Health) { } @@ -36,7 +36,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item charge view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index e3f38a54c1..c7c10e8e4b 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -31,7 +31,7 @@ namespace MWGui ItemStack::ItemStack() : mType(Type_Normal) , mFlags(0) - , mCreator(NULL) + , mCreator(nullptr) , mCount(0) { } @@ -106,7 +106,7 @@ namespace MWGui ProxyItemModel::ProxyItemModel() - : mSourceModel(NULL) + : mSourceModel(nullptr) { } @@ -167,7 +167,7 @@ namespace MWGui if (mSourceModel) { delete mSourceModel; - mSourceModel = NULL; + mSourceModel = nullptr; } mSourceModel = sourceModel; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index effd11d557..23f1398eaa 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -12,8 +12,8 @@ namespace MWGui ItemSelectionDialog::ItemSelectionDialog(const std::string &label) : WindowModal("openmw_itemselection_dialog.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) { getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 2cdfcada4d..94dcc77c5e 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -18,8 +18,8 @@ namespace MWGui { ItemView::ItemView() - : mModel(NULL) - , mScrollView(NULL) + : mModel(nullptr) + , mScrollView(nullptr) { } @@ -44,7 +44,7 @@ void ItemView::initialiseOverride() Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 6b5be0314b..223bced4fa 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -32,10 +32,10 @@ namespace MWGui { ItemWidget::ItemWidget() - : mItem(NULL) - , mItemShadow(NULL) - , mFrame(NULL) - , mText(NULL) + : mItem(nullptr) + , mItemShadow(nullptr) + , mFrame(nullptr) + , mText(nullptr) { } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 63b48eab12..3621656bc3 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -39,7 +39,7 @@ struct JournalViewModelImpl : JournalViewModel static Utf8Span toUtf8Span (std::string const & str) { if (str.size () == 0) - return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); + return Utf8Span (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point point = reinterpret_cast (str.c_str ()); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index be3d477e1e..38ab70d544 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -140,7 +140,7 @@ namespace MWGui // Callback removes itself when done if (renderInfo.getCurrentCamera()) - renderInfo.getCurrentCamera()->setInitialDrawCallback(NULL); + renderInfo.getCurrentCamera()->setInitialDrawCallback(nullptr); } private: @@ -208,7 +208,7 @@ namespace MWGui else mImportantLabel = false; // label was already shown on loading screen - mViewer->getSceneData()->setComputeBoundingSphereCallback(NULL); + mViewer->getSceneData()->setComputeBoundingSphereCallback(nullptr); mViewer->getSceneData()->dirtyBound(); //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5017b88932..1b9e024ba6 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -25,10 +25,10 @@ namespace MWGui : WindowBase("openmw_mainmenu.layout") , mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0) - , mBackground(NULL) - , mVideoBackground(NULL) - , mVideo(NULL) - , mSaveGameDialog(NULL) + , mBackground(nullptr) + , mVideoBackground(nullptr) + , mVideo(nullptr) + , mSaveGameDialog(nullptr) { getWidget(mVersionText, "VersionText"); mVersionText->setCaption(versionDescription); @@ -147,13 +147,13 @@ namespace MWGui if (mVideo && !show) { MyGUI::Gui::getInstance().destroyWidget(mVideoBackground); - mVideoBackground = NULL; - mVideo = NULL; + mVideoBackground = nullptr; + mVideo = nullptr; } if (mBackground && !show) { MyGUI::Gui::getInstance().destroyWidget(mBackground); - mBackground = NULL; + mBackground = nullptr; } if (!show) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2c8ad0565f..b9b59965db 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -161,8 +161,8 @@ namespace MWGui , mCurX(0) , mCurY(0) , mInterior(false) - , mLocalMap(NULL) - , mCompass(NULL) + , mLocalMap(nullptr) + , mCompass(nullptr) , mChanged(true) , mFogOfWarToggled(true) , mFogOfWarEnabled(fogOfWarEnabled) @@ -388,7 +388,7 @@ namespace MWGui box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } else - box->setRenderItemTexture(NULL); + box->setRenderItemTexture(nullptr); } } mMapTextures.swap(textures); @@ -598,7 +598,7 @@ namespace MWGui addDetectionMarkers(MWBase::World::Detect_Enchantment); // Add marker for the spot marked with Mark magic effect - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell && markedCell->isExterior() == !mInterior @@ -627,11 +627,11 @@ namespace MWGui , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) , mGlobalMap(0) - , mGlobalMapImage(NULL) - , mGlobalMapOverlay(NULL) + , mGlobalMapImage(nullptr) + , mGlobalMapOverlay(nullptr) , mGlobal(Settings::Manager::getBool("global", "Map")) - , mEventBoxGlobal(NULL) - , mEventBoxLocal(NULL) + , mEventBoxGlobal(nullptr) + , mEventBoxLocal(nullptr) , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { @@ -872,7 +872,7 @@ namespace MWGui if (!destNotes.empty()) { - MarkerUserData data (NULL); + MarkerUserData data (nullptr); data.notes = destNotes; data.caption = markerWidget->getUserString("Caption_TextOneLine"); markerWidget->setUserData(data); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2fbce97d46..e83c4b238a 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -20,8 +20,8 @@ namespace MWGui MessageBoxManager::MessageBoxManager (float timePerChar) { - mInterMessageBoxe = NULL; - mStaticMessageBox = NULL; + mInterMessageBoxe = nullptr; + mStaticMessageBox = nullptr; mLastButtonPressed = -1; mMessageBoxSpeed = timePerChar; } @@ -42,14 +42,14 @@ namespace MWGui mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) { if (*it == mStaticMessageBox) - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; delete *it; } mMessageBoxes.clear(); @@ -81,11 +81,11 @@ namespace MWGui ++it; } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + if(mInterMessageBoxe != nullptr && mInterMessageBoxe->mMarkedToDelete) { mLastButtonPressed = mInterMessageBoxe->readPressedButton(); mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; MWBase::Environment::get().getInputManager()->changeInputMode( MWBase::Environment::get().getWindowManager()->isGuiMode()); } @@ -119,17 +119,17 @@ namespace MWGui void MessageBoxManager::removeStaticMessageBox () { removeMessageBox(mStaticMessageBox); - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; } bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) { - if (mInterMessageBoxe != NULL) + if (mInterMessageBoxe != nullptr) { Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet"; mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); @@ -140,7 +140,7 @@ namespace MWGui bool MessageBoxManager::isInteractiveMessageBox () { - return mInterMessageBoxe != NULL; + return mInterMessageBoxe != nullptr; } @@ -373,7 +373,7 @@ namespace MWGui } } } - return NULL; + return nullptr; } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index ee058e02e3..be0dff6600 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -129,10 +129,10 @@ namespace MWGui updateSkills(); updateSpellPowers(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreview.reset(NULL); - mPreviewTexture.reset(NULL); + mPreview.reset(nullptr); + mPreviewTexture.reset(nullptr); mPreview.reset(new MWRender::RaceSelectionPreview(mParent, mResourceSystem)); mPreview->rebuild(); @@ -190,10 +190,10 @@ namespace MWGui { WindowModal::onClose(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreviewTexture.reset(NULL); - mPreview.reset(NULL); + mPreviewTexture.reset(nullptr); + mPreview.reset(nullptr); } // widget controls diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index a6ed16bd40..93047956af 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -31,7 +31,7 @@ namespace MWGui Recharge::Recharge() : WindowBase("openmw_recharge_dialog.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mBox, "Box"); getWidget(mGemBox, "GemBox"); @@ -90,7 +90,7 @@ void Recharge::updateView() mBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 11a8aece29..ea79e0326e 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -27,7 +27,7 @@ namespace MWGui Repair::Repair() : WindowBase("openmw_repair.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mRepairBox, "RepairBox"); getWidget(mToolBox, "ToolBox"); @@ -99,7 +99,7 @@ void Repair::updateRepairView() mRepairBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b59b1582cf..f2f1cf8921 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -379,7 +379,7 @@ namespace MWGui // starting spells std::vector spells; - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (!mRaceId.empty()) race = MWBase::Environment::get().getWorld()->getStore().get().find(mRaceId); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 3264e5e339..2027210d7b 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -37,8 +37,8 @@ namespace MWGui SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) - , mCurrentCharacter(NULL) - , mCurrentSlot(NULL) + , mCurrentCharacter(nullptr) + , mCurrentSlot(nullptr) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); @@ -99,7 +99,7 @@ namespace MWGui if (mSaveList->getItemCount() == 0) { size_t previousIndex = mCharacterSelection->getIndexSelected(); - mCurrentCharacter = NULL; + mCurrentCharacter = nullptr; mCharacterSelection->removeItemAt(previousIndex); if (mCharacterSelection->getItemCount()) { @@ -146,8 +146,8 @@ namespace MWGui mCharacterSelection->setCaption(""); mCharacterSelection->removeAllItems(); - mCurrentCharacter = NULL; - mCurrentSlot = NULL; + mCurrentCharacter = nullptr; + mCurrentSlot = nullptr; mSaveList->removeAllItems(); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); @@ -250,12 +250,12 @@ namespace MWGui void SaveGameDialog::accept(bool reallySure) { // Remove for MyGUI 3.2.2 - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); if (mSaving) { // If overwriting an existing slot, ask for confirmation first - if (mCurrentSlot != NULL && !reallySure) + if (mCurrentSlot != nullptr && !reallySure) { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage4}"); @@ -318,7 +318,7 @@ namespace MWGui MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); unsigned int i=0; - const MWState::Character* character = NULL; + const MWState::Character* character = nullptr; for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i) { if (i == pos) @@ -327,7 +327,7 @@ namespace MWGui assert(character && "Can't find selected character"); mCurrentCharacter = character; - mCurrentSlot = NULL; + mCurrentSlot = nullptr; fillSaveList(); } @@ -379,7 +379,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { - mCurrentSlot = NULL; + mCurrentSlot = nullptr; mInfoText->setCaption(""); mScreenshot->setImageTexture(""); return; @@ -388,7 +388,7 @@ namespace MWGui if (mSaving) mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); - mCurrentSlot = NULL; + mCurrentSlot = nullptr; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) { @@ -404,7 +404,7 @@ namespace MWGui timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting - char* oldLctime = setlocale(LC_TIME, NULL); + char* oldLctime = setlocale(LC_TIME, nullptr); setlocale(LC_TIME, ""); const int size=1024; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 2062831337..46d8b38ecb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -55,7 +55,7 @@ namespace MWGui EditEffectDialog::EditEffectDialog() : WindowModal("openmw_edit_effect.layout") , mEditing(false) - , mMagicEffect(NULL) + , mMagicEffect(nullptr) , mConstantEffect(false) { init(mEffect); @@ -481,7 +481,7 @@ namespace MWGui mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL); + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); int intChance = std::min(100, int(chance)); mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); @@ -491,11 +491,11 @@ namespace MWGui EffectEditorBase::EffectEditorBase(Type type) - : mAvailableEffectsList(NULL) - , mUsedEffectsView(NULL) + : mAvailableEffectsList(nullptr) + , mUsedEffectsView(nullptr) , mAddEffectDialog() - , mSelectAttributeDialog(NULL) - , mSelectSkillDialog(NULL) + , mSelectAttributeDialog(nullptr) + , mSelectSkillDialog(nullptr) , mSelectedEffect(0) , mSelectedKnownEffectId(0) , mConstantEffect(false) diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 0933737ca2..6dadebca27 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -71,7 +71,7 @@ namespace MWGui { newSpell.mType = Spell::Type_Spell; std::string cost = std::to_string(spell->mData.mCost); - std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, NULL, true, true))); + std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, nullptr, true, true))); newSpell.mCostColumn = cost + "/" + chance; } else diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 702c2f8409..879ce471f0 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -24,7 +24,7 @@ namespace MWGui } SpellView::SpellView() - : mScrollView(NULL) + : mScrollView(nullptr) , mShowCostColumn(true) , mHighlightSelected(true) { @@ -35,7 +35,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); @@ -131,7 +131,7 @@ namespace MWGui mLines.push_back(LineInfo(t, costChance, i)); } else - mLines.push_back(LineInfo(t, (MyGUI::Widget*)NULL, i)); + mLines.push_back(LineInfo(t, (MyGUI::Widget*)nullptr, i)); t->setStateSelected(spell.mSelected); } @@ -177,7 +177,7 @@ namespace MWGui { maxSpellIndexFound = spellIndex; Gui::SharedStateButton* costButton = reinterpret_cast(it->mRightWidget); - if ((costButton != NULL) && (costButton->getCaption() != spell.mCostColumn)) + if ((costButton != nullptr) && (costButton->getCaption() != spell.mCostColumn)) { costButton->setCaption(spell.mCostColumn); } @@ -238,7 +238,7 @@ namespace MWGui MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); - mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(separator, (MyGUI::Widget*)nullptr, NoSpellIndex)); } MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", @@ -260,7 +260,7 @@ namespace MWGui mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex)); } else - mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex)); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 38de9288bf..2177e9e2af 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -32,7 +32,7 @@ namespace MWGui SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) - , mSpellView(NULL) + , mSpellView(nullptr) , mUpdateTimer(0.0f) { mSpellIcons = new SpellIcons(); @@ -72,7 +72,7 @@ namespace MWGui // Reset the filter focus when opening the window MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (focus == mFilterEdit) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); updateSpells(); } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index dfa0294670..e58993a55e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -26,7 +26,7 @@ namespace MWGui StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) - , mSkillView(NULL) + , mSkillView(nullptr) , mMajorSkills() , mMinorSkills() , mMiscSkills() @@ -68,7 +68,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); - mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)NULL, (MyGUI::TextBox*)NULL))); + mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr))); } MyGUI::Window* t = mMainWidget->castType(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 726c72b99e..4404b2b1a6 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -50,8 +50,8 @@ namespace MWGui TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mItemToSell(-1) , mCurrentBalance(0) , mCurrentMerchantOffer(0) @@ -205,7 +205,7 @@ namespace MWGui else { mItemToSell = mSortModel->mapToSource(index); - sellItem (NULL, count); + sellItem (nullptr, count); } } @@ -514,8 +514,8 @@ namespace MWGui void TradeWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mTradeModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mTradeModel = nullptr; + mSortModel = nullptr; } } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 28432b811d..5dec6e48ad 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -16,7 +16,7 @@ namespace MWGui { VideoWidget::VideoWidget() - : mVFS(NULL) + : mVFS(nullptr) { mPlayer.reset(new Video::VideoPlayer()); setNeedKeyFocus(true); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index db92b355ce..037fcb19be 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -281,7 +281,7 @@ namespace MWGui mSleeping = canRest; Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); center(); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 024a91fc60..c3bc1ec196 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -23,8 +23,8 @@ namespace MWGui MWSkill::MWSkill() : mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) + , mSkillNameWidget(nullptr) + , mSkillValueWidget(nullptr) { } @@ -114,8 +114,8 @@ namespace MWGui MWAttribute::MWAttribute() : mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) + , mAttributeNameWidget(nullptr) + , mAttributeValueWidget(nullptr) { } @@ -204,7 +204,7 @@ namespace MWGui /* MWSpell */ MWSpell::MWSpell() - : mSpellNameWidget(NULL) + : mSpellNameWidget(nullptr) { } @@ -286,7 +286,7 @@ namespace MWGui { // We don't know the width of all the elements beforehand, so we do it in // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = NULL; + MWSpellEffectPtr effect = nullptr; int maxwidth = coord.width; for (SpellEffectList::iterator it=mEffectList.begin(); @@ -359,8 +359,8 @@ namespace MWGui /* MWSpellEffect */ MWSpellEffect::MWSpellEffect() - : mImageWidget(NULL) - , mTextWidget(NULL) + : mImageWidget(nullptr) + , mTextWidget(nullptr) , mRequestedWidth(0) { } @@ -489,9 +489,9 @@ namespace MWGui MWDynamicStat::MWDynamicStat() : mValue(0) , mMax(1) - , mTextWidget(NULL) - , mBarWidget(NULL) - , mBarTextWidget(NULL) + , mTextWidget(nullptr) + , mBarWidget(nullptr) + , mBarTextWidget(nullptr) { } diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 93440a50a8..86c780325a 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -33,11 +33,11 @@ void WindowBase::setVisible(bool visible) if (!visible) { MyGUI::Widget* keyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); - while (keyFocus != mMainWidget && keyFocus != NULL) + while (keyFocus != mMainWidget && keyFocus != nullptr) keyFocus = keyFocus->getParent(); if (keyFocus == mMainWidget) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); } } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fde1f2ac93..7d8fb2f1e6 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -23,7 +23,7 @@ namespace MWGui public: WindowBase(const std::string& parLayout); - virtual MyGUI::Widget* getDefaultKeyFocus() { return NULL; } + virtual MyGUI::Widget* getDefaultKeyFocus() { return nullptr; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7fca0058fd..e4515fdc3f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,44 +134,44 @@ namespace MWGui osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& userDataPath) - : mStore(NULL) + : mStore(nullptr) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() - , mHud(NULL) - , mMap(NULL) - , mLocalMapRender(NULL) - , mToolTips(NULL) - , mStatsWindow(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mDialogueWindow(NULL) - , mDragAndDrop(NULL) - , mInventoryWindow(NULL) - , mScrollWindow(NULL) - , mBookWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mSpellWindow(NULL) - , mQuickKeysMenu(NULL) - , mLoadingScreen(NULL) - , mWaitDialog(NULL) - , mSoulgemDialog(NULL) - , mVideoBackground(NULL) - , mVideoWidget(NULL) - , mWerewolfFader(NULL) - , mBlindnessFader(NULL) - , mHitFader(NULL) - , mScreenFader(NULL) - , mDebugWindow(NULL) - , mJailScreen(NULL) + , mHud(nullptr) + , mMap(nullptr) + , mLocalMapRender(nullptr) + , mToolTips(nullptr) + , mStatsWindow(nullptr) + , mMessageBoxManager(nullptr) + , mConsole(nullptr) + , mDialogueWindow(nullptr) + , mDragAndDrop(nullptr) + , mInventoryWindow(nullptr) + , mScrollWindow(nullptr) + , mBookWindow(nullptr) + , mCountDialog(nullptr) + , mTradeWindow(nullptr) + , mSettingsWindow(nullptr) + , mConfirmationDialog(nullptr) + , mSpellWindow(nullptr) + , mQuickKeysMenu(nullptr) + , mLoadingScreen(nullptr) + , mWaitDialog(nullptr) + , mSoulgemDialog(nullptr) + , mVideoBackground(nullptr) + , mVideoWidget(nullptr) + , mWerewolfFader(nullptr) + , mBlindnessFader(nullptr) + , mHitFader(nullptr) + , mScreenFader(nullptr) + , mDebugWindow(nullptr) + , mJailScreen(nullptr) , mTranslationDataStorage (translationDataStorage) - , mCharGen(NULL) - , mInputBlocker(NULL) + , mCharGen(nullptr) + , mInputBlocker(nullptr) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI")) @@ -185,9 +185,9 @@ namespace MWGui , mPlayerMajorSkills() , mPlayerMinorSkills() , mPlayerSkillValues() - , mGui(NULL) + , mGui(nullptr) , mGuiModes() - , mCursorManager(NULL) + , mCursorManager(nullptr) , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) @@ -704,7 +704,7 @@ namespace MWGui setCursorVisible(!gameMode); if (gameMode) - setKeyFocusWidget (NULL); + setKeyFocusWidget (nullptr); // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); @@ -1651,7 +1651,7 @@ namespace MWGui // Remove this method for MyGUI 3.2.2 void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) { - if (widget == NULL) + if (widget == nullptr) MyGUI::InputManager::getInstance().resetKeyFocusWidget(); else MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); @@ -1913,7 +1913,7 @@ namespace MWGui } if (mCurrentModals.empty()) { - mKeyboardNavigation->setModalWindow(NULL); + mKeyboardNavigation->setModalWindow(nullptr); mKeyboardNavigation->restoreFocus(getMode()); } else diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index a6984b5a48..271d4e3a85 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -14,7 +14,7 @@ namespace MWGui mPinButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonPressed); - MyGUI::Button* button = NULL; + MyGUI::Button* button = nullptr; MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action"); for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it) { diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 101b4f2f73..b0a98bafc2 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,9 +46,9 @@ namespace MWInput , mScreenCaptureHandler(screenCaptureHandler) , mScreenCaptureOperation(screenCaptureOperation) , mJoystickLastUsed(false) - , mPlayer(NULL) - , mInputManager(NULL) - , mVideoWrapper(NULL) + , mPlayer(nullptr) + , mInputManager(nullptr) + , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) @@ -84,7 +84,7 @@ namespace MWInput Settings::Manager::getFloat("contrast", "Video")); std::string file = userFileExists ? userFile : ""; - mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); + mInputBinder = new ICS::InputControlSystem(file, true, this, nullptr, A_Last); loadKeyDefaults(); loadControllerDefaults(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e18ba41191..2a68591a8f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1420,7 +1420,7 @@ namespace MWMechanics iter->second->getCharacterController()->updateContinuousVfx(); // Animation/movement update - CharacterController* playerCharacter = NULL; + CharacterController* playerCharacter = nullptr; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. @@ -1987,7 +1987,7 @@ namespace MWMechanics for (; it != mActors.end(); ++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } mActors.clear(); mDeathCount.clear(); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9824347e32..8f9545f99d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -586,7 +586,7 @@ std::string chooseBestAttack(const ESM::Weapon* weapon) { std::string attackType; - if (weapon != NULL) + if (weapon != nullptr) { //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 88feba4811..f89e716782 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -64,7 +64,7 @@ namespace MWMechanics mAttackRange(0.0f), mCombatMove(false), mLastTargetPos(0,0,0), - mCell(NULL), + mCell(nullptr), mCurrentAction(), mActionCooldown(0.0f), mStrength(), diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 6fffff6377..804e0dd059 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -138,7 +138,7 @@ namespace MWMechanics const ESM::Weapon* ActionWeapon::getWeapon() const { if (mWeapon.isEmpty()) - return NULL; + return nullptr; return mWeapon.get()->mBase; } diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 466ae2dc43..cf7dc78fd9 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -17,7 +17,7 @@ namespace MWMechanics virtual void prepare(const MWWorld::Ptr& actor) = 0; virtual float getCombatRange (bool& isRanged) const = 0; virtual float getActionCooldown() { return 0.f; } - virtual const ESM::Weapon* getWeapon() const { return NULL; }; + virtual const ESM::Weapon* getWeapon() const { return nullptr; }; virtual bool isAttackingOrSpell() const { return true; } virtual bool isFleeing() const { return false; } }; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index e6cca05235..9a42f191ef 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -275,7 +275,7 @@ bool MWMechanics::AiPackage::shortcutPath(const ESM::Pathgrid::Point& startPoint static_cast(startPoint.mX), static_cast(startPoint.mY), static_cast(startPoint.mZ), static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ)); - if (destInLOS != NULL) *destInLOS = isPathClear; + if (destInLOS != nullptr) *destInLOS = isPathClear; if (!isPathClear) return false; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index f1941ff1d2..5a468ae30c 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -114,7 +114,7 @@ namespace MWMechanics /// Check if there aren't any obstacles along the path to make shortcut possible /// If a shortcut is possible then path will be cleared and filled with the destination point. - /// \param destInLOS If not NULL function will return ray cast check result + /// \param destInLOS If not nullptr function will return ray cast check result /// \return If can shortcut the path bool shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS, bool isPathClear); diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index 19f0ecf994..40f4ab1d4e 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -59,7 +59,7 @@ namespace MWMechanics bool empty() const { - return mStorage == NULL; + return mStorage == nullptr; } const std::type_info& getType() const @@ -67,16 +67,12 @@ namespace MWMechanics return typeid(mStorage); } - - DerivedClassStorage():mStorage(NULL){} + DerivedClassStorage():mStorage(nullptr){} ~DerivedClassStorage() { if(mStorage) delete mStorage; } - - - }; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 4a0811f507..479ed4869c 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -77,7 +77,7 @@ namespace MWMechanics mReaction(0), mSaidGreeting(Greet_None), mGreetingTimer(0), - mCell(NULL), + mCell(nullptr), mState(Wander_ChooseAction), mIsWanderingManually(false), mCanWanderAlongPathGrid(true), diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 80ac14ddde..144449cf09 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -149,7 +149,7 @@ namespace MWMechanics float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; - const ESM::Spell* weakestSpell = NULL; + const ESM::Spell* weakestSpell = nullptr; int minCost = std::numeric_limits::max(); std::vector selectedSpells; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c4975b308b..9a0ffa67d6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -931,7 +931,7 @@ CharacterController::~CharacterController() if (mAnimation) { persistAnimationState(); - mAnimation->setTextKeyListener(NULL); + mAnimation->setTextKeyListener(nullptr); } } @@ -1159,7 +1159,7 @@ bool CharacterController::updateCreatureState() if (!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); if (!mAnimation->hasAnimation("spellcast")) @@ -1463,7 +1463,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle) if(!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -2319,7 +2319,7 @@ void CharacterController::persistAnimationState() { anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup); float complete; - mAnimation->getInfo(anim.mGroup, &complete, NULL); + mAnimation->getInfo(anim.mGroup, &complete, nullptr); anim.mTime = complete; } else @@ -2451,7 +2451,7 @@ bool CharacterController::isPersistentAnimPlaying() bool CharacterController::isAnimPlaying(const std::string &groupName) { - if(mAnimation == NULL) + if(mAnimation == nullptr) return false; return mAnimation->isPlaying(groupName); } @@ -2748,9 +2748,9 @@ void CharacterController::updateHeadTracking(float duration) if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); - if (node == NULL) + if (node == nullptr) node = anim->getNode("Bip01 Head"); - if (node != NULL) + if (node != nullptr) { nodepaths = node->getParentalNodePaths(); if (!nodepaths.empty()) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 1db497828e..f97614ef45 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -240,8 +240,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener void playRandomDeath(float startpoint = 0.0f); /// choose a random animation group with \a prefix and numeric suffix - /// @param num if non-NULL, the chosen animation number will be written here - std::string chooseRandomGroup (const std::string& prefix, int* num = NULL) const; + /// @param num if non-nullptr, the chosen animation number will be written here + std::string chooseRandomGroup (const std::string& prefix, int* num = nullptr) const; bool updateCarriedLeftVisible(WeaponType weaptype) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 33357b79a7..6cef45e287 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -203,7 +203,7 @@ namespace MWMechanics } // F_PCStart spells - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (mRaceSelected) race = esmStore.get().find(player->mRace); diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index d8821276e4..e95c0a704f 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -20,7 +20,7 @@ Objects::~Objects() for (; it != mObjects.end();++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 6a84e0ef94..c553745014 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -16,7 +16,7 @@ namespace MWMechanics bool proximityToDoor(const MWWorld::Ptr& actor, float minDist); /// Returns door pointer within range. No guarantee is given as to which one - /** \return Pointer to the door, or NULL if none exists **/ + /** \return Pointer to the door, or empty pointer if none exists **/ const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); class ObstacleCheck diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b1b04f304d..1da97a6459 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -120,8 +120,8 @@ namespace MWMechanics } PathFinder::PathFinder() - : mPathgrid(NULL) - , mCell(NULL) + : mPathgrid(nullptr) + , mCell(nullptr) { } diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index ea4c973b72..6b5db64eab 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -50,8 +50,8 @@ namespace namespace MWMechanics { PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) - : mCell(NULL) - , mPathgrid(NULL) + : mCell(nullptr) + , mPathgrid(nullptr) , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f1ee2520b2..22631ca3f8 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -242,9 +242,9 @@ namespace MWMechanics // This makes spells that are easy to cast harder to resist and vice versa float castChance = 100.f; - if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) + if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor()) { - castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance + castChance = getSpellSuccessChance(spell, caster, nullptr, false); // Uncapped casting chance } if (castChance > 0) x *= 50 / castChance; @@ -737,7 +737,7 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Recall) { - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 2844e7f23f..5ac406368a 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -39,8 +39,8 @@ namespace MWMechanics * @note actor can be an NPC or a creature * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. */ - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); - float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); @@ -58,14 +58,14 @@ namespace MWMechanics /// @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. float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @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. float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 31da729ea0..363f28e70f 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -810,7 +810,7 @@ namespace MWPhysics btScalar mLeastDistSqr; DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin) - : mMe(me), mTargets(targets), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), + : mMe(me), mTargets(targets), mOrigin(origin), mObject(nullptr), mContactPoint(0,0,0), mLeastDistSqr(std::numeric_limits::max()) { } @@ -873,7 +873,7 @@ namespace MWPhysics object.setCollisionShape(&shape); object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; const Actor* physactor = getActor(actor); @@ -906,7 +906,7 @@ namespace MWPhysics float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const { - btCollisionObject* targetCollisionObj = NULL; + btCollisionObject* targetCollisionObj = nullptr; const Actor* actor = getActor(target); if (actor) targetCollisionObj = actor->getCollisionObject(); @@ -968,7 +968,7 @@ namespace MWPhysics btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; if (!ignore.isEmpty()) @@ -1127,7 +1127,7 @@ namespace MWPhysics std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { - btCollisionObject* me = NULL; + btCollisionObject* me = nullptr; ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) @@ -1255,7 +1255,7 @@ namespace MWPhysics ActorMap::iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const @@ -1263,7 +1263,7 @@ namespace MWPhysics ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Object* PhysicsSystem::getObject(const MWWorld::ConstPtr &ptr) const @@ -1271,7 +1271,7 @@ namespace MWPhysics ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) return found->second; - return NULL; + return nullptr; } void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 14a5dac48a..204ec14678 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -86,7 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; mHitPoint = end; - mHitObject = NULL; + mHitObject = nullptr; } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2133f9835a..6e2c76d1d8 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -367,7 +367,7 @@ namespace void applyNode(osg::Node& node) { if (node.getStateSet()) - node.setStateSet(NULL); + node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) mToRemove.push_back(std::make_pair(&node, node.getParent(0))); @@ -635,12 +635,12 @@ namespace MWRender Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mNodeMapCreated(false) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) - , mTextKeyListener(NULL) + , mTextKeyListener(nullptr) , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) , mHasMagicEffects(false) @@ -826,7 +826,7 @@ namespace MWRender for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i]->setTimePtr(std::shared_ptr()); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; mAnimSources.clear(); @@ -1084,12 +1084,12 @@ namespace MWRender node->removeUpdateCallback(it->second); // Should be no longer needed with OSG 3.4 - it->second->setNestedCallback(NULL); + it->second->setNestedCallback(nullptr); } mActiveControllers.clear(); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++) { @@ -1416,14 +1416,14 @@ namespace MWRender previousStateset = mObjectRoot->getStateSet(); mObjectRoot->getParent(0)->removeChild(mObjectRoot); } - mObjectRoot = NULL; - mSkeleton = NULL; + mObjectRoot = nullptr; + mSkeleton = nullptr; mNodeMap.clear(); mNodeMapCreated = false; mActiveControllers.clear(); - mAccumRoot = NULL; - mAccumCtrl = NULL; + mAccumRoot = nullptr; + mAccumCtrl = nullptr; if (!forceskeleton) { @@ -1556,7 +1556,7 @@ namespace MWRender node->addUpdateCallback(glowUpdater); // set a texture now so that the ShaderVisitor can find it - osg::ref_ptr writableStateSet = NULL; + osg::ref_ptr writableStateSet = nullptr; if (!node->getStateSet()) writableStateSet = node->getOrCreateStateSet(); else @@ -1735,7 +1735,7 @@ namespace MWRender std::string lowerName = Misc::StringUtils::lowerCase(name); NodeMap::const_iterator found = getNodeMap().find(lowerName); if (found == getNodeMap().end()) - return NULL; + return nullptr; else return found->second; } @@ -1766,7 +1766,7 @@ namespace MWRender } else { - mObjectRoot->setStateSet(NULL); + mObjectRoot->setStateSet(nullptr); mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } @@ -1793,7 +1793,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } } else @@ -1807,7 +1807,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } osg::ref_ptr light (new osg::Light); @@ -1828,7 +1828,7 @@ namespace MWRender void Animation::addControllers() { - mHeadController = NULL; + mHeadController = nullptr; if (mPtr.getClass().isBipedal(mPtr)) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2d8ac152f0..47edbdc0b3 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -425,7 +425,7 @@ public: * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + bool getInfo(const std::string &groupname, float *complete=nullptr, float *speedmult=nullptr) const; /// Get the absolute position in the animation track of the first text key with the given group. float getStartTime(const std::string &groupname) const; @@ -453,7 +453,7 @@ public: /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(); - /// Return a node with the specified name, or NULL if not existing. + /// Return a node with the specified name, or nullptr if not existing. /// @note The matching is case-insensitive. const osg::Node* getNode(const std::string& name) const; diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index eb3775cb46..4cf76e473a 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -54,9 +54,9 @@ void DebugDrawer::destroyGeometry() if (mGeometry) { mParentNode->removeChild(mGeometry); - mGeometry = NULL; - mVertices = NULL; - mDrawArrays = NULL; + mGeometry = nullptr; + mVertices = nullptr; + mDrawArrays = nullptr; } } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index cb6188a54e..74f2ba9117 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -45,7 +45,7 @@ namespace MWRender Camera::Camera (osg::Camera* camera) : mHeightScale(1.f), mCamera(camera), - mAnimation(NULL), + mAnimation(nullptr), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 2e0249e608..075d2e03c6 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -239,7 +239,7 @@ namespace MWRender void CharacterPreview::rebuild() { - mAnimation = NULL; + mAnimation = nullptr; mAnimation = new NpcAnimation(mCharacter, mNode, mResourceSystem, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); @@ -380,7 +380,7 @@ namespace MWRender void InventoryPreview::updatePtr(const MWWorld::Ptr &ptr) { - mCharacter = MWWorld::Ptr(ptr.getBase(), NULL); + mCharacter = MWWorld::Ptr(ptr.getBase(), nullptr); } void InventoryPreview::onSetup() @@ -403,7 +403,7 @@ namespace MWRender , mRef(&mBase) , mPitchRadians(osg::DegreesToRadians(6.f)) { - mCharacter = MWWorld::Ptr(&mRef, NULL); + mCharacter = MWWorld::Ptr(&mRef, nullptr); } RaceSelectionPreview::~RaceSelectionPreview() diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 6db223bd54..a262d00211 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -170,7 +170,7 @@ void CreatureWeaponAnimation::releaseArrow(float attackStrength) osg::Group *CreatureWeaponAnimation::getArrowBone() { if (!mWeapon) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); mWeapon->getNode()->accept(findVisitor); @@ -180,7 +180,7 @@ osg::Group *CreatureWeaponAnimation::getArrowBone() osg::Node *CreatureWeaponAnimation::getWeaponNode() { - return mWeapon ? mWeapon->getNode().get() : NULL; + return mWeapon ? mWeapon->getNode().get() : nullptr; } Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem() diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 7712469e1a..d1b6cd239f 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -563,7 +563,7 @@ namespace MWRender requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr(), true, false); - mWorkItem = NULL; + mWorkItem = nullptr; } } diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index 5edce2c3f6..38c28a72a9 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -14,7 +14,7 @@ namespace MWRender { LandManager::LandManager(int loadFlags) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mLoadFlags(loadFlags) { } @@ -32,7 +32,7 @@ osg::ref_ptr LandManager::getLand(int x, int y) { const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search(x,y); if (!land) - return NULL; + return nullptr; osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); mCache->addEntryToObjectCache(idstr, landObj.get()); return landObj; diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 392a8b406e..81253bba35 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -19,7 +19,7 @@ namespace MWRender public: LandManager(int loadFlags); - /// @note Will return NULL if not found. + /// @note Will return nullptr if not found. osg::ref_ptr getLand(int x, int y); virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 2716ea19ba..c7669f5e90 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -72,7 +72,7 @@ std::string getVampireHead(const std::string& race, bool female) } if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) - sVampireMapping[thisCombination] = NULL; + sVampireMapping[thisCombination] = nullptr; const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; if (!bodyPart) @@ -535,7 +535,7 @@ void NpcAnimation::updateParts() }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); - bool wasArrowAttached = (mAmmunition.get() != NULL); + bool wasArrowAttached = (mAmmunition.get() != nullptr); mAmmunition.reset(); const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); @@ -823,7 +823,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'"; @@ -838,7 +838,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'"; @@ -855,7 +855,7 @@ void NpcAnimation::addControllers() { Animation::addControllers(); - mFirstPersonNeckController = NULL; + mFirstPersonNeckController = nullptr; WeaponAnimation::deleteControllers(); if (mViewMode == VM_FirstPerson) @@ -947,7 +947,7 @@ osg::Group* NpcAnimation::getArrowBone() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); part->getNode()->accept(findVisitor); @@ -959,7 +959,7 @@ osg::Node* NpcAnimation::getWeaponNode() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; return part->getNode(); } @@ -1083,7 +1083,7 @@ const std::vector& NpcAnimation::getBodyParts(const std:: sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } - parts.resize(ESM::PRT_Count, NULL); + parts.resize(ESM::PRT_Count, nullptr); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7f3a8da2dc..335ca5d5a3 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -75,16 +75,16 @@ private: void updateNpcBase(); PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, - const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=NULL); + const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); virtual void setRenderBin(); @@ -155,7 +155,7 @@ public: virtual void updatePtr(const MWWorld::Ptr& updated); /// Get a list of body parts that may be used by an NPC of given race and gender. - /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain NULL body parts. + /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain nullptr body parts. static const std::vector& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf); }; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index db6772eaad..a0f48ad275 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -126,14 +126,14 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) if (ptr.getClass().isActor()) { if (ptr.getClass().hasInventoryStore(ptr)) - ptr.getClass().getInventoryStore(ptr).setInvListener(NULL, ptr); + ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr); - ptr.getClass().getContainerStore(ptr).setContListener(NULL); + ptr.getClass().getContainerStore(ptr).setContListener(nullptr); } ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); return true; } return false; @@ -153,8 +153,8 @@ void Objects::removeCell(const MWWorld::CellStore* store) if (ptr.getClass().isNpc() && ptr.getRefData().getCustomData()) { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - invStore.setInvListener(NULL, ptr); - invStore.setContListener(NULL); + invStore.setInvListener(nullptr, ptr); + invStore.setContListener(nullptr); } mObjects.erase(iter++); @@ -218,7 +218,7 @@ Animation* Objects::getAnimation(const MWWorld::Ptr &ptr) if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const @@ -227,7 +227,7 @@ const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } } diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index aae97fe35a..ee4120d6f8 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -27,8 +27,8 @@ namespace MWRender Pathgrid::Pathgrid(osg::ref_ptr root) : mPathgridEnabled(false) , mRootNode(root) - , mPathGridRoot(NULL) - , mInteriorPathgridNode(NULL) + , mPathGridRoot(nullptr) + , mInteriorPathgridNode(nullptr) { } @@ -94,7 +94,7 @@ void Pathgrid::togglePathgrid() if (mPathGridRoot) { mRootNode->removeChild(mPathGridRoot); - mPathGridRoot = NULL; + mPathGridRoot = nullptr; } } } @@ -124,7 +124,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) } else { - assert(mInteriorPathgridNode == NULL); + assert(mInteriorPathgridNode == nullptr); mInteriorPathgridNode = cellPathGrid; } } @@ -146,7 +146,7 @@ void Pathgrid::disableCellPathgrid(const MWWorld::CellStore *store) if (mInteriorPathgridNode) { mPathGridRoot->removeChild(mInteriorPathgridNode); - mInteriorPathgridNode = NULL; + mInteriorPathgridNode = nullptr; } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ce0a41c9a0..bb0bb2f130 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -232,7 +232,7 @@ namespace MWRender mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); - if (getenv("OPENMW_DONT_PRECOMPILE") == NULL) + if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr) { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); @@ -336,7 +336,7 @@ namespace MWRender RenderingManager::~RenderingManager() { // let background loading thread finish before we delete anything else - mWorkQueue = NULL; + mWorkQueue = nullptr; } MWRender::Objects& RenderingManager::getObjects() @@ -832,7 +832,7 @@ namespace MWRender stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); quad->setStateSet(stateset); - quad->setUpdateCallback(NULL); + quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); @@ -975,7 +975,7 @@ namespace MWRender result.mHitNormalWorld = intersection.getWorldIntersectNormal(); result.mRatio = intersection.ratio; - PtrHolder* ptrHolder = NULL; + PtrHolder* ptrHolder = nullptr; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1113,7 +1113,7 @@ namespace MWRender void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) { - NpcAnimation *anim = NULL; + NpcAnimation *anim = nullptr; if(ptr == mPlayerAnimation->getPtr()) anim = mPlayerAnimation.get(); else diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 141a3eb874..21bd48d9ae 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -196,7 +196,7 @@ void RippleSimulation::emitRipple(const osg::Vec3f &pos) { if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20) { - osgParticle::Particle* p = mParticleSystem->createParticle(NULL); + osgParticle::Particle* p = mParticleSystem->createParticle(nullptr); p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f)); p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 24769019b1..d6a0b9f019 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -629,7 +629,7 @@ private: if (mSunFlashNode) { mSunFlashNode->removeCullCallback(mSunFlashCallback); - mSunFlashCallback = NULL; + mSunFlashCallback = nullptr; } } @@ -671,7 +671,7 @@ private: if (mSunGlareNode) { mSunGlareNode->removeCullCallback(mSunGlareCallback); - mSunGlareCallback = NULL; + mSunGlareCallback = nullptr; } } @@ -1095,8 +1095,8 @@ private: SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) - , mCamera(NULL) - , mRainIntensityUniform(NULL) + , mCamera(nullptr) + , mRainIntensityUniform(nullptr) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) @@ -1302,7 +1302,7 @@ public: { if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) { - SceneUtil::CompositeStateSetUpdater* composite = NULL; + SceneUtil::CompositeStateSetUpdater* composite = nullptr; osg::Callback* callback = node.getUpdateCallback(); while (callback) @@ -1383,12 +1383,12 @@ public: virtual osg::Object *cloneType() const override { - return NULL; + return nullptr; } virtual osg::Object *clone(const osg::CopyOp &op) const override { - return NULL; + return nullptr; } virtual void operate(osgParticle::Particle *P, double dt) override @@ -1520,10 +1520,10 @@ void SkyManager::destroyRain() return; mRootNode->removeChild(mRainNode); - mRainNode = NULL; - mRainParticleSystem = NULL; - mRainShooter = NULL; - mRainFader = NULL; + mRainNode = nullptr; + mRainParticleSystem = nullptr; + mRainShooter = nullptr; + mRainFader = nullptr; } SkyManager::~SkyManager() @@ -1531,7 +1531,7 @@ SkyManager::~SkyManager() if (mRootNode) { mRootNode->getParent(0)->removeChild(mRootNode); - mRootNode = NULL; + mRootNode = nullptr; } } @@ -1554,7 +1554,7 @@ bool SkyManager::isEnabled() bool SkyManager::hasRain() { - return mRainNode != NULL; + return mRainNode != nullptr; } void SkyManager::update(float duration) @@ -1658,7 +1658,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleEffect) { mParticleNode->removeChild(mParticleEffect); - mParticleEffect = NULL; + mParticleEffect = nullptr; mParticleFaders.clear(); } @@ -1667,7 +1667,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleNode) { mRootNode->removeChild(mParticleNode); - mParticleNode = NULL; + mParticleNode = nullptr; } } else diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 8fd47d2c50..f668b0820f 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -441,13 +441,13 @@ void Water::updateWaterMaterial() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } if (Settings::Manager::getBool("shader", "Water")) @@ -579,7 +579,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); - node->setUpdateCallback(NULL); + node->setUpdateCallback(nullptr); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -595,13 +595,13 @@ Water::~Water() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } } diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 32990482a2..d5a8cb10d9 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -164,7 +164,7 @@ void WeaponAnimation::addControllers(const std::map >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); if (found != nodes.end()) @@ -180,7 +180,7 @@ void WeaponAnimation::addControllers(const std::mapdisable(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); if (wasEnabled) MWBase::Environment::get().getWorld()->enable(ptr); } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 9d7a5c6edc..2695aed764 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -484,7 +484,7 @@ namespace MWScript if (!player.isInCell()) throw std::runtime_error("player not in a cell"); - MWWorld::CellStore* store = NULL; + MWWorld::CellStore* store = nullptr; if (player.getCell()->isExterior()) { int cx,cy; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 7dffd685a3..53452c3054 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -122,7 +122,7 @@ bool FFmpeg_Decoder::getAVAudioData() if(!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), + if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), mFrame->nb_samples, mOutputSampleFormat, 0) < 0) return false; else @@ -181,34 +181,34 @@ void FFmpeg_Decoder::open(const std::string &fname) close(); mDataStream = mResourceMgr->get(fname); - if((mFormatCtx=avformat_alloc_context()) == NULL) + if((mFormatCtx=avformat_alloc_context()) == nullptr) throw std::runtime_error("Failed to allocate context"); try { - mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); - if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) + mFormatCtx->pb = avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek); + if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), nullptr, nullptr) != 0) { // "Note that a user-supplied AVFormatContext will be freed on failure". if (mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_free_context(mFormatCtx); } - mFormatCtx = NULL; + mFormatCtx = nullptr; throw std::runtime_error("Failed to allocate input stream"); } - if(avformat_find_stream_info(mFormatCtx, NULL) < 0) + if(avformat_find_stream_info(mFormatCtx, nullptr) < 0) throw std::runtime_error("Failed to find stream info in "+fname); for(size_t j = 0;j < mFormatCtx->nb_streams;j++) @@ -231,7 +231,7 @@ void FFmpeg_Decoder::open(const std::string &fname) std::to_string((*mStream)->codec->codec_id); throw std::runtime_error(ss); } - if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) + if(avcodec_open2((*mStream)->codec, codec, nullptr) < 0) throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name); @@ -255,17 +255,17 @@ void FFmpeg_Decoder::open(const std::string &fname) { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; - if (mFormatCtx != NULL) + if (mFormatCtx != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; avformat_close_input(&mFormatCtx); } @@ -276,7 +276,7 @@ void FFmpeg_Decoder::close() { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; av_free_packet(&mPacket); av_freep(&mFrame); @@ -285,20 +285,20 @@ void FFmpeg_Decoder::close() if(mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { // mFormatCtx->pb->buffer must be freed by hand, // if not, valgrind will show memleak, see: // // https://trac.ffmpeg.org/ticket/1357 // - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_close_input(&mFormatCtx); } @@ -373,7 +373,7 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * (*mStream)->codec->sample_fmt, // input sample format (*mStream)->codec->sample_rate, // input sample rate 0, // logging level offset - NULL); // log context + nullptr); // log context if(!mSwr) throw std::runtime_error("Couldn't allocate SwrContext"); if(swr_init(mSwr) < 0) @@ -417,17 +417,17 @@ size_t FFmpeg_Decoder::getSampleOffset() FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) : Sound_Decoder(vfs) - , mFormatCtx(NULL) - , mStream(NULL) - , mFrame(NULL) + , mFormatCtx(nullptr) + , mStream(nullptr) + , mFrame(nullptr) , mFrameSize(0) , mFramePos(0) , mNextPts(0.0) , mSwr(0) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) , mOutputChannelLayout(0) - , mDataBuf(NULL) - , mFrameData(NULL) + , mDataBuf(nullptr) + , mFrameData(nullptr) , mDataBufLen(0) { memset(&mPacket, 0, sizeof(mPacket)); diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 4975625169..faa8d94ca3 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -19,7 +19,7 @@ namespace MWSound { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) - : Sound_Decoder(NULL) + : Sound_Decoder(nullptr) , mDecoder(decoder) { } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index cadcdc4abe..39e872b0c9 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -572,10 +572,10 @@ std::vector OpenAL_Output::enumerate() std::vector devlist; const ALCchar *devnames; - if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) - devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) + devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); else - devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); while(devnames && *devnames) { devlist.push_back(devnames); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 9c12584d71..8e3488906c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -819,7 +819,7 @@ namespace MWSound } const ESM::Region *regn = world->getStore().get().search(regionName); - if(regn == NULL) + if(regn == nullptr) return; if(total == 0) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index a91bd4e8ce..d440ba8696 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -46,7 +46,7 @@ void MWState::CharacterManager::deleteSlot(const MWState::Character *character, // All slots deleted, cleanup and remove this character it->cleanup(); if (character == mCurrent) - mCurrent = NULL; + mCurrent = nullptr; mCharacters.erase(it); } } @@ -96,7 +96,7 @@ std::list::iterator MWState::CharacterManager::findCharacter void MWState::CharacterManager::setCurrentCharacter (const Character *character) { if (!character) - mCurrent = NULL; + mCurrent = nullptr; else { std::list::iterator it = findCharacter(character); diff --git a/apps/openmw/mwstate/quicksavemanager.cpp b/apps/openmw/mwstate/quicksavemanager.cpp index 59658ce6e1..df078e026c 100644 --- a/apps/openmw/mwstate/quicksavemanager.cpp +++ b/apps/openmw/mwstate/quicksavemanager.cpp @@ -20,7 +20,7 @@ void MWState::QuickSaveManager::visitSave(const Slot *saveSlot) bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) { - if(mOldestSlotVisited == NULL) + if(mOldestSlotVisited == nullptr) return true; return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp); } @@ -33,6 +33,6 @@ bool MWState::QuickSaveManager::shouldCreateNewSlot() const MWState::Slot *MWState::QuickSaveManager::getNextQuickSaveSlot() { if(shouldCreateNewSlot()) - return NULL; + return nullptr; return mOldestSlotVisited; } diff --git a/apps/openmw/mwstate/quicksavemanager.hpp b/apps/openmw/mwstate/quicksavemanager.hpp index e52cd609f7..a5237d7c30 100644 --- a/apps/openmw/mwstate/quicksavemanager.hpp +++ b/apps/openmw/mwstate/quicksavemanager.hpp @@ -28,7 +28,7 @@ namespace MWState{ const Slot *getNextQuickSaveSlot(); ///< Get the slot that the next quicksave should use. /// - ///\return Either the oldest quicksave slot visited, or NULL if a new slot can be made + ///\return Either the oldest quicksave slot visited, or nullptr if a new slot can be made }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 5604338206..9804531d70 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -55,7 +55,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getMechanicsManager()->clear(); mState = State_NoGame; - mCharacterManager.setCurrentCharacter(NULL); + mCharacterManager.setCurrentCharacter(nullptr); mTimePlayed = 0; MWMechanics::CreatureStats::cleanup(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9163f1f2e3..a06c208b59 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -206,13 +206,13 @@ namespace MWWorld { mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = NULL; + mTerrainPreloadItem = nullptr; } if (mUpdateCacheItem) { mUpdateCacheItem->waitTillDone(); - mUpdateCacheItem = NULL; + mUpdateCacheItem = nullptr; } for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 8506cefa37..2b032e1d70 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -340,7 +340,7 @@ public: } catch (...) { - return NULL; + return nullptr; } } }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5cd8346a7c..239d714a06 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -156,7 +156,7 @@ namespace ESM::RefNum mRefNumToFind; SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(NULL) + : mFound(nullptr) , mRefNumToFind(toFind) { } @@ -259,7 +259,7 @@ namespace MWWorld { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); - object.getRefData().setBaseNode(NULL); + object.getRefData().setBaseNode(nullptr); return copied; } @@ -901,7 +901,7 @@ namespace MWWorld CellStore* otherCell = callback->getCellStore(movedTo); - if (otherCell == NULL) + if (otherCell == nullptr) { Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index bcf4b4ada0..1c4d8f5d87 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -64,7 +64,7 @@ namespace MWWorld // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. - // Note this is NULL until the cell is explored to save some memory + // Note this is nullptr until the cell is explored to save some memory std::shared_ptr mFogState; const ESM::Cell *mCell; @@ -367,7 +367,7 @@ namespace MWWorld struct GetCellStoreCallback { public: - ///@note must return NULL if the cell is not found + ///@note must return nullptr if the cell is not found virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; }; diff --git a/apps/openmw/mwworld/cellvisitors.hpp b/apps/openmw/mwworld/cellvisitors.hpp index cfb07f7492..e68b383b77 100644 --- a/apps/openmw/mwworld/cellvisitors.hpp +++ b/apps/openmw/mwworld/cellvisitors.hpp @@ -17,7 +17,7 @@ namespace MWWorld { if (ptr.getRefData().getBaseNode()) { - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); mObjects.push_back (ptr); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8fa16ff515..6a24ae4f01 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -114,7 +114,7 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; -MWWorld::ContainerStore::ContainerStore() : mListener(NULL), mCachedWeight (0), mWeightUpToDate (false) {} +MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -286,7 +286,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first - item.getRefData().setBaseNode(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell + item.getRefData().setBaseNode(nullptr); // Especially important, otherwise scripts on the item could think that it's actually in a cell ESM::Position pos; pos.rot[0] = 0; pos.rot[1] = 0; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 7f3018c559..2ca7b94eeb 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -99,7 +99,7 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt } MWWorld::InventoryStore::InventoryStore() - : mListener(NULL) + : mListener(nullptr) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 2631f513fb..4b61c0a08d 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -76,11 +76,11 @@ namespace MWWorld template struct LiveCellRef : public LiveCellRefBase { - LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + LiveCellRef(const ESM::CellRef& cref, const X* b = nullptr) : LiveCellRefBase(typeid(X).name(), cref), mBase(b) {} - LiveCellRef(const X* b = NULL) + LiveCellRef(const X* b = nullptr) : LiveCellRefBase(typeid(X).name()), mBase(b) {} diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 49b7dd8bb3..85181e998e 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -30,7 +30,7 @@ namespace MWWorld Player::Player (const ESM::NPC *player) : mCellStore(0), mLastKnownExteriorPosition(0,0,0), - mMarkedCell(NULL), + mMarkedCell(nullptr), mAutoMove(false), mForwardBackward(0), mTeleported(false), @@ -406,7 +406,7 @@ namespace MWWorld { Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists"; // Cell no longer exists. The loader will have to choose a default cell. - mCellStore = NULL; + mCellStore = nullptr; } if (!player.mBirthsign.empty()) diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 4b92fa3962..7f37de0100 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -38,7 +38,7 @@ namespace MWWorld osg::Vec3f mLastKnownExteriorPosition; ESM::Position mMarkedPosition; - // If no position was marked, this is NULL + // If no position was marked, this is nullptr CellStore* mMarkedCell; bool mAutoMove; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dc3d4fd308..30e73df588 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -94,7 +94,7 @@ namespace void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { - if (ptr.getRefData().getBaseNode() != NULL) + if (ptr.getRefData().getBaseNode() != nullptr) { float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec (scale, scale, scale); @@ -330,7 +330,7 @@ namespace MWWorld while (active!=mActiveCells.end()) unloadCell (active++); assert(mActiveCells.empty()); - mCurrentCell = NULL; + mCurrentCell = nullptr; mPreloader->clear(); } @@ -519,7 +519,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); - bool loadcell = (mCurrentCell == NULL); + bool loadcell = (mCurrentCell == nullptr); if(!loadcell) loadcell = *mCurrentCell != *cell; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 2ac78bb85d..b6bf2b7eba 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -96,7 +96,7 @@ namespace MWWorld typename Static::const_iterator it = mStatic.find(index); if (it != mStatic.end()) return &(it->second); - return NULL; + return nullptr; } template const T *IndexedStore::find(int index) const @@ -165,7 +165,7 @@ namespace MWWorld std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; - return NULL; + return nullptr; } template const T *Store::find(const std::string &id) const @@ -358,7 +358,7 @@ namespace MWWorld const LandTextureList <exl = mStatic[plugin]; if (index >= ltexl.size()) - return NULL; + return nullptr; return <exl[index]; } const ESM::LandTexture *Store::find(size_t index, size_t plugin) const @@ -867,7 +867,7 @@ namespace MWWorld //========================================================================= Store::Store() - : mCells(NULL) + : mCells(nullptr) { } @@ -887,7 +887,7 @@ namespace MWWorld // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created. // A proper fix should be made for future versions of the file format. - bool interior = mCells->search(pathgrid.mCell) != NULL; + bool interior = mCells->search(pathgrid.mCell) != nullptr; // Try to overwrite existing record if (interior) @@ -917,14 +917,14 @@ namespace MWWorld Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); if (it != mExt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::search(const std::string& name) const { Interior::const_iterator it = mInt.find(name); if (it != mInt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::find(int x, int y) const { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 00ea6f66f8..2ed81af482 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -166,7 +166,7 @@ namespace MWWorld */ bool isDynamic(const std::string &id) const; - /** Returns a random record that starts with the named ID, or NULL if not found. */ + /** Returns a random record that starts with the named ID, or nullptr if not found. */ const T *searchRandom(const std::string &id) const; const T *find(const std::string &id) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3cb6b3a594..0dcdea1316 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1111,7 +1111,7 @@ namespace MWWorld void World::deleteObject (const Ptr& ptr) { - if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == NULL) + if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr) { if (ptr == getPlayerPtr()) throw std::runtime_error("can not delete player object"); @@ -2192,7 +2192,7 @@ namespace MWWorld pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z(); - const CellStore *currCell = object.isInCell() ? object.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + const CellStore *currCell = object.isInCell() ? object.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup return isUnderwater(currCell, pos); } @@ -2867,7 +2867,7 @@ namespace MWWorld // if the faced object can not be activated, do not use it if (!target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; if (target.isEmpty()) { @@ -2920,14 +2920,14 @@ namespace MWWorld target = result1.first; hitPosition = result1.second; if (dist1 > getMaxActivationDistance()) - target = NULL; + target = nullptr; } else if (result2.mHit) { target = result2.mHitObject; hitPosition = result2.mHitPointWorld; if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; } } } diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index ac21470ded..63e4bd6af4 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -28,7 +28,7 @@ struct ContentFileTest : public ::testing::Test for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) { ESM::ESMReader lEsm; - lEsm.setEncoder(NULL); + lEsm.setEncoder(nullptr); lEsm.setIndex(index); lEsm.setGlobalReaderList(&readerList); lEsm.open(it->string()); @@ -309,7 +309,7 @@ TEST_F(StoreTest, overwrite_test) // verify that changes were actually applied const RecordType* overwrittenRec = mEsmStore.get().search(recordId); - ASSERT_TRUE (overwrittenRec != NULL); + ASSERT_TRUE (overwrittenRec != nullptr); ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model"); } diff --git a/apps/wizard/existinginstallationpage.cpp b/apps/wizard/existinginstallationpage.cpp index a04e721d85..3ec98a9353 100644 --- a/apps/wizard/existinginstallationpage.cpp +++ b/apps/wizard/existinginstallationpage.cpp @@ -96,7 +96,7 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked() tr("Select master file"), QDir::currentPath(), QString(tr("Morrowind master file (*.esm)")), - NULL, + nullptr, QFileDialog::DontResolveSymlinks); if (selectedFile.isEmpty()) diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e26f9a9421..4f95b7fe46 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -196,7 +196,7 @@ void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool s { QString fileName = ui.gameFileView->itemText(index); const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName); - if (file != NULL) + if (file != nullptr) { QModelIndex index2(mContentModel->indexFromItem(file)); mContentModel->setData(index2, selected, Qt::UserRole + 1); diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 64824b6b35..2e2ddd1f27 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -71,7 +71,7 @@ static const struct { { "Illegal instruction", SIGILL }, { "FPU exception", SIGFPE }, { "System BUS error", SIGBUS }, -{ NULL, 0 } +{ nullptr, 0 } }; static const struct { @@ -88,7 +88,7 @@ static const struct { { ILL_COPROC, "Coprocessor error" }, { ILL_BADSTK, "Internal stack error" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -103,7 +103,7 @@ static const struct { { FPE_FLTRES, "Floating point inexact result" }, { FPE_FLTINV, "Floating point invalid operation" }, { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -114,7 +114,7 @@ static const struct { { SEGV_MAPERR, "Address not mapped to object" }, { SEGV_ACCERR, "Invalid permissions for mapped object" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -126,7 +126,7 @@ static const struct { { BUS_ADRERR, "Non-existent physical address" }, { BUS_OBJERR, "Object specific hardware error" }, #endif - { 0, NULL } + { 0, nullptr } }; static int (*cc_user_info)(char*, char*); @@ -140,7 +140,7 @@ static void gdb_info(pid_t pid) /* Create a temp file to put gdb commands into */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr) { fprintf(f, "attach %d\n" "shell echo \"\"\n" @@ -267,7 +267,7 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) close(fd[0]); close(fd[1]); - execl(argv0, argv0, crash_switch, NULL); + execl(argv0, argv0, crash_switch, nullptr); safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); _exit(1); @@ -407,7 +407,7 @@ static void crash_handler(const char *logfile) if(logfile) { std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; - SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); + SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); } exit(0); @@ -444,7 +444,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig altss.ss_sp = altstack; altss.ss_flags = 0; altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); + sigaltstack(&altss, nullptr); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crash_catcher; @@ -455,7 +455,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig while(num_signals--) { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + *signals != SIGBUS) || sigaction(*signals, &sa, nullptr) == -1) { *signals = 0; retval = -1; @@ -502,7 +502,7 @@ static bool is_debugger_present() // Call sysctl. size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); assert(junk == 0); // We're being debugged if the P_TRACED flag is set. @@ -516,7 +516,7 @@ void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_present()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1) + if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), nullptr) == -1) { Log(Debug::Warning) << "Installing crash handler failed"; } diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index a4c59c2216..e89a659568 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -101,7 +101,7 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) if (!isatty(fileno(stdin))) #endif - SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); + SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), nullptr); Log(Debug::Error) << "Error: " << e.what(); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 67b9d6a385..a6b947dd3e 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -23,8 +23,8 @@ ESMReader::ESMReader() : mIdx(0) , mRecordFlags(0) , mBuffer(50*1024) - , mGlobalReaderList(NULL) - , mEncoder(NULL) + , mGlobalReaderList(nullptr) + , mEncoder(nullptr) , mFileSize(0) { } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b8c78a2b95..09fca4b262 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -10,9 +10,9 @@ namespace ESM { ESMWriter::ESMWriter() : mRecords() - , mStream(NULL) + , mStream(nullptr) , mHeaderPos() - , mEncoder(NULL) + , mEncoder(nullptr) , mRecordCount(0) , mCounting(true) , mHeader() diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index a91dfe3d3d..9e5e9d07e4 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -16,7 +16,7 @@ namespace ESM , mY(0) , mPlugin(0) , mDataTypes(0) - , mLandData(NULL) + , mLandData(nullptr) { } @@ -73,7 +73,7 @@ namespace ESM mContext = esm.getContext(); - mLandData = NULL; + mLandData = nullptr; // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) @@ -298,7 +298,7 @@ namespace ESM if (mLandData) { delete mLandData; - mLandData = NULL; + mLandData = nullptr; } } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index eaf766442a..b4b66c6019 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -129,9 +129,9 @@ struct Land /** * Actually loads data into target - * If target is NULL, assumed target is mLandData + * If target is nullptr, assumed target is mLandData */ - void loadData(int flags, LandData* target = NULL) const; + void loadData(int flags, LandData* target = nullptr) const; /** * Frees memory allocated for mLandData diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 0fa28cf0b4..dfdc9718df 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -50,7 +50,7 @@ namespace ESMTerrain const ESM::Land::LandData *LandObject::getData(int flags) const { if ((mData.mDataLoaded & flags) != flags) - return NULL; + return nullptr; return &mData; } diff --git a/components/files/androidpath.cpp b/components/files/androidpath.cpp index 84886f4734..bf5c985774 100644 --- a/components/files/androidpath.cpp +++ b/components/files/androidpath.cpp @@ -40,15 +40,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 1f6a3d9130..3743eef4c9 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 169a6f8131..aff15777f4 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -21,22 +21,22 @@ LowLevelFile::LowLevelFile () { - mHandle = NULL; + mHandle = nullptr; } LowLevelFile::~LowLevelFile () { - if (mHandle != NULL) + if (mHandle != nullptr) fclose (mHandle); } void LowLevelFile::open (char const * filename) { - assert (mHandle == NULL); + assert (mHandle == nullptr); mHandle = fopen (filename, "rb"); - if (mHandle == NULL) + if (mHandle == nullptr) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; @@ -46,16 +46,16 @@ void LowLevelFile::open (char const * filename) void LowLevelFile::close () { - assert (mHandle != NULL); + assert (mHandle != nullptr); fclose (mHandle); - mHandle = NULL; + mHandle = nullptr; } size_t LowLevelFile::size () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long oldPosition = ftell (mHandle); @@ -78,7 +78,7 @@ size_t LowLevelFile::size () void LowLevelFile::seek (size_t Position) { - assert (mHandle != NULL); + assert (mHandle != nullptr); if (fseek (mHandle, Position, SEEK_SET) != 0) throw std::runtime_error ("A seek operation on a file failed."); @@ -86,7 +86,7 @@ void LowLevelFile::seek (size_t Position) size_t LowLevelFile::tell () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long Position = ftell (mHandle); @@ -98,7 +98,7 @@ size_t LowLevelFile::tell () size_t LowLevelFile::read (void * data, size_t size) { - assert (mHandle != NULL); + assert (mHandle != nullptr); int amount = fread (data, 1, size, mHandle); @@ -296,7 +296,7 @@ void LowLevelFile::seek (size_t Position) { assert (mHandle != INVALID_HANDLE_VALUE); - if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (SetFilePointer (mHandle, Position, nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) if (GetLastError () != NO_ERROR) throw std::runtime_error ("A seek operation on a file failed."); } @@ -305,7 +305,7 @@ size_t LowLevelFile::tell () { assert (mHandle != INVALID_HANDLE_VALUE); - DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); + DWORD value = SetFilePointer (mHandle, 0, nullptr, SEEK_CUR); if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) throw std::runtime_error ("A query operation on a file failed."); @@ -319,7 +319,7 @@ size_t LowLevelFile::read (void * data, size_t size) DWORD read; - if (!ReadFile (mHandle, data, size, &read, NULL)) + if (!ReadFile (mHandle, data, size, &read, nullptr)) throw std::runtime_error ("A read operation on a file failed."); return read; diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index b49b72e46a..32c35c98f1 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 4b7c6c50a8..2354e6f31c 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -42,7 +42,7 @@ boost::filesystem::path WindowsPath::getUserConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, path))) { userPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -63,7 +63,7 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, nullptr, 0, path))) { globalPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -99,7 +99,7 @@ boost::filesystem::path WindowsPath::getInstallPath() const std::vector buf(512); int len = 512; - if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) + if (RegQueryValueEx(hKey, TEXT("Installed Path"), nullptr, nullptr, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) { installPath = &buf[0]; } diff --git a/components/myguiplatform/additivelayer.cpp b/components/myguiplatform/additivelayer.cpp index aa2ef08b3b..49558cf8e1 100644 --- a/components/myguiplatform/additivelayer.cpp +++ b/components/myguiplatform/additivelayer.cpp @@ -27,7 +27,7 @@ namespace osgMyGUI MyGUI::OverlappedLayer::renderToTarget(_target, _update); - renderManager.setInjectState(NULL); + renderManager.setInjectState(nullptr); } } diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index ddd7ca342d..c90e092215 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -24,7 +24,7 @@ MyGUI::IDataStream *DataManager::getData(const std::string &name) if (stream->fail()) { Log(Debug::Error) << "DataManager::getData: Failed to open '" << name << "'"; - return NULL; + return nullptr; } return new MyGUI::DataFileStream(stream.release()); } diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 5ab7ab736b..845d0c4849 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -52,7 +52,7 @@ public: { public: FrameUpdate() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -76,7 +76,7 @@ public: { public: CollectDrawCalls() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)NULL + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16); } else { @@ -355,7 +355,7 @@ RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, R , mUpdate(false) , mIsInitialise(false) , mInvScalingFactor(1.f) - , mInjectState(NULL) + , mInjectState(nullptr) { if (scalingFactor != 0.f) mInvScalingFactor = 1.f / scalingFactor; @@ -537,7 +537,7 @@ void RenderManager::destroyTexture(MyGUI::ITexture *texture) MyGUI::ITexture* RenderManager::getTexture(const std::string &name) { if (name.empty()) - return NULL; + return nullptr; MapTexture::const_iterator item = mTextures.find(name); if(item == mTextures.end()) diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index 4a0aae3cda..8c9e3e30f3 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -98,7 +98,7 @@ public: /** @see IRenderTarget::doRender */ virtual void doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count); - /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to NULL again. */ + /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to nullptr again. */ void setInjectState(osg::StateSet* stateSet); /** @see IRenderTarget::getInfo */ diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 6c53a96998..756893974b 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -23,7 +23,7 @@ namespace osgMyGUI } OSGTexture::OSGTexture(osg::Texture2D *texture) - : mImageManager(NULL) + : mImageManager(nullptr) , mTexture(texture) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 4061247b50..66bbfdb65c 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -162,7 +162,7 @@ void NIFFile::parse(Files::IStreamPtr stream) for(size_t i = 0;i < recNum;i++) { - Record *r = NULL; + Record *r = nullptr; std::string rec = nif.getString(); if(rec.empty()) @@ -182,7 +182,7 @@ void NIFFile::parse(Files::IStreamPtr stream) else fail("Unknown record type " + rec); - assert(r != NULL); + assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; r->recIndex = i; @@ -203,7 +203,7 @@ void NIFFile::parse(Files::IStreamPtr stream) } else { - roots[i] = NULL; + roots[i] = nullptr; warn("Null Root found"); } } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index c32969d1b6..d9afbbed71 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -53,7 +53,7 @@ public: boundXYZ = nif->getVector3(); } - parent = NULL; + parent = nullptr; isBone = false; } @@ -64,7 +64,7 @@ public: props.post(nif); } - // Parent node, or NULL for the root node. As far as I'm aware, only + // Parent node, or nullptr for the root node. As far as I'm aware, only // NiNodes (or types derived from NiNodes) can be parents. NiNode *parent; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 09c3809872..e8aa8cb5b5 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -40,25 +40,25 @@ public: void post(NIFFile *nif) { if(index < 0) - ptr = NULL; + ptr = nullptr; else { Record *r = nif->getRecord(index); // And cast it ptr = dynamic_cast(r); - assert(ptr != NULL); + assert(ptr != nullptr); } } /// Look up the actual object from the index const X* getPtr() const { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } X* getPtr() { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } @@ -75,7 +75,7 @@ public: /// Pointers are allowed to be empty bool empty() const - { return ptr == NULL; } + { return ptr == nullptr; } }; /** A list of references to other records. These are read as a list, diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 5239e93fcb..7b206e40c2 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -25,7 +25,7 @@ namespace osg::Matrixf getWorldTransform(const Nif::Node *node) { - if(node->parent != NULL) + if(node->parent != nullptr) return node->trafo.toMatrix() * getWorldTransform(node->parent); return node->trafo.toMatrix(); } @@ -61,8 +61,8 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; - mCompoundShape = NULL; - mStaticMesh = NULL; + mCompoundShape = nullptr; + mStaticMesh = nullptr; if (nif.numRoots() < 1) { @@ -71,10 +71,10 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) } Nif::Record *r = nif.getRoot(0); - assert(r != NULL); + assert(r != nullptr); Nif::Node *node = dynamic_cast(r); - if (node == NULL) + if (node == nullptr) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); @@ -216,7 +216,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n { // Get the next extra data in the list e = e->extra.getPtr(); - assert(e != NULL); + assert(e != nullptr); if (e->recType == Nif::RC_NiStringExtraData) { @@ -263,7 +263,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { - assert(shape != NULL); + assert(shape != nullptr); // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3198e995c5..eb20b77021 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -200,7 +200,7 @@ namespace NifOsg } const Nif::Record *r = nif->getRoot(0); - assert(r != NULL); + assert(r != nullptr); if(r->recType != Nif::RC_NiSequenceStreamHelper) { @@ -257,12 +257,12 @@ namespace NifOsg const Nif::Record* r = nif->getRoot(0); const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == NULL) + if (nifNode == nullptr) nif->fail("First root was not a node, but a " + r->recName); osg::ref_ptr textkeys (new TextKeyMapHolder); - osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); + osg::ref_ptr created = handleNode(nifNode, nullptr, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { @@ -297,7 +297,7 @@ namespace NifOsg { // Get the lowest numbered recIndex of the NiTexturingProperty root node. // This is what is overridden when a spell effect "particle texture" is used. - if (nifNode->parent == NULL && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) + if (nifNode->parent == nullptr && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) { mFirstRootTextureIndex = props[i].getPtr()->recIndex; mFoundFirstRootTexturingProperty = true; @@ -338,7 +338,7 @@ namespace NifOsg osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st, Resource::ImageManager* imageManager) { if (!st) - return NULL; + return nullptr; osg::ref_ptr image; if (!st->external && !st->data.empty()) @@ -464,10 +464,10 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=NULL) + std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) { - if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) - return NULL; + if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) + return nullptr; osg::ref_ptr node = createNode(nifNode); @@ -791,7 +791,7 @@ namespace NifOsg // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - const Nif::NiAutoNormalParticlesData *particledata = NULL; + const Nif::NiAutoNormalParticlesData *particledata = nullptr; if(nifNode->recType == Nif::RC_NiAutoNormalParticles) particledata = static_cast(nifNode)->data.getPtr(); else if(nifNode->recType == Nif::RC_NiRotatingParticles) @@ -873,7 +873,7 @@ namespace NifOsg osg::ref_ptr partsys (new ParticleSystem); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); - const Nif::NiParticleSystemController* partctrl = NULL; + const Nif::NiParticleSystemController* partctrl = nullptr; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1193,11 +1193,11 @@ namespace NifOsg break; default: Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; - return NULL; + return nullptr; } if (pixelData->mipmaps.empty()) - return NULL; + return nullptr; int width = 0; int height = 0; @@ -1211,7 +1211,7 @@ namespace NifOsg if (mipSize + mip.dataOffset > pixelData->data.size()) { Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; - return NULL; + return nullptr; } if (i != 0) @@ -1226,7 +1226,7 @@ namespace NifOsg if (width <= 0 || height <= 0) { Log(Debug::Info) << "Internal Texture Width and height must be non zero, ignoring texture"; - return NULL; + return nullptr; } unsigned char* data = new unsigned char[pixelData->data.size()]; diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 03e836a0fd..f5c055a15a 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -39,7 +39,7 @@ osgParticle::Particle* ParticleSystem::createParticle(const osgParticle::Particl { if (numParticles()-numDeadParticles() < mQuota) return osgParticle::ParticleSystem::createParticle(ptemplate); - return NULL; + return nullptr; } void InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv) @@ -312,7 +312,7 @@ void Emitter::emitParticles(double dt) FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mFound(NULL) + , mFound(nullptr) , mRecIndex(recIndex) { } diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index dbdbf0c6e3..15a747dd3a 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -12,7 +12,7 @@ namespace Resource { BulletShape::BulletShape() - : mCollisionShape(NULL) + : mCollisionShape(nullptr) { } @@ -32,7 +32,7 @@ BulletShape::~BulletShape() void BulletShape::deleteShape(btCollisionShape* shape) { - if(shape!=NULL) + if(shape!=nullptr) { if(shape->isCompound()) { diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 622506d6b0..4193cd5b42 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -24,7 +24,7 @@ namespace Resource struct GetTriangleFunctor { GetTriangleFunctor() - : mTriMesh(NULL) + : mTriMesh(nullptr) { } diff --git a/components/resource/multiobjectcache.hpp b/components/resource/multiobjectcache.hpp index 527247bf96..b73e29d50f 100644 --- a/components/resource/multiobjectcache.hpp +++ b/components/resource/multiobjectcache.hpp @@ -30,7 +30,7 @@ namespace Resource void addEntryToObjectCache(const std::string& filename, osg::Object* object); - /** Take an Object from cache. Return NULL if no object found. */ + /** Take an Object from cache. Return nullptr if no object found. */ osg::ref_ptr takeFromObjectCache(const std::string& fileName); /** call releaseGLObjects on all objects attached to the object cache.*/ diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 08f36cfcf1..99dd7bad3c 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -26,7 +26,7 @@ namespace SceneUtil osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const { if (!stateset) - return NULL; + return nullptr; if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) return osg::clone(stateset, *this); return const_cast(stateset); diff --git a/components/sceneutil/controller.cpp b/components/sceneutil/controller.cpp index 8fef00333b..dfc72918aa 100644 --- a/components/sceneutil/controller.cpp +++ b/components/sceneutil/controller.cpp @@ -19,7 +19,7 @@ namespace SceneUtil bool Controller::hasInput() const { - return mSource.get() != NULL; + return mSource.get() != nullptr; } float Controller::getInputValue(osg::NodeVisitor* nv) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 018abeefc6..e102653b90 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -102,7 +102,7 @@ namespace SceneUtil if (LightManager* lightManager = dynamic_cast(path[i])) return lightManager; } - return NULL; + return nullptr; } // Set on a LightSource. Adds the light source to its light manager for the current frame. @@ -299,7 +299,7 @@ namespace SceneUtil virtual osg::Object* cloneType() const { return new DisableLight(mIndex); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new DisableLight(*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* libraryName() const { return "SceneUtil"; } virtual const char* className() const { return "DisableLight"; } virtual Type getType() const { return LIGHT; } @@ -323,17 +323,17 @@ namespace SceneUtil virtual void apply(osg::State& state) const { int lightNum = GL_LIGHT0 + mIndex; - glLightfv( lightNum, GL_AMBIENT, mNull.ptr() ); - glLightfv( lightNum, GL_DIFFUSE, mNull.ptr() ); - glLightfv( lightNum, GL_SPECULAR, mNull.ptr() ); + glLightfv( lightNum, GL_AMBIENT, mnullptr.ptr() ); + glLightfv( lightNum, GL_DIFFUSE, mnullptr.ptr() ); + glLightfv( lightNum, GL_SPECULAR, mnullptr.ptr() ); LightStateCache* cache = getLightStateCache(state.getContextID()); - cache->lastAppliedLight[mIndex] = NULL; + cache->lastAppliedLight[mIndex] = nullptr; } private: unsigned int mIndex; - osg::Vec4f mNull; + osg::Vec4f mnullptr; }; void LightManager::setStartLight(int start) @@ -447,7 +447,7 @@ namespace SceneUtil { unsigned int maxLights = static_cast (8 - mLightManager->getStartLight()); - osg::StateSet* stateset = NULL; + osg::StateSet* stateset = nullptr; if (mLightList.size() > maxLights) { diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 55d0c69cde..d35aa6058e 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -158,7 +158,7 @@ namespace SceneUtil { public: LightListCallback() - : mLightManager(NULL) + : mLightManager(nullptr) , mLastFrameNumber(0) {} LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop) diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index 5ad9bd6dce..d44724e8fb 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -45,7 +45,7 @@ namespace SceneUtil SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); - osg::Group* attachTo = NULL; + osg::Group* attachTo = nullptr; if (visitor.mFoundNode) { attachTo = visitor.mFoundNode; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index b86744599e..5d50c369d9 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -166,7 +166,7 @@ class CollectLowestTransformsVisitor : public BaseOptimizerVisitor } else { - // for all current objects mark a NULL transform for them. + // for all current objects mark a nullptr transform for them. registerWithCurrentObjects(0); } } @@ -826,7 +826,7 @@ void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform) isOperationPermissible(transform)) { osg::Matrix matrix; - transform.computeWorldToLocalMatrix(matrix,NULL); + transform.computeWorldToLocalMatrix(matrix,nullptr); if (matrix.isIdentity()) { _redundantNodeList.insert(&transform); @@ -1000,7 +1000,7 @@ struct LessGeometryPrimitiveType }; -/// Shortcut to get size of an array, even if pointer is NULL. +/// Shortcut to get size of an array, even if pointer is nullptr. inline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; } /// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices. @@ -1170,7 +1170,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) MergeList::iterator eachMergeList=mergeListTmp.begin(); for(;eachMergeList!=mergeListTmp.end();++eachMergeList) { - if (!eachMergeList->empty() && eachMergeList->front()!=NULL + if (!eachMergeList->empty() && eachMergeList->front()!=nullptr && isAbleToMerge(*eachMergeList->front(),*geomToPush)) { eachMergeList->push_back(geomToPush); diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index c409bcd5cc..30a3f076cf 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -37,7 +37,7 @@ namespace SceneUtil { RigGeometry::RigGeometry() - : mSkeleton(NULL) + : mSkeleton(nullptr) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -47,7 +47,7 @@ RigGeometry::RigGeometry() RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mInfluenceMap(copy.mInfluenceMap) , mLastFrameNumber(0) , mBoundsFirstFrame(true) @@ -98,7 +98,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); } else - mSourceTangents = NULL; + mSourceTangents = nullptr; } } @@ -296,7 +296,7 @@ void RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath) { if (!geomToSkelMatrix) geomToSkelMatrix = new osg::RefMatrix; - trans->computeWorldToLocalMatrix(*geomToSkelMatrix, NULL); + trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr); } } } diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 58ed9a27cf..9be440d93e 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -64,7 +64,7 @@ Bone* Skeleton::getBone(const std::string &name) BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name)); if (found == mBoneCache.end()) - return NULL; + return nullptr; // find or insert in the bone hierarchy @@ -81,7 +81,7 @@ Bone* Skeleton::getBone(const std::string &name) if (!matrixTransform) continue; - Bone* child = NULL; + Bone* child = nullptr; for (unsigned int i=0; imChildren.size(); ++i) { if (bone->mChildren[i]->mNode == *it) @@ -117,7 +117,7 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber) if (mRootBone.get()) { for (unsigned int i=0; imChildren.size(); ++i) - mRootBone->mChildren[i]->update(NULL); + mRootBone->mChildren[i]->update(nullptr); } mNeedToUpdateBoneMatrices = false; @@ -167,7 +167,7 @@ void Skeleton::childRemoved(unsigned int, unsigned int) } Bone::Bone() - : mNode(NULL) + : mNode(nullptr) { } diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 5a5ecaded0..121cdaca67 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -30,8 +30,8 @@ namespace SceneUtil void StateSetUpdater::reset() { - mStateSets[0] = NULL; - mStateSets[1] = NULL; + mStateSets[0] = nullptr; + mStateSets[1] = nullptr; } StateSetUpdater::StateSetUpdater() diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 536331132d..3c3559a083 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -101,7 +101,7 @@ namespace SceneUtil void CleanObjectRootVisitor::applyNode(osg::Node& node) { if (node.getStateSet()) - node.setStateSet(NULL); + node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) mToRemove.push_back(std::make_pair(&node, node.getParent(0))); diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 3e9a565c03..02a351da7c 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -9,14 +9,14 @@ namespace SceneUtil { // Find a Group by name, case-insensitive - // If not found, mFoundNode will be NULL + // If not found, mFoundNode will be nullptr class FindByNameVisitor : public osg::NodeVisitor { public: FindByNameVisitor(const std::string& nameToFind) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mNameToFind(nameToFind) - , mFoundNode(NULL) + , mFoundNode(nullptr) { } diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 37e8563e12..0ca5e4d3e0 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -97,7 +97,7 @@ osg::ref_ptr WorkQueue::removeWorkItem() return item; } else - return NULL; + return nullptr; } unsigned int WorkQueue::getNumItems() const diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 7de6a5af52..2084df328b 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -57,7 +57,7 @@ namespace SceneUtil void addWorkItem(osg::ref_ptr item, bool front=false); /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added. - /// If the workqueue is in the process of being destroyed, may return NULL. + /// If the workqueue is in the process of being destroyed, may return nullptr. /// @par Used internally by the WorkThread. osg::ref_ptr removeWorkItem(); diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 1560b74b3e..6d21175753 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -190,7 +190,7 @@ namespace CursorDecompression SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); - SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); + SDL_RenderCopyEx(renderer, cursorTexture, nullptr, nullptr, -rotDegrees, nullptr, SDL_FLIP_VERTICAL); SDL_DestroyTexture(cursorTexture); SDL_FreeSurface(cursorSurface); diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index f9767238f0..dc6129e43f 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -77,7 +77,7 @@ void GraphicsWindowSDL2::init() return; WindowData *inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); - mWindow = inheritedWindowData ? inheritedWindowData->mWindow : NULL; + mWindow = inheritedWindowData ? inheritedWindowData->mWindow : nullptr; mOwnsWindow = (mWindow == 0); if(mOwnsWindow) @@ -162,7 +162,7 @@ bool GraphicsWindowSDL2::releaseContextImplementation() return false; } - return SDL_GL_MakeCurrent(NULL, NULL)==0; + return SDL_GL_MakeCurrent(nullptr, nullptr)==0; } @@ -170,11 +170,11 @@ void GraphicsWindowSDL2::closeImplementation() { if(mContext) SDL_GL_DeleteContext(mContext); - mContext = NULL; + mContext = nullptr; if(mWindow && mOwnsWindow) SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; mValid = false; mRealized = false; diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 5fc828fb59..f7111fdf80 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -10,10 +10,10 @@ namespace SDLUtil InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr viewer, bool grab) : mSDLWindow(window), mViewer(viewer), - mMouseListener(NULL), - mKeyboardListener(NULL), - mWindowListener(NULL), - mConListener(NULL), + mMouseListener(nullptr), + mKeyboardListener(nullptr), + mWindowListener(nullptr), + mConListener(nullptr), mWarpX(0), mWarpY(0), mWarpCompensate(false), @@ -234,7 +234,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v bool InputWrapper::isKeyDown(SDL_Scancode key) { - return (SDL_GetKeyboardState(NULL)[key]) != 0; + return (SDL_GetKeyboardState(nullptr)[key]) != 0; } /// \brief Moves the mouse to the specified point within the viewport diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 28f4300c29..f3d52f5f04 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -115,7 +115,7 @@ namespace Shader if (stream.fail()) { Log(Debug::Error) << "Failed to open " << p.string(); - return NULL; + return nullptr; } std::stringstream buffer; buffer << stream.rdbuf(); @@ -123,7 +123,7 @@ namespace Shader // parse includes std::string source = buffer.str(); if (!parseIncludes(boost::filesystem::path(mPath), source)) - return NULL; + return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; } @@ -136,7 +136,7 @@ namespace Shader { // Add to the cache anyway to avoid logging the same error over and over. mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), nullptr)); - return NULL; + return nullptr; } osg::ref_ptr shader (new osg::Shader(shaderType)); diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index bd820a725c..6b2e021242 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -26,7 +26,7 @@ namespace Shader /// @param shaderTemplate The filename of the shader template. /// @param defines Define values that can be retrieved by the shader template. /// @param shaderType The type of shader (usually vertex or fragment shader). - /// @note May return NULL on failure. + /// @note May return nullptr on failure. /// @note Thread safe. osg::ref_ptr getShader(const std::string& shaderTemplate, const DefineMap& defines, osg::Shader::Type shaderType); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index cbd950ea3b..a0a6832d06 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -28,7 +28,7 @@ namespace Shader , mMaterialOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) - , mNode(NULL) + , mNode(nullptr) { } @@ -102,15 +102,15 @@ namespace Shader void ShaderVisitor::applyStateSet(osg::ref_ptr stateset, osg::Node& node) { - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); if (!texAttributes.empty()) { - const osg::Texture* diffuseMap = NULL; - const osg::Texture* normalMap = NULL; - const osg::Texture* specularMap = NULL; + const osg::Texture* diffuseMap = nullptr; + const osg::Texture* normalMap = nullptr; + const osg::Texture* specularMap = nullptr; for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); @@ -153,7 +153,7 @@ namespace Shader } } - if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseNormalMaps && diffuseMap != nullptr && normalMap == nullptr && diffuseMap->getImage(0)) { std::string normalMapFileName = diffuseMap->getImage(0)->getFileName(); @@ -195,7 +195,7 @@ namespace Shader mRequirements.back().mNormalHeight = normalHeight; } } - if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); @@ -254,7 +254,7 @@ namespace Shader return; osg::Node& node = *reqs.mNode; - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getOrCreateStateSet(); else @@ -321,7 +321,7 @@ namespace Shader // make sure that all UV sets are there for (std::map::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it) { - if (sourceGeometry.getTexCoordArray(it->first) == NULL) + if (sourceGeometry.getTexCoordArray(it->first) == nullptr) { sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0)); changed = true; @@ -342,7 +342,7 @@ namespace Shader void ShaderVisitor::apply(osg::Geometry& geometry) { - bool needPop = (geometry.getStateSet() != NULL); + bool needPop = (geometry.getStateSet() != nullptr); if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it { pushRequirements(geometry); @@ -365,7 +365,7 @@ namespace Shader void ShaderVisitor::apply(osg::Drawable& drawable) { // non-Geometry drawable (e.g. particle system) - bool needPop = (drawable.getStateSet() != NULL); + bool needPop = (drawable.getStateSet() != nullptr); if (drawable.getStateSet()) { diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 7575113ef2..b23b0b76c0 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -22,7 +22,7 @@ namespace Terrain { ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index d3fdfa1f8e..3dc0aa41c5 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -45,7 +45,7 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const CompositeMap* node = *mImmediateCompileSet.begin(); mCompiled.insert(node); - compile(*node, renderInfo, NULL); + compile(*node, renderInfo, nullptr); mImmediateCompileSet.erase(mImmediateCompileSet.begin()); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index f4fc8df897..2222cbb844 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -41,7 +41,7 @@ bool adjacent(ChildDirection dir, Direction dir2) QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) { if (currentNode->getDirection() == Root) - return NULL; // Arrived at root node, the root node does not have neighbours + return nullptr; // Arrived at root node, the root node does not have neighbours QuadTreeNode* nextNode; if (adjacent(currentNode->getDirection(), dir)) @@ -52,7 +52,7 @@ QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) if (nextNode && nextNode->getNumChildren()) return nextNode->getChild(reflect(currentNode->getDirection(), dir)); else - return NULL; + return nullptr; } QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 9b9bbccd61..40f7a6fb0c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -96,8 +96,8 @@ class RootNode : public QuadTreeNode { public: RootNode(float size, const osg::Vec2f& center) - : QuadTreeNode(NULL, Root, size, center) - , mWorld(NULL) + : QuadTreeNode(nullptr, Root, size, center) + , mWorld(nullptr) { } @@ -317,7 +317,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); if (lodFlags != entry.mLodFlags) { - entry.mRenderingNode = NULL; + entry.mRenderingNode = nullptr; entry.mLodFlags = lodFlags; } } @@ -367,7 +367,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (udc && udc->getUserData()) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); - udc->setUserData(NULL); + udc->setUserData(nullptr); } entry.mRenderingNode->accept(nv); } @@ -430,7 +430,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); for (unsigned int i=0; igetNumEntries(); ++i) { diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index 79a28deebf..6bef60bc73 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -24,7 +24,7 @@ namespace Terrain public: virtual osg::Object* cloneType() const { return new TerrainDrawable (); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new TerrainDrawable (*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* className() const { return "TerrainDrawable"; } virtual const char* libraryName() const { return "Terrain"; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 74f683774c..dbc1429da2 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -34,7 +34,7 @@ TerrainGrid::~TerrainGrid() void TerrainGrid::cacheCell(View* view, int x, int y) { osg::Vec2f center(x+0.5f, y+0.5f); - static_cast(view)->mLoaded = buildTerrain(NULL, 1.f, center); + static_cast(view)->mLoaded = buildTerrain(nullptr, 1.f, center); } osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) @@ -57,7 +57,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu { osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); if (!node) - return NULL; + return nullptr; if (parent) parent->addChild(node); @@ -71,7 +71,7 @@ void TerrainGrid::loadCell(int x, int y) return; // already loaded osg::Vec2f center(x+0.5f, y+0.5f); - osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); + osg::ref_ptr terrainNode = buildTerrain(nullptr, 1.f, center); if (!terrainNode) return; // no terrain defined diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 5c70f65f20..2f9cb66413 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -64,7 +64,7 @@ void ViewData::reset(unsigned int frame) { // clear any unused entries for (unsigned int i=mNumEntries; isecond; if (vd->getFrameLastUsed() + 2 < frame) { - vd->setViewer(NULL); + vd->setViewer(nullptr); vd->clear(); mUnusedViews.push_back(vd); mViews.erase(it++); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index ae71693bdf..da10047832 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -90,7 +90,7 @@ namespace Terrain /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. - virtual View* createView() { return NULL; } + virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. virtual void preload(View* view, const osg::Vec3f& eyePoint) {} diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 14a32f178d..ef0f432075 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -5,7 +5,7 @@ namespace Translation { Storage::Storage() - : mEncoder(NULL) + : mEncoder(nullptr) { } diff --git a/components/widgets/windowcaption.cpp b/components/widgets/windowcaption.cpp index 1c8fb5608d..0fed4beadf 100644 --- a/components/widgets/windowcaption.cpp +++ b/components/widgets/windowcaption.cpp @@ -6,9 +6,9 @@ namespace Gui { WindowCaption::WindowCaption() - : mLeft(NULL) - , mRight(NULL) - , mClient(NULL) + : mLeft(nullptr) + , mRight(nullptr) + , mClient(nullptr) { } From 5617bb3f0c0c70debcded2c00bdd12a642d5eb73 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Oct 2018 11:29:57 +0400 Subject: [PATCH 136/150] Improve pitch factor handling for crossbow animations (bug #4672) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 35 +++++++++------------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f093742..99cc5eabd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4672: Pitch factor is handled incorrectly for crossbow animations Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c4975b308b..a74e7a49b9 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1666,7 +1666,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) } mAnimation->setPitchFactor(0.f); - if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + if (mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_Thrown || + mWeaponType == WeapType_Crossbow) { switch (mUpperBodyState) { @@ -1680,29 +1682,14 @@ bool CharacterController::updateWeaponState(CharacterState& idle) break; case UpperCharState_FollowStartToFollowStop: if (animPlaying) - mAnimation->setPitchFactor(1.f-complete); - break; - default: - break; - } - } - else if (mWeaponType == WeapType_Crossbow) - { - switch (mUpperBodyState) - { - case UpperCharState_EquipingWeap: - mAnimation->setPitchFactor(complete); - break; - case UpperCharState_UnEquipingWeap: - mAnimation->setPitchFactor(1.f-complete); - break; - case UpperCharState_WeapEquiped: - case UpperCharState_StartToMinAttack: - case UpperCharState_MinAttackToMaxAttack: - case UpperCharState_MaxAttackToMinHit: - case UpperCharState_MinHitToHit: - case UpperCharState_FollowStartToFollowStop: - mAnimation->setPitchFactor(1.f); + { + // technically we do not need a pitch for crossbow reload animation, + // but we should avoid abrupt repositioning + if (mWeaponType == WeapType_Crossbow) + mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f)); + else + mAnimation->setPitchFactor(1.f-complete); + } break; default: break; From 4ec727c50b4a624b9b191a0b74294fb15d995f84 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 10 Oct 2018 18:33:56 +0300 Subject: [PATCH 137/150] Disallow to open the journal while settings window is open (bug #4674) --- CHANGELOG.md | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa877affbc..3ad7e9aa6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,6 +138,7 @@ Bug #4669: ToggleCollision should trace the player down after collision being enabled Bug #4671: knownEffect functions should use modified Alchemy skill Bug #4672: Pitch factor is handled incorrectly for crossbow animations + Bug #4674: Journal can be opened when settings window is open Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b0a98bafc2..0d017ba90f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1107,6 +1107,7 @@ namespace MWInput if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); From 47b1b0ac39c722de9409390b31e4371fcfb438a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 10 Oct 2018 22:44:26 +0300 Subject: [PATCH 138/150] Re-fix water reflections while making a no-GUI screenshot --- apps/openmw/mwgui/loadingscreen.cpp | 13 ++++---- apps/openmw/mwrender/renderingmanager.cpp | 37 +++++------------------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 38ab70d544..3bb3ee260d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -169,19 +169,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); - mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); + mVisible = visible; + mLoadingBox->setVisible(mVisible); + setVisible(true); - if (!visible) + if (!mVisible) { + mShowWallpaper = false; draw(); return; } - mVisible = visible; - mLoadingBox->setVisible(mVisible); - - setVisible(true); + mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; if (mShowWallpaper) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb0bb2f130..476beb990b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -805,7 +805,7 @@ namespace MWRender cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST); cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST); - + cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -830,19 +830,14 @@ namespace MWRender stateset->addUniform(new osg::Uniform("cubeMap",0)); stateset->addUniform(new osg::Uniform("mapping",screenshotMapping)); stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); - + quad->setStateSet(stateset); quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); - mRootNode->addChild(screenshotCamera); - renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH); - screenshotCamera->removeChildren(0,screenshotCamera->getNumChildren()); - mRootNode->removeChild(screenshotCamera); - return true; } @@ -867,6 +862,8 @@ namespace MWRender image->setDataType(GL_UNSIGNED_BYTE); image->setPixelFormat(texture->getInternalFormat()); + mRootNode->addChild(camera); + // The draw needs to complete before we can copy back our image. osg::ref_ptr callback (new NotifyDrawCompletedCallback); camera->setFinalDrawCallback(callback); @@ -882,32 +879,17 @@ namespace MWRender // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + + camera->removeChildren(0, camera->getNumChildren()); + mRootNode->removeChild(camera); } void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform) { osg::ref_ptr rttCamera (new osg::Camera); - rttCamera->setNodeMask(Mask_RenderToTexture); - rttCamera->attach(osg::Camera::COLOR_BUFFER, image); - rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); - rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform); - rttCamera->setViewport(0, 0, w, h); - - osg::ref_ptr texture (new osg::Texture2D); - texture->setInternalFormat(GL_RGB); - texture->setTextureSize(w, h); - texture->setResizeNonPowerOfTwoHint(false); - texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); - texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); - rttCamera->attach(osg::Camera::COLOR_BUFFER, texture); - - image->setDataType(GL_UNSIGNED_BYTE); - image->setPixelFormat(texture->getInternalFormat()); - rttCamera->setUpdateCallback(new NoTraverseCallback); rttCamera->addChild(mSceneRoot); @@ -916,14 +898,9 @@ namespace MWRender rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); - mRootNode->addChild(rttCamera); - rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderCameraToImage(rttCamera.get(),image,w,h); - - rttCamera->removeChildren(0, rttCamera->getNumChildren()); - mRootNode->removeChild(rttCamera); } osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) From f9271fd7f090d75b2022373aa1028afaf49bac5c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 10:36:50 +0400 Subject: [PATCH 139/150] Fix crash in the ESM reader, when list is empty (bug #4677) --- CHANGELOG.md | 1 + components/esm/transport.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad7e9aa6e..2c45c8eb46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,7 @@ Bug #4671: knownEffect functions should use modified Alchemy skill Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open + Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/transport.cpp b/components/esm/transport.cpp index 063c033299..11676ea723 100644 --- a/components/esm/transport.cpp +++ b/components/esm/transport.cpp @@ -1,5 +1,7 @@ #include "transport.hpp" +#include + #include #include @@ -16,7 +18,11 @@ namespace ESM } else if (esm.retSubName().intval == ESM::FourCC<'D','N','A','M'>::value) { - mList.back().mCellName = esm.getHString(); + const std::string name = esm.getHString(); + if (mList.empty()) + Log(Debug::Warning) << "Encountered DNAM record without DODT record, skipped."; + else + mList.back().mCellName = name; } } From ca8744af56812d7d51420b937b3a157304b10a3d Mon Sep 17 00:00:00 2001 From: jvoisin Date: Fri, 12 Oct 2018 09:28:18 +0200 Subject: [PATCH 140/150] Fix crash in the ESM reader, when SCVR has no variable names Fixes bug [#4678](https://gitlab.com/OpenMW/openmw/issues/4678) The `loadSCVR` method assumes that the list of variable names won't be empty, which it might. Instead of crashing, we show a warning and ignore the record. --- CHANGELOG.md | 1 + components/esm/loadscpt.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c45c8eb46..320e486937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one + Bug #4678: Crash in ESP parser when SCVR has no variable names Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 474c2b4234..808b416d7b 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -30,6 +30,12 @@ namespace ESM // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = &tmp[0]; + if (!str && mVarNames.size() > 0) + { + Log(Debug::Warning) << "SCVR with no variable names"; + return; + } + for (size_t i = 0; i < mVarNames.size(); i++) { // Support '\r' terminated strings like vanilla. See Bug #1324. From dad0b78901b1ddb57949982c4099ea641749bfb2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 19:41:34 +0400 Subject: [PATCH 141/150] Avoid overflow when handling output characters (bug #4676) --- apps/openmw/mwgui/bookpage.cpp | 8 ++++---- apps/openmw/mwgui/bookpage.hpp | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 29dfe7f3a6..80e92f15ae 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -506,7 +506,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -516,7 +516,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -765,7 +765,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint < 0) + if (!info.charFound) return; MyGUI::FloatRect vr; @@ -787,7 +787,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint >= 0) + if (info.charFound) mCursor.left += static_cast(info.bearingX + info.advance); } diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 4cf99794d7..4ea59414df 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -43,6 +43,7 @@ namespace MWGui float advance; float bearingX; float bearingY; + bool charFound; MyGUI::FloatRect uvRect; GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) @@ -61,15 +62,17 @@ namespace MWGui height = (int) gi->height / scale; advance = (int) gi->advance / scale; uvRect = gi->uvRect; + charFound = true; } else { - codePoint = -1; + codePoint = 0; bearingX = 0; bearingY = 0; width = 0; height = 0; advance = 0; + charFound = false; } } }; From 292f4fcafe204be73543cfd862078bdc1e472815 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Fri, 12 Oct 2018 21:09:27 -0700 Subject: [PATCH 142/150] Unified settings list and cfg file names. Added brief tutorial. --- docs/source/conf.py | 4 ++-- .../reference/modding/settings/index.rst | 18 +++++++++++++++++- .../reference/modding/settings/shaders.rst | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index da59c02e1d..60b25ae57b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -169,8 +169,8 @@ def setup(app): # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static', - 'manuals/openmw-cs/_static' +html_static_path = [ + '_static' ] # Add any extra paths that contain custom files (such as robots.txt or diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 56d76a8d10..f7c86b5678 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -8,10 +8,26 @@ If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this w All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. See :doc:`../paths` for this location. +Changing Settings +################# + +#. Once you have located your ``settings.cfg`` file, open it in a plain text editor. +#. Find the setting(s) you wish to change in the following pages. +#. If the setting is not already in ``settings.cfg``, + add it by copy and pasting the name exactly as written in this guide. +#. Set the value of the setting by typing ``= `` after the setting on the same line, + using an appropriate value in place of ````. +#. If this is the first setting from it's category that you're adding, + be sure to add the heading in square brackets ``[]`` above it using just the setting type, + i.e. without the word "Settings". + + For example, to delay tooltips popping up by 1 second, add the line ``tooltip delay = 1.0``. + Then to the line above, type ``[GUI]``, as the tooltip delay setting comes from the "GUI Settings" section. + Although this guide attempts to be comprehensive and up to date, you will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` in your main OpenMW installation directory. -The ranges I have included with each setting are the physically possible ranges, not recommendations. +The ranges included with each setting are the physically possible ranges, not recommendations. .. warning:: As the title suggests, these are advanced settings. diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index 17d0929174..be1ecebf0c 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -1,5 +1,5 @@ -Shader Settings -############### +Shaders Settings +################ force shaders ------------- From b5df385111241e14fcee20795b8c0a88431ffdbb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 14:11:41 +0400 Subject: [PATCH 143/150] Allow apps without logging system to display log messages --- components/debug/debuglog.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index f4a8e17bef..f5cdffeda1 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -8,12 +8,13 @@ namespace Debug { enum Level { - NoLevel = 0, Error = 1, Warning = 2, Info = 3, Verbose = 4, - Marker = Verbose + Marker = Verbose, + + NoLevel = 5 // Do not filter messages in this case }; extern Level CurrentDebugLevel; @@ -30,6 +31,11 @@ public: mLock(sLock), mLevel(level) { + // If the app has no logging system enabled, log level is not specified. + // Show all messages without marker - we just use the plain cout in this case. + if (Debug::CurrentDebugLevel == Debug::NoLevel) + return; + if (mLevel <= Debug::CurrentDebugLevel) std::cout << static_cast(mLevel); } From ca07e3a36449e09ee7f8346161e7cb6beb6ffe3b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 08:38:55 +0400 Subject: [PATCH 144/150] Check for obstacle before back up (bug #4656) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 4 +- apps/openmw/mwmechanics/aicombat.cpp | 59 +++++++++++++++++++------ apps/openmw/mwmechanics/pathfinding.cpp | 5 ++- apps/openmw/mwworld/worldimp.cpp | 13 +++--- apps/openmw/mwworld/worldimp.hpp | 4 +- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e486937..abed0ba91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects + Bug #4656: Combat AI: back up behaviour is incorrect Bug #4668: Editor: Light source color is displayed as an integer Bug #4669: ToggleCollision should trace the player down after collision being enabled Bug #4671: knownEffect functions should use modified Alchemy skill diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e17935abcd..027d1fd102 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -297,9 +297,11 @@ namespace MWBase ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0; + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0; ///< cast a Ray and return true if there is an object in the ray path. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f9545f99d..a96832b698 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,6 +4,10 @@ #include +#include + +#include "../mwphysics/collisiontype.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -456,7 +460,48 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } + else if (isDistantCombat) + { + // Backing up behaviour + // Actor backs up slightly further away than opponent's weapon range + // (in vanilla - only as far as oponent's weapon range), + // or not at all if opponent is using a ranged weapon + + if (targetUsesRanged || distToTarget > rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon + return; + + // actor should not back up into water + if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) + return; + + int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door; + + // Actor can not back up if there is no free space behind + // Currently we take the 35% of actor's height from the ground as vector height. + // This approach allows us to detect small obstacles (e.g. crates) and curved walls. + osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + osg::Vec3f pos = actor.getRefData().getPosition().asVec3(); + osg::Vec3f source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()); + osg::Vec3f fallbackDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,-1,0); + osg::Vec3f destination = source + fallbackDirection * (halfExtents.y() + 16); + + bool isObstacleDetected = MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask); + if (isObstacleDetected) + return; + + // Check if there is nothing behind - probably actor is near cliff. + // A current approach: cast ray 1.5-yard ray down in 1.5 yard behind actor from 35% of actor's height. + // If we did not hit anything, there is a cliff behind actor. + source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()) + fallbackDirection * (halfExtents.y() + 96); + destination = source - osg::Vec3f(0, 0, 0.75f * halfExtents.z() + 96); + bool isCliffDetected = !MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask); + if (isCliffDetected) + return; + + mMovement.mPosition[1] = -1; + } // dodge movements (for NPCs and bipedal creatures) + // Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff else if (actor.getClass().isBipedal(actor)) { // apply sideway movement (kind of dodging) with some probability @@ -468,20 +513,6 @@ namespace MWMechanics mCombatMove = true; } } - - // Backing up behaviour - // Actor backs up slightly further away than opponent's weapon range - // (in vanilla - only as far as oponent's weapon range), - // or not at all if opponent is using a ranged weapon - if (isDistantCombat) - { - // actor should not back up into water - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) - return; - - if (!targetUsesRanged && distToTarget <= rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon - mMovement.mPosition[1] = -1; - } } void AiCombatStorage::updateCombatMove(float duration) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1da97a6459..c16cff9e10 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -5,6 +5,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwphysics/collisiontype.hpp" + #include "../mwworld/cellstore.hpp" #include "pathgrid.hpp" @@ -246,8 +248,9 @@ namespace MWMechanics converter.toWorld(temp); // Add Z offset since path node can overlap with other objects. // Also ignore doors in raytesting. + int mask = MWPhysics::CollisionType_World; bool isPathClear = !MWBase::Environment::get().getWorld()->castRay( - startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true); + startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, mask); if (isPathClear) mPath.pop_front(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0872e589dc..0f20fa05a1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1505,15 +1505,18 @@ namespace MWWorld moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); } - bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors) + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door; + bool result = castRay(x1, y1, z1, x2, y2, z2, mask); + return result; + } + + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - int mask = MWPhysics::CollisionType_World; - if (!ignoreDoors) - mask |= MWPhysics::CollisionType_Door; - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), mask); return result.mHit; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7df8d1af5b..1592453a20 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -402,9 +402,11 @@ namespace MWWorld ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override; + bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) override; ///< cast a Ray and return true if there is an object in the ray path. + bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override; + bool toggleCollisionMode() override; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. From 19fd404b7ba0fd32e45473f91ae3c361758b7513 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 14:44:32 +0300 Subject: [PATCH 145/150] Support soundgen calls for activators (feature #4285) --- CHANGELOG.md | 1 + apps/openmw/mwclass/activator.cpp | 70 +++++++++++++++++++++++++++++++ apps/openmw/mwclass/activator.hpp | 4 ++ 3 files changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e486937..ddf20fa7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ Feature #4012: Editor: Write a log file if OpenCS crashes Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command + Feature #4285: Support soundgen calls for activators Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts Feature #4345: Add equivalents for the command line commands to Launcher Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 8df262f240..7f53709d6c 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -1,6 +1,7 @@ #include "activator.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -134,4 +135,73 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } + + std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const + { + std::string model = getModel(ptr); + if (model.empty()) + return std::string(); + + const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string creatureId; + + for (const ESM::Creature &iter : creaturestore) + { + if (iter.mModel.empty()) + continue; + + if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + { + creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; + break; + } + } + + if (creatureId.empty()) + return std::string(); + + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + int type = getSndGenTypeFromName(name); + std::vector sounds; + + MWWorld::Store::iterator sound = store.begin(); + + while (sound != store.end()) + { + if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) + sounds.push_back(&*sound); + ++sound; + } + + if (!sounds.empty()) + return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + + if (type == ESM::SoundGenerator::Land) + return "Body Fall Large"; + + return std::string(); + } + + int Activator::getSndGenTypeFromName(const std::string &name) const + { + if (name == "left") + return 0; + if (name == "right") + return 1; + if (name == "swimleft") + return 2; + if (name == "swimright") + return 3; + if (name == "moan") + return 4; + if (name == "roar") + return 5; + if (name == "scream") + return 6; + if (name == "land") + return 7; + + throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); + } } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 3f333f4cbe..0f66b74b45 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -44,6 +44,10 @@ namespace MWClass ///< Whether or not to use animated variant of model (default false) virtual bool isActivator() const; + + virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; + + virtual int getSndGenTypeFromName(const std::string &name) const; }; } From 6ef7be3fd3e50925b26bd8d0cd2cb3b87e1846db Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:11:21 +0300 Subject: [PATCH 146/150] Re-enable using soundgen land for creatures --- apps/openmw/mwclass/npc.cpp | 10 ++-------- apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4339f37e29..d6dafd2a25 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1242,15 +1242,9 @@ namespace MWClass return ""; } + // Morrowind ignores land soundgen for NPCs if(name == "land") - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return "DefaultLandWater"; - - return "DefaultLand"; - } + return ""; if(name == "swimleft") return "Swim Left"; if(name == "swimright") diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7ad8d61cd2..0931aeda12 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -979,17 +979,13 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } - if (soundgen == "land") // Morrowind ignores land soundgen for some reason - return; - std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(soundgen == "left" || soundgen == "right") + // NB: landing sound is not played for NPCs here + if(soundgen == "left" || soundgen == "right" || soundgen == "land") { - // Don't make foot sounds local for the player, it makes sense to keep them - // positioned on the ground. sndMgr->playSound3D(mPtr, sound, volume, pitch, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } @@ -2071,11 +2067,17 @@ void CharacterController::update(float duration) } } - // Play landing sound - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); - if (!sound.empty()) + // Play landing sound for NPCs + if (mPtr.getClass().isNpc()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = "DefaultLand"; + osg::Vec3f pos(mPtr.getRefData().getPosition().asVec3()); + if (world->isUnderwater(mPtr.getCell(), pos) || world->isWalkingOnWater(mPtr)) + sound = "DefaultLandWater"; + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); + } } else { From bf3f82b9d4c6f259fe50fe12063b6838d688283a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:37:40 +0300 Subject: [PATCH 147/150] Cleanup --- apps/openmw/mwclass/activator.cpp | 43 +++++++++++-------------------- apps/openmw/mwclass/activator.hpp | 4 +-- apps/openmw/mwclass/creature.cpp | 20 +++++++------- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 7f53709d6c..e0e2013912 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -138,19 +138,13 @@ namespace MWClass std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { - std::string model = getModel(ptr); - if (model.empty()) - return std::string(); - - const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::string creatureId; - - for (const ESM::Creature &iter : creaturestore) - { - if (iter.mModel.empty()) - continue; - if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + for (const ESM::Creature &iter : store.get()) + { + if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) { creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; break; @@ -160,19 +154,12 @@ namespace MWClass if (creatureId.empty()) return std::string(); - const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); - int type = getSndGenTypeFromName(name); std::vector sounds; - MWWorld::Store::iterator sound = store.begin(); - - while (sound != store.end()) - { + for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) sounds.push_back(&*sound); - ++sound; - } if (!sounds.empty()) return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; @@ -183,24 +170,24 @@ namespace MWClass return std::string(); } - int Activator::getSndGenTypeFromName(const std::string &name) const + int Activator::getSndGenTypeFromName(const std::string &name) { if (name == "left") - return 0; + return ESM::SoundGenerator::LeftFoot; if (name == "right") - return 1; + return ESM::SoundGenerator::RightFoot; if (name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if (name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if (name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if (name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if (name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if (name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 0f66b74b45..b92dc75cbd 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -10,6 +10,8 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; + static int getSndGenTypeFromName(const std::string &name); + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; @@ -46,8 +48,6 @@ namespace MWClass virtual bool isActivator() const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; - - virtual int getSndGenTypeFromName(const std::string &name) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6d0b42bfeb..788e5cd689 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -688,9 +688,9 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 2; + return ESM::SoundGenerator::SwimLeft; if(world->isOnGround(ptr)) - return 0; + return ESM::SoundGenerator::LeftFoot; return -1; } if(name == "right") @@ -698,23 +698,23 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 3; + return ESM::SoundGenerator::SwimRight; if(world->isOnGround(ptr)) - return 1; + return ESM::SoundGenerator::RightFoot; return -1; } if(name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if(name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if(name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if(name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if(name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if(name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } From 13bd81f8969808d6bd13fc4d24e375b67c2ab504 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Oct 2018 22:28:19 +0400 Subject: [PATCH 148/150] Try to use collisions from basic actor model if an animated one has no collisions (feature #4682) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/physicssystem.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf20fa7f3..cda9150b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -175,6 +175,7 @@ Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu Feature #4642: Batching potion creation + Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 363f28e70f..2ce498f377 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1337,6 +1337,16 @@ namespace MWPhysics if (!shape) return; + // Try to get shape from basic model as fallback for creatures + if (!ptr.getClass().isNpc() && shape->mCollisionBoxHalfExtents.length2() == 0) + { + const std::string fallbackModel = ptr.getClass().getModel(ptr); + if (fallbackModel != mesh) + { + shape = mShapeManager->getShape(fallbackModel); + } + } + Actor* actor = new Actor(ptr, shape, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); } From d7d9050d4ae558708a4a291a3e0709d14cee452a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 11:42:03 +0400 Subject: [PATCH 149/150] Force actor to the 'weapon equipped' state if the weapon disappeared in the middle of attack (bug #4646) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f7..44932f2ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4646: Weapon force-equipment messes up ongoing attack animations Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0931aeda12..ce844674f4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1278,6 +1278,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle) bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; + // If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires), + // we should force actor to the "weapon equipped" state, interrupt attack and update animations. + if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped) + { + forcestateupdate = true; + mUpperBodyState = UpperCharState_WeapEquiped; + mAttackingOrSpell = false; + mAnimation->disable(mCurrentWeapon); + if (mPtr == getPlayer()) + MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); + } + if(!isKnockedOut() && !isKnockedDown() && !isRecovery()) { std::string weapgroup; From 8fa0ffcfe4e0753651c1435fc1aed0dc735b6cce Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 14:59:39 +0400 Subject: [PATCH 150/150] Catch exceptions inside the loadVoice() (bug #4685) --- CHANGELOG.md | 1 + apps/openmw/mwsound/soundmanagerimp.cpp | 36 +++++++++++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f7..14abe86b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names + Bug #4685: Missing sound causes an exception inside Say command Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8e3488906c..7b722a8352 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -245,20 +245,30 @@ namespace MWSound DecoderPtr SoundManager::loadVoice(const std::string &voicefile) { - DecoderPtr decoder = getDecoder(); - // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. - if(mVFS->exists(voicefile)) - decoder->open(voicefile); - else + try { - std::string file = voicefile; - std::string::size_type pos = file.rfind('.'); - if(pos != std::string::npos) - file = file.substr(0, pos)+".mp3"; - decoder->open(file); + DecoderPtr decoder = getDecoder(); + + // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. + if(mVFS->exists(voicefile)) + decoder->open(voicefile); + else + { + std::string file = voicefile; + std::string::size_type pos = file.rfind('.'); + if(pos != std::string::npos) + file = file.substr(0, pos)+".mp3"; + decoder->open(file); + } + + return decoder; + } + catch(std::exception &e) + { + Log(Debug::Error) << "Failed to load audio from " << voicefile << ": " << e.what(); } - return decoder; + return nullptr; } Sound *SoundManager::getSoundRef() @@ -471,6 +481,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); @@ -503,6 +515,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; stopSay(MWWorld::ConstPtr()); Stream *sound = playVoice(decoder, osg::Vec3f(), true); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 4064a05afe..d8a4cfc8cc 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -117,7 +117,7 @@ namespace MWSound Sound_Buffer *lookupSound(const std::string &soundId) const; Sound_Buffer *loadSound(const std::string &soundId); - // returns a decoder to start streaming + // returns a decoder to start streaming, or nullptr if the sound was not found DecoderPtr loadVoice(const std::string &voicefile); Sound *getSoundRef();