From 0b074f22111bfbf41e09cc85fa43b35b0547c57e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 30 Jun 2019 01:50:17 +0300 Subject: [PATCH 01/23] Rewrite messed up playerlooking switch handling (bug #5078) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 1 - apps/openmw/mwinput/inputmanagerimp.cpp | 11 ++++------- apps/openmw/mwrender/camera.cpp | 6 ------ apps/openmw/mwrender/camera.hpp | 3 --- apps/openmw/mwrender/renderingmanager.cpp | 5 ----- apps/openmw/mwrender/renderingmanager.hpp | 1 - apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 2 -- 9 files changed, 5 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1f80705..3f818b706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,7 @@ Bug #5069: Blocking creatures' attacks doesn't degrade shields Bug #5074: Paralyzed actors greet the player Bug #5075: Enchanting cast style can be changed if there's no object + Bug #5078: DisablePlayerLooking is broken Bug #5082: Scrolling with controller in GUI mode is broken Bug #5092: NPCs with enchanted weapons play sound when out of charges Bug #5093: Hand to hand sound plays on knocked out enemies diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 256c8b1d5..63f69ab65 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -407,7 +407,6 @@ namespace MWBase virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable) = 0; virtual void allowVanityMode(bool allow) = 0; - virtual void togglePlayerLooking(bool enable) = 0; virtual void changeVanityModeScale(float factor) = 0; virtual bool vanityRotateCamera(float * rot) = 0; virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 581a96e7b..d3b79806c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -589,7 +589,7 @@ namespace MWInput rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"]) { mPlayer->yaw(rot[2]); mPlayer->pitch(rot[0]); @@ -827,9 +827,6 @@ namespace MWInput void InputManager::toggleControlSwitch (const std::string& sw, bool value) { - if (mControlSwitch[sw] == value) { - return; - } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { mPlayer->setLeftRight(0); @@ -841,8 +838,8 @@ namespace MWInput mPlayer->setUpDown(0); } else if (sw == "vanitymode") { MWBase::Environment::get().getWorld()->allowVanityMode(value); - } else if (sw == "playerlooking") { - MWBase::Environment::get().getWorld()->togglePlayerLooking(value); + } else if (sw == "playerlooking" && !value) { + MWBase::Environment::get().getWorld()->rotateObject(mPlayer->getPlayer(), 0.f, 0.f, 0.f); } mControlSwitch[sw] = value; } @@ -976,7 +973,7 @@ namespace MWInput rot[2] = -x; // Only actually turn player when we're not in vanity mode - if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"]) { mPlayer->yaw(x); mPlayer->pitch(y); diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 74f2ba911..e0818101d 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -48,7 +48,6 @@ namespace MWRender mAnimation(nullptr), mFirstPersonView(true), mPreviewMode(false), - mFreeLook(true), mNearest(30.f), mFurthest(800.f), mIsNearest(false), @@ -393,11 +392,6 @@ namespace MWRender camera = focal + offset; } - void Camera::togglePlayerLooking(bool enable) - { - mFreeLook = enable; - } - bool Camera::isVanityOrPreviewModeEnabled() { return mPreviewMode || mVanity.enabled; diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index db5ad25f0..573cf936f 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -37,7 +37,6 @@ namespace MWRender bool mFirstPersonView; bool mPreviewMode; - bool mFreeLook; float mNearest; float mFurthest; bool mIsNearest; @@ -119,8 +118,6 @@ namespace MWRender /// Stores focal and camera world positions in passed arguments void getPosition(osg::Vec3f &focal, osg::Vec3f &camera); - void togglePlayerLooking(bool enable); - bool isVanityOrPreviewModeEnabled(); bool isNearest(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0d9cbd9b0..c361ddf03 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1353,11 +1353,6 @@ namespace MWRender mCamera->allowVanityMode(allow); } - void RenderingManager::togglePlayerLooking(bool enable) - { - mCamera->togglePlayerLooking(enable); - } - void RenderingManager::changeVanityModeScale(float factor) { if(mCamera->isVanityOrPreviewModeEnabled()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 7fb436863..49da50be3 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -208,7 +208,6 @@ namespace MWRender void togglePreviewMode(bool enable); bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); - void togglePlayerLooking(bool enable); void changeVanityModeScale(float factor); /// temporarily override the field of view with given value. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3426e9115..73962c8f2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2389,11 +2389,6 @@ namespace MWWorld mRendering->allowVanityMode(allow); } - void World::togglePlayerLooking(bool enable) - { - mRendering->togglePlayerLooking(enable); - } - void World::changeVanityModeScale(float factor) { mRendering->changeVanityModeScale(factor); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index dd915c8bd..a8963a909 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -526,8 +526,6 @@ namespace MWWorld void allowVanityMode(bool allow) override; - void togglePlayerLooking(bool enable) override; - void changeVanityModeScale(float factor) override; bool vanityRotateCamera(float * rot) override; From 4e54ed86b3fb5f84ed15510cae62cbdfbf686b11 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 30 Jun 2019 17:38:49 +0300 Subject: [PATCH 02/23] Fix jumping mechanics framerate dependency (bug #4991) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 26 +++---------------------- apps/openmw/mwphysics/physicssystem.cpp | 26 ++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1f80705..650e29788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Bug #4984: "Friendly hits" feature should be used only for player's followers Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent Bug #4990: Dead bodies prevent you from hitting + Bug #4991: Jumping occasionally takes too much fatigue Bug #4999: Drop instruction behaves differently from vanilla Bug #5001: Possible data race in the Animation::setAlpha() Bug #5004: Werewolves shield their eyes during storm diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 00b3c0c67..7f047da5c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2099,28 +2099,6 @@ void CharacterController::update(float duration, bool animationOnly) lat.normalize(); vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f; } - - // advance acrobatics - // 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(); - const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); - float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - const float fatigueDecrease = fatigueJumpBase + normalizedEncumbrance * fatigueJumpMult; - - if (!godmode) - { - fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); - cls.getCreatureStats(mPtr).setFatigue(fatigue); - } } } else if(mJumpState == JumpState_InAir && !inwater && !flying && solid) @@ -2333,7 +2311,9 @@ void CharacterController::update(float duration, bool animationOnly) movement = vec; cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0; - // Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame + if (movement.z() == 0.f) + cls.getMovementSettings(mPtr).mPosition[2] = 0; + // Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled. if (!mSkipAnim) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 32d1d5ca8..31325cf21 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -34,6 +34,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/bulletdebugdraw.hpp" @@ -326,7 +327,30 @@ namespace MWPhysics if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel) velocity = osg::Vec3f(0,0,1) * 25; - ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; + if (ptr.getClass().getMovementSettings(ptr).mPosition[2]) + { + const bool isPlayer = (ptr == MWMechanics::getPlayer()); + // Advance acrobatics and set flag for GetPCJumping + if (isPlayer) + { + ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0); + MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + } + + // Decrease fatigue + if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState()) + { + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); + const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); + const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr)); + const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult; + MWMechanics::DynamicStat fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue(); + fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); + ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue); + } + ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; + } // Now that we have the effective movement vector, apply wind forces to it if (MWBase::Environment::get().getWorld()->isInStorm()) From 15bf05215e4dee7053728085bfa495a4e83e4229 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Mon, 29 Jul 2019 16:13:52 +0300 Subject: [PATCH 03/23] Revert questionable input manager decisions Remove Options Menu shortcut Revert to more pleasant 0.45.0 Escape behavior Re-enable keyboard GUI arrow conversion feature Remove gamepad button release handling --- apps/openmw/mwinput/inputmanagerimp.cpp | 64 ++++++++----------------- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +- 2 files changed, 20 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 581a96e7b..0c6d19200 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -197,6 +197,11 @@ namespace MWInput void InputManager::handleGuiArrowKey(int action) { + // This is currently keyboard-specific code + // TODO: see if GUI controls can be refactored into a single function + if (mJoystickLastUsed) + return; + if (SDL_IsTextInputActive()) return; @@ -224,13 +229,10 @@ namespace MWInput MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); } - bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release=false) + bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg) { // Presumption of GUI mode will be removed in the future. // MyGUI KeyCodes *may* change. - // Currently button releases are ignored. - if (release) - return false; MyGUI::KeyCode key = MyGUI::KeyCode::None; switch (arg.button) @@ -381,9 +383,6 @@ namespace MWInput case A_GameMenu: toggleMainMenu (); break; - case A_OptionsMenu: - toggleOptionsMenu(); - break; case A_Screenshot: screenshot(); break; @@ -401,8 +400,7 @@ namespace MWInput case A_MoveRight: case A_MoveForward: case A_MoveBackward: - // Temporary shut-down of this function until deemed necessary. - //handleGuiArrowKey(action); + handleGuiArrowKey(action); break; case A_Journal: toggleJournal (); @@ -1000,9 +998,9 @@ namespace MWInput mJoystickLastUsed = true; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { - if (gamepadToGuiControl(arg, false)) + if (gamepadToGuiControl(arg)) return; - else if (mGamepadGuiCursorEnabled) + if (mGamepadGuiCursorEnabled) { // Temporary mouse binding until keyboard controls are available: if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. @@ -1043,9 +1041,7 @@ namespace MWInput mJoystickLastUsed = true; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { - if (gamepadToGuiControl(arg, true)) - return; - else if (mGamepadGuiCursorEnabled) + if (mGamepadGuiCursorEnabled) { // Temporary mouse binding until keyboard controls are available: if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. @@ -1144,37 +1140,19 @@ namespace MWInput } if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - return; - - bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame; - MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - - if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings) - MWBase::Environment::get().getWindowManager()->popGuiMode(); - - if (inGame && mode != MWGui::GM_MainMenu) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu); - } - - void InputManager::toggleOptionsMenu() - { - if (MyGUI::InputManager::getInstance().isModalAny()) { - MWBase::Environment::get().getWindowManager()->exitCurrentModal(); + MWBase::Environment::get().getWindowManager()->toggleConsole(); return; } - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - return; - - MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame; - - if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings) - MWBase::Environment::get().getWindowManager()->popGuiMode(); - - if (inGame && mode != MWGui::GM_Settings) - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Settings); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else //Close current GUI + { + MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + } } void InputManager::quickLoad() { @@ -1529,7 +1507,6 @@ namespace MWInput defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B; defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START; - defaultButtonBindings[A_OptionsMenu] = SDL_CONTROLLER_BUTTON_BACK; defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE; defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP; defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; @@ -1610,7 +1587,6 @@ namespace MWInput descriptions[A_Journal] = "sJournal"; descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; - descriptions[A_OptionsMenu] = "sPreferences"; descriptions[A_TogglePOV] = "sTogglePOVCmd"; descriptions[A_QuickKeysMenu] = "sQuickMenu"; descriptions[A_QuickKey1] = "sQuick1Cmd"; @@ -1748,7 +1724,6 @@ namespace MWInput ret.push_back(A_Inventory); ret.push_back(A_Journal); ret.push_back(A_Rest); - ret.push_back(A_OptionsMenu); ret.push_back(A_Console); ret.push_back(A_QuickSave); ret.push_back(A_QuickLoad); @@ -1781,7 +1756,6 @@ namespace MWInput ret.push_back(A_Inventory); ret.push_back(A_Journal); ret.push_back(A_Rest); - ret.push_back(A_OptionsMenu); ret.push_back(A_QuickSave); ret.push_back(A_QuickLoad); ret.push_back(A_Screenshot); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index caf57681d..8762047ec 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -225,7 +225,7 @@ namespace MWInput void setPlayerControlsEnabled(bool enabled); void handleGuiArrowKey(int action); // Return true if GUI consumes input. - bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release); + bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg); bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg); void updateCursorMode(); @@ -234,7 +234,6 @@ namespace MWInput private: void toggleMainMenu(); - void toggleOptionsMenu(); void toggleSpell(); void toggleWeapon(); void toggleInventory(); @@ -327,8 +326,6 @@ namespace MWInput A_MoveForwardBackward, A_MoveLeftRight, - A_OptionsMenu, - A_Last // Marker for the last item }; }; From 2affb8ed6386b2f714cb340f0edac69f91fb4d09 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Wed, 31 Jul 2019 15:41:32 +0300 Subject: [PATCH 04/23] Human-readable light source duration (feature #5091) --- CHANGELOG.md | 1 + apps/openmw/mwclass/light.cpp | 10 ++---- apps/openmw/mwgui/spellicons.cpp | 23 ++----------- apps/openmw/mwgui/tooltips.cpp | 57 ++++++++++++++++++++++++++++++++ apps/openmw/mwgui/tooltips.hpp | 3 ++ 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c481994ea..25a518c87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,6 +149,7 @@ Feature #5036: Allow scripted faction leaving Feature #5046: Gamepad thumbstick cursor speed Feature #5051: Provide a separate textures for scrollbars + Feature #5091: Human-readable light source duration Feature #5094: Unix like console hotkeys Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 38b7137f3..fb4cae63b 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -156,13 +156,9 @@ namespace MWClass std::string text; - if (Settings::Manager::getBool("show effect duration","Game")) - { - // -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)); - } + // Don't show duration for infinite light sources. + if (Settings::Manager::getBool("show effect duration","Game") && ptr.getClass().getRemainingUsageTime(ptr) != -1) + text += MWGui::ToolTips::getDurationString(ptr.getClass().getRemainingUsageTime(ptr), "\n#{sDuration}"); text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 51508cd06..8a501e598 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -137,27 +137,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") ); } } - if (effectInfo.mRemainingTime > -1 && - Settings::Manager::getBool("show effect duration","Game")) { - sourcesDescription += " #{sDuration}: "; - float duration = effectInfo.mRemainingTime; - if (duration > 3600) - { - int hour = duration / 3600; - duration -= hour*3600; - sourcesDescription += MWGui::ToolTips::toString(hour) + "h"; - } - if (duration > 60) - { - int minute = duration / 60; - duration -= minute*60; - sourcesDescription += MWGui::ToolTips::toString(minute) + "m"; - } - if (duration > 0.1) - { - sourcesDescription += MWGui::ToolTips::toString(duration) + "s"; - } - } + if (effectInfo.mRemainingTime > -1 && Settings::Manager::getBool("show effect duration","Game")) + sourcesDescription += MWGui::ToolTips::getDurationString(effectInfo.mRemainingTime, " #{sDuration}"); addNewLine = true; } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index dc7a9668d..6b3f8cac6 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -667,6 +667,63 @@ namespace MWGui return ret; } + std::string ToolTips::getDurationString(float duration, const std::string& prefix) + { + std::string ret; + ret = prefix + ": "; + + if (duration < 1.f) + { + ret += "0 s"; + return ret; + } + + constexpr float secondsPerMinute = 60.f; // 60 seconds + constexpr float secondsPerHour = secondsPerMinute * 60.f; // 60 minutes + constexpr float secondsPerDay = secondsPerHour * 24.f; // 24 hours + constexpr float secondsPerMonth = secondsPerDay * 30.f; // 30 days + constexpr float secondsPerYear = secondsPerDay * 365.f; + int units = 0; + if (duration >= secondsPerYear) + { + units++; + int years = duration / secondsPerYear; + duration -= years * secondsPerYear; + ret += toString(years) + " y "; + } + if (duration >= secondsPerMonth) + { + units++; + int months = duration / secondsPerMonth; + duration -= months * secondsPerMonth; + ret += toString(months) + " mo "; + } + if (units < 2 && duration >= secondsPerDay) + { + units++; + int days = duration / secondsPerDay; + duration -= days * secondsPerDay; + ret += toString(days) + " d "; + } + if (units < 2 && duration >= secondsPerHour) + { + units++; + int hours = duration / secondsPerHour; + duration -= hours * secondsPerHour; + ret += toString(hours) + " h "; + } + if (units < 2 && duration >= secondsPerMinute) + { + int minutes = duration / secondsPerMinute; + duration -= minutes * secondsPerMinute; + ret += toString(minutes) + " min "; + } + if (units < 2 && duration >= 1.f) + ret += toString(int(duration)) + " s "; + + return ret; + } + bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 43187dc5c..afdc7dec0 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -84,6 +84,9 @@ namespace MWGui static std::string getCellRefString(const MWWorld::CellRef& cellref); ///< Returns a string containing debug tooltip information about the given cellref. + static std::string getDurationString (float duration, const std::string& prefix); + ///< Returns duration as two largest time units, rounded down. Note: not localized; no line break. + // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, int skillId); From cac05ef82609f0fc0bf6a3a2a5502be4e3dba45c Mon Sep 17 00:00:00 2001 From: capostrophic Date: Tue, 30 Jul 2019 20:58:19 +0300 Subject: [PATCH 05/23] Sneaking consistency fixes (bug #5103) Correct pickpocketing behavior when not actually sneaking Fix first person swimming animation regression when sneaking stance is on Move typical sneaking use case checks into mechanics manager Correct awareness checks --- CHANGELOG.md | 1 + apps/openmw/mwclass/npc.cpp | 5 ++--- apps/openmw/mwmechanics/actors.cpp | 10 ++-------- apps/openmw/mwmechanics/character.cpp | 6 +----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 16 +++++++++------- apps/openmw/mwscript/controlextensions.cpp | 9 +-------- 6 files changed, 16 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 923b25b02..8fcada696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,7 @@ Bug #5092: NPCs with enchanted weapons play sound when out of charges Bug #5093: Hand to hand sound plays on knocked out enemies Bug #5099: Non-swimming enemies will enter water if player is water walking + Bug #5103: Sneaking state behavior is still inconsistent Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5105: NPCs start combat with werewolves from any distance Bug #5110: ModRegion with a redundant numerical argument breaks script execution diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index aa326412f..9f2987004 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -939,10 +939,9 @@ namespace MWClass const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); bool swimming = world->isSwimming(ptr); + bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); + bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); - bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - sneaking = sneaking && (inair || MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr)); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 656694b25..929560ef1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1785,14 +1785,7 @@ namespace MWMechanics MWWorld::Ptr player = getPlayer(); - CreatureStats& stats = player.getClass().getCreatureStats(player); - MWBase::World* world = MWBase::Environment::get().getWorld(); - - bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool inair = !world->isOnGround(player) && !world->isSwimming(player) && !world->isFlying(player); - sneaking = sneaking && (ctrl->isSneaking() || inair); - - if (!sneaking) + if (!MWBase::Environment::get().getMechanicsManager()->isSneaking(player)) { MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); return; @@ -1800,6 +1793,7 @@ namespace MWMechanics static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice" + MWBase::World* world = MWBase::Environment::get().getWorld(); const MWWorld::Store& gmst = world->getStore().get(); static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat(); static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 00b3c0c67..3e730f7c2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -549,10 +549,6 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character mCurrentMovement = movementAnimName; if(!mCurrentMovement.empty()) { - bool isflying = MWBase::Environment::get().getWorld()->isFlying(mPtr); - bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !isflying; - bool issneaking = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !isflying; - // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity // even if we are running. This must be replicated, otherwise the observed speed would differ drastically. std::string anim = mCurrentMovement; @@ -585,7 +581,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character // The first person anims don't have any velocity to calculate a speed multiplier from. // We use the third person velocities instead. // FIXME: should be pulled from the actual animation, but it is not presently loaded. - mMovementAnimSpeed = (issneaking ? 33.5452f : (isrunning ? 222.857f : 154.064f)); + mMovementAnimSpeed = (isSneaking() ? 33.5452f : (isRunning() ? 222.857f : 154.064f)); mMovementAnimationControlled = false; } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fd7ab63fe..fe5f60ec4 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -477,7 +477,12 @@ namespace MWMechanics bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr) { - return mActors.isSneaking(ptr); + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + MWBase::World* world = MWBase::Environment::get().getWorld(); + bool animActive = mActors.isSneaking(ptr); + bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr); + return stanceOn && (animActive || inair); } void MechanicsManager::rest(double hours, bool sleep) @@ -965,8 +970,7 @@ namespace MWMechanics return true; // check if a player tries to pickpocket a target NPC - if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak) - || target.getClass().getCreatureStats(target).getKnockedDown()) + if (target.getClass().getCreatureStats(target).getKnockedDown() || isSneaking(ptr)) return false; return true; @@ -1586,9 +1590,7 @@ namespace MWMechanics return false; float sneakTerm = 0; - if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak) - && !MWBase::Environment::get().getWorld()->isSwimming(ptr) - && MWBase::Environment::get().getWorld()->isOnGround(ptr)) + if (isSneaking(ptr)) { static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); @@ -1596,7 +1598,7 @@ namespace MWMechanics int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; - if (ptr.getClass().isNpc()) + if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { const MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ConstContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 0d5b1bf3b..956792863 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -186,14 +186,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - MWBase::World* world = MWBase::Environment::get().getWorld(); - - bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); - bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr); - - runtime.push(stanceOn && (sneaking || inair)); + runtime.push(MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr)); } }; From 26f59e47db5702077b3bddeb34f9b83d05c2d08e Mon Sep 17 00:00:00 2001 From: capostrophic Date: Fri, 9 Aug 2019 19:56:21 +0300 Subject: [PATCH 06/23] Improve readability --- apps/openmw/mwgui/tooltips.cpp | 45 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 6b3f8cac6..7d8a07c21 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -678,48 +678,45 @@ namespace MWGui return ret; } - constexpr float secondsPerMinute = 60.f; // 60 seconds - constexpr float secondsPerHour = secondsPerMinute * 60.f; // 60 minutes - constexpr float secondsPerDay = secondsPerHour * 24.f; // 24 hours - constexpr float secondsPerMonth = secondsPerDay * 30.f; // 30 days - constexpr float secondsPerYear = secondsPerDay * 365.f; + constexpr int secondsPerMinute = 60; // 60 seconds + constexpr int secondsPerHour = secondsPerMinute * 60; // 60 minutes + constexpr int secondsPerDay = secondsPerHour * 24; // 24 hours + constexpr int secondsPerMonth = secondsPerDay * 30; // 30 days + constexpr int secondsPerYear = secondsPerDay * 365; + int fullDuration = static_cast(duration); int units = 0; - if (duration >= secondsPerYear) + int years = fullDuration / secondsPerYear; + int months = fullDuration % secondsPerYear / secondsPerMonth; + int days = fullDuration % secondsPerYear % secondsPerMonth / secondsPerDay; // Because a year is not exactly 12 "months" + int hours = fullDuration % secondsPerDay / secondsPerHour; + int minutes = fullDuration % secondsPerHour / secondsPerMinute; + int seconds = fullDuration % secondsPerMinute; + if (years) { units++; - int years = duration / secondsPerYear; - duration -= years * secondsPerYear; ret += toString(years) + " y "; } - if (duration >= secondsPerMonth) + if (months) { units++; - int months = duration / secondsPerMonth; - duration -= months * secondsPerMonth; ret += toString(months) + " mo "; } - if (units < 2 && duration >= secondsPerDay) + if (units < 2 && days) { units++; - int days = duration / secondsPerDay; - duration -= days * secondsPerDay; ret += toString(days) + " d "; } - if (units < 2 && duration >= secondsPerHour) + if (units < 2 && hours) { units++; - int hours = duration / secondsPerHour; - duration -= hours * secondsPerHour; ret += toString(hours) + " h "; } - if (units < 2 && duration >= secondsPerMinute) - { - int minutes = duration / secondsPerMinute; - duration -= minutes * secondsPerMinute; + if (units >= 2) + return ret; + if (minutes) ret += toString(minutes) + " min "; - } - if (units < 2 && duration >= 1.f) - ret += toString(int(duration)) + " s "; + if (seconds) + ret += toString(seconds) + " s "; return ret; } From 4d86bf3bf28adf5dc3c76ea59817f1f40e6b74d2 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 15 Aug 2019 18:18:53 +0200 Subject: [PATCH 07/23] Update rotated world objects in navigator --- apps/openmw/mwworld/worldimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ce253b8ba..dbcf1cd14 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1458,6 +1458,9 @@ namespace MWWorld { mRendering->rotateObject(ptr, rotate); mPhysics->updateRotation(ptr); + + if (const auto object = mPhysics->getObject(ptr)) + updateNavigatorObject(object); } } From efa463af8f1b921cffe96c592095591eeda796bd Mon Sep 17 00:00:00 2001 From: capostrophic Date: Fri, 16 Aug 2019 16:25:17 +0300 Subject: [PATCH 08/23] Fix non-tiling textures clamp mode (bug #5137) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 195f02c56..cfe21ab8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,7 @@ Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5123: Script won't run on respawn Bug #5124: Arrow remains attached to actor if pulling animation was cancelled + Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d8a3a80fa..f0e41c3ac 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -408,8 +408,8 @@ namespace NifOsg unsigned int clamp = static_cast(textureEffect->clamp); int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; - texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); - texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); osg::ref_ptr texEnv = new osg::TexEnvCombine; texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); @@ -777,8 +777,8 @@ namespace NifOsg // inherit wrap settings from the target slot osg::Texture2D* inherit = dynamic_cast(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE)); - osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP; - osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP; + osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP_TO_EDGE; + osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP_TO_EDGE; if (inherit) { wrapS = inherit->getWrap(osg::Texture2D::WRAP_S); @@ -1392,8 +1392,8 @@ namespace NifOsg int wrapT = (clamp) & 0x1; int wrapS = (clamp >> 1) & 0x1; - texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); - texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); int texUnit = boundTextures.size(); From f779529cabbf6dc95d8a8c8eccc19d32f60da5b0 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Fri, 16 Aug 2019 17:37:38 +0300 Subject: [PATCH 09/23] Change the default reverb preset to improve underwater FX transition --- apps/openmw/mwsound/openal_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 22989a33d..4c41a5b40 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -798,7 +798,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname if(alGetError() == AL_NO_ERROR) Log(Debug::Info) << "Standard Reverb supported"; } - EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_GENERIC; + EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_LIVINGROOM; props.flGain = 0.0f; LoadEffect(mDefaultEffect, props); } From 9c7474f88c8a7a9f3127eba14a0f6ec793df64f1 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Tue, 13 Aug 2019 01:20:30 +0300 Subject: [PATCH 10/23] Implement NiPalette support (feature #4882) --- CHANGELOG.md | 1 + components/nif/data.cpp | 38 +++++++++++++++------ components/nif/data.hpp | 17 ++++++++-- components/nif/niffile.cpp | 1 + components/nif/record.hpp | 3 +- components/nif/recordptr.hpp | 2 ++ components/nifosg/nifloader.cpp | 59 ++++++++++++++++++++++++++++++--- 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe21ab8d..45ffb8618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,6 +148,7 @@ Feature #4812: Support NiSwitchNode Feature #4836: Daytime node switch Feature #4859: Make water reflections more configurable + Feature #4882: Support for NiPalette node Feature #4887: Add openmw command option to set initial random seed Feature #4890: Make Distant Terrain configurable Feature #4958: Support eight blood types diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 4a9266239..5f7722794 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -163,22 +163,23 @@ void NiPixelData::read(NIFStream *nif) { fmt = (Format)nif->getUInt(); - rmask = nif->getInt(); // usually 0xff - gmask = nif->getInt(); // usually 0xff00 - bmask = nif->getInt(); // usually 0xff0000 - amask = nif->getInt(); // usually 0xff000000 or zero + rmask = nif->getUInt(); // usually 0xff + gmask = nif->getUInt(); // usually 0xff00 + bmask = nif->getUInt(); // usually 0xff0000 + amask = nif->getUInt(); // usually 0xff000000 or zero - bpp = nif->getInt(); + bpp = nif->getUInt(); - // Unknown - nif->skip(12); + // 8 bytes of "Old Fast Compare". Whatever that means. + nif->skip(8); + palette.read(nif); - numberOfMipmaps = nif->getInt(); + numberOfMipmaps = nif->getUInt(); // Bytes per pixel, should be bpp * 8 - /* int bytes = */ nif->getInt(); + /* int bytes = */ nif->getUInt(); - for(int i=0; igetInt(); + unsigned int dataSize = nif->getUInt(); data.reserve(dataSize); for (unsigned i=0; igetChar()); } +void NiPixelData::post(NIFFile *nif) +{ + palette.post(nif); +} + void NiColorData::read(NIFStream *nif) { mKeyMap = std::make_shared(); @@ -278,4 +284,14 @@ void NiKeyframeData::read(NIFStream *nif) mScales->read(nif); } +void NiPalette::read(NIFStream *nif) +{ + unsigned int alphaMask = !nif->getChar() ? 0xFF000000 : 0; + // Fill the entire palette with black even if there isn't enough entries. + colors.resize(256); + unsigned int numEntries = nif->getUInt(); + for (unsigned int i = 0; i < numEntries; i++) + colors[i] = nif->getUInt() | alphaMask; +} + } // Namespace diff --git a/components/nif/data.hpp b/components/nif/data.hpp index fb1199cff..a0d4960e0 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -116,6 +116,7 @@ public: NIPXFMT_RGB8, NIPXFMT_RGBA8, NIPXFMT_PAL8, + NIPXFMT_PALA8, NIPXFMT_DXT1, NIPXFMT_DXT3, NIPXFMT_DXT5, @@ -123,8 +124,10 @@ public: }; Format fmt; - unsigned int rmask, gmask, bmask, amask; - int bpp, numberOfMipmaps; + unsigned int rmask, gmask, bmask, amask, bpp; + + NiPalettePtr palette; + unsigned int numberOfMipmaps; struct Mipmap { @@ -136,6 +139,7 @@ public: std::vector data; void read(NIFStream *nif); + void post(NIFFile *nif); }; class NiColorData : public Record @@ -219,5 +223,14 @@ struct NiKeyframeData : public Record void read(NIFStream *nif); }; +class NiPalette : public Record +{ +public: + // 32-bit RGBA colors that correspond to 8-bit indices + std::vector colors; + + void read(NIFStream *nif); +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 4b3760ba2..d4f1203cc 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -112,6 +112,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiSourceTexture", &construct , RC_NiSourceTexture )); newFactory.insert(makeEntry("NiSkinInstance", &construct , RC_NiSkinInstance )); newFactory.insert(makeEntry("NiLookAtController", &construct , RC_NiLookAtController )); + newFactory.insert(makeEntry("NiPalette", &construct , RC_NiPalette )); return newFactory; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 909c268bb..67ffbc574 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -97,7 +97,8 @@ enum RecordType RC_NiSkinInstance, RC_RootCollisionNode, RC_NiSphericalCollider, - RC_NiLookAtController + RC_NiLookAtController, + RC_NiPalette }; /// Base class for all records diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 977973517..e23beb786 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -140,6 +140,7 @@ class NiSkinInstance; class NiSourceTexture; class NiRotatingParticlesData; class NiAutoNormalParticlesData; +class NiPalette; typedef RecordPtrT NodePtr; typedef RecordPtrT ExtraPtr; @@ -160,6 +161,7 @@ typedef RecordPtrT NiSkinInstancePtr; typedef RecordPtrT NiSourceTexturePtr; typedef RecordPtrT NiRotatingParticlesDataPtr; typedef RecordPtrT NiAutoNormalParticlesDataPtr; +typedef RecordPtrT NiPalettePtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f0e41c3ac..31dcd2a55 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1276,9 +1276,11 @@ namespace NifOsg switch (pixelData->fmt) { case Nif::NiPixelData::NIPXFMT_RGB8: + case Nif::NiPixelData::NIPXFMT_PAL8: pixelformat = GL_RGB; break; case Nif::NiPixelData::NIPXFMT_RGBA8: + case Nif::NiPixelData::NIPXFMT_PALA8: pixelformat = GL_RGBA; break; default: @@ -1293,7 +1295,7 @@ namespace NifOsg int height = 0; std::vector mipmapVector; - for (unsigned int i=0; imipmaps.size()-3; ++i) + for (unsigned int i=0; imipmaps.size(); ++i) { const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i]; @@ -1319,10 +1321,59 @@ namespace NifOsg return nullptr; } - unsigned char* data = new unsigned char[pixelData->data.size()]; - memcpy(data, pixelData->data.data(), pixelData->data.size()); + const std::vector& pixels = pixelData->data; + switch (pixelData->fmt) + { + case Nif::NiPixelData::NIPXFMT_RGB8: + case Nif::NiPixelData::NIPXFMT_RGBA8: + { + unsigned char* data = new unsigned char[pixels.size()]; + memcpy(data, pixels.data(), pixels.size()); + image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); + break; + } + case Nif::NiPixelData::NIPXFMT_PAL8: + case Nif::NiPixelData::NIPXFMT_PALA8: + { + if (pixelData->palette.empty() || pixelData->bpp != 8) + { + Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring"; + return nullptr; + } + // We're going to convert the indices that pixel data contains + // into real colors using the palette. + const std::vector& palette = pixelData->palette->colors; + if (pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8) + { + unsigned char* data = new unsigned char[pixels.size() * 3]; + for (size_t i = 0; i < pixels.size(); i++) + { + unsigned int color = palette[pixels[i]]; + data[i * 3 + 0] = (color >> 0) & 0xFF; + data[i * 3 + 1] = (color >> 8) & 0xFF; + data[i * 3 + 2] = (color >> 16) & 0xFF; + } + image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); + } + else // if (fmt = NIPXFMT_PALA8) + { + unsigned char* data = new unsigned char[pixels.size() * 4]; + for (size_t i = 0; i < pixels.size(); i++) + { + unsigned int color = palette[pixels[i]]; + data[i * 4 + 0] = (color >> 0) & 0xFF; + data[i * 4 + 1] = (color >> 8) & 0xFF; + data[i * 4 + 2] = (color >> 16) & 0xFF; + data[i * 4 + 3] = (color >> 24) & 0xFF; + } + image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); + } + break; + } + default: + return nullptr; + } - image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); image->setMipmapLevels(mipmapVector); image->flipVertical(); From 786a6c6d42c1aa9420ef8b9a3eb62726bf97a4ad Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 15 Aug 2019 10:46:47 +0400 Subject: [PATCH 11/23] Use a common doors rotation code in the "lock" command handler --- CHANGELOG.md | 1 + apps/openmw/mwscript/miscextensions.cpp | 6 -- apps/openmw/mwworld/worldimp.cpp | 77 ++++++++++++++----------- apps/openmw/mwworld/worldimp.hpp | 2 + 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe21ab8d..febf869c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,7 @@ Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5123: Script won't run on respawn Bug #5124: Arrow remains attached to actor if pulling animation was cancelled + Bug #5134: Doors rotation by "Lock" console command is inconsistent Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ccdc6becf..47873a3b6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -189,12 +189,6 @@ namespace MWScript if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport()) { MWBase::Environment::get().getWorld()->activateDoor(ptr, 0); - - float xr = ptr.getCellRef().getPosition().rot[0]; - float yr = ptr.getCellRef().getPosition().rot[1]; - float zr = ptr.getCellRef().getPosition().rot[2]; - - MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr); } } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dbcf1cd14..ef65ec162 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1617,6 +1617,45 @@ namespace MWWorld return result.mHit; } + bool World::rotateDoor(const Ptr door, int state, float duration) + { + const ESM::Position& objPos = door.getRefData().getPosition(); + float oldRot = objPos.rot[2]; + + float minRot = door.getCellRef().getPosition().rot[2]; + float maxRot = minRot + osg::DegreesToRadians(90.f); + + float diff = duration * osg::DegreesToRadians(90.f); + float targetRot = std::min(std::max(minRot, oldRot + diff * (state == 1 ? 1 : -1)), maxRot); + rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot); + + bool reached = (targetRot == maxRot && state) || targetRot == minRot; + + /// \todo should use convexSweepTest here + std::vector collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); + for (MWWorld::Ptr& ptr : collisions) + { + if (ptr.getClass().isActor()) + { + // Collided with actor, ask actor to try to avoid door + if(ptr != getPlayerPtr() ) + { + MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once + seq.stack(MWMechanics::AiAvoidDoor(door),ptr); + } + + // we need to undo the rotation + rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot); + reached = false; + } + } + + // the rotation order we want to use + mWorldScene->updateObjectRotation(door, false); + return reached; + } + void World::processDoors(float duration) { std::map::iterator it = mDoorStates.begin(); @@ -1631,40 +1670,7 @@ namespace MWWorld } else { - const ESM::Position& objPos = it->first.getRefData().getPosition(); - float oldRot = objPos.rot[2]; - - float minRot = it->first.getCellRef().getPosition().rot[2]; - float maxRot = minRot + osg::DegreesToRadians(90.f); - - float diff = duration * osg::DegreesToRadians(90.f); - float targetRot = std::min(std::max(minRot, oldRot + diff * (it->second == 1 ? 1 : -1)), maxRot); - rotateObject(it->first, objPos.rot[0], objPos.rot[1], targetRot); - - bool reached = (targetRot == maxRot && it->second) || targetRot == minRot; - - /// \todo should use convexSweepTest here - std::vector collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); - for (MWWorld::Ptr& ptr : collisions) - { - if (ptr.getClass().isActor()) - { - // Collided with actor, ask actor to try to avoid door - if(ptr != getPlayerPtr() ) - { - MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); - if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once - seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr); - } - - // we need to undo the rotation - rotateObject(it->first, objPos.rot[0], objPos.rot[1], oldRot); - reached = false; - } - } - - // the rotation order we want to use - mWorldScene->updateObjectRotation(it->first, false); + bool reached = rotateDoor(it->first, it->second, duration); if (reached) { @@ -2535,7 +2541,10 @@ namespace MWWorld door.getClass().setDoorState(door, state); mDoorStates[door] = state; if (state == 0) + { mDoorStates.erase(door); + rotateDoor(door, state, 1); + } } bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index dd915c8bd..0284d8fee 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -146,6 +146,8 @@ namespace MWWorld private: void PCDropped (const Ptr& item); + bool rotateDoor(const Ptr door, int state, float duration); + void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. From 99710e8f559583796740f39d1407406d31b61d21 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Sat, 10 Aug 2019 00:55:18 +0300 Subject: [PATCH 12/23] Allow SwimRunForward state WalkForward fallback (bug #5126) --- CHANGELOG.md | 2 ++ apps/openmw/mwmechanics/character.cpp | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b15ef7270..88a48d60c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,7 +127,9 @@ Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5123: Script won't run on respawn Bug #5124: Arrow remains attached to actor if pulling animation was cancelled + Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5134: Doors rotation by "Lock" console command is inconsistent + Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index e2021bea9..ac7edcbfd 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -529,19 +529,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup if(!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type swimpos = movementAnimName.find("swim"); - if(swimpos == std::string::npos) - { - std::string::size_type runpos = movementAnimName.find("run"); - if (runpos != std::string::npos) - { - movementAnimName.replace(runpos, runpos+3, "walk"); - if (!mAnimation->hasAnimation(movementAnimName)) - movementAnimName.clear(); - } - else - movementAnimName.clear(); - } - else + if (swimpos != std::string::npos) { movementAnimName.erase(swimpos, 4); if (!weapShortGroup.empty()) @@ -552,8 +540,18 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup else movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask); } + } - if (!mAnimation->hasAnimation(movementAnimName)) + if (swimpos == std::string::npos || !mAnimation->hasAnimation(movementAnimName)) + { + std::string::size_type runpos = movementAnimName.find("run"); + if (runpos != std::string::npos) + { + movementAnimName.replace(runpos, runpos+3, "walk"); + if (!mAnimation->hasAnimation(movementAnimName)) + movementAnimName.clear(); + } + else movementAnimName.clear(); } } From 0afb142b8ee82ce8e92866f27c92d2a852bad9a6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 16 Aug 2019 21:56:14 +0400 Subject: [PATCH 13/23] [Regression] restore ammo inventory sounds --- apps/openmw/mwmechanics/weapontype.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/weapontype.hpp b/apps/openmw/mwmechanics/weapontype.hpp index 7e40b895c..32e321d45 100644 --- a/apps/openmw/mwmechanics/weapontype.hpp +++ b/apps/openmw/mwmechanics/weapontype.hpp @@ -238,7 +238,7 @@ namespace MWMechanics { /* short group */ "", /* long group */ "", - /* sound ID */ "Item Weapon Ammo", + /* sound ID */ "Item Ammo", /* attach bone */ "ArrowBone", /* sheath bone */ "", /* usage skill */ ESM::Skill::Marksman, @@ -252,7 +252,7 @@ namespace MWMechanics { /* short group */ "", /* long group */ "", - /* sound ID */ "Item Weapon Ammo", + /* sound ID */ "Item Ammo", /* attach bone */ "ArrowBone", /* sheath bone */ "", /* usage skill */ ESM::Skill::Marksman, From 6156ab45de2d8ab36a0dec1edb8a2d1993978bef Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 17 Aug 2019 12:17:13 +0400 Subject: [PATCH 14/23] [Regression] inject bones for NPC's with custom animation models --- apps/openmw/mwrender/animation.cpp | 71 ++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ff09c7aef..41b86317e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -18,12 +18,14 @@ #include #include +#include #include // KeyframeHolder #include #include +#include #include #include #include @@ -1378,6 +1380,9 @@ namespace MWRender void injectCustomBones(osg::ref_ptr& node, const std::string& model, Resource::ResourceSystem* resourceSystem) { + if (model.empty()) + return; + const std::map& index = resourceSystem->getVFS()->getIndex(); std::string animationPath = model; @@ -1405,14 +1410,7 @@ namespace MWRender } } - enum InjectType - { - None, - Model, - ModelWithFallback - }; - - osg::ref_ptr getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, InjectType inject) + osg::ref_ptr getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, bool inject, const std::string& defaultSkeleton) { Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager(); if (baseonly) @@ -1424,11 +1422,11 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - if (inject == InjectType::ModelWithFallback) - injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem); - - if (inject != InjectType::None) + if (inject) + { + injectCustomBones(created, defaultSkeleton, resourceSystem); injectCustomBones(created, model, resourceSystem); + } SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; created->accept(removeDrawableVisitor); @@ -1445,11 +1443,11 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - if (inject == InjectType::ModelWithFallback) - injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem); - - if (inject != InjectType::None) + if (inject) + { + injectCustomBones(created, defaultSkeleton, resourceSystem); injectCustomBones(created, model, resourceSystem); + } return created; } @@ -1475,17 +1473,44 @@ namespace MWRender mAccumCtrl = nullptr; static const bool useAdditionalSources = Settings::Manager::getBool ("use additional anim sources", "Game"); - InjectType inject = useAdditionalSources && mPtr.getClass().isActor() ? InjectType::Model : InjectType::None; - if (inject != InjectType::None && isCreature) + std::string defaultSkeleton; + bool inject = false; + + if (useAdditionalSources && mPtr.getClass().isActor()) { - MWWorld::LiveCellRef *ref = mPtr.get(); - if(ref->mBase->mFlags & ESM::Creature::Bipedal) - inject = InjectType::ModelWithFallback; + if (isCreature) + { + MWWorld::LiveCellRef *ref = mPtr.get(); + if(ref->mBase->mFlags & ESM::Creature::Bipedal) + { + defaultSkeleton = "meshes\\xbase_anim.nif"; + inject = true; + } + } + else + { + inject = true; + MWWorld::LiveCellRef *ref = mPtr.get(); + if (!ref->mBase->mModel.empty()) + { + // If NPC has a custom animation model attached, we should inject bones from default skeleton for given race and gender as well + // Since it is a quite rare case, there should not be a noticable performance loss + // Note: consider that player and werewolves have no custom animation files attached for now + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(ref->mBase->mRace); + + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + bool isFemale = !ref->mBase->isMale(); + + defaultSkeleton = SceneUtil::getActorSkeleton(false, isFemale, isBeast, false); + defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS()); + } + } } if (!forceskeleton) { - osg::ref_ptr created = getModelInstance(mResourceSystem, model, baseonly, inject); + osg::ref_ptr created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton); mInsert->addChild(created); mObjectRoot = created->asGroup(); if (!mObjectRoot) @@ -1501,7 +1526,7 @@ namespace MWRender } else { - osg::ref_ptr created = getModelInstance(mResourceSystem, model, baseonly, inject); + osg::ref_ptr created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton); osg::ref_ptr skel = dynamic_cast(created.get()); if (!skel) { From 7b94183d83abb31ef6017f3886dc01e868f3f1b8 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 17 Aug 2019 17:55:49 +0200 Subject: [PATCH 15/23] Revert "Take in account actor half extents for obstacle check" This reverts commit 5434e924375ae878124aaab1b3353a1781b4ed8a. --- apps/openmw/mwmechanics/obstacle.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 63167e302..c8b548bad 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -2,8 +2,6 @@ #include -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -125,17 +123,15 @@ namespace MWMechanics */ void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) { - const ESM::Position pos = actor.getRefData().getPosition(); + const MWWorld::Class& cls = actor.getClass(); + ESM::Position pos = actor.getRefData().getPosition(); - if (mDistSameSpot == -1) - { - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); - mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y()); - } + if(mDistSameSpot == -1) + mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor); - const float distSameSpot = mDistSameSpot * duration; - const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2(); - const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot; + float distSameSpot = mDistSameSpot * duration; + + bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot; // update position mPrevX = pos.pos[0]; From ad05de44aeb1d9568aa1479a54fa929257397d7f Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 17 Aug 2019 17:57:28 +0200 Subject: [PATCH 16/23] Use 3d coordinates to detect stuck To able water and flying creatures move to player going up or down. --- apps/openmw/mwmechanics/obstacle.cpp | 22 ++++++++-------------- apps/openmw/mwmechanics/obstacle.hpp | 7 ++++--- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index c8b548bad..b8676a883 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -76,10 +76,8 @@ namespace MWMechanics return MWWorld::Ptr(); // none found } - ObstacleCheck::ObstacleCheck(): - mPrevX(0) // to see if the moved since last time - , mPrevY(0) - , mWalkState(State_Norm) + ObstacleCheck::ObstacleCheck() + : mWalkState(State_Norm) , mStuckDuration(0) , mEvadeDuration(0) , mDistSameSpot(-1) // avoid calculating it each time @@ -123,19 +121,15 @@ namespace MWMechanics */ void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) { - const MWWorld::Class& cls = actor.getClass(); - ESM::Position pos = actor.getRefData().getPosition(); + const osg::Vec3f pos = actor.getRefData().getPosition().asVec3(); - if(mDistSameSpot == -1) - mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor); + if (mDistSameSpot == -1) + mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor); - float distSameSpot = mDistSameSpot * duration; + const float distSameSpot = mDistSameSpot * duration; + const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot; - bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot; - - // update position - mPrevX = pos.pos[0]; - mPrevY = pos.pos[1]; + mPrev = pos; switch(mWalkState) { diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 46c1bc83d..2934ceb1f 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_MECHANICS_OBSTACLE_H #define OPENMW_MECHANICS_OBSTACLE_H +#include + namespace MWWorld { class Ptr; @@ -37,9 +39,8 @@ namespace MWMechanics private: - // for checking if we're stuck (ignoring Z axis) - float mPrevX; - float mPrevY; + // for checking if we're stuck + osg::Vec3f mPrev; // directions to try moving in when get stuck static const float evadeDirections[NUM_EVADE_DIRECTIONS][2]; From e5331aab280fc904efb66831645ba99de7363371 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Sat, 17 Aug 2019 21:53:52 +0300 Subject: [PATCH 17/23] Escape number signs in object names for tooltips --- apps/openmw/mwclass/activator.cpp | 2 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 4 ++-- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index eabac1644..411b720fe 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -103,7 +103,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 5e7c1b933..ca610efc2 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -105,7 +105,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 75a55a077..44e708d5b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -211,7 +211,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 4b5ca2ee7..ce08ce422 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -121,7 +121,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 7e3e77d58..c25208595 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -169,7 +169,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 1a5cd354d..2b36f2339 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -271,7 +271,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName; + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); std::string text; int lockLevel = ptr.getCellRef().getLockLevel(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index fd89ef4a3..ba0bbf97f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -586,7 +586,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName; + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 9667b694a..89b628d91 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -293,7 +293,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName; + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); std::string text; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 37b2d7196..b63295391 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -117,7 +117,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 38b7137f3..36440076b 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -151,7 +151,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 013808bb2..33d4632ca 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -116,7 +116,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 51b641d4d..af68f4743 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -159,7 +159,7 @@ namespace MWClass else // gold displays its count also if it's 1. countString = " (" + std::to_string(count) + ")"; - info.caption = ref->mBase->mName + countString; + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + countString; info.icon = ref->mBase->mIcon; if (ref->mRef.getSoul() != "") diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 92845aa88..b1677667d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1071,11 +1071,11 @@ namespace MWClass bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp(); MWGui::ToolTipInfo info; - info.caption = getName(ptr); + info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)); if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) { info.caption += " ("; - info.caption += ref->mBase->mName; + info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName); info.caption += ")"; } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 4e95c6294..5c40f17d3 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -110,7 +110,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index b53e2418a..0a0c1548f 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -116,7 +116,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 922858623..9050d9f94 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -117,7 +117,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; std::string text; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 267ba0c75..82740a2e1 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -168,7 +168,7 @@ namespace MWClass const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType); MWGui::ToolTipInfo info; - info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); + info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.icon = ref->mBase->mIcon; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); From fe71ce9cb25e4886da16642bfff850fabff83539 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 18 Aug 2019 10:30:21 +0400 Subject: [PATCH 18/23] Optimize custom bones search visitor --- apps/openmw/mwrender/animation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 41b86317e..56c3846af 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -247,7 +247,10 @@ namespace void apply(osg::Node& node) { if (SceneUtil::hasUserDescription(&node, "CustomBone")) + { mFoundBones.emplace_back(&node, node.getParent(0)); + return; + } traverse(node); } From cec9e2590264b2cfb906c534d5f8db6a076e643f Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sun, 18 Aug 2019 21:47:45 +0300 Subject: [PATCH 19/23] Fix changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74ad52cd7..65adf8add 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,7 +132,6 @@ Bug #5124: Arrow remains attached to actor if pulling animation was cancelled Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5134: Doors rotation by "Lock" console command is inconsistent - Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI From 64fde2d7c24990558097d2e5ac2c22c575400e5f Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Sat, 24 Aug 2019 22:34:56 +0300 Subject: [PATCH 20/23] Fix crash when NiGeometry lacks NiGeometryData --- components/nifosg/nifloader.cpp | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 31dcd2a55..e199a3b72 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1072,28 +1072,32 @@ namespace NifOsg if (nifNode->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape* triShape = static_cast(nifNode); - const Nif::NiTriShapeData* data = triShape->data.getPtr(); - vertexColorsPresent = !data->colors.empty(); - triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); - if (!data->triangles.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), - (unsigned short*)data->triangles.data())); + if (const Nif::NiTriShapeData* data = triShape->data.getPtr()) + { + vertexColorsPresent = !data->colors.empty(); + triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); + if (!data->triangles.empty()) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), + (unsigned short*)data->triangles.data())); + } } else { const Nif::NiTriStrips* triStrips = static_cast(nifNode); - const Nif::NiTriStripsData* data = triStrips->data.getPtr(); - vertexColorsPresent = !data->colors.empty(); - triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); - if (!data->strips.empty()) + if (const Nif::NiTriStripsData* data = triStrips->data.getPtr()) { - for (const std::vector& strip : data->strips) + vertexColorsPresent = !data->colors.empty(); + triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); + if (!data->strips.empty()) { - // Can't make a triangle from less than three vertices. - if (strip.size() < 3) - continue; - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), - (unsigned short*)strip.data())); + for (const std::vector& strip : data->strips) + { + // Can't make a triangle from less than three vertices. + if (strip.size() < 3) + continue; + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); + } } } } From 16170131b7442f7e568a98cd8663c6066911475b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 25 Aug 2019 15:20:14 +0200 Subject: [PATCH 21/23] Add enum type for door state --- apps/openmw/mwbase/world.hpp | 3 ++- apps/openmw/mwclass/door.cpp | 22 +++++++++--------- apps/openmw/mwclass/door.hpp | 5 ++--- apps/openmw/mwmechanics/aiavoiddoor.cpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 2 +- apps/openmw/mwmechanics/obstacle.cpp | 4 ++-- apps/openmw/mwscript/miscextensions.cpp | 2 +- apps/openmw/mwworld/class.cpp | 4 ++-- apps/openmw/mwworld/class.hpp | 8 +++---- apps/openmw/mwworld/doorstate.hpp | 14 ++++++++++++ apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++++------------- apps/openmw/mwworld/worldimp.hpp | 6 ++--- 12 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 apps/openmw/mwworld/doorstate.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 63f69ab65..955e77168 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -9,6 +9,7 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/doorstate.hpp" #include "../mwrender/rendermode.hpp" @@ -419,7 +420,7 @@ namespace MWBase /// update movement state of a non-teleport door as specified /// @param state see MWClass::setDoorState /// @note throws an exception when invoked on a teleport door - virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; + virtual void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0; virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) = 0; ///< get a list of actors standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 89b628d91..4f144e1f7 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -34,7 +34,7 @@ namespace MWClass class DoorCustomData : public MWWorld::CustomData { public: - int mDoorState; // 0 = nothing, 1 = opening, 2 = closing + MWWorld::DoorState mDoorState; virtual MWWorld::CustomData *clone() const; @@ -71,7 +71,7 @@ namespace MWClass if (ptr.getRefData().getCustomData()) { const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); - if (customData.mDoorState > 0) + if (customData.mDoorState != MWWorld::DoorState::Idle) { MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); } @@ -201,12 +201,12 @@ namespace MWClass { // animated door std::shared_ptr action(new MWWorld::ActionDoor(ptr)); - int doorstate = getDoorState(ptr); + const auto doorState = getDoorState(ptr); bool opening = true; float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2]; - if (doorstate == 1) + if (doorState == MWWorld::DoorState::Opening) opening = false; - if (doorstate == 0 && doorRot != 0) + if (doorState == MWWorld::DoorState::Idle && doorRot != 0) opening = false; if (opening) @@ -365,20 +365,20 @@ namespace MWClass { std::unique_ptr data(new DoorCustomData); - data->mDoorState = 0; + data->mDoorState = MWWorld::DoorState::Idle; ptr.getRefData().setCustomData(data.release()); } } - int Door::getDoorState (const MWWorld::ConstPtr &ptr) const + MWWorld::DoorState Door::getDoorState (const MWWorld::ConstPtr &ptr) const { if (!ptr.getRefData().getCustomData()) - return 0; + return MWWorld::DoorState::Idle; const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); return customData.mDoorState; } - void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const + void Door::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const { if (ptr.getCellRef().getTeleport()) throw std::runtime_error("load doors can't be moved"); @@ -396,7 +396,7 @@ namespace MWClass DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); const ESM::DoorState& state2 = dynamic_cast(state); - customData.mDoorState = state2.mDoorState; + customData.mDoorState = static_cast(state2.mDoorState); } void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const @@ -409,7 +409,7 @@ namespace MWClass const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); ESM::DoorState& state2 = dynamic_cast(state); - state2.mDoorState = customData.mDoorState; + state2.mDoorState = static_cast(customData.mDoorState); } } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 57e475382..b3e4e383c 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -59,10 +59,9 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - /// 0 = nothing, 1 = opening, 2 = closing - virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; + virtual MWWorld::DoorState getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. - virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; + virtual void setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 8fc35de49..793bd89ea 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -44,7 +44,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return true; // We have tried backing up for more than one second, we've probably cleared it } - if (!mDoorPtr.getClass().getDoorState(mDoorPtr)) + if (mDoorPtr.getClass().getDoorState(mDoorPtr) == MWWorld::DoorState::Idle) return true; //Door is no longer opening ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 598292fc3..646b37669 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -232,7 +232,7 @@ void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor) return; // note: AiWander currently does not open doors - if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0) + if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == MWWorld::DoorState::Idle) { if ((door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 )) { diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index b8676a883..c3da92e31 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -52,10 +52,10 @@ namespace MWMechanics // FIXME: cast const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); - int doorState = doorPtr.getClass().getDoorState(doorPtr); + const auto doorState = doorPtr.getClass().getDoorState(doorPtr); float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2]; - if (doorState != 0 || doorRot != 0) + if (doorState != MWWorld::DoorState::Idle || doorRot != 0) continue; // the door is already opened/opening doorPos.z() = 0; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 47873a3b6..29a25c997 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -188,7 +188,7 @@ namespace MWScript // This is done when using Lock in scripts, but not when using Lock spells. if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport()) { - MWBase::Environment::get().getWorld()->activateDoor(ptr, 0); + MWBase::Environment::get().getWorld()->activateDoor(ptr, MWWorld::DoorState::Idle); } } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f84c7bb8b..191552bc5 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -460,12 +460,12 @@ namespace MWWorld return false; } - int Class::getDoorState (const MWWorld::ConstPtr &ptr) const + MWWorld::DoorState Class::getDoorState (const MWWorld::ConstPtr &ptr) const { throw std::runtime_error("this is not a door"); } - void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const + void Class::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const { throw std::runtime_error("this is not a door"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 8e5cfb981..964ef19a5 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -9,6 +9,7 @@ #include #include "ptr.hpp" +#include "doorstate.hpp" namespace ESM { @@ -298,7 +299,7 @@ namespace MWWorld virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const { return true; } ///< Return whether this class of object can be activated with telekinesis - + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; @@ -350,10 +351,9 @@ namespace MWWorld virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; - /// 0 = nothing, 1 = opening, 2 = closing - virtual int getDoorState (const MWWorld::ConstPtr &ptr) const; + virtual DoorState getDoorState (const MWWorld::ConstPtr &ptr) const; /// This does not actually cause the door to move. Use World::activateDoor instead. - virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; + virtual void setDoorState (const MWWorld::Ptr &ptr, DoorState state) const; virtual void respawn (const MWWorld::Ptr& ptr) const {} diff --git a/apps/openmw/mwworld/doorstate.hpp b/apps/openmw/mwworld/doorstate.hpp new file mode 100644 index 000000000..aeafa7d8c --- /dev/null +++ b/apps/openmw/mwworld/doorstate.hpp @@ -0,0 +1,14 @@ +#ifndef GAME_MWWORLD_DOORSTATE_H +#define GAME_MWWORLD_DOORSTATE_H + +namespace MWWorld +{ + enum class DoorState + { + Idle = 0, + Opening = 1, + Closing = 2, + }; +} + +#endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5babc57be..3ea994683 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1617,7 +1617,7 @@ namespace MWWorld return result.mHit; } - bool World::rotateDoor(const Ptr door, int state, float duration) + bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration) { const ESM::Position& objPos = door.getRefData().getPosition(); float oldRot = objPos.rot[2]; @@ -1626,10 +1626,10 @@ namespace MWWorld float maxRot = minRot + osg::DegreesToRadians(90.f); float diff = duration * osg::DegreesToRadians(90.f); - float targetRot = std::min(std::max(minRot, oldRot + diff * (state == 1 ? 1 : -1)), maxRot); + float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot); rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot); - bool reached = (targetRot == maxRot && state) || targetRot == minRot; + bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot; /// \todo should use convexSweepTest here std::vector collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); @@ -1658,7 +1658,7 @@ namespace MWWorld void World::processDoors(float duration) { - std::map::iterator it = mDoorStates.begin(); + auto it = mDoorStates.begin(); while (it != mDoorStates.end()) { if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) @@ -1675,7 +1675,7 @@ namespace MWWorld if (reached) { // Mark as non-moving - it->first.getClass().setDoorState(it->first, 0); + it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle); mDoorStates.erase(it++); } else @@ -2510,32 +2510,32 @@ namespace MWWorld void World::activateDoor(const MWWorld::Ptr& door) { - int state = door.getClass().getDoorState(door); + auto state = door.getClass().getDoorState(door); switch (state) { - case 0: + case MWWorld::DoorState::Idle: if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2]) - state = 1; // if closed, then open + state = MWWorld::DoorState::Opening; // if closed, then open else - state = 2; // if open, then close + state = MWWorld::DoorState::Closing; // if open, then close break; - case 2: - state = 1; // if closing, then open + case MWWorld::DoorState::Closing: + state = MWWorld::DoorState::Opening; // if closing, then open break; - case 1: + case MWWorld::DoorState::Opening: default: - state = 2; // if opening, then close + state = MWWorld::DoorState::Closing; // if opening, then close break; } door.getClass().setDoorState(door, state); mDoorStates[door] = state; } - void World::activateDoor(const Ptr &door, int state) + void World::activateDoor(const Ptr &door, MWWorld::DoorState state) { door.getClass().setDoorState(door, state); mDoorStates[door] = state; - if (state == 0) + if (state == MWWorld::DoorState::Idle) { mDoorStates.erase(door); rotateDoor(door, state, 1); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0f090bf8d..76d015477 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -118,7 +118,7 @@ namespace MWWorld int mActivationDistanceOverride; - std::map mDoorStates; + std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing std::string mStartCell; @@ -146,7 +146,7 @@ namespace MWWorld private: void PCDropped (const Ptr& item); - bool rotateDoor(const Ptr door, int state, float duration); + bool rotateDoor(const Ptr door, DoorState state, float duration); void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. @@ -542,7 +542,7 @@ namespace MWWorld /// update movement state of a non-teleport door as specified /// @param state see MWClass::setDoorState /// @note throws an exception when invoked on a teleport door - void activateDoor(const MWWorld::Ptr& door, int state) override; + void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) override; void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) override; ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object From c0438a0c6ba18f72ae815e7046ad36d6f00434d7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 26 Aug 2019 22:59:51 +0300 Subject: [PATCH 22/23] Avoid using getPtr on empty geometry data --- components/nifosg/nifloader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index e199a3b72..568286a4d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1072,8 +1072,9 @@ namespace NifOsg if (nifNode->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape* triShape = static_cast(nifNode); - if (const Nif::NiTriShapeData* data = triShape->data.getPtr()) + if (!triShape->data.empty()) { + const Nif::NiTriShapeData* data = triShape->data.getPtr(); vertexColorsPresent = !data->colors.empty(); triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); if (!data->triangles.empty()) @@ -1084,8 +1085,9 @@ namespace NifOsg else { const Nif::NiTriStrips* triStrips = static_cast(nifNode); - if (const Nif::NiTriStripsData* data = triStrips->data.getPtr()) + if (!triStrips->data.empty()) { + const Nif::NiTriStripsData* data = triStrips->data.getPtr(); vertexColorsPresent = !data->colors.empty(); triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); if (!data->strips.empty()) From c059bf04cc01737f82470dbf15fef5dc73830ac7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 27 Aug 2019 09:55:02 +0400 Subject: [PATCH 23/23] Rename getWeaponPart() function --- apps/openmw/mwrender/actoranimation.cpp | 6 +++--- apps/openmw/mwrender/actoranimation.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index abc2e4eb3..d05215b72 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -64,7 +64,7 @@ ActorAnimation::~ActorAnimation() mScabbard.reset(); } -PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor) +PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor) { osg::Group* parent = getBoneByName(bonename); if (!parent) @@ -160,7 +160,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) if (showHolsteredWeapons) { osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); - mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor); + mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor); if (mScabbard) resetControllers(mScabbard->getNode()); } @@ -168,7 +168,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) return; } - mScabbard = getWeaponPart(scabbardName, boneName); + mScabbard = attachMesh(scabbardName, boneName); osg::Group* weaponNode = getBoneByName("Bip01 Weapon"); if (!weaponNode) diff --git a/apps/openmw/mwrender/actoranimation.hpp b/apps/openmw/mwrender/actoranimation.hpp index f1f6f6ca8..038dcde6d 100644 --- a/apps/openmw/mwrender/actoranimation.hpp +++ b/apps/openmw/mwrender/actoranimation.hpp @@ -44,11 +44,11 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener virtual void updateHolsteredWeapon(bool showHolsteredWeapons); virtual void updateQuiver(); virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon); - virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor); - virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename) + virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor); + virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename) { osg::Vec4f stubColor = osg::Vec4f(0,0,0,0); - return getWeaponPart(model, bonename, false, &stubColor); + return attachMesh(model, bonename, false, &stubColor); }; PartHolderPtr mScabbard;