From ae65b0228aa7bafcc08d900d549fd02d92ba1af3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 4 Mar 2020 15:14:22 +0400 Subject: [PATCH 01/34] Do not write custom data for disposed actors --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/npc.cpp | 6 ++++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9d5bd14e4..e379ddf41 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -813,6 +813,12 @@ namespace MWClass return; } + if (ptr.getRefData().getCount() <= 0) + { + state.mHasCustomState = false; + return; + } + const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); customData.mContainerStore->writeState (state2.mInventory); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4f1a996e7..e264dbbb1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1331,6 +1331,12 @@ namespace MWClass return; } + if (ptr.getRefData().getCount() <= 0) + { + state.mHasCustomState = false; + return; + } + const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); customData.mInventoryStore.writeState (state2.mInventory); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 695abe105..0f9664d9c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1197,7 +1197,7 @@ namespace MWMechanics if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) { - if (victim.isEmpty() || (victim.getClass().isActor() && !victim.getClass().getCreatureStats(victim).isDead())) + if (victim.isEmpty() || (victim.getClass().isActor() && victim.getRefData().getCount() > 0 && !victim.getClass().getCreatureStats(victim).isDead())) mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; } if (alarm) From 08e5d93c9bebeaa4930b8293f8f5dc8787d49b87 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 18 May 2020 17:36:07 +0100 Subject: [PATCH 02/34] Print MSVC activation info in verbose mode, too. --- CI/before_script.msvc.sh | 54 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2f40aef9c..62f4ee4a3 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -988,34 +988,36 @@ RET=$? if [ -z $VERBOSE ]; then if [ $RET -eq 0 ]; then echo Done. - if [ -n $ACTIVATE_MSVC ]; then - echo - echo "Note: you must manually activate MSVC for the shell in which you want to do the build." - echo - echo "Some scripts have been created in the build directory to do so in an existing shell." - echo "Bash: source activate_msvc.sh" - echo "CMD: ActivateMSVC.bat" - echo "PowerShell: ActivateMSVC.ps1" - echo - echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio." - echo - if [ $(uname -m) == 'x86_64' ]; then - if [ $BITS -eq 64 ]; then - inheritEnvironments=msvc_x64_x64 - else - inheritEnvironments=msvc_x64 - fi - else - if [ $BITS -eq 64 ]; then - inheritEnvironments=msvc_x86_x64 - else - inheritEnvironments=msvc_x86 - fi - fi - echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." - fi else echo Failed. fi fi + +if [ -n $ACTIVATE_MSVC ]; then + echo + echo "Note: you must manually activate MSVC for the shell in which you want to do the build." + echo + echo "Some scripts have been created in the build directory to do so in an existing shell." + echo "Bash: source activate_msvc.sh" + echo "CMD: ActivateMSVC.bat" + echo "PowerShell: ActivateMSVC.ps1" + echo + echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio." + echo + if [ $(uname -m) == 'x86_64' ]; then + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x64_x64 + else + inheritEnvironments=msvc_x64 + fi + else + if [ $BITS -eq 64 ]; then + inheritEnvironments=msvc_x86_x64 + else + inheritEnvironments=msvc_x86 + fi + fi + echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." +fi + wrappedExit $RET From 103188b61dcb39221438bb0c309e2ab7a8160316 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:10:36 +0200 Subject: [PATCH 03/34] Derive all AI package classes from template to support CRTP features --- apps/openmw/mwmechanics/aiactivate.hpp | 4 ++-- apps/openmw/mwmechanics/aiavoiddoor.cpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 4 ++-- apps/openmw/mwmechanics/aibreathe.cpp | 6 ------ apps/openmw/mwmechanics/aibreathe.hpp | 6 ++---- apps/openmw/mwmechanics/aicast.hpp | 4 ++-- apps/openmw/mwmechanics/aicombat.hpp | 4 ++-- apps/openmw/mwmechanics/aiescort.hpp | 4 ++-- apps/openmw/mwmechanics/aiface.hpp | 4 ++-- apps/openmw/mwmechanics/aifollow.hpp | 4 ++-- apps/openmw/mwmechanics/aipursue.hpp | 4 ++-- apps/openmw/mwmechanics/aitravel.hpp | 4 ++-- apps/openmw/mwmechanics/aiwander.hpp | 4 ++-- apps/openmw/mwmechanics/typedaipackage.hpp | 14 ++++++++++++++ 14 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 apps/openmw/mwmechanics/typedaipackage.hpp diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 4cc9f3036..cd7a8b422 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIACTIVATE_H #define GAME_MWMECHANICS_AIACTIVATE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -19,7 +19,7 @@ namespace MWMechanics { /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ - class AiActivate final : public AiPackage + class AiActivate final : public TypedAiPackage { public: /// Constructor diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index c476c9b57..38ae98139 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -16,7 +16,7 @@ static const int MAX_DIRECTIONS = 4; MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) -: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0) +: mDuration(1), mDoorPtr(doorPtr), mDirection(0) { } diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 39a78192b..312870a2e 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H #define GAME_MWMECHANICS_AIAVOIDDOOR_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -16,7 +16,7 @@ namespace MWMechanics /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it **/ - class AiAvoidDoor final : public AiPackage + class AiAvoidDoor final : public TypedAiPackage { public: /// Avoid door until the door is fully open diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 4955f683c..a919808dd 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -11,12 +11,6 @@ #include "movement.hpp" #include "steering.hpp" -MWMechanics::AiBreathe::AiBreathe() -: AiPackage() -{ - -} - bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index daa2782c2..a9ab56c70 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -1,17 +1,15 @@ #ifndef GAME_MWMECHANICS_AIBREATHE_H #define GAME_MWMECHANICS_AIBREATHE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWMechanics { /// \brief AiPackage to have an actor resurface to breathe // The AI will go up if lesser than half breath left - class AiBreathe final : public AiPackage + class AiBreathe final : public TypedAiPackage { public: - AiBreathe(); - AiBreathe *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 6b10370c6..26eca15ef 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AICAST_H #define GAME_MWMECHANICS_AICAST_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWWorld { @@ -11,7 +11,7 @@ namespace MWWorld namespace MWMechanics { /// AiPackage which makes an actor to cast given spell. - class AiCast final : public AiPackage { + class AiCast final : public TypedAiPackage { public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 049857e71..610e402e4 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AICOMBAT_H #define GAME_MWMECHANICS_AICOMBAT_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include "../mwworld/cellstore.hpp" // for Doors @@ -91,7 +91,7 @@ namespace MWMechanics }; /// \brief Causes the actor to fight another actor - class AiCombat final : public AiPackage + class AiCombat final : public TypedAiPackage { public: ///Constructor diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 5b49807a2..651abb2dd 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIESCORT_H #define GAME_MWMECHANICS_AIESCORT_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -16,7 +16,7 @@ namespace AiSequence namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point - class AiEscort final : public AiPackage + class AiEscort final : public TypedAiPackage { public: /// Implementation of AiEscort diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 98d9ea04b..9defaf2f2 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -1,12 +1,12 @@ #ifndef GAME_MWMECHANICS_AIFACE_H #define GAME_MWMECHANICS_AIFACE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace MWMechanics { /// AiPackage which makes an actor face a certain direction. - class AiFace final : public AiPackage { + class AiFace final : public TypedAiPackage { public: AiFace(float targetX, float targetY); diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index fc4b7fc0b..3d22af9b1 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIFOLLOW_H #define GAME_MWMECHANICS_AIFOLLOW_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -39,7 +39,7 @@ namespace MWMechanics /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely **/ - class AiFollow final : public AiPackage + class AiFollow final : public TypedAiPackage { public: AiFollow(const std::string &actorId, float duration, float x, float y, float z); diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 3f2c2923e..5f1976dd5 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIPURSUE_H #define GAME_MWMECHANICS_AIPURSUE_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace ESM { @@ -17,7 +17,7 @@ namespace MWMechanics /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the path is completed). **/ - class AiPursue final : public AiPackage + class AiPursue final : public TypedAiPackage { public: ///Constructor diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 43b6c9d16..c1420987a 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" namespace ESM { @@ -14,7 +14,7 @@ namespace AiSequence namespace MWMechanics { /// \brief Causes the AI to travel to the specified point - class AiTravel final : public AiPackage + class AiTravel final : public TypedAiPackage { public: /// Default constructor diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index f6e7f6d0c..f887fae67 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_AIWANDER_H #define GAME_MWMECHANICS_AIWANDER_H -#include "aipackage.hpp" +#include "typedaipackage.hpp" #include @@ -78,7 +78,7 @@ namespace MWMechanics }; /// \brief Causes the Actor to wander within a specified range - class AiWander final : public AiPackage + class AiWander final : public TypedAiPackage { public: /// Constructor diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp new file mode 100644 index 000000000..a298bce71 --- /dev/null +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -0,0 +1,14 @@ +#ifndef GAME_MWMECHANICS_TYPEDAIPACKAGE_H +#define GAME_MWMECHANICS_TYPEDAIPACKAGE_H + +#include "aipackage.hpp" + +namespace MWMechanics +{ + template + struct TypedAiPackage : public AiPackage + { + }; +} + +#endif From 8e0934cbd89f6f8aa5091e00bc2ef9917dc98f38 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 22:00:44 +0200 Subject: [PATCH 04/34] Single AI package clone definition --- apps/openmw/mwmechanics/aiactivate.cpp | 5 ----- apps/openmw/mwmechanics/aiactivate.hpp | 1 - apps/openmw/mwmechanics/aiavoiddoor.cpp | 5 ----- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 -- apps/openmw/mwmechanics/aibreathe.cpp | 5 ----- apps/openmw/mwmechanics/aibreathe.hpp | 2 -- apps/openmw/mwmechanics/aicast.cpp | 5 ----- apps/openmw/mwmechanics/aicast.hpp | 2 -- apps/openmw/mwmechanics/aicombat.cpp | 5 ----- apps/openmw/mwmechanics/aicombat.hpp | 2 -- apps/openmw/mwmechanics/aiescort.cpp | 6 ------ apps/openmw/mwmechanics/aiescort.hpp | 2 -- apps/openmw/mwmechanics/aiface.cpp | 5 ----- apps/openmw/mwmechanics/aiface.hpp | 2 -- apps/openmw/mwmechanics/aifollow.cpp | 5 ----- apps/openmw/mwmechanics/aifollow.hpp | 2 -- apps/openmw/mwmechanics/aipursue.cpp | 4 ---- apps/openmw/mwmechanics/aipursue.hpp | 1 - apps/openmw/mwmechanics/aitravel.cpp | 5 ----- apps/openmw/mwmechanics/aitravel.hpp | 2 -- apps/openmw/mwmechanics/aiwander.cpp | 5 ----- apps/openmw/mwmechanics/aiwander.hpp | 2 -- apps/openmw/mwmechanics/typedaipackage.hpp | 4 ++++ 23 files changed, 4 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index c412a7ea1..6764eba23 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -18,11 +18,6 @@ namespace MWMechanics { } - AiActivate *MWMechanics::AiActivate::clone() const - { - return new AiActivate(*this); - } - bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index cd7a8b422..5a9e3d6d8 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -28,7 +28,6 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); - AiActivate *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 38ae98139..9cdb8d90b 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -72,11 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return false; } -MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const -{ - return new AiAvoidDoor(*this); -} - int MWMechanics::AiAvoidDoor::getTypeId() const { return TypeIdAvoidDoor; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 312870a2e..cc02c4de1 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -22,8 +22,6 @@ namespace MWMechanics /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); - AiAvoidDoor *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index a919808dd..5cb81b3d8 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -32,11 +32,6 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro return true; } -MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const -{ - return new AiBreathe(*this); -} - int MWMechanics::AiBreathe::getTypeId() const { return TypeIdBreathe; diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index a9ab56c70..6e3bb912a 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -10,8 +10,6 @@ namespace MWMechanics class AiBreathe final : public TypedAiPackage { public: - AiBreathe *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index b7c9bac3b..de61851cd 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -18,11 +18,6 @@ MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spel mDistance = action.getCombatRange(isRanged); } -MWMechanics::AiPackage *MWMechanics::AiCast::clone() const -{ - return new AiCast(*this); -} - bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) { MWWorld::Ptr target; diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 26eca15ef..21b629f24 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -15,8 +15,6 @@ namespace MWMechanics public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); - AiPackage *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ee1b9cecd..4a3c7aee6 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -421,11 +421,6 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } - AiCombat *MWMechanics::AiCombat::clone() const - { - return new AiCombat(*this); - } - void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr combat(new ESM::AiSequence::AiCombat()); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 610e402e4..2ef0298fc 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -102,8 +102,6 @@ namespace MWMechanics void init(); - AiCombat *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 03951d279..5e1d38331 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -55,12 +55,6 @@ namespace MWMechanics mDuration = 0; } - - AiEscort *MWMechanics::AiEscort::clone() const - { - return new AiEscort(*this); - } - bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // If AiEscort has ran for as long or longer then the duration specified diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 651abb2dd..9f5ac2f42 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -30,8 +30,6 @@ namespace MWMechanics AiEscort(const ESM::AiSequence::AiEscort* escort); - AiEscort *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiface.cpp b/apps/openmw/mwmechanics/aiface.cpp index b99a8c1f4..0bfd00c87 100644 --- a/apps/openmw/mwmechanics/aiface.cpp +++ b/apps/openmw/mwmechanics/aiface.cpp @@ -9,11 +9,6 @@ MWMechanics::AiFace::AiFace(float targetX, float targetY) { } -MWMechanics::AiPackage *MWMechanics::AiFace::clone() const -{ - return new AiFace(*this); -} - bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& /*characterController*/, MWMechanics::AiState& /*state*/, float /*duration*/) { osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 9defaf2f2..ce1c9847b 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -10,8 +10,6 @@ namespace MWMechanics public: AiFace(float targetX, float targetY); - AiPackage *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 09e8d0ecd..bffa238d5 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -201,11 +201,6 @@ std::string AiFollow::getFollowedActor() return mTargetActorRefId; } -AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - int AiFollow::getTypeId() const { return TypeIdFollow; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 3d22af9b1..39a10294b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -57,8 +57,6 @@ namespace MWMechanics bool followTargetThroughDoors() const final { return true; } bool shouldCancelPreviousAi() const final { return !mCommanded; } - AiFollow *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 52b2866bb..76b4ac0c9 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -27,10 +27,6 @@ AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue) mTargetActorId = pursue->mTargetActorId; } -AiPursue *MWMechanics::AiPursue::clone() const -{ - return new AiPursue(*this); -} bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { if(actor.getClass().getCreatureStats(actor).isDead()) diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 5f1976dd5..6898a8dd3 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -26,7 +26,6 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); - AiPursue *clone() const final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index dba70316b..94c782c02 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -36,11 +36,6 @@ namespace MWMechanics { } - AiTravel *MWMechanics::AiTravel::clone() const - { - return new AiTravel(*this); - } - bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { auto& stats = actor.getClass().getCreatureStats(actor); diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index c1420987a..beaf2819f 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -26,8 +26,6 @@ namespace MWMechanics void writeState(ESM::AiSequence::AiSequence &sequence) const final; - AiTravel *clone() const final; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 597453409..158a62c1d 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -115,11 +115,6 @@ namespace MWMechanics mDuration = 0; } - AiPackage * MWMechanics::AiWander::clone() const - { - return new AiWander(*this); - } - /* * AiWander high level states (0.29.0). Not entirely accurate in some cases * e.g. non-NPC actors do not greet and some creatures may be moving even in diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index f887fae67..bb5872eef 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -91,8 +91,6 @@ namespace MWMechanics AiWander (const ESM::AiSequence::AiWander* wander); - AiPackage *clone() const final; - bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; int getTypeId() const final; diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index a298bce71..2801accc4 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,6 +8,10 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { + virtual TypedAiPackage *clone() const override + { + return new T(*static_cast(this)); + } }; } From 7aca18f92b2bd91f8e22a41c89dd4376f3e262dd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 May 2020 17:59:36 +0300 Subject: [PATCH 05/34] Handle NiLines (feature #5445) --- CHANGELOG.md | 1 + components/nif/data.cpp | 26 ++++++++++++++ components/nif/data.hpp | 8 +++++ components/nif/niffile.cpp | 2 ++ components/nif/node.hpp | 20 +++++++++++ components/nif/record.hpp | 2 ++ components/nif/recordptr.hpp | 2 ++ components/nifosg/nifloader.cpp | 64 ++++++++++++++++++++------------- 8 files changed, 101 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f293cb2..48cbe2ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5441: Enemies can't push a player character when in critical strike stance Feature #5362: Show the soul gems' trapped soul in count dialog + Feature #5445: Handle NiLines 0.46.0 ------ diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 8ae49476b..82865faa7 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -110,6 +110,32 @@ void NiTriStripsData::read(NIFStream *nif) nif->getUShorts(strips[i], lengths[i]); } +void NiLinesData::read(NIFStream *nif) +{ + NiGeometryData::read(nif); + size_t num = vertices.size(); + std::vector flags; + nif->getChars(flags, num); + // Can't construct a line from a single vertex. + if (num < 2) + return; + // Convert connectivity flags into usable geometry. The last element needs special handling. + for (size_t i = 0; i < num-1; ++i) + { + if (flags[i] & 1) + { + lines.emplace_back(i); + lines.emplace_back(i+1); + } + } + // If there are just two vertices, they can be connected twice. Probably isn't critical. + if (flags[num-1] & 1) + { + lines.emplace_back(num-1); + lines.emplace_back(0); + } +} + void NiAutoNormalParticlesData::read(NIFStream *nif) { NiGeometryData::read(nif); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 33818810a..66b3f693a 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -62,6 +62,14 @@ public: void read(NIFStream *nif); }; +struct NiLinesData : public NiGeometryData +{ + // Lines, series of indices that correspond to connected vertices. + std::vector lines; + + void read(NIFStream *nif); +}; + class NiAutoNormalParticlesData : public NiGeometryData { public: diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 13c9ced60..b33f0c051 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -54,6 +54,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiBillboardNode", &construct , RC_NiBillboardNode )); newFactory.insert(makeEntry("NiTriShape", &construct , RC_NiTriShape )); newFactory.insert(makeEntry("NiTriStrips", &construct , RC_NiTriStrips )); + newFactory.insert(makeEntry("NiLines", &construct , RC_NiLines )); newFactory.insert(makeEntry("NiRotatingParticles", &construct , RC_NiRotatingParticles )); newFactory.insert(makeEntry("NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles )); newFactory.insert(makeEntry("NiCamera", &construct , RC_NiCamera )); @@ -97,6 +98,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiFloatData", &construct , RC_NiFloatData )); newFactory.insert(makeEntry("NiTriShapeData", &construct , RC_NiTriShapeData )); newFactory.insert(makeEntry("NiTriStripsData", &construct , RC_NiTriStripsData )); + newFactory.insert(makeEntry("NiLinesData", &construct , RC_NiLinesData )); newFactory.insert(makeEntry("NiVisData", &construct , RC_NiVisData )); newFactory.insert(makeEntry("NiColorData", &construct , RC_NiColorData )); newFactory.insert(makeEntry("NiPixelData", &construct , RC_NiPixelData )); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 06a1a3b76..e605df32a 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -182,6 +182,26 @@ struct NiTriStrips : NiGeometry } }; +struct NiLines : NiGeometry +{ + NiLinesDataPtr data; + + void read(NIFStream *nif) + { + Node::read(nif); + data.read(nif); + skin.read(nif); + } + + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + skin.post(nif); + if (!skin.empty()) + nif->setUseSkinning(true); + } +}; struct NiCamera : Node { diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 074dea9cf..67202d2fe 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -43,6 +43,7 @@ enum RecordType RC_NiCollisionSwitch, RC_NiTriShape, RC_NiTriStrips, + RC_NiLines, RC_NiRotatingParticles, RC_NiAutoNormalParticles, RC_NiBSParticleNode, @@ -83,6 +84,7 @@ enum RecordType RC_NiFloatData, RC_NiTriShapeData, RC_NiTriStripsData, + RC_NiLinesData, RC_NiVisData, RC_NiColorData, RC_NiPixelData, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 478ecfdbb..57607cb6a 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -142,6 +142,7 @@ class NiRotatingParticlesData; class NiAutoNormalParticlesData; class NiPalette; struct NiParticleModifier; +struct NiLinesData; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -158,6 +159,7 @@ using NiColorDataPtr = RecordPtrT; using NiKeyframeDataPtr = RecordPtrT; using NiTriShapeDataPtr = RecordPtrT; using NiTriStripsDataPtr = RecordPtrT; +using NiLinesDataPtr = RecordPtrT; using NiSkinInstancePtr = RecordPtrT; using NiSourceTexturePtr = RecordPtrT; using NiRotatingParticlesDataPtr = RecordPtrT; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 131f0c470..bff414707 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -612,7 +612,9 @@ namespace NifOsg applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); - if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && !skipMeshes) + const bool isGeometry = nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines; + + if (isGeometry && !skipMeshes) { const std::string nodeName = Misc::StringUtils::lowerCase(nifNode->name); static const std::string markerName = "tri editormarker"; @@ -624,9 +626,9 @@ namespace NifOsg Nif::NiSkinInstancePtr skin = static_cast(nifNode)->skin; if (skin.empty()) - handleTriShape(nifNode, node, composite, boundTextures, animflags); + handleGeometry(nifNode, node, composite, boundTextures, animflags); else - handleSkinnedTriShape(nifNode, node, composite, boundTextures, animflags); + handleSkinnedGeometry(nifNode, node, composite, boundTextures, animflags); if (!nifNode->controller.empty()) handleMeshControllers(nifNode, node, composite, boundTextures, animflags); @@ -1099,8 +1101,11 @@ namespace NifOsg partsys->getOrCreateStateSet(); } - void triCommonToGeometry(osg::Geometry *geometry, const std::vector& vertices, const std::vector& normals, const std::vector>& uvlist, const std::vector& colors, const std::vector& boundTextures, const std::string& name) + void handleNiGeometryData(osg::Geometry *geometry, const Nif::NiGeometryData* data, const std::vector& boundTextures, const std::string& name) { + const auto& vertices = data->vertices; + const auto& normals = data->normals; + const auto& colors = data->colors; if (!vertices.empty()) geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data())); if (!normals.empty()) @@ -1108,6 +1113,7 @@ namespace NifOsg if (!colors.empty()) geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX); + const auto& uvlist = data->uvlist; int textureStage = 0; for (const unsigned int uvSet : boundTextures) { @@ -1124,43 +1130,53 @@ namespace NifOsg } } - void triShapeToGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleNiGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - bool vertexColorsPresent = false; + const Nif::NiGeometryData* niGeometryData = nullptr; if (nifNode->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape* triShape = static_cast(nifNode); 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); + niGeometryData = static_cast(data); if (!data->triangles.empty()) geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), (unsigned short*)data->triangles.data())); } } - else + else if (nifNode->recType == Nif::RC_NiTriStrips) { const Nif::NiTriStrips* triStrips = static_cast(nifNode); 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); + niGeometryData = static_cast(data); if (!data->strips.empty()) { - for (const std::vector& strip : data->strips) + for (const auto& 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())); + if (strip.size() >= 3) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); } } } } + else if (nifNode->recType == Nif::RC_NiLines) + { + const Nif::NiLines* lines = static_cast(nifNode); + if (!lines->data.empty()) + { + const Nif::NiLinesData* data = lines->data.getPtr(); + niGeometryData = static_cast(data); + const auto& line = data->lines; + if (!line.empty()) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::LINES, line.size(), (unsigned short*)line.data())); + } + } + if (niGeometryData) + handleNiGeometryData(geometry, niGeometryData, boundTextures, nifNode->name); // osg::Material properties are handled here for two reasons: // - if there are no vertex colors, we need to disable colorMode. @@ -1168,15 +1184,15 @@ namespace NifOsg // above the actual renderable would be tedious. std::vector drawableProps; collectDrawableProperties(nifNode, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, vertexColorsPresent, animflags); + applyDrawableProperties(parentNode, drawableProps, composite, niGeometryData && !niGeometryData->colors.empty(), animflags); } - void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); - triShapeToGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); + handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1215,12 +1231,12 @@ namespace NifOsg return morphGeom; } - void handleSkinnedTriShape(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, + void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); osg::ref_ptr geometry (new osg::Geometry); - triShapeToGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); + handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); rig->setName(nifNode->name); From d72152183f9cd8d4aeba13ab5ae8a720bb645993 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 30 May 2020 21:41:25 +0300 Subject: [PATCH 06/34] Update spell effects during death animation (#5403) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 7 ++++++- apps/openmw/mwmechanics/spellcasting.cpp | 13 +++++++++---- apps/openmw/mwworld/inventorystore.cpp | 3 ++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d65526d..62dd1c2db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #5370: Opening an unlocked but trapped door uses the key Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts + Bug #5403: Enchantment effect doesn't show on an enemy during death animation Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5424: Creatures do not headtrack player diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 873d5fa7c..41836c74f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1623,9 +1623,14 @@ namespace MWMechanics iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); - // For dead actors we need to remove looping spell particles + // For dead actors we need to update looping spell particles if (iter->first.getClass().getCreatureStats(iter->first).isDead()) + { + // They can be added during the death animation + if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished()) + adjustMagicEffects(iter->first); ctrl->updateContinuousVfx(); + } else { bool cellChanged = world->hasCellChanged(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7aec5d471..9f7108239 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -60,8 +60,13 @@ namespace MWMechanics void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { - if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) - return; + if (!target.isEmpty() && target.getClass().isActor()) + { + // Early-out for characters that have departed. + const auto& stats = target.getClass().getCreatureStats(target); + if (stats.isDead() && stats.isDeathAnimationFinished()) + return; + } // If none of the effects need to apply, we can early-out bool found = false; @@ -201,9 +206,9 @@ namespace MWMechanics } bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth; - if (castByPlayer && target != caster && effectAffectsHealth) + if (castByPlayer && target != caster && !target.getClass().getCreatureStats(target).isDead() && effectAffectsHealth) { - // If player is attempting to cast a harmful spell or is healing someone, show the target's HP bar. + // If player is attempting to cast a harmful spell on or is healing a living target, show the target's HP bar. MWBase::Environment::get().getWindowManager()->setEnemy(target); } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index d4358532c..7ef0dd09a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -572,7 +572,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) mMagicEffects = MWMechanics::MagicEffects(); - if (actor.getClass().getCreatureStats(actor).isDead()) + const auto& stats = actor.getClass().getCreatureStats(actor); + if (stats.isDead() && stats.isDeathAnimationFinished()) return; for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) From ce7c47ee121043b3edb78ae7fceb842604e17c62 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 1 Jun 2020 15:36:14 +0200 Subject: [PATCH 07/34] Return cloned AiPackage as unique_ptr --- apps/openmw/mwmechanics/aipackage.hpp | 4 +++- apps/openmw/mwmechanics/aisequence.cpp | 8 ++++---- apps/openmw/mwmechanics/typedaipackage.hpp | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index a09362baa..873ad1c29 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWMECHANICS_AIPACKAGE_H #define GAME_MWMECHANICS_AIPACKAGE_H +#include + #include #include "pathfinding.hpp" @@ -59,7 +61,7 @@ namespace MWMechanics virtual ~AiPackage() = default; ///Clones the package - virtual AiPackage *clone() const = 0; + virtual std::unique_ptr clone() const = 0; /// Updates and runs the package (Should run every frame) /// \return Package completed? diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 00d44202a..8ed1f0bee 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -27,7 +27,7 @@ void AiSequence::copy (const AiSequence& sequence) { for (std::list::const_iterator iter (sequence.mPackages.begin()); iter!=sequence.mPackages.end(); ++iter) - mPackages.push_back ((*iter)->clone()); + mPackages.push_back ((*iter)->clone().release()); // We need to keep an AiWander storage, if present - it has a state machine. // Not sure about another temporary storages @@ -288,7 +288,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) { package->reset(); - mPackages.push_back(package->clone()); + mPackages.push_back(package->clone().release()); } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) @@ -380,12 +380,12 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo if((*it)->getPriority() <= package.getPriority()) { - mPackages.insert(it,package.clone()); + mPackages.insert(it,package.clone().release()); return; } } - mPackages.push_back (package.clone()); + mPackages.push_back (package.clone().release()); // Make sure that temporary storage is empty if (cancelOther) diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index 2801accc4..e2b5f8688 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,9 +8,9 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { - virtual TypedAiPackage *clone() const override + virtual std::unique_ptr clone() const override { - return new T(*static_cast(this)); + return std::make_unique(*static_cast(this)); } }; } From b67e18329e4abb47c1299f5d0a8e5258e91e44fc Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 17 May 2020 00:29:21 +0200 Subject: [PATCH 08/34] Store AI packages as unique_ptr --- apps/openmw/mwmechanics/actors.cpp | 28 ++++---- apps/openmw/mwmechanics/aisequence.cpp | 89 +++++++++++--------------- apps/openmw/mwmechanics/aisequence.hpp | 11 ++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 4 files changed, 58 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 873d5fa7c..4fdacfd05 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -112,11 +112,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) bool hasCommandPackage = false; - std::list::const_iterator it; - for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + auto it = stats.getAiSequence().begin(); + for (; it != stats.getAiSequence().end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && - static_cast(*it)->isCommanded()) + static_cast(it->get())->isCommanded()) { hasCommandPackage = true; break; @@ -419,7 +419,7 @@ namespace MWMechanics if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000) return; - // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated. + // Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated. // We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST. const float delta = MWBase::Environment::get().getFrameDuration() * 6.f; static const float fVoiceIdleOdds = world->getStore().get().find("fVoiceIdleOdds")->mValue.getFloat(); @@ -435,9 +435,9 @@ namespace MWMechanics CreatureStats &stats = actor.getClass().getCreatureStats(actor); MWMechanics::AiSequence& seq = stats.getAiSequence(); - if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed()) + if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed()) { - osg::Vec3f targetPos = seq.getActivePackage()->getDestination(); + osg::Vec3f targetPos = seq.getActivePackage().getDestination(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); float distance = (targetPos - actorPos).length(); if (distance < DECELERATE_DISTANCE) @@ -604,7 +604,7 @@ namespace MWMechanics bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end(); // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them - // Doesn't apply for player followers/escorters + // Doesn't apply for player followers/escorters if (!aggressive && !isPlayerFollowerOrEscorter) { // Check that actor2 is in combat with actor1 @@ -673,7 +673,7 @@ namespace MWMechanics return; bool followerOrEscorter = false; - for (const AiPackage* package : creatureStats2.getAiSequence()) + for (const auto& package : creatureStats2.getAiSequence()) { // The follow package must be first or have nothing but combat before it if (package->sideWithTarget()) @@ -1738,7 +1738,7 @@ namespace MWMechanics if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed()) { MWMechanics::AiSequence& seq = stats.getAiSequence(); - alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive(); + alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive(); } bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive; int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -2158,7 +2158,7 @@ namespace MWMechanics // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - for (const AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->sideWithTarget() && !package->getTarget().isEmpty()) { @@ -2192,9 +2192,9 @@ namespace MWMechanics if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, + // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (const AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->followTargetThroughDoors() && package->getTarget() == actor) list.push_back(iteratedActor); @@ -2257,11 +2257,11 @@ namespace MWMechanics // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->followTargetThroughDoors() && package->getTarget() == actor) { - list.push_back(static_cast(package)->getFollowIndex()); + list.push_back(static_cast(package.get())->getFollowIndex()); break; } else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 8ed1f0bee..9f5d4ccaf 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -25,9 +25,8 @@ namespace MWMechanics void AiSequence::copy (const AiSequence& sequence) { - for (std::list::const_iterator iter (sequence.mPackages.begin()); - iter!=sequence.mPackages.end(); ++iter) - mPackages.push_back ((*iter)->clone().release()); + for (const auto& package : sequence.mPackages) + mPackages.push_back(package->clone()); // We need to keep an AiWander storage, if present - it has a state machine. // Not sure about another temporary storages @@ -74,7 +73,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const { if (getTypeId() != AiPackage::TypeIdCombat) return false; - + targetActor = mPackages.front()->getTarget(); return !targetActor.isEmpty(); @@ -82,7 +81,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const bool AiSequence::getCombatTargets(std::vector &targetActors) const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) targetActors.push_back((*it)->getTarget()); @@ -91,24 +90,23 @@ bool AiSequence::getCombatTargets(std::vector &targetActors) const return !targetActors.empty(); } -std::list::const_iterator AiSequence::begin() const +std::list>::const_iterator AiSequence::begin() const { return mPackages.begin(); } -std::list::const_iterator AiSequence::end() const +std::list>::const_iterator AiSequence::end() const { return mPackages.end(); } -void AiSequence::erase(std::list::const_iterator package) +void AiSequence::erase(std::list>::const_iterator package) { // Not sure if manually terminated packages should trigger mDone, probably not? - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for(auto it = mPackages.begin(); it != mPackages.end(); ++it) { if (package == it) { - delete *it; mPackages.erase(it); return; } @@ -118,7 +116,7 @@ void AiSequence::erase(std::list::const_iterator package) bool AiSequence::isInCombat() const { - for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) return true; @@ -128,7 +126,7 @@ bool AiSequence::isInCombat() const bool AiSequence::isEngagedWithActor() const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { @@ -142,7 +140,7 @@ bool AiSequence::isEngagedWithActor() const bool AiSequence::hasPackage(int typeId) const { - for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == typeId) return true; @@ -152,7 +150,7 @@ bool AiSequence::hasPackage(int typeId) const bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const { - for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { @@ -165,11 +163,10 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const void AiSequence::stopCombat() { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) + for(auto it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { - delete *it; it = mPackages.erase(it); } else @@ -179,11 +176,10 @@ void AiSequence::stopCombat() void AiSequence::stopPursuit() { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ) + for(auto it = mPackages.begin(); it != mPackages.end(); ) { if ((*it)->getTypeId() == AiPackage::TypeIdPursue) { - delete *it; it = mPackages.erase(it); } else @@ -213,7 +209,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } auto packageIt = mPackages.begin(); - MWMechanics::AiPackage* package = *packageIt; + MWMechanics::AiPackage* package = packageIt->get(); if (!package->alwaysActive() && outOfRange) return; @@ -231,7 +227,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac float bestRating = 0.f; - for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) + for (auto it = mPackages.begin(); it != mPackages.end();) { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; @@ -240,7 +236,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac // target disappeared (e.g. summoned creatures) if (target.isEmpty()) { - delete *it; it = mPackages.erase(it); } else @@ -276,24 +271,23 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } packageIt = mPackages.begin(); - package = *packageIt; + package = packageIt->get(); packageTypeId = package->getTypeId(); } try { - if (package->execute (actor, characterController, mAiState, duration)) + if (package->execute(actor, characterController, mAiState, duration)) { // Put repeating noncombat AI packages on the end of the stack so they can be used again if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat())) { package->reset(); - mPackages.push_back(package->clone().release()); + mPackages.push_back(package->clone()); } // To account for the rare case where AiPackage::execute() queued another AI package // (e.g. AiPursue executing a dialogue script that uses startCombat) mPackages.erase(packageIt); - delete package; if (isActualAiPackage(packageTypeId)) mDone = true; } @@ -311,9 +305,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac void AiSequence::clear() { - for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) - delete *iter; - mPackages.clear(); } @@ -340,8 +331,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo osg::Vec3f dest; if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) { - AiPackage* activePackage = getActivePackage(); - dest = activePackage->getDestination(actor); + dest = getActivePackage().getDestination(actor); } else { @@ -355,11 +345,10 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo // remove previous packages if required if (cancelOther && package.shouldCancelPreviousAi()) { - for(std::list::iterator it = mPackages.begin(); it != mPackages.end();) + for (auto it = mPackages.begin(); it != mPackages.end();) { if((*it)->canCancel()) { - delete *it; it = mPackages.erase(it); } else @@ -369,7 +358,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo } // insert new package in correct place depending on priority - for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + for (auto it = mPackages.begin(); it != mPackages.end(); ++it) { // We should keep current AiCast package, if we try to add a new one. if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && @@ -380,12 +369,12 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo if((*it)->getPriority() <= package.getPriority()) { - mPackages.insert(it,package.clone().release()); + mPackages.insert(it, package.clone()); return; } } - mPackages.push_back (package.clone().release()); + mPackages.push_back(package.clone()); // Make sure that temporary storage is empty if (cancelOther) @@ -401,12 +390,11 @@ bool MWMechanics::AiSequence::isEmpty() const return mPackages.empty(); } -AiPackage* MWMechanics::AiSequence::getActivePackage() +const AiPackage& MWMechanics::AiSequence::getActivePackage() { if(mPackages.empty()) throw std::runtime_error(std::string("No AI Package!")); - else - return mPackages.front(); + return *mPackages.front(); } void AiSequence::fill(const ESM::AIPackageList &list) @@ -417,7 +405,7 @@ void AiSequence::fill(const ESM::AIPackageList &list) for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { - MWMechanics::AiPackage* package; + std::unique_ptr package; if (it->mType == ESM::AI_Wander) { ESM::AIWander data = it->mWander; @@ -425,38 +413,36 @@ void AiSequence::fill(const ESM::AIPackageList &list) idles.reserve(8); for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); - package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); + package = std::make_unique(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); } else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { ESM::AITravel data = it->mTravel; - package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ); + package = std::make_unique(data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Activate) { ESM::AIActivate data = it->mActivate; - package = new MWMechanics::AiActivate(data.mName.toString()); + package = std::make_unique(data.mName.toString()); } else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + package = std::make_unique(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); } - mPackages.push_back(package); + mPackages.push_back(std::move(package)); } } void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const { - for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) - { - (*iter)->writeState(sequence); - } + for (const auto& package : mPackages) + package->writeState(sequence); sequence.mLastAiPackage = mLastAiPackage; } @@ -527,7 +513,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) if (!package.get()) continue; - mPackages.push_back(package.release()); + mPackages.push_back(std::move(package)); } mLastAiPackage = sequence.mLastAiPackage; @@ -537,8 +523,7 @@ void AiSequence::fastForward(const MWWorld::Ptr& actor) { if (!mPackages.empty()) { - MWMechanics::AiPackage* package = mPackages.front(); - package->fastForward(actor, mAiState); + mPackages.front()->fastForward(actor, mAiState); } } diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 7f07d5aae..12b837d87 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_AISEQUENCE_H #include +#include #include "aistate.hpp" @@ -36,7 +37,7 @@ namespace MWMechanics class AiSequence { ///AiPackages to run though - std::list mPackages; + std::list> mPackages; ///Finished with top AIPackage, set for one frame bool mDone; @@ -64,10 +65,10 @@ namespace MWMechanics virtual ~AiSequence(); /// Iterator may be invalidated by any function calls other than begin() or end(). - std::list::const_iterator begin() const; - std::list::const_iterator end() const; + std::list>::const_iterator begin() const; + std::list>::const_iterator end() const; - void erase (std::list::const_iterator package); + void erase(std::list>::const_iterator package); /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ @@ -125,7 +126,7 @@ namespace MWMechanics /// Return the current active package. /** If there is no active package, it will throw an exception **/ - AiPackage* getActivePackage(); + const AiPackage& getActivePackage(); /// Fills the AiSequence with packages /** Typically used for loading from the ESM diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b7a1d22bf..4a513e98f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3063,7 +3063,7 @@ namespace MWWorld { if (actor != MWMechanics::getPlayer()) { - for (const MWMechanics::AiPackage* package : stats.getAiSequence()) + for (const auto& package : stats.getAiSequence()) { if (package->getTypeId() == MWMechanics::AiPackage::TypeIdCast) { From 4d7947d27c68201f5d8d544556c869829eebfbfe Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 2 Jun 2020 21:59:37 +0200 Subject: [PATCH 09/34] Mutate base records when editing AI settings (#2798) --- apps/openmw/mwbase/world.hpp | 9 ++++++ apps/openmw/mwclass/creature.cpp | 5 ++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 5 ++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwmechanics/actorutil.hpp | 37 +++++++++++++++++++++++++ apps/openmw/mwscript/aiextensions.cpp | 7 +++-- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/class.cpp | 5 ++++ apps/openmw/mwworld/class.hpp | 4 ++- apps/openmw/mwworld/esmstore.cpp | 35 +++++++++++------------ apps/openmw/mwworld/esmstore.hpp | 3 ++ apps/openmw/mwworld/worldimp.cpp | 11 ++++++++ apps/openmw/mwworld/worldimp.hpp | 8 ++++++ 14 files changed, 112 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 84f9b4984..05802c658 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -35,6 +35,7 @@ namespace ESM struct Position; struct Cell; struct Class; + struct Creature; struct Potion; struct Spell; struct NPC; @@ -377,6 +378,14 @@ namespace MWBase ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + virtual void update (float duration, bool paused) = 0; virtual void updatePhysics (float duration, bool paused) = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d1a439528..375b70dde 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -878,4 +878,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); scale *= ref->mBase->mScale; } + + void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 35688ed1a..9be5c4272 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -129,6 +129,8 @@ namespace MWClass virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 297471e07..df483962a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1437,4 +1437,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->getFactionRank(); } + + void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 9b92f6338..3d63697fb 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -164,6 +164,8 @@ namespace MWClass virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799..cdb787311 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -1,6 +1,16 @@ #ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #define OPENMW_MWMECHANICS_ACTORUTIL_H +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/esmstore.hpp" + +#include "./creaturestats.hpp" + namespace MWWorld { class Ptr; @@ -11,6 +21,33 @@ namespace MWMechanics MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); + + template + void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) + { + T copy = *MWBase::Environment::get().getWorld()->getStore().get().find(id); + switch(setting) + { + case MWMechanics::CreatureStats::AiSetting::AI_Hello: + copy.mAiData.mHello = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Fight: + copy.mAiData.mFight = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Flee: + copy.mAiData.mFlee = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Alarm: + copy.mAiData.mAlarm = value; + break; + default: + assert(0); + } + MWBase::Environment::get().getWorld()->createOverrideRecord(copy); + } + + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); } #endif diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 79639197d..fdd13cfb6 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -257,8 +257,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, - ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value); + int modified = ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value; + + ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, modified); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, modified); } }; template @@ -277,6 +279,7 @@ namespace MWScript MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex); stat.setModified(value, 0); ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 86a26212f..560c24578 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -460,6 +460,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: + case ESM::REC_CREA: MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap); break; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0287db56f..07981bf2a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -516,4 +516,9 @@ namespace MWWorld result.z() = magicEffect->mData.mBlue / 255.f; return result; } + + void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + throw std::runtime_error ("class does not have creature stats"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 8c3740edd..fb816d810 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -10,6 +10,7 @@ #include "ptr.hpp" #include "doorstate.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace ESM { @@ -28,7 +29,6 @@ namespace MWPhysics namespace MWMechanics { - class CreatureStats; class NpcStats; struct Movement; } @@ -360,6 +360,8 @@ namespace MWWorld virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 1f6ed5102..f86226640 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -273,7 +273,8 @@ void ESMStore::validate() +mSpells.getDynamicSize() +mWeapons.getDynamicSize() +mCreatureLists.getDynamicSize() - +mItemLists.getDynamicSize(); + +mItemLists.getDynamicSize() + +mCreatures.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -295,6 +296,7 @@ void ESMStore::validate() mNpcs.write (writer, progress); mItemLists.write (writer, progress); mCreatureLists.write (writer, progress); + mCreatures.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) @@ -312,24 +314,8 @@ void ESMStore::validate() case ESM::REC_NPC_: case ESM::REC_LEVI: case ESM::REC_LEVC: - - { - mStores[type]->read (reader); - } - - if (type==ESM::REC_NPC_) - { - // NPC record will always be last and we know that there can be only one - // dynamic NPC record (player) -> We are done here with dynamic record loading - setUp(); - - const ESM::NPC *player = mNpcs.find ("player"); - - if (!mRaces.find (player->mRace) || - !mClasses.find (player->mClass)) - throw std::runtime_error ("Invalid player record (race or class unavailable"); - } - + case ESM::REC_CREA: + mStores[type]->read (reader); return true; case ESM::REC_DYNA: @@ -343,4 +329,15 @@ void ESMStore::validate() } } + void ESMStore::checkPlayer() + { + setUp(); + + const ESM::NPC *player = mNpcs.find ("player"); + + if (!mRaces.find (player->mRace) || + !mClasses.find (player->mClass)) + throw std::runtime_error ("Invalid player record (race or class unavailable"); + } + } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d170a32c5..fe1cfc708 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -239,6 +239,9 @@ namespace MWWorld bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< \return Known type? + + // To be called when we are done with dynamic record loading + void checkPlayer(); }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ba4a6467a..6d20f5d45 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -403,6 +403,7 @@ namespace MWWorld reader.getHNT(mLevitationEnabled, "LEVT"); return; case ESM::REC_PLAY: + mStore.checkPlayer(); mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { @@ -1815,6 +1816,16 @@ namespace MWWorld return mStore.overrideRecord(record); } + const ESM::Creature *World::createOverrideRecord(const ESM::Creature &record) + { + return mStore.overrideRecord(record); + } + + const ESM::NPC *World::createOverrideRecord(const ESM::NPC &record) + { + return mStore.overrideRecord(record); + } + const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b6d2afdc..201803a48 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -490,6 +490,14 @@ namespace MWWorld ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + const ESM::Creature *createOverrideRecord (const ESM::Creature& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + const ESM::NPC *createOverrideRecord (const ESM::NPC& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + void update (float duration, bool paused) override; void updatePhysics (float duration, bool paused) override; From c0e62e9529f0e9fcf6c0b7ab0a5aab04fcdc81b2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 29 May 2020 13:37:13 +0400 Subject: [PATCH 10/34] Refactor list of variables in the WorldManager --- apps/openmw/mwworld/worldimp.cpp | 7 +++-- apps/openmw/mwworld/worldimp.hpp | 48 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4a513e98f..d660d90af 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -164,10 +164,11 @@ namespace MWWorld const std::string& startCell, const std::string& startupScript, const std::string& resourcePath, const std::string& userDataPath) : mResourceSystem(resourceSystem), mLocalScripts (mStore), - mSky (true), mCells (mStore, mEsm), - mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), + mCells (mStore, mEsm), mSky (true), + mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), + mUserDataPath(userDataPath), mShouldUpdateNavigator(false), mActivationDistanceOverride (activationDistanceOverride), - mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), + mStartCell(startCell), mDistanceToFacedObject(-1.f), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 942788499..f6341b509 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -77,13 +77,13 @@ namespace MWWorld class World final: public MWBase::World { + private: Resource::ResourceSystem* mResourceSystem; std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - bool mSky; ESM::Variant* mGameHour; ESM::Variant* mDaysPassed; @@ -104,6 +104,7 @@ namespace MWWorld std::unique_ptr mWeatherManager; std::shared_ptr mProjectileManager; + bool mSky; bool mGodMode; bool mScriptsEnabled; std::vector mContentFiles; @@ -111,18 +112,31 @@ namespace MWWorld std::string mUserDataPath; osg::Vec3f mDefaultHalfExtents; - bool mShouldUpdateNavigator = false; - - // not implemented - World (const World&); - World& operator= (const World&); + bool mShouldUpdateNavigator; int mActivationDistanceOverride; + std::string mStartCell; + + float mSwimHeightScale; + + float mDistanceToFacedObject; + + bool mTeleportEnabled; + bool mLevitationEnabled; + bool mGoToJail; + int mDaysInPrison; + bool mPlayerTraveling; + bool mPlayerInJail; + + float mSpellPreloadTimer; + std::map mDoorStates; ///< only holds doors that are currently moving. 1 = opening, 2 = closing - std::string mStartCell; + // not implemented + World (const World&); + World& operator= (const World&); void updateWeather(float duration, bool paused = false); int getDaysPerMonth (int month) const; @@ -141,10 +155,6 @@ namespace MWWorld MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true); - public: // FIXME - void addContainerScripts(const Ptr& reference, CellStore* cell) override; - void removeContainerScripts(const Ptr& reference) override; - private: void PCDropped (const Ptr& item); bool rotateDoor(const Ptr door, DoorState state, float duration); @@ -172,19 +182,6 @@ namespace MWWorld void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ContentLoader& contentLoader); - float mSwimHeightScale; - - float mDistanceToFacedObject; - - bool mTeleportEnabled; - bool mLevitationEnabled; - bool mGoToJail; - int mDaysInPrison; - bool mPlayerTraveling; - bool mPlayerInJail; - - float mSpellPreloadTimer; - float feetToGameUnits(float feet); float getActivationDistancePlusTelekinesis(); @@ -192,6 +189,9 @@ namespace MWWorld MWWorld::ConstPtr getClosestMarkerFromExteriorPosition( const osg::Vec3f& worldPos, const std::string &id ); public: + // FIXME + void addContainerScripts(const Ptr& reference, CellStore* cell) override; + void removeContainerScripts(const Ptr& reference) override; World ( osgViewer::Viewer* viewer, From 3dce225f28cae4a7e6ac1d5c992eb64c873677e3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Sep 2019 20:27:42 +0400 Subject: [PATCH 11/34] Implement vanilla-style corprus handling (bug #3714, bug #4623) --- CHANGELOG.md | 2 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 + apps/openmw/mwgui/jailscreen.cpp | 6 + apps/openmw/mwmechanics/activespells.cpp | 41 ++++- apps/openmw/mwmechanics/activespells.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 113 ++++++++++++- apps/openmw/mwmechanics/creaturestats.cpp | 38 ++++- apps/openmw/mwmechanics/creaturestats.hpp | 20 ++- .../mwmechanics/mechanicsmanagerimp.cpp | 18 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 + apps/openmw/mwmechanics/spells.cpp | 152 ++++++------------ apps/openmw/mwmechanics/spells.hpp | 22 +-- apps/openmw/mwmechanics/stat.cpp | 9 +- apps/openmw/mwscript/statsextensions.cpp | 1 + apps/openmw/mwworld/inventorystore.cpp | 12 +- apps/openmw/mwworld/inventorystore.hpp | 4 +- components/esm/attr.hpp | 2 +- components/esm/creaturestats.cpp | 21 +++ components/esm/creaturestats.hpp | 11 +- components/esm/savedgame.cpp | 2 +- components/esm/spellstate.cpp | 34 ++-- components/esm/spellstate.hpp | 5 +- 22 files changed, 360 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87796d015..58fe39c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Bug #1952: Incorrect particle lighting Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly + Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5165: Active spells should use real time intead of timestamps diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3bde83e94..c9ce43e01 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -280,6 +280,8 @@ namespace MWBase virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; + + virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 0; }; } diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 9c8fbfb17..4a89304b3 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -81,6 +81,12 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->rest(mDays * 24, true); MWBase::Environment::get().getWorld()->advanceTime(mDays * 24); + // We should not worsen corprus when in prison + for (auto& spell : player.getClass().getCreatureStats(player).getCorprusSpells()) + { + spell.second.mNextWorsening += mDays * 24; + } + std::set skills; for (int day=0; day& effects = iter->second.mEffects; for (std::vector::iterator effectIt = effects.begin(); effectIt != effects.end();) { if (effectIt->mTimeLeft <= 0) { - effectIt = effects.erase(effectIt); rebuild = true; + + // Note: it we expire a Corprus effect, we should remove the whole spell. + if (effectIt->mEffectId == ESM::MagicEffect::Corprus) + { + iter = mSpells.erase (iter); + interrupt = true; + break; + } + + effectIt = effects.erase(effectIt); } else { @@ -43,7 +53,9 @@ namespace MWMechanics ++effectIt; } } - ++iter; + + if (!interrupt) + ++iter; } } } @@ -278,6 +290,31 @@ namespace MWMechanics mSpellsChanged = true; } + void ActiveSpells::purgeCorprusDisease() + { + for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();) + { + bool hasCorprusEffect = false; + for (std::vector::iterator effectIt = iter->second.mEffects.begin(); + effectIt != iter->second.mEffects.end();++effectIt) + { + if (effectIt->mEffectId == ESM::MagicEffect::Corprus) + { + hasCorprusEffect = true; + break; + } + } + + if (hasCorprusEffect) + { + mSpells.erase(iter++); + mSpellsChanged = true; + } + else + ++iter; + } + } + void ActiveSpells::clear() { mSpells.clear(); diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index ddfa56ecf..9a1783bc9 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -99,6 +99,8 @@ namespace MWMechanics bool isSpellActive (const std::string& id) const; ///< case insensitive + void purgeCorprusDisease(); + const MagicEffects& getMagicEffects() const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4fdacfd05..584a09492 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -176,6 +176,49 @@ namespace MWMechanics } }; + class GetCurrentMagnitudes : public MWMechanics::EffectSourceVisitor + { + std::string mSpellId; + + public: + GetCurrentMagnitudes(const std::string& spellId) + : mSpellId(spellId) + { + } + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (magnitude <= 0) + return; + + if (sourceId != mSpellId) + return; + + mMagnitudes.push_back(std::make_pair(key, magnitude)); + } + + std::vector> mMagnitudes; + }; + + class GetCorprusSpells : public MWMechanics::EffectSourceVisitor + { + + public: + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (key.mId != ESM::MagicEffect::Corprus) + return; + + mSpells.push_back(sourceId); + } + + std::vector mSpells; + }; + class SoulTrap : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mCreature; @@ -942,21 +985,75 @@ namespace MWMechanics if (creatureStats.needToRecalcDynamicStats()) calculateDynamicStats(ptr); + if (ptr == getPlayer()) { - Spells & spells = creatureStats.getSpells(); - for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + GetCorprusSpells getCorprusSpellsVisitor; + creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor); + creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor); + ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor); + std::vector corprusSpells = getCorprusSpellsVisitor.mSpells; + std::vector corprusSpellsToRemove; + + for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it) { - if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end()) + if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end()) { - if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening) - { - spells.worsenCorprus(it->first); + // Corprus effect expired, remove entry and restore stats. + MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, it->first); + corprusSpellsToRemove.push_back(it->first); + corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end()); + continue; + } + + corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end()); - if (ptr == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); + if (MWBase::Environment::get().getWorld()->getTimeStamp() >= it->second.mNextWorsening) + { + it->second.mNextWorsening += CorprusStats::sWorseningPeriod; + GetCurrentMagnitudes getMagnitudesVisitor (it->first); + creatureStats.getSpells().visitEffectSources(getMagnitudesVisitor); + creatureStats.getActiveSpells().visitEffectSources(getMagnitudesVisitor); + ptr.getClass().getInventoryStore(ptr).visitEffectSources(getMagnitudesVisitor); + for (auto& effectMagnitude : getMagnitudesVisitor.mMagnitudes) + { + if (effectMagnitude.first.mId == ESM::MagicEffect::FortifyAttribute) + { + AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg); + attr.damage(-effectMagnitude.second); + creatureStats.setAttribute(effectMagnitude.first.mArg, attr); + it->second.mWorsenings[effectMagnitude.first.mArg] = 0; + } + else if (effectMagnitude.first.mId == ESM::MagicEffect::DrainAttribute) + { + AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg); + int currentDamage = attr.getDamage(); + if (currentDamage >= 0) + it->second.mWorsenings[effectMagnitude.first.mArg] = std::min(it->second.mWorsenings[effectMagnitude.first.mArg], currentDamage); + + it->second.mWorsenings[effectMagnitude.first.mArg] += effectMagnitude.second; + attr.damage(effectMagnitude.second); + creatureStats.setAttribute(effectMagnitude.first.mArg, attr); + } } + + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); } } + + for (std::string& oldCorprusSpell : corprusSpellsToRemove) + { + creatureStats.removeCorprusSpell(oldCorprusSpell); + } + + for (std::string& newCorprusSpell : corprusSpells) + { + CorprusStats corprus; + for (int i=0; igetTimeStamp() + CorprusStats::sWorseningPeriod; + + creatureStats.addCorprusSpell(newCorprusSpell, corprus); + } } // AI setting modifiers diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 800b5c22f..a64fb087c 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -551,6 +551,14 @@ namespace MWMechanics state.mHasAiSettings = true; for (int i=0; i<4; ++i) mAiSettings[i].writeState (state.mAiSettings[i]); + + for (auto it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + { + for (int i=0; ifirst].mWorsenings[i] = mCorprusSpells.at(it->first).mWorsenings[i]; + + state.mCorprusSpells[it->first].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); + } } void CreatureStats::readState (const ESM::CreatureStats& state) @@ -589,7 +597,7 @@ namespace MWMechanics mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); //mHitAttemptActorId = state.mHitAttemptActorId; - mSpells.readState(state.mSpells); + mSpells.readState(state.mSpells, this); mActiveSpells.readState(state.mActiveSpells); mAiSequence.readState(state.mAiSequence); mMagicEffects.readState(state.mMagicEffects); @@ -600,6 +608,15 @@ namespace MWMechanics if (state.mHasAiSettings) for (int i=0; i<4; ++i) mAiSettings[i].readState(state.mAiSettings[i]); + + mCorprusSpells.clear(); + for (auto it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) + { + for (int i=0; ifirst].mWorsenings[i] = state.mCorprusSpells.at(it->first).mWorsenings[i]; + + mCorprusSpells[it->first].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); + } } void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) @@ -675,4 +692,23 @@ namespace MWMechanics { return mSummonGraveyard; } + + std::map &CreatureStats::getCorprusSpells() + { + return mCorprusSpells; + } + + void CreatureStats::addCorprusSpell(const std::string& sourceId, CorprusStats& stats) + { + mCorprusSpells[sourceId] = stats; + } + + void CreatureStats::removeCorprusSpell(const std::string& sourceId) + { + auto corprusIt = mCorprusSpells.find(sourceId); + if (corprusIt != mCorprusSpells.end()) + { + mCorprusSpells.erase(corprusIt); + } + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a4ecb23b3..5a5dd3f12 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -12,6 +12,8 @@ #include "aisequence.hpp" #include "drawstate.hpp" +#include + namespace ESM { struct CreatureStats; @@ -19,6 +21,14 @@ namespace ESM namespace MWMechanics { + struct CorprusStats + { + static const int sWorseningPeriod = 24; + + int mWorsenings[ESM::Attribute::Length]; + MWWorld::TimeStamp mNextWorsening; + }; + /// \brief Common creature stats /// /// @@ -26,7 +36,7 @@ namespace MWMechanics { static int sActorId; DrawState_ mDrawState; - AttributeValue mAttributes[8]; + AttributeValue mAttributes[ESM::Attribute::Length]; DynamicStat mDynamic[3]; // health, magicka, fatigue Spells mSpells; ActiveSpells mActiveSpells; @@ -79,6 +89,8 @@ namespace MWMechanics // This may be necessary when the creature is in an inactive cell. std::vector mSummonGraveyard; + std::map mCorprusSpells; + protected: int mLevel; @@ -280,6 +292,12 @@ namespace MWMechanics /// assigned this function will return false). static void cleanup(); + + std::map & getCorprusSpells(); + + void addCorprusSpell(const std::string& sourceId, CorprusStats& stats); + + void removeCorprusSpell(const std::string& sourceId); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 88045ec44..5376ec86e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -289,6 +289,24 @@ namespace MWMechanics mWatched = ptr; } + void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) + { + auto& stats = actor.getClass().getCreatureStats (actor); + auto& corprusSpells = stats.getCorprusSpells(); + + auto corprusIt = corprusSpells.find(sourceId); + + if (corprusIt != corprusSpells.end()) + { + for (int i = 0; i < ESM::Attribute::Length; ++i) + { + MWMechanics::AttributeValue attr = stats.getAttribute(i); + attr.restore(corprusIt->second.mWorsenings[i]); + actor.getClass().getCreatureStats(actor).setAttribute(i, attr); + } + } + } + void MechanicsManager::update(float duration, bool paused) { if(!mWatched.isEmpty()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 6785f979f..86bc4c720 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -247,6 +247,8 @@ namespace MWMechanics virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; + virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) override; + private: bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set &playerFollowers); diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 0eaa531e2..ae7454f19 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -7,9 +7,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" +#include "actorutil.hpp" +#include "creaturestats.hpp" #include "magiceffects.hpp" +#include "stat.hpp" namespace MWMechanics { @@ -63,12 +67,6 @@ namespace MWMechanics } } } - - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - mEffects += it->second; - mSourcedEffects[it->first] += it->second; - } } bool Spells::hasSpell(const std::string &spell) const @@ -101,15 +99,6 @@ namespace MWMechanics } } - if (hasCorprusEffect(spell)) - { - CorprusStats corprus; - corprus.mWorsenings = 0; - corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - - mCorprusSpells[spell] = corprus; - } - SpellParams params; params.mEffectRands = random; mSpells.insert (std::make_pair (spell, params)); @@ -127,27 +116,6 @@ namespace MWMechanics const ESM::Spell* spell = getSpell(spellId); TContainer::iterator iter = mSpells.find (spell); - std::map::iterator corprusIt = mCorprusSpells.find(spell); - - // if it's corprus, remove negative and keep positive effects - if (corprusIt != mCorprusSpells.end()) - { - worsenCorprus(spell); - if (mPermanentSpellEffects.find(spell) != mPermanentSpellEffects.end()) - { - MagicEffects & effects = mPermanentSpellEffects[spell]; - for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end();) - { - const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->first.mId); - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) - effects.remove((effectIt++)->first); - else - ++effectIt; - } - } - mCorprusSpells.erase(corprusIt); - } - if (iter!=mSpells.end()) { mSpells.erase (iter); @@ -320,31 +288,6 @@ namespace MWMechanics } } - void Spells::worsenCorprus(const ESM::Spell* spell) - { - mCorprusSpells[spell].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod; - mCorprusSpells[spell].mWorsenings++; - - // update worsened effects - mPermanentSpellEffects[spell] = MagicEffects(); - int i=0; - for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i) - { - const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); - if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce)) - { - float random = 1.f; - if (mSpells[spell].mEffectRands.find(i) != mSpells[spell].mEffectRands.end()) - random = mSpells[spell].mEffectRands.at(i); - - float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - magnitude *= std::max(1, mCorprusSpells[spell].mWorsenings); - mPermanentSpellEffects[spell].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude)); - mSpellsChanged = true; - } - } - } - bool Spells::hasCorprusEffect(const ESM::Spell *spell) { for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) @@ -357,11 +300,6 @@ namespace MWMechanics return false; } - const std::map &Spells::getCorprusSpells() const - { - return mCorprusSpells; - } - void Spells::purgeEffect(int effectId) { for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt) @@ -412,7 +350,7 @@ namespace MWMechanics mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp(); } - void Spells::readState(const ESM::SpellState &state) + void Spells::readState(const ESM::SpellState &state, CreatureStats* creatureStats) { for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) { @@ -436,31 +374,64 @@ namespace MWMechanics mUsedPowers[spell] = MWWorld::TimeStamp(it->second); } - for (std::map >::const_iterator it = - state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) + for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) { const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); if (!spell) continue; - mPermanentSpellEffects[spell] = MagicEffects(); - for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) + CorprusStats stats; + + int worsening = state.mCorprusSpells.at(it->first).mWorsenings; + + for (int i=0; imEffects.mList) { - mPermanentSpellEffects[spell].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); + if (effect.mEffectID == ESM::MagicEffect::DrainAttribute) + stats.mWorsenings[effect.mAttribute] = worsening; } + stats.mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); + + creatureStats->addCorprusSpell(it->first, stats); } - mCorprusSpells.clear(); - for (std::map::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it) + mSpellsChanged = true; + + // Permanent effects are used only to keep the custom magnitude of corprus spells effects (after cure too), and only in old saves. Convert data to the new approach. + for (std::map >::const_iterator it = + state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) { - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); - if (!spell) // Discard unavailable corprus spells + const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first); + if (!spell) continue; - mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; - mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); - } - mSpellsChanged = true; + // Import data only for player, other actors should not suffer from corprus worsening. + MWWorld::Ptr player = getPlayer(); + if (creatureStats->getActorId() != player.getClass().getCreatureStats(player).getActorId()) + return; + + // Note: if target actor has the Restore attirbute effects, stats will be restored. + for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) + { + // Applied corprus effects are already in loaded stats modifiers + if (effectIt->mId == ESM::MagicEffect::FortifyAttribute) + { + AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); + attr.setModifier(attr.getModifier() - effectIt->mMagnitude); + attr.damage(-effectIt->mMagnitude); + creatureStats->setAttribute(effectIt->mArg, attr); + } + else if (effectIt->mId == ESM::MagicEffect::DrainAttribute) + { + AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); + attr.setModifier(attr.getModifier() + effectIt->mMagnitude); + attr.damage(effectIt->mMagnitude); + creatureStats->setAttribute(effectIt->mArg, attr); + } + } + } } void Spells::writeState(ESM::SpellState &state) const @@ -477,26 +448,5 @@ namespace MWMechanics for (std::map::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) state.mUsedPowers[it->first->mId] = it->second.toEsm(); - - for (std::map::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - std::vector effectList; - for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) - { - ESM::SpellState::PermanentSpellEffectInfo info; - info.mId = effectIt->first.mId; - info.mArg = effectIt->first.mArg; - info.mMagnitude = effectIt->second.getModifier(); - - effectList.push_back(info); - } - state.mPermanentSpellEffects[it->first->mId] = effectList; - } - - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) - { - state.mCorprusSpells[it->first->mId].mWorsenings = mCorprusSpells.at(it->first).mWorsenings; - state.mCorprusSpells[it->first->mId].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm(); - } } } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index e635f4f56..a4a599f8b 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -22,6 +22,8 @@ namespace ESM namespace MWMechanics { + class CreatureStats; + class MagicEffects; /// \brief Spell list @@ -33,7 +35,8 @@ namespace MWMechanics public: typedef const ESM::Spell* SpellKey; - struct SpellParams { + struct SpellParams + { std::map mEffectRands; // std::set mPurgedEffects; // indices of purged effects }; @@ -41,27 +44,14 @@ namespace MWMechanics typedef std::map TContainer; typedef TContainer::const_iterator TIterator; - struct CorprusStats - { - static const int sWorseningPeriod = 24; - - int mWorsenings; - MWWorld::TimeStamp mNextWorsening; - }; - private: TContainer mSpells; - // spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed) - std::map mPermanentSpellEffects; - // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different) std::string mSelectedSpell; std::map mUsedPowers; - std::map mCorprusSpells; - mutable bool mSpellsChanged; mutable MagicEffects mEffects; mutable std::map mSourcedEffects; @@ -73,9 +63,7 @@ namespace MWMechanics public: Spells(); - void worsenCorprus(const ESM::Spell* spell); static bool hasCorprusEffect(const ESM::Spell *spell); - const std::map & getCorprusSpells() const; void purgeEffect(int effectId); void purgeEffect(int effectId, const std::string & sourceId); @@ -128,7 +116,7 @@ namespace MWMechanics void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; - void readState (const ESM::SpellState& state); + void readState (const ESM::SpellState& state, CreatureStats* creatureStats); void writeState (ESM::SpellState& state) const; }; } diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index f53052a28..6a559a361 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -256,10 +256,17 @@ namespace MWMechanics void AttributeValue::damage(float damage) { - mDamage += std::min(damage, (float)getModified()); + float threshold = mBase + mModifier; + + if (mDamage + damage > threshold) + mDamage = threshold; + else + mDamage += damage; } void AttributeValue::restore(float amount) { + if (mDamage <= 0) return; + mDamage -= std::min(mDamage, amount); } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 45eafa960..8ba0cdcf2 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -499,6 +499,7 @@ namespace MWScript creatureStats.getSpells().purgeEffect(effect.first.mId); } + MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, id); creatureStats.getSpells().remove (id); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index d4358532c..8d2dfe6a4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -921,16 +921,16 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito } } -void MWWorld::InventoryStore::purgeEffect(short effectId) +void MWWorld::InventoryStore::purgeEffect(short effectId, bool wholeSpell) { for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) { if (*it != end()) - purgeEffect(effectId, (*it)->getCellRef().getRefId()); + purgeEffect(effectId, (*it)->getCellRef().getRefId(), wholeSpell); } } -void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) +void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell) { TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) @@ -963,6 +963,12 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sou if (effectIt->mEffectID != effectId) continue; + if (wholeSpell) + { + mPermanentMagicEffectMagnitudes.erase(sourceId); + return; + } + float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; magnitude *= params[i].mMultiplier; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e18132f4b..d597e5f30 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -203,10 +203,10 @@ namespace MWWorld void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); - void purgeEffect (short effectId); + void purgeEffect (short effectId, bool wholeSpell = false); ///< Remove a magic effect - void purgeEffect (short effectId, const std::string& sourceId); + void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false); ///< Remove a magic effect virtual void clear(); diff --git a/components/esm/attr.hpp b/components/esm/attr.hpp index c0a54c659..dd891a96d 100644 --- a/components/esm/attr.hpp +++ b/components/esm/attr.hpp @@ -21,7 +21,7 @@ struct Attribute Endurance = 5, Personality = 6, Luck = 7, - Length + Length = 8 }; AttributeID mId; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 2270bb6dd..9c6ee838e 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -134,6 +134,17 @@ void ESM::CreatureStats::load (ESMReader &esm) for (int i=0; i<4; ++i) mAiSettings[i].load(esm); } + + while (esm.isNextSub("CORP")) + { + std::string id = esm.getHString(); + + CorprusStats stats; + esm.getHNT(stats.mWorsenings, "WORS"); + esm.getHNT(stats.mNextWorsening, "TIME"); + + mCorprusSpells[id] = stats; + } } void ESM::CreatureStats::save (ESMWriter &esm) const @@ -218,6 +229,15 @@ void ESM::CreatureStats::save (ESMWriter &esm) const for (int i=0; i<4; ++i) mAiSettings[i].save(esm); } + + for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) + { + esm.writeHNString("CORP", it->first); + + const CorprusStats & stats = it->second; + esm.writeHNT("WORS", stats.mWorsenings); + esm.writeHNT("TIME", stats.mNextWorsening); + } } void ESM::CreatureStats::blank() @@ -245,4 +265,5 @@ void ESM::CreatureStats::blank() mDrawState = 0; mDeathAnimation = -1; mLevel = 1; + mCorprusSpells.clear(); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 8c69553a3..17ab4bbe7 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -9,6 +9,7 @@ #include "defs.hpp" +#include "attr.hpp" #include "spellstate.hpp" #include "activespells.hpp" #include "magiceffects.hpp" @@ -22,7 +23,13 @@ namespace ESM // format 0, saved games only struct CreatureStats { - StatState mAttributes[8]; + struct CorprusStats + { + int mWorsenings[Attribute::Length]; + TimeStamp mNextWorsening; + }; + + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; @@ -76,9 +83,9 @@ namespace ESM int mDrawState; signed char mDeathAnimation; ESM::TimeStamp mTimeOfDeath; - int mLevel; + std::map mCorprusSpells; SpellState mSpells; ActiveSpells mActiveSpells; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 6696ed478..cda411314 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 9; +int ESM::SavedGame::sCurrentFormat = 10; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/spellstate.cpp b/components/esm/spellstate.cpp index a21078e10..2eb1e7867 100644 --- a/components/esm/spellstate.cpp +++ b/components/esm/spellstate.cpp @@ -33,23 +33,36 @@ namespace ESM mSpells[id] = state; } + // Obsolete while (esm.isNextSub("PERM")) { std::string spellId = esm.getHString(); - std::vector permEffectList; - while (esm.isNextSub("EFID")) + + while (true) { + ESM_Context restorePoint = esm.getContext(); + + if (!esm.isNextSub("EFID")) + break; + PermanentSpellEffectInfo info; esm.getHT(info.mId); - esm.getHNT(info.mArg, "ARG_"); - esm.getHNT(info.mMagnitude, "MAGN"); + if (esm.isNextSub("BASE")) + { + esm.restoreContext(restorePoint); + return; + } + else + esm.getHNT(info.mArg, "ARG_"); + esm.getHNT(info.mMagnitude, "MAGN"); permEffectList.push_back(info); } mPermanentSpellEffects[spellId] = permEffectList; } + // Obsolete while (esm.isNextSub("CORP")) { std::string id = esm.getHString(); @@ -91,19 +104,6 @@ namespace ESM esm.writeHNT("PURG", *pIt); } - for (std::map >::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it) - { - esm.writeHNString("PERM", it->first); - - const std::vector & effects = it->second; - for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) - { - esm.writeHNT("EFID", effectIt->mId); - esm.writeHNT("ARG_", effectIt->mArg); - esm.writeHNT("MAGN", effectIt->mMagnitude); - } - } - for (std::map::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it) { esm.writeHNString("CORP", it->first); diff --git a/components/esm/spellstate.hpp b/components/esm/spellstate.hpp index ec613afab..55c57611a 100644 --- a/components/esm/spellstate.hpp +++ b/components/esm/spellstate.hpp @@ -29,15 +29,16 @@ namespace ESM float mMagnitude; }; - struct SpellParams { + struct SpellParams + { std::map mEffectRands; std::set mPurgedEffects; }; typedef std::map TContainer; TContainer mSpells; + // FIXME: obsolete, used only for old saves std::map > mPermanentSpellEffects; - std::map mCorprusSpells; std::map mUsedPowers; From 5468fcb29f0636bd82b56b4bb1f9a8a72f059098 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Dec 2018 15:18:33 +0400 Subject: [PATCH 12/34] Store attributes and skills values as floats (bug #4021) --- CHANGELOG.md | 1 + apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 10 ++--- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwgui/jailscreen.cpp | 4 +- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 8 ++-- apps/openmw/mwmechanics/alchemy.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 ++--- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 14 +++--- apps/openmw/mwmechanics/npcstats.cpp | 10 ++--- apps/openmw/mwmechanics/pickpocket.cpp | 4 +- apps/openmw/mwmechanics/repair.cpp | 6 +-- apps/openmw/mwmechanics/security.cpp | 4 +- apps/openmw/mwmechanics/spellresistance.cpp | 4 +- apps/openmw/mwmechanics/spellutil.cpp | 4 +- apps/openmw/mwmechanics/stat.cpp | 22 ++++----- apps/openmw/mwmechanics/stat.hpp | 22 ++++----- apps/openmw/mwscript/statsextensions.cpp | 24 +++++----- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 6 +-- components/compiler/extensions0.cpp | 12 ++--- components/esm/creaturestats.cpp | 3 +- components/esm/creaturestats.hpp | 2 +- components/esm/npcstats.cpp | 13 +++--- components/esm/npcstats.hpp | 2 +- components/esm/player.cpp | 5 ++- components/esm/player.hpp | 4 +- components/esm/savedgame.cpp | 2 +- components/esm/statstate.cpp | 45 ++++++++++++++----- components/esm/statstate.hpp | 2 +- 41 files changed, 153 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..298220699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4021: Attributes and skills are not stored as floats Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e649bba12..3f9bfb859 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -276,7 +276,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); - int armorSkill = actor.getClass().getSkill(actor, armorSkillType); + float armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 375b70dde..3feb25ca7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -605,7 +605,7 @@ namespace MWClass float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return static_cast(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5); + return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5; } int Creature::getServices(const MWWorld::ConstPtr &actor) const @@ -745,7 +745,7 @@ namespace MWClass throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } - int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const + float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 9be5c4272..3288c0d11 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -108,7 +108,7 @@ namespace MWClass virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bd61131bf..a007ad115 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -125,7 +125,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index df483962a..5efc96ca1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -127,8 +127,8 @@ namespace } // initial health - int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); - int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); + float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int multiplier = 3; @@ -1011,7 +1011,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); + float a = getSkill(ptr, ESM::Skill::Acrobatics); float b = 0.0f; if(a > 50.0f) { @@ -1136,7 +1136,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); + float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) @@ -1283,7 +1283,7 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { return getNpcStats(ptr).getSkill(skill).getModified(); } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3d63697fb..ae4f32d13 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -129,7 +129,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 4a89304b3..67124884f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -95,9 +95,9 @@ namespace MWGui MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) - value.setBase(std::min(100, value.getBase()+1)); + value.setBase(std::min(100.f, value.getBase()+1)); else - value.setBase(std::max(0, value.getBase()-1)); + value.setBase(std::max(0.f, value.getBase()-1)); } const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eb165254a..86f92db6f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -157,7 +157,7 @@ namespace MWGui mAttributeValues[i]->setEnabled(true); availableAttributes++; - int mult = pcStats.getLevelupAttributeMultiplier (i); + float mult = pcStats.getLevelupAttributeMultiplier (i); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 2a167be2d..b4de5cb50 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -22,7 +22,7 @@ namespace MWGui { MWWorld::Ptr player = MWMechanics::getPlayer(); mSourceModel = sourceModel; - int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); + float chance = player.getClass().getSkill(player, ESM::Skill::Sneak); mSourceModel->update(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 8998e3a80..4959396cc 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -159,7 +159,7 @@ namespace MWGui for (int i=0; ids[i]; ++i) if (ids[i]==id) { - setText (id, std::to_string(value.getModified())); + setText (id, std::to_string(static_cast(value.getModified()))); MyGUI::TextBox* box; getWidget(box, id); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index acc2ef72a..e4e4bae5a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -74,11 +74,11 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); // NPC can train you in his best 3 skills - std::vector< std::pair > skills; + std::vector< std::pair > skills; for (int i=0; i& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); health = 0.1f * endurance; float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); @@ -765,7 +765,7 @@ namespace MWMechanics { CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); - int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); + float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float base = 1.f; if (ptr == getPlayer()) @@ -844,7 +844,7 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) @@ -871,7 +871,7 @@ namespace MWMechanics return; // Restore fatigue - int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); + float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b490db436..116937fcd 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); + float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a332a10c..e4f870ed0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2128,7 +2128,7 @@ void CharacterController::update(float duration, bool animationOnly) cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } - const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); + const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { if (!godmode) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9698892e4..183845b8c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -101,7 +101,7 @@ namespace MWMechanics blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); - int attackerSkill = 0; + float attackerSkill = 0; if (weapon.isEmpty()) attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); else diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a64fb087c..0f11b8b2e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -126,7 +126,7 @@ namespace MWMechanics return mMagicEffects; } - void CreatureStats::setAttribute(int index, int base) + void CreatureStats::setAttribute(int index, float base) { AttributeValue current = getAttribute(index); current.setBase(base); @@ -152,10 +152,10 @@ namespace MWMechanics index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) { - int strength = getAttribute(ESM::Attribute::Strength).getModified(); - int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); - int agility = getAttribute(ESM::Attribute::Agility).getModified(); - int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); + float strength = getAttribute(ESM::Attribute::Strength).getModified(); + float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); + float agility = getAttribute(ESM::Attribute::Agility).getModified(); + float endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5a5dd3f12..b35c1e3b6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -138,7 +138,7 @@ namespace MWMechanics void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base - void setAttribute(int index, int base); + void setAttribute(int index, float base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5376ec86e..6933907db 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -688,10 +688,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); + float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); + float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); @@ -1621,8 +1621,8 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); - int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); - int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { @@ -1645,10 +1645,10 @@ namespace MWMechanics float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); - int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); - int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); - int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); + float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ee48ea7d5..1e9003a2f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { - int base = getSkill (skillIndex).getBase(); + float base = getSkill (skillIndex).getBase(); - if (base >= 100) + if (base >= 100.f) return; base += 1; @@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp() for (int i=0; i(stats.getAttribute(ESM::Attribute::Agility).getModified()); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index faa0e3b09..389d00d85 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -32,9 +32,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); float fatigueTerm = stats.getFatigueTerm(); - int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); - int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); + float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index ab286cbee..001375feb 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -19,8 +19,8 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); - mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index a187600fb..1edf14091 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -40,8 +40,8 @@ namespace MWMechanics float resistance = getEffectResistanceAttribute(effectId, magicEffects); - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index bb4953e48..8b2f5c46c 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -94,8 +94,8 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 6a559a361..7f71cf9b1 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -227,29 +227,29 @@ namespace MWMechanics } AttributeValue::AttributeValue() : - mBase(0), mModifier(0), mDamage(0) + mBase(0.f), mModifier(0.f), mDamage(0.f) { } - int AttributeValue::getModified() const + float AttributeValue::getModified() const { - return std::max(0, mBase - (int) mDamage + mModifier); + return std::max(0.f, mBase - mDamage + mModifier); } - int AttributeValue::getBase() const + float AttributeValue::getBase() const { return mBase; } - int AttributeValue::getModifier() const + float AttributeValue::getModifier() const { return mModifier; } - void AttributeValue::setBase(int base) + void AttributeValue::setBase(float base) { mBase = base; } - void AttributeValue::setModifier(int mod) + void AttributeValue::setModifier(float mod) { mModifier = mod; } @@ -275,14 +275,14 @@ namespace MWMechanics return mDamage; } - void AttributeValue::writeState (ESM::StatState& state) const + void AttributeValue::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModifier; state.mDamage = mDamage; } - void AttributeValue::readState (const ESM::StatState& state) + void AttributeValue::readState (const ESM::StatState& state) { mBase = state.mBase; mModifier = state.mMod; @@ -303,13 +303,13 @@ namespace MWMechanics mProgress = progress; } - void SkillValue::writeState (ESM::StatState& state) const + void SkillValue::writeState (ESM::StatState& state) const { AttributeValue::writeState (state); state.mProgress = mProgress; } - void SkillValue::readState (const ESM::StatState& state) + void SkillValue::readState (const ESM::StatState& state) { AttributeValue::readState (state); mProgress = state.mProgress; diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 751b80b9c..5f49da48e 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -122,20 +122,20 @@ namespace MWMechanics class AttributeValue { - int mBase; - int mModifier; + float mBase; + float mModifier; float mDamage; // needs to be float to allow continuous damage public: AttributeValue(); - int getModified() const; - int getBase() const; - int getModifier() const; + float getModified() const; + float getBase() const; + float getModifier() const; - void setBase(int base); + void setBase(float base); - void setModifier(int mod); + void setModifier(float mod); // Maximum attribute damage is limited to the modified value. // Note: I think MW applies damage directly to mModified, since you can also @@ -145,8 +145,8 @@ namespace MWMechanics float getDamage() const; - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -157,8 +157,8 @@ namespace MWMechanics float getProgress() const; void setProgress(float progress); - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8ba0cdcf2..37751c6d4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -95,7 +95,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = + Interpreter::Type_Float value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) @@ -118,7 +118,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); @@ -140,7 +140,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() @@ -155,9 +155,9 @@ namespace MWScript return; if (value < 0) - attribute.setBase(std::max(0, attribute.getBase() + value)); + attribute.setBase(std::max(0.f, attribute.getBase() + value)); else - attribute.setBase(std::min(100, attribute.getBase() + value)); + attribute.setBase(std::min(100.f, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -345,7 +345,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); + Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } @@ -364,7 +364,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); @@ -386,7 +386,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::SkillValue &skill = ptr.getClass() @@ -396,14 +396,14 @@ namespace MWScript if (value == 0) return; - if (((skill.getBase() <= 0) && (value < 0)) - || ((skill.getBase() >= 100) && (value > 0))) + if (((skill.getBase() <= 0.f) && (value < 0.f)) + || ((skill.getBase() >= 100.f) && (value > 0.f))) return; if (value < 0) - skill.setBase(std::max(0, skill.getBase() + value)); + skill.setBase(std::max(0.f, skill.getBase() + value)); else - skill.setBase(std::min(100, skill.getBase() + value)); + skill.setBase(std::min(100.f, skill.getBase() + value)); } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 07981bf2a..d7ee59ee2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -421,7 +421,7 @@ namespace MWWorld return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } - int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fb816d810..fd679c43f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -321,7 +321,7 @@ namespace MWWorld bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index a2bd84953..ada211470 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots // rate weapon for (int i = 0; i < static_cast(weaponSkillsLength); ++i) { - int max = 0; + float max = 0; int maxWeaponSkill = -1; for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + float skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { max = skillValue; @@ -399,7 +399,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c67af29c2..60b4a7451 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -423,13 +423,13 @@ namespace Compiler for (int i=0; i mAttributes[Attribute::Length]; + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index c5fa2a09e..277335e8c 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -31,8 +31,9 @@ void ESM::NpcStats::load (ESMReader &esm) mDisposition = 0; esm.getHNOT (mDisposition, "DISP"); + bool intFallback = esm.getFormat() < 11; for (int i=0; i<27; ++i) - mSkills[i].load (esm); + mSkills[i].load (esm, intFallback); mWerewolfDeprecatedData = false; if (esm.getFormat() < 8 && esm.peekNextSub("STBA")) @@ -40,17 +41,17 @@ void ESM::NpcStats::load (ESMReader &esm) // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value mWerewolfDeprecatedData = true; - std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); + std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); for (int i=0; i<27; ++i) { - ESM::StatState skill; - skill.load(esm); + ESM::StatState skill; + skill.load(esm, intFallback); skills.push_back(skill); } int i=0; - for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) + for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) { if (i%2 == 1) it = skills.erase(it); @@ -68,7 +69,7 @@ void ESM::NpcStats::load (ESMReader &esm) { ESM::StatState dummy; for (int i=0; i<8; ++i) - dummy.load(esm); + dummy.load(esm, intFallback); mWerewolfDeprecatedData = true; } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 467a099ce..3ad94b543 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -31,7 +31,7 @@ namespace ESM std::map mFactions; // lower case IDs int mDisposition; - StatState mSkills[27]; + StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 571a10a8c..3850390e9 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -43,12 +43,13 @@ void ESM::Player::load (ESMReader &esm) checkPrevItems = false; } + bool intFallback = esm.getFormat() < 11; if (esm.hasMoreSubs()) { for (int i=0; i mSaveAttributes[ESM::Attribute::Length]; - StatState mSaveSkills[ESM::Skill::Length]; + StatState mSaveAttributes[ESM::Attribute::Length]; + StatState mSaveSkills[ESM::Skill::Length]; typedef std::map PreviousItems; // previous equipped items, needed for bound spells PreviousItems mPreviousItems; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..063ae86ed 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 10; +int ESM::SavedGame::sCurrentFormat = 11; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/statstate.cpp b/components/esm/statstate.cpp index c17bedd81..b9ddc3efd 100644 --- a/components/esm/statstate.cpp +++ b/components/esm/statstate.cpp @@ -9,19 +9,44 @@ namespace ESM StatState::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {} template - void StatState::load(ESMReader &esm) + void StatState::load(ESMReader &esm, bool intFallback) { - esm.getHNT(mBase, "STBA"); + // We changed stats values from integers to floats; ensure backwards compatibility + if (intFallback) + { + int base = 0; + esm.getHNT(base, "STBA"); + mBase = static_cast(base); - mMod = 0; - esm.getHNOT(mMod, "STMO"); - mCurrent = 0; - esm.getHNOT(mCurrent, "STCU"); + int mod = 0; + esm.getHNOT(mod, "STMO"); + mMod = static_cast(mod); - // mDamage was changed to a float; ensure backwards compatibility - T oldDamage = 0; - esm.getHNOT(oldDamage, "STDA"); - mDamage = static_cast(oldDamage); + int current = 0; + esm.getHNOT(current, "STCU"); + mCurrent = static_cast(current); + + // mDamage was changed to a float; ensure backwards compatibility + int oldDamage = 0; + esm.getHNOT(oldDamage, "STDA"); + mDamage = static_cast(oldDamage); + } + else + { + mBase = 0; + esm.getHNT(mBase, "STBA"); + + mMod = 0; + esm.getHNOT(mMod, "STMO"); + + mCurrent = 0; + esm.getHNOT(mCurrent, "STCU"); + + mDamage = 0; + esm.getHNOT(mDamage, "STDF"); + + mProgress = 0; + } esm.getHNOT(mDamage, "STDF"); diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 47aeb0331..d81d24a61 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -20,7 +20,7 @@ namespace ESM StatState(); - void load (ESMReader &esm); + void load (ESMReader &esm, bool intFallback = false); void save (ESMWriter &esm) const; }; } From d35ccc39c6f95b0957e3e8b943f7694559b8f3c5 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 1 Jun 2020 13:51:56 +0200 Subject: [PATCH 13/34] Fix build tests with double precision bullet --- apps/openmw/CMakeLists.txt | 4 ---- components/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..a6bd85b1d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,10 +15,6 @@ set(GAME_HEADER engine.hpp ) -if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) -endif() - source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f626fe714..507b25cd1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -287,5 +287,5 @@ endif() set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) + target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION) endif() From 86c1d0f4beffe86f367a74fd32b53d9fca5a4343 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:36:55 +0000 Subject: [PATCH 14/34] Warn about fake stub Python --- CI/before_script.msvc.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2f40aef9c..8f70e5369 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -13,7 +13,16 @@ MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } -command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; } + +MISSINGPYTHON=0 +if ! command -v python >/dev/null 2>&1; then + echo "Warning: Python is not on the path, automatic Qt installation impossible." + MISSINGPYTHON=1 +elif ! python --version >/dev/null 2>&1; then + echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible." + echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings." + MISSINGPYTHON=1 +fi if [ $MISSINGTOOLS -ne 0 ]; then wrappedExit 1 @@ -745,6 +754,11 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then + if [ $MISSINGTOOLS -ne 0 ]; then + echo "Can't be automatically installed without Python." + wrappedExit 1 + fi + pushd "$DEPS" > /dev/null if ! [ -d 'aqt-venv' ]; then echo " Creating Virtualenv for aqt..." From 6e267e398e642e8cd200dd6cbcd2eb7c85ecaeb2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:38:08 +0000 Subject: [PATCH 15/34] Fix copy-paste snafu --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 8f70e5369..75bde1f81 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -754,7 +754,7 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - if [ $MISSINGTOOLS -ne 0 ]; then + if [ $MISSINGPYTHON -ne 0 ]; then echo "Can't be automatically installed without Python." wrappedExit 1 fi From da8ea9d8c7a94326307ec1122572d54caee33c09 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 2 Jun 2020 21:30:46 +0200 Subject: [PATCH 16/34] Mark not changing AiPackages fields as const --- apps/openmw/mwmechanics/aiactivate.hpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 +- apps/openmw/mwmechanics/aicast.cpp | 18 ++++++++--- apps/openmw/mwmechanics/aicast.hpp | 8 ++--- apps/openmw/mwmechanics/aiescort.cpp | 13 +++----- apps/openmw/mwmechanics/aiescort.hpp | 16 ++++----- apps/openmw/mwmechanics/aiface.hpp | 3 +- apps/openmw/mwmechanics/aifollow.cpp | 13 ++++---- apps/openmw/mwmechanics/aifollow.hpp | 16 ++++----- apps/openmw/mwmechanics/aitravel.hpp | 8 ++--- apps/openmw/mwmechanics/aiwander.cpp | 43 +++++++++++++------------ apps/openmw/mwmechanics/aiwander.hpp | 12 +++---- 12 files changed, 80 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a9e3d6d8..5a96f4cdb 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -34,7 +34,7 @@ namespace MWMechanics void writeState(ESM::AiSequence::AiSequence& sequence) const final; private: - std::string mObjectId; + const std::string mObjectId; }; } #endif // GAME_MWMECHANICS_AIACTIVATE_H diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index cc02c4de1..fdbf7ebc7 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -33,7 +33,7 @@ namespace MWMechanics private: float mDuration; - MWWorld::ConstPtr mDoorPtr; + const MWWorld::ConstPtr mDoorPtr; osg::Vec3f mLastPos; int mDirection; diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index de61851cd..cc4c03bf1 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -10,12 +10,22 @@ #include "creaturestats.hpp" #include "steering.hpp" +namespace MWMechanics +{ + namespace + { + float getInitialDistance(const std::string& spellId) + { + ActionSpell action = ActionSpell(spellId); + bool isRanged; + return action.getCombatRange(isRanged); + } + } +} + MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) - : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0) + : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId)) { - ActionSpell action = ActionSpell(spellId); - bool isRanged; - mDistance = action.getCombatRange(isRanged); } bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 21b629f24..cdf7db2bf 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -27,11 +27,11 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - std::string mTargetId; - std::string mSpellId; + const std::string mTargetId; + const std::string mSpellId; bool mCasting; - bool mManual; - float mDistance; + const bool mManual; + const float mDistance; }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5e1d38331..216547f58 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -26,7 +26,6 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z) @@ -35,24 +34,20 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) : mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) - , mMaxDist(450) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(escort->mRemainingDuration > 0) , mRemainingDuration(escort->mRemainingDuration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mTargetActorRefId = escort->mTargetId; mTargetActorId = escort->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 9f5ac2f42..c12de1fac 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -45,16 +45,16 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - std::string mCellId; - float mX; - float mY; - float mZ; - float mMaxDist; - float mDuration; // In hours + const std::string mCellId; + const float mX; + const float mY; + const float mZ; + float mMaxDist = 450; + const float mDuration; // In hours float mRemainingDuration; // In hours - int mCellX; - int mCellY; + const int mCellX; + const int mCellY; }; } #endif diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index ce1c9847b..516dd18dc 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -20,7 +20,8 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - float mTargetX, mTargetY; + const float mTargetX; + const float mTargetY; }; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index bffa238d5..eb5b595ab 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -58,18 +58,17 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration) + : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(follow->mRemainingDuration) + , mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = follow->mTargetId; mTargetActorId = follow->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 39a10294b..865f4171b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -86,16 +86,16 @@ namespace MWMechanics private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ - bool mAlwaysFollow; - bool mCommanded; - float mDuration; // Hours + const bool mAlwaysFollow; + const bool mCommanded; + const float mDuration; // Hours float mRemainingDuration; // Hours - float mX; - float mY; - float mZ; - std::string mCellId; + const float mX; + const float mY; + const float mZ; + const std::string mCellId; bool mActive; // have we spotted the target? - int mFollowIndex; + const int mFollowIndex; static int mFollowIndexCounter; }; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index beaf2819f..4f785e237 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -37,11 +37,11 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - float mX; - float mY; - float mZ; + const float mX; + const float mY; + const float mZ; - bool mHidden; + const bool mHidden; }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 50a46432b..584131bbe 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,5 +1,7 @@ #include "aiwander.hpp" +#include + #include #include #include @@ -33,6 +35,8 @@ namespace MWMechanics // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; + static const std::size_t MAX_IDLE_SIZE = 8; + const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), @@ -94,25 +98,28 @@ namespace MWMechanics { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } + + std::vector getInitialIdle(const std::vector& idle) + { + std::vector result(MAX_IDLE_SIZE, 0); + std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin()); + return result; + } + + std::vector getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE]) + { + return std::vector(std::begin(idle), std::end(idle)); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): - mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), + mDistance(std::max(0, distance)), + mDuration(std::max(0, duration)), + mRemainingDuration(duration), mTimeOfDay(timeOfDay), + mIdle(getInitialIdle(idle)), mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { - mIdle.resize(8, 0); - init(); - } - - void AiWander::init() - { - // NOTE: mDistance and mDuration must be set already - - if(mDistance < 0) - mDistance = 0; - if(mDuration < 0) - mDuration = 0; } /* @@ -235,7 +242,6 @@ namespace MWMechanics stopWalking(actor); // Reset package so it can be used again mRemainingDuration=mDuration; - init(); return true; } @@ -879,10 +885,11 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(wander->mData.mDistance) - , mDuration(wander->mData.mDuration) + : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) + , mIdle(getInitialIdle(wander->mData.mIdle)) , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) @@ -891,11 +898,7 @@ namespace MWMechanics { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; - for (int i=0; i<8; ++i) - mIdle.push_back(wander->mData.mIdle[i]); if (mRemainingDuration <= 0 || mRemainingDuration >= 24) mRemainingDuration = mDuration; - - init(); } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index bb5872eef..8eb735205 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -114,8 +114,6 @@ namespace MWMechanics } private: - // NOTE: mDistance and mDuration must be set already - void init(); void stopWalking(const MWWorld::Ptr& actor); /// Have the given actor play an idle animation @@ -136,12 +134,12 @@ namespace MWMechanics bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); - int mDistance; // how far the actor can wander from the spawn point - int mDuration; + const int mDistance; // how far the actor can wander from the spawn point + const int mDuration; float mRemainingDuration; - int mTimeOfDay; - std::vector mIdle; - bool mRepeat; + const int mTimeOfDay; + const std::vector mIdle; + const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell From 81805b726337c8c2da9c104af72ecb428a86de0b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 3 Jun 2020 11:32:28 +0400 Subject: [PATCH 17/34] Introduce a separate class to control world date and time --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 19 +- apps/openmw/mwgui/waitdialog.cpp | 9 +- apps/openmw/mwstate/statemanagerimp.cpp | 6 +- apps/openmw/mwworld/datetimemanager.cpp | 227 ++++++++++++++++++++++++ apps/openmw/mwworld/datetimemanager.hpp | 43 +++++ apps/openmw/mwworld/globals.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 197 ++++---------------- apps/openmw/mwworld/worldimp.hpp | 30 +--- components/esm/defs.hpp | 8 + components/esm/savedgame.cpp | 1 - components/esm/savedgame.hpp | 12 +- 12 files changed, 338 insertions(+), 219 deletions(-) create mode 100644 apps/openmw/mwworld/datetimemanager.cpp create mode 100644 apps/openmw/mwworld/datetimemanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..9072deb48 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,7 +71,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager - cellpreloader + cellpreloader datetimemanager ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6bf4cbaae..627e2f2b7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -48,6 +48,7 @@ namespace ESM struct EffectList; struct CreatureLevList; struct ItemLevList; + struct TimeStamp; } namespace MWRender @@ -204,24 +205,14 @@ namespace MWBase virtual void advanceTime (double hours, bool incremental = false) = 0; ///< Advance in-game time. - virtual void setHour (double hour) = 0; - ///< Set in-game time hour. - - virtual void setMonth (int month) = 0; - ///< Set in-game time month. - - virtual void setDay (int day) = 0; - ///< Set in-game time day. - - virtual int getDay() const = 0; - virtual int getMonth() const = 0; - virtual int getYear() const = 0; - virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0; + ///< Return current in-game date and time. virtual bool toggleSky() = 0; ///< \return Resulting mode diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 64f912298..18cc187c1 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -150,11 +150,10 @@ namespace MWGui if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - std::string dateTimeText = - MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) - + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); - + ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp(); + int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay(); + std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"; + std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour); mDateTimeText->setCaptionWithReplacing (dateTimeText); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index db83f72c1..9974b8f16 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); + profile.mInGameTime = world.getEpochTimeStamp(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; diff --git a/apps/openmw/mwworld/datetimemanager.cpp b/apps/openmw/mwworld/datetimemanager.cpp new file mode 100644 index 000000000..0894c974d --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.cpp @@ -0,0 +1,227 @@ +#include "datetimemanager.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "esmstore.hpp" +#include "globals.hpp" +#include "timestamp.hpp" + +namespace +{ + static int getDaysPerMonth(int month) + { + switch (month) + { + case 0: return 31; + case 1: return 28; + case 2: return 31; + case 3: return 30; + case 4: return 31; + case 5: return 30; + case 6: return 31; + case 7: return 31; + case 8: return 30; + case 9: return 31; + case 10: return 30; + case 11: return 31; + } + + throw std::runtime_error ("month out of range"); + } +} + +namespace MWWorld +{ + void DateTimeManager::setup(Globals& globalVariables) + { + mGameHour = globalVariables["gamehour"].getFloat(); + mDaysPassed = globalVariables["dayspassed"].getInteger(); + mDay = globalVariables["day"].getInteger(); + mMonth = globalVariables["month"].getInteger(); + mYear = globalVariables["year"].getInteger(); + mTimeScale = globalVariables["timescale"].getFloat(); + } + + void DateTimeManager::setHour(double hour) + { + if (hour < 0) + hour = 0; + + int days = static_cast(hour / 24); + hour = std::fmod(hour, 24); + mGameHour = static_cast(hour); + + if (days > 0) + setDay(days + mDay); + } + + void DateTimeManager::setDay(int day) + { + if (day < 1) + day = 1; + + int month = mMonth; + while (true) + { + int days = getDaysPerMonth(month); + if (day <= days) + break; + + if (month < 11) + { + ++month; + } + else + { + month = 0; + mYear++; + } + + day -= days; + } + + mDay = day; + mMonth = month; + } + + TimeStamp DateTimeManager::getTimeStamp() const + { + return TimeStamp(mGameHour, mDaysPassed); + } + + float DateTimeManager::getTimeScaleFactor() const + { + return mTimeScale; + } + + ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const + { + ESM::EpochTimeStamp timeStamp; + timeStamp.mGameHour = mGameHour; + timeStamp.mDay = mDay; + timeStamp.mMonth = mMonth; + timeStamp.mYear = mYear; + return timeStamp; + } + + void DateTimeManager::setMonth(int month) + { + if (month < 0) + month = 0; + + int years = month / 12; + month = month % 12; + + int days = getDaysPerMonth(month); + if (mDay > days) + mDay = days; + + mMonth = month; + + if (years > 0) + mYear += years; + } + + void DateTimeManager::advanceTime(double hours, Globals& globalVariables) + { + hours += mGameHour; + setHour(hours); + + int days = static_cast(hours / 24); + if (days > 0) + mDaysPassed += days; + + globalVariables["gamehour"].setFloat(mGameHour); + globalVariables["dayspassed"].setInteger(mDaysPassed); + globalVariables["day"].setInteger(mDay); + globalVariables["month"].setInteger(mMonth); + globalVariables["year"].setInteger(mYear); + } + + std::string DateTimeManager::getMonthName(int month) const + { + if (month == -1) + month = mMonth; + + const int months = 12; + if (month < 0 || month >= months) + return std::string(); + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get().find(monthNames[month]); + return setting->mValue.getString(); + } + + bool DateTimeManager::updateGlobalFloat(const std::string& name, float value) + { + if (name=="gamehour") + { + setHour(value); + return true; + } + else if (name=="day") + { + setDay(static_cast(value)); + return true; + } + else if (name=="month") + { + setMonth(static_cast(value)); + return true; + } + else if (name=="year") + { + mYear = static_cast(value); + } + else if (name=="timescale") + { + mTimeScale = value; + } + else if (name=="dayspassed") + { + mDaysPassed = static_cast(value); + } + + return false; + } + + bool DateTimeManager::updateGlobalInt(const std::string& name, int value) + { + if (name=="gamehour") + { + setHour(static_cast(value)); + return true; + } + else if (name=="day") + { + setDay(value); + return true; + } + else if (name=="month") + { + setMonth(value); + return true; + } + else if (name=="year") + { + mYear = value; + } + else if (name=="timescale") + { + mTimeScale = static_cast(value); + } + else if (name=="dayspassed") + { + mDaysPassed = value; + } + + return false; + } +} diff --git a/apps/openmw/mwworld/datetimemanager.hpp b/apps/openmw/mwworld/datetimemanager.hpp new file mode 100644 index 000000000..b460be746 --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.hpp @@ -0,0 +1,43 @@ +#ifndef GAME_MWWORLD_DATETIMEMANAGER_H +#define GAME_MWWORLD_DATETIMEMANAGER_H + +#include + +namespace ESM +{ + struct EpochTimeStamp; +} + +namespace MWWorld +{ + class Globals; + class TimeStamp; + + class DateTimeManager + { + int mDaysPassed = 0; + int mDay = 0; + int mMonth = 0; + int mYear = 0; + float mGameHour = 0.f; + float mTimeScale = 0.f; + + void setHour(double hour); + void setDay(int day); + void setMonth(int month); + + public: + std::string getMonthName(int month) const; + TimeStamp getTimeStamp() const; + ESM::EpochTimeStamp getEpochTimeStamp() const; + float getTimeScaleFactor() const; + + void advanceTime(double hours, Globals& globalVariables); + + void setup(Globals& globalVariables); + bool updateGlobalInt(const std::string& name, int value); + bool updateGlobalFloat(const std::string& name, float value); + }; +} + +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 69ec5563b..8a481334e 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include #include "esmstore.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index aee98e7ea..c51266bab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -60,6 +60,7 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/constants.hpp" +#include "datetimemanager.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" @@ -121,33 +122,11 @@ namespace MWWorld LoadersContainer mLoaders; }; - int World::getDaysPerMonth (int month) const - { - switch (month) - { - case 0: return 31; - case 1: return 28; - case 2: return 31; - case 3: return 30; - case 4: return 31; - case 5: return 30; - case 6: return 31; - case 7: return 31; - case 8: return 30; - case 9: return 31; - case 10: return 30; - case 11: return 31; - } - - throw std::runtime_error ("month out of range"); - } - void World::adjustSky() { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); - + updateSkyDate(); mRendering->setSkyEnabled(true); } else @@ -193,6 +172,8 @@ namespace MWWorld if (mEsm[0].getFormat() == 0) ensureNeededRecords(); + mCurrentDate.reset(new DateTimeManager()); + fillGlobalVariables(); mStore.setUp(true); @@ -227,13 +208,7 @@ namespace MWWorld void World::fillGlobalVariables() { mGlobalVariables.fill (mStore); - - mGameHour = &mGlobalVariables["gamehour"]; - mDaysPassed = &mGlobalVariables["dayspassed"]; - mDay = &mGlobalVariables["day"]; - mMonth = &mGlobalVariables["month"]; - mYear = &mGlobalVariables["year"]; - mTimeScale = &mGlobalVariables["timescale"]; + mCurrentDate->setup(mGlobalVariables); } void World::startNewGame (bool bypass) @@ -310,6 +285,7 @@ namespace MWWorld mPhysics->toggleCollisionMode(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + mCurrentDate->setup(mGlobalVariables); } void World::clear() @@ -639,26 +615,20 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay (value); - else if (name=="month") - setMonth (value); - else - mGlobalVariables[name].setInteger (value); + bool dateUpdated = mCurrentDate->updateGlobalInt(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay(static_cast(value)); - else if (name=="month") - setMonth(static_cast(value)); - else - mGlobalVariables[name].setFloat (value); + bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setFloat(value); } int World::getGlobalInt (const std::string& name) const @@ -676,6 +646,11 @@ namespace MWWorld return mGlobalVariables.getType (name); } + std::string World::getMonthName (int month) const + { + return mCurrentDate->getMonthName(month); + } + std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) @@ -894,130 +869,29 @@ namespace MWWorld } mWeatherManager->advanceTime (hours, incremental); + mCurrentDate->advanceTime(hours, mGlobalVariables); + updateSkyDate(); if (!incremental) { mRendering->notifyWorldSpaceChanged(); mProjectileManager->clear(); } - - hours += mGameHour->getFloat(); - - setHour (hours); - - int days = static_cast(hours / 24); - - if (days>0) - mDaysPassed->setInteger ( - days + mDaysPassed->getInteger()); - } - - void World::setHour (double hour) - { - if (hour<0) - hour = 0; - - int days = static_cast(hour / 24); - - hour = std::fmod (hour, 24); - - mGameHour->setFloat(static_cast(hour)); - - if (days>0) - setDay (days + mDay->getInteger()); - } - - void World::setDay (int day) - { - if (day<1) - day = 1; - - int month = mMonth->getInteger(); - - while (true) - { - int days = getDaysPerMonth (month); - if (day<=days) - break; - - if (month<11) - { - ++month; - } - else - { - month = 0; - mYear->setInteger(mYear->getInteger()+1); - } - - day -= days; - } - - mDay->setInteger(day); - mMonth->setInteger(month); - - mRendering->skySetDate(day, month); - } - - void World::setMonth (int month) - { - if (month<0) - month = 0; - - int years = month / 12; - month = month % 12; - - int days = getDaysPerMonth (month); - - if (mDay->getInteger()>days) - mDay->setInteger (days); - - mMonth->setInteger (month); - - if (years>0) - mYear->setInteger (years+mYear->getInteger()); - - mRendering->skySetDate (mDay->getInteger(), month); } - int World::getDay() const - { - return mDay->getInteger(); - } - - int World::getMonth() const - { - return mMonth->getInteger(); - } - - int World::getYear() const + float World::getTimeScaleFactor() const { - return mYear->getInteger(); + return mCurrentDate->getTimeScaleFactor(); } - std::string World::getMonthName (int month) const + TimeStamp World::getTimeStamp() const { - if (month==-1) - month = getMonth(); - - const int months = 12; - - if (month<0 || month>=months) - return ""; - - static const char *monthNames[months] = - { - "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", - "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", - "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" - }; - - return mStore.get().find (monthNames[month])->mValue.getString(); + return mCurrentDate->getTimeStamp(); } - TimeStamp World::getTimeStamp() const + ESM::EpochTimeStamp World::getEpochTimeStamp() const { - return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); + return mCurrentDate->getEpochTimeStamp(); } bool World::toggleSky() @@ -1042,11 +916,6 @@ namespace MWWorld mRendering->skySetMoonColour (red); } - float World::getTimeScaleFactor() const - { - return mTimeScale->getFloat(); - } - void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { mPhysics->clearQueuedMovement(); @@ -1089,6 +958,8 @@ namespace MWWorld changeToExteriorCell (position, adjustPlayerPos, changeEvent); else changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent); + + mCurrentDate->setup(mGlobalVariables); } void World::markCellAsUnchanged() @@ -3968,4 +3839,10 @@ namespace MWWorld mNavigator->reportStats(frameNumber, stats); mPhysics->reportStats(frameNumber, stats); } + + void World::updateSkyDate() + { + ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp(); + mRendering->skySetDate(currentDate.mDay, currentDate.mMonth); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fa2f7778b..aeb6bbae4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -69,6 +69,7 @@ namespace MWPhysics namespace MWWorld { + class DateTimeManager; class WeatherManager; class Player; class ProjectileManager; @@ -85,13 +86,6 @@ namespace MWWorld LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - ESM::Variant* mGameHour; - ESM::Variant* mDaysPassed; - ESM::Variant* mDay; - ESM::Variant* mMonth; - ESM::Variant* mYear; - ESM::Variant* mTimeScale; - Cells mCells; std::string mCurrentWorldSpace; @@ -102,6 +96,7 @@ namespace MWWorld std::unique_ptr mRendering; std::unique_ptr mWorldScene; std::unique_ptr mWeatherManager; + std::unique_ptr mCurrentDate; std::shared_ptr mProjectileManager; bool mSky; @@ -139,7 +134,6 @@ namespace MWWorld World& operator= (const World&); void updateWeather(float duration, bool paused = false); - int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); @@ -173,6 +167,8 @@ namespace MWWorld void fillGlobalVariables(); + void updateSkyDate(); + /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths @@ -318,24 +314,14 @@ namespace MWWorld void advanceTime (double hours, bool incremental = false) override; ///< Advance in-game time. - void setHour (double hour) override; - ///< Set in-game time hour. - - void setMonth (int month) override; - ///< Set in-game time month. - - void setDay (int day) override; - ///< Set in-game time day. - - int getDay() const override; - int getMonth() const override; - int getYear() const override; - std::string getMonthName (int month = -1) const override; ///< Return name of month (-1: current month) TimeStamp getTimeStamp() const override; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + ESM::EpochTimeStamp getEpochTimeStamp() const override; + ///< Return current in-game date and time. bool toggleSky() override; ///< \return Resulting mode diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 0f0478faa..6c0c33526 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -14,6 +14,14 @@ struct TimeStamp int mDay; }; +struct EpochTimeStamp +{ + float mGameHour; + int mDay; + int mMonth; + int mYear; +}; + // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef uint32_t Color; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..b5bf6e406 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -2,7 +2,6 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; int ESM::SavedGame::sCurrentFormat = 10; diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index aa0429657..26efae824 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -4,6 +4,8 @@ #include #include +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -17,14 +19,6 @@ namespace ESM static int sCurrentFormat; - struct TimeStamp - { - float mGameHour; - int mDay; - int mMonth; - int mYear; - }; - std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; @@ -36,7 +30,7 @@ namespace ESM std::string mPlayerClassName; std::string mPlayerCell; - TimeStamp mInGameTime; + EpochTimeStamp mInGameTime; double mTimePlayed; std::string mDescription; std::vector mScreenshot; // raw jpg-encoded data From 5b34ef224bfe31e07c32c343f34e1f9a490f72dc Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 21:08:39 +0200 Subject: [PATCH 18/34] Replace AiPackage virtual methods by options --- apps/openmw/mwmechanics/aiactivate.cpp | 5 -- apps/openmw/mwmechanics/aiactivate.hpp | 3 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 10 ---- apps/openmw/mwmechanics/aiavoiddoor.hpp | 16 ++++--- apps/openmw/mwmechanics/aibreathe.cpp | 10 ---- apps/openmw/mwmechanics/aibreathe.hpp | 15 +++--- apps/openmw/mwmechanics/aicast.cpp | 10 ---- apps/openmw/mwmechanics/aicast.hpp | 14 ++++-- apps/openmw/mwmechanics/aicombat.cpp | 10 ---- apps/openmw/mwmechanics/aicombat.hpp | 14 ++++-- apps/openmw/mwmechanics/aiescort.cpp | 5 -- apps/openmw/mwmechanics/aiescort.hpp | 12 +++-- apps/openmw/mwmechanics/aiface.cpp | 10 ---- apps/openmw/mwmechanics/aiface.hpp | 16 ++++--- apps/openmw/mwmechanics/aifollow.cpp | 24 ++++------ apps/openmw/mwmechanics/aifollow.hpp | 16 ++++--- apps/openmw/mwmechanics/aipackage.cpp | 29 ++---------- apps/openmw/mwmechanics/aipackage.hpp | 55 +++++++++++++++++----- apps/openmw/mwmechanics/aipursue.cpp | 5 -- apps/openmw/mwmechanics/aipursue.hpp | 14 ++++-- apps/openmw/mwmechanics/aisequence.cpp | 8 +++- apps/openmw/mwmechanics/aitravel.cpp | 38 +++++++++++---- apps/openmw/mwmechanics/aitravel.hpp | 35 +++++++++++--- apps/openmw/mwmechanics/aiwander.cpp | 19 ++------ apps/openmw/mwmechanics/aiwander.hpp | 15 +++--- apps/openmw/mwmechanics/typedaipackage.hpp | 10 ++++ 26 files changed, 220 insertions(+), 198 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 6764eba23..b4ddf0c03 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -44,11 +44,6 @@ namespace MWMechanics return false; } - int AiActivate::getTypeId() const - { - return TypeIdActivate; - } - void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr activate(new ESM::AiSequence::AiActivate()); diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a96f4cdb..b263e74a6 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -29,7 +29,8 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdActivate; } void writeState(ESM::AiSequence::AiSequence& sequence) const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 9cdb8d90b..d8517c5c9 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -72,16 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return false; } -int MWMechanics::AiAvoidDoor::getTypeId() const -{ - return TypeIdAvoidDoor; -} - -unsigned int MWMechanics::AiAvoidDoor::getPriority() const -{ - return 2; -} - bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const { return (actorPos - mLastPos).length2() < 10 * 10; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index fdbf7ebc7..72cde1026 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -24,12 +24,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: float mDuration; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 5cb81b3d8..15251e125 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -31,13 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro return true; } - -int MWMechanics::AiBreathe::getTypeId() const -{ - return TypeIdBreathe; -} - -unsigned int MWMechanics::AiBreathe::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 6e3bb912a..2a04ab2ad 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -12,13 +12,16 @@ namespace MWMechanics public: bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdBreathe; } - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } }; } #endif - diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index cc4c03bf1..9ad7b4c56 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -89,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const return target; } - -int MWMechanics::AiCast::getTypeId() const -{ - return AiPackage::TypeIdCast; -} - -unsigned int MWMechanics::AiCast::getPriority() const -{ - return 3; -} diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index cdf7db2bf..22575c7bc 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -17,14 +17,18 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCast; } MWWorld::Ptr getTarget() const final; - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 3; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const std::string mTargetId; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4a3c7aee6..883a8cc1c 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -406,16 +406,6 @@ namespace MWMechanics } } - int AiCombat::getTypeId() const - { - return TypeIdCombat; - } - - unsigned int AiCombat::getPriority() const - { - return 1; - } - MWWorld::Ptr AiCombat::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 2ef0298fc..ef8782ae1 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -104,18 +104,22 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCombat; } - unsigned int getPriority() const final; + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 1; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } ///Returns target ID MWWorld::Ptr getTarget() const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final; - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } - private: /// Returns true if combat should end bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 216547f58..5dc1e44db 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -95,11 +95,6 @@ namespace MWMechanics return false; } - int AiEscort::getTypeId() const - { - return TypeIdEscort; - } - void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr escort(new ESM::AiSequence::AiEscort()); diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index c12de1fac..42558bf7c 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -32,11 +32,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdEscort; } - bool useVariableSpeed() const final { return true; } - - bool sideWithTarget() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; diff --git a/apps/openmw/mwmechanics/aiface.cpp b/apps/openmw/mwmechanics/aiface.cpp index 0bfd00c87..17b18babc 100644 --- a/apps/openmw/mwmechanics/aiface.cpp +++ b/apps/openmw/mwmechanics/aiface.cpp @@ -14,13 +14,3 @@ bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::Charac osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f)); } - -int MWMechanics::AiFace::getTypeId() const -{ - return AiPackage::TypeIdFace; -} - -unsigned int MWMechanics::AiFace::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 516dd18dc..3a9a482c0 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -12,12 +12,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdFace; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const float mTargetX; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index eb5b595ab..a9e43b3c3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -16,25 +16,24 @@ namespace MWMechanics { - int AiFollow::mFollowIndexCounter = 0; AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -42,7 +41,7 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, } AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -50,7 +49,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float d } AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) -: mAlwaysFollow(true), mCommanded(commanded), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) +: TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!commanded)) +, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -58,7 +58,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + : TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded)) + , mAlwaysFollow(follow->mAlwaysFollow) // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. // The exact value of mDuration only matters for repeating packages. // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. @@ -200,14 +201,9 @@ std::string AiFollow::getFollowedActor() return mTargetActorRefId; } -int AiFollow::getTypeId() const -{ - return TypeIdFollow; -} - bool AiFollow::isCommanded() const { - return mCommanded; + return !mOptions.mShouldCancelPreviousAi; } void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const @@ -221,7 +217,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const follow->mRemainingDuration = mRemainingDuration; follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; - follow->mCommanded = mCommanded; + follow->mCommanded = isCommanded(); follow->mActive = mActive; ESM::AiSequence::AiPackageContainer package; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 865f4171b..b4cf88be8 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -53,15 +53,18 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - bool sideWithTarget() const final { return true; } - bool followTargetThroughDoors() const final { return true; } - bool shouldCancelPreviousAi() const final { return !mCommanded; } - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdFollow; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + options.mFollowTargetThroughDoors = true; + return options; + } /// Returns the actor being followed std::string getFollowedActor(); @@ -87,7 +90,6 @@ namespace MWMechanics /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ const bool mAlwaysFollow; - const bool mCommanded; const float mDuration; // Hours float mRemainingDuration; // Hours const float mX; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index dca882a3b..66b41db54 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,7 +24,9 @@ #include -MWMechanics::AiPackage::AiPackage() : +MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : + mTypeId(typeId), + mOptions(options), mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), mTargetActorId(-1), @@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const return MWWorld::Ptr(); } -bool MWMechanics::AiPackage::sideWithTarget() const -{ - return false; -} - -bool MWMechanics::AiPackage::followTargetThroughDoors() const -{ - return false; -} - -bool MWMechanics::AiPackage::canCancel() const -{ - return true; -} - -bool MWMechanics::AiPackage::shouldCancelPreviousAi() const -{ - return true; -} - -bool MWMechanics::AiPackage::getRepeat() const -{ - return false; -} - void MWMechanics::AiPackage::reset() { // reset all members diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 873ad1c29..c32fb93aa 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -55,11 +55,39 @@ namespace MWMechanics TypeIdCast = 11 }; - ///Default constructor - AiPackage(); + struct Options + { + unsigned int mPriority = 0; + bool mUseVariableSpeed = false; + bool mSideWithTarget = false; + bool mFollowTargetThroughDoors = false; + bool mCanCancel = true; + bool mShouldCancelPreviousAi = true; + bool mRepeat = false; + bool mAlwaysActive = false; + + constexpr Options withRepeat(bool value) + { + mRepeat = value; + return *this; + } + + constexpr Options withShouldCancelPreviousAi(bool value) + { + mShouldCancelPreviousAi = value; + return *this; + } + }; + + AiPackage(TypeId typeId, const Options& options); virtual ~AiPackage() = default; + static constexpr Options makeDefaultOptions() + { + return Options{}; + } + ///Clones the package virtual std::unique_ptr clone() const = 0; @@ -69,13 +97,13 @@ namespace MWMechanics /// Returns the TypeID of the AiPackage /// \see enum TypeId - virtual int getTypeId() const = 0; + TypeId getTypeId() const { return mTypeId; } /// Higher number is higher priority (0 being the lowest) - virtual unsigned int getPriority() const {return 0;} + unsigned int getPriority() const { return mOptions.mPriority; } /// Check if package use movement with variable speed - virtual bool useVariableSpeed() const { return false;} + bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; } virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} @@ -89,24 +117,24 @@ namespace MWMechanics virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); }; /// Return true if having this AiPackage makes the actor side with the target in fights (default false) - virtual bool sideWithTarget() const; + bool sideWithTarget() const { return mOptions.mSideWithTarget; } /// Return true if the actor should follow the target through teleport doors (default false) - virtual bool followTargetThroughDoors() const; + bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; } /// Can this Ai package be canceled? (default true) - virtual bool canCancel() const; + bool canCancel() const { return mOptions.mCanCancel; } /// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)? - virtual bool shouldCancelPreviousAi() const; + bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; } /// Return true if this package should repeat. Currently only used for Wander packages. - virtual bool getRepeat() const; + bool getRepeat() const { return mOptions.mRepeat; } virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); } - // Return true if any loaded actor with this AI package must be active. - virtual bool alwaysActive() const { return false; } + /// Return true if any loaded actor with this AI package must be active. + bool alwaysActive() const { return mOptions.mAlwaysActive; } /// Reset pathfinding state void reset(); @@ -139,6 +167,9 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + const TypeId mTypeId; + const Options mOptions; + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 76b4ac0c9..7aa2a9554 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -66,11 +66,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte return false; } -int AiPursue::getTypeId() const -{ - return TypeIdPursue; -} - MWWorld::Ptr AiPursue::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 6898a8dd3..6031f84fb 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -27,14 +27,20 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdPursue; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } MWWorld::Ptr getTarget() const final; void writeState (ESM::AiSequence::AiSequence& sequence) const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 9f5d4ccaf..4a23dc788 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -338,7 +338,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo dest = actor.getRefData().getPosition().asVec3(); } - MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true); + MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z()); stack(travelPackage, actor, false); } @@ -478,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) } case ESM::AiSequence::Ai_Travel: { - package.reset(new AiTravel(static_cast(it->mPackage))); + const auto source = static_cast(it->mPackage); + if (source->mHidden) + package.reset(new AiInternalTravel(source)); + else + package.reset(new AiTravel(source)); break; } case ESM::AiSequence::Ai_Escort: diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ea65f4670..b2a506ca6 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -27,14 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) namespace MWMechanics { - AiTravel::AiTravel(float x, float y, float z, bool hidden) - : mX(x),mY(y),mZ(z),mHidden(hidden) + AiTravel::AiTravel(float x, float y, float z, AiTravel*) + : mX(x), mY(y), mZ(z), mHidden(false) + { + } + + AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived) + : TypedAiPackage(derived), mX(x), mY(y), mZ(z), mHidden(true) + { + } + + AiTravel::AiTravel(float x, float y, float z) + : AiTravel(x, y, z, this) { } AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) - : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(travel->mHidden) + : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false) { + // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type + assert(!travel->mHidden); } bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) @@ -78,11 +90,6 @@ namespace MWMechanics return false; } - int AiTravel::getTypeId() const - { - return mHidden ? TypeIdInternalTravel : TypeIdTravel; - } - void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) { if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) @@ -107,5 +114,20 @@ namespace MWMechanics package.mPackage = travel.release(); sequence.mPackages.push_back(package); } + + AiInternalTravel::AiInternalTravel(float x, float y, float z) + : AiTravel(x, y, z, this) + { + } + + AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel) + : AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this) + { + } + + std::unique_ptr AiInternalTravel::clone() const + { + return std::make_unique(*this); + } } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 4f785e237..3049801cd 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -13,12 +13,18 @@ namespace AiSequence namespace MWMechanics { + struct AiInternalTravel; + /// \brief Causes the AI to travel to the specified point - class AiTravel final : public TypedAiPackage + class AiTravel : public TypedAiPackage { public: - /// Default constructor - AiTravel(float x, float y, float z, bool hidden = false); + AiTravel(float x, float y, float z, AiTravel* derived); + + AiTravel(float x, float y, float z, AiInternalTravel* derived); + + AiTravel(float x, float y, float z); + AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time @@ -28,11 +34,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - bool useVariableSpeed() const final { return true; } + static constexpr TypeId getTypeId() { return TypeIdTravel; } - bool alwaysActive() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mAlwaysActive = true; + return options; + } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } @@ -43,6 +53,17 @@ namespace MWMechanics const bool mHidden; }; + + struct AiInternalTravel final : public AiTravel + { + AiInternalTravel(float x, float y, float z); + + explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); + + static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } + + std::unique_ptr clone() const final; + }; } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 584131bbe..fd978717e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -113,11 +113,12 @@ namespace MWMechanics } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + TypedAiPackage(makeDefaultOptions().withRepeat(repeat)), mDistance(std::max(0, distance)), mDuration(std::max(0, duration)), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(getInitialIdle(idle)), - mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), + mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { } @@ -309,11 +310,6 @@ namespace MWMechanics return false; // AiWander package not yet completed } - bool AiWander::getRepeat() const - { - return mRepeat; - } - osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const { if (mHasDestination) @@ -599,11 +595,6 @@ namespace MWMechanics } } - int AiWander::getTypeId() const - { - return TypeIdWander; - } - void AiWander::stopWalking(const MWWorld::Ptr& actor) { mPathFinder.clearPath(); @@ -873,7 +864,7 @@ namespace MWMechanics assert (mIdle.size() == 8); for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; - wander->mData.mShouldRepeat = mRepeat; + wander->mData.mShouldRepeat = mOptions.mRepeat; wander->mStoredInitialActorPosition = mStoredInitialActorPosition; if (mStoredInitialActorPosition) wander->mInitialActorPosition = mInitialActorPosition; @@ -885,12 +876,12 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + : TypedAiPackage(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0)) + , mDistance(std::max(static_cast(0), wander->mData.mDistance)) , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) , mIdle(getInitialIdle(wander->mData.mIdle)) - , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) , mDestination(osg::Vec3f(0, 0, 0)) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8eb735205..8171107d9 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -93,16 +93,20 @@ namespace MWMechanics bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdWander; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mRepeat = false; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - bool getRepeat() const final; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; osg::Vec3f getDestination() const final @@ -139,7 +143,6 @@ namespace MWMechanics float mRemainingDuration; const int mTimeOfDay; const std::vector mIdle; - const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell @@ -174,7 +177,7 @@ namespace MWMechanics static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static int OffsetToPreventOvercrowding(); - }; + }; } #endif diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index e2b5f8688..c959f4d68 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,6 +8,16 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { + TypedAiPackage() : + AiPackage(T::getTypeId(), T::makeDefaultOptions()) {} + + TypedAiPackage(const Options& options) : + AiPackage(T::getTypeId(), options) {} + + template + TypedAiPackage(Derived*) : + AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {} + virtual std::unique_ptr clone() const override { return std::make_unique(*static_cast(this)); From 6de97e6bc2f0308a3a437d875b6eef01757c96a4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 6 Jun 2020 14:10:24 +0400 Subject: [PATCH 19/34] Remove redundant variables from RenderingManager --- apps/openmw/mwrender/renderingmanager.cpp | 12 ++++-------- apps/openmw/mwrender/renderingmanager.hpp | 2 -- components/terrain/world.hpp | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6b41854e0..3eb71db03 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -213,10 +213,8 @@ namespace MWRender , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) , mDistantFog(false) - , mDistantTerrain(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) - , mBorders(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -290,9 +288,7 @@ namespace MWRender DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); @@ -302,7 +298,7 @@ namespace MWRender mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); - if (mDistantTerrain) + if (Settings::Manager::getBool("distant terrain", "Terrain")) { const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); @@ -558,9 +554,9 @@ namespace MWRender bool RenderingManager::toggleBorders() { - mBorders = !mBorders; - mTerrain->setBordersVisible(mBorders); - return mBorders; + bool borders = !mTerrain->getBordersVisible(); + mTerrain->setBordersVisible(borders); + return borders; } bool RenderingManager::toggleRenderMode(RenderMode mode) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 09cff26f1..30826fb38 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -299,12 +299,10 @@ namespace MWRender float mNearClip; float mViewDistance; bool mDistantFog : 1; - bool mDistantTerrain : 1; bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; - bool mBorders; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index a69d03ca9..618095a60 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -139,6 +139,7 @@ namespace Terrain virtual void enable(bool enabled) {} virtual void setBordersVisible(bool visible); + virtual bool getBordersVisible() { return mBorderVisible; } /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. From 48758116d6f228dfab4ec82e9c26975e7bcefdb4 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Sat, 6 Jun 2020 14:24:33 +0200 Subject: [PATCH 20/34] Make sure the skill level up message box displays the value correctly Fixes regression introduced in 204d2acf2570073d9d9736493d79e9a1326d6136. --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1e9003a2f..0e4b3f44c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager()->playSound("skillraise"); std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""); - message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), base); + message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast(base)); if (readBook) message = "#{sBookSkillMessage}\n" + message; From 1873da4c91189e45ca2b1023eae5e73acd40e38a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 15:45:48 +0300 Subject: [PATCH 21/34] Don't save to or read automove state from saved games (#5452) --- CHANGELOG.md | 1 + apps/essimporter/importercontext.hpp | 1 - apps/openmw/mwworld/player.cpp | 4 ---- components/esm/player.cpp | 8 +++----- components/esm/player.hpp | 3 +-- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..c381af951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance + Bug #5452: Autowalk is being included in savegames Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 1a91b7cea..179e00f08 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -63,7 +63,6 @@ namespace ESSImport , mHour(0.f) , mNextActorId(0) { - mPlayer.mAutoMove = 0; ESM::CellId playerCellId; playerCellId.mPaged = true; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 11444c8eb..2b157f18a 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -356,8 +356,6 @@ namespace MWWorld else player.mHasMark = false; - player.mAutoMove = mAutoMove ? 1 : 0; - for (int i=0; i Date: Sat, 6 Jun 2020 19:09:18 +0300 Subject: [PATCH 22/34] Clean up magic bolts of actors that are gone (#5451) --- CHANGELOG.md | 1 + apps/openmw/mwworld/projectilemanager.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..a136994ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance + Bug #5451: Magic projectiles don't disappear with the caster Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 38458fdb9..6ace82ea1 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -387,6 +387,18 @@ namespace MWWorld { for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) { + // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame. + MWWorld::Ptr caster = it->getCaster(); + if (!caster.isEmpty() && caster.getClass().isActor()) + { + if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead()) + { + cleanupMagicBolt(*it); + it = mMagicBolts.erase(it); + continue; + } + } + osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->mValue.getFloat(); @@ -405,8 +417,6 @@ namespace MWWorld update(*it, duration); - MWWorld::Ptr caster = it->getCaster(); - // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) From e02b66cdf4b0ea50fd50203d153c5c88ef92c319 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 20:04:09 +0300 Subject: [PATCH 23/34] Ignore bogus string arguments for Disable/Enable again --- components/compiler/extensions0.cpp | 4 ++-- components/compiler/lineparser.cpp | 10 +--------- components/compiler/lineparser.hpp | 3 +-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 60b4a7451..3989ef0f4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -247,8 +247,8 @@ namespace Compiler extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); extensions.registerInstruction ("stopscript", "c", opcodeStopScript); extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed); - extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); - extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); + extensions.registerInstruction ("enable", "x", opcodeEnable, opcodeEnableExplicit); + extensions.registerInstruction ("disable", "x", opcodeDisable, opcodeDisableExplicit); extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 326b5f9f6..829ad6f08 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -86,13 +86,6 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { - if (mState==PotentialEndState) - { - getErrorHandler().warning ("Stray string argument", loc); - mState = EndState; - return true; - } - if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); @@ -445,8 +438,7 @@ namespace Compiler return true; } - if (code==Scanner::S_newline && - (mState==EndState || mState==BeginState || mState==PotentialEndState)) + if (code==Scanner::S_newline && (mState==EndState || mState==BeginState)) return false; if (code==Scanner::S_comma && mState==MessageState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index 8f7f64bf2..cc32b9592 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -25,8 +25,7 @@ namespace Compiler SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, - EndState, PotentialEndState /* may have a stray string argument */, - PotentialExplicitState, ExplicitState, MemberState + EndState, PotentialExplicitState, ExplicitState, MemberState }; Locals& mLocals; From 5209f5ff6d2139b5d3d3a8af797bcdb5703741dd Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 6 Jun 2020 23:00:53 +0200 Subject: [PATCH 24/34] Mark all derived classes from ESM::ObjectsState and overriden functions as final --- components/esm/containerstate.hpp | 10 +++++----- components/esm/creaturelevliststate.hpp | 10 +++++----- components/esm/creaturestate.hpp | 10 +++++----- components/esm/doorstate.hpp | 10 +++++----- components/esm/npcstate.hpp | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp index 33818d4f1..b0c4da2d1 100644 --- a/components/esm/containerstate.hpp +++ b/components/esm/containerstate.hpp @@ -8,18 +8,18 @@ namespace ESM { // format 0, saved games only - struct ContainerState : public ObjectState + struct ContainerState final : public ObjectState { InventoryState mInventory; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual ContainerState& asContainerState() + ContainerState& asContainerState() final { return *this; } - virtual const ContainerState& asContainerState() const + const ContainerState& asContainerState() const final { return *this; } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index 4d0b726a0..ec0bc8667 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -7,19 +7,19 @@ namespace ESM { // format 0, saved games only - struct CreatureLevListState : public ObjectState + struct CreatureLevListState final : public ObjectState { int mSpawnActorId; bool mSpawn; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureLevListState& asCreatureLevListState() + CreatureLevListState& asCreatureLevListState() final { return *this; } - virtual const CreatureLevListState& asCreatureLevListState() const + const CreatureLevListState& asCreatureLevListState() const final { return *this; } diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index ca0d2601a..3e9a44d56 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -9,7 +9,7 @@ namespace ESM { // format 0, saved games only - struct CreatureState : public ObjectState + struct CreatureState final : public ObjectState { InventoryState mInventory; CreatureStats mCreatureStats; @@ -17,14 +17,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureState& asCreatureState() + CreatureState& asCreatureState() final { return *this; } - virtual const CreatureState& asCreatureState() const + const CreatureState& asCreatureState() const final { return *this; } diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp index 1251b9059..04ad110d6 100644 --- a/components/esm/doorstate.hpp +++ b/components/esm/doorstate.hpp @@ -7,18 +7,18 @@ namespace ESM { // format 0, saved games only - struct DoorState : public ObjectState + struct DoorState final : public ObjectState { int mDoorState = 0; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual DoorState& asDoorState() + DoorState& asDoorState() final { return *this; } - virtual const DoorState& asDoorState() const + const DoorState& asDoorState() const final { return *this; } diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 4ae026da6..6c0469050 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -10,7 +10,7 @@ namespace ESM { // format 0, saved games only - struct NpcState : public ObjectState + struct NpcState final : public ObjectState { InventoryState mInventory; NpcStats mNpcStats; @@ -19,14 +19,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual NpcState& asNpcState() + NpcState& asNpcState() final { return *this; } - virtual const NpcState& asNpcState() const + const NpcState& asNpcState() const final { return *this; } From 75e7a3e8b1cbe4d72859ca75b082a8f93e377667 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 09:25:46 +0400 Subject: [PATCH 25/34] Do not store object position, if it is the same as in CellRef --- components/esm/defs.hpp | 20 ++++++++++++++++++++ components/esm/objectstate.cpp | 3 ++- components/esm/savedgame.cpp | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 6c0c33526..61b7b161b 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -57,6 +57,26 @@ struct Position }; #pragma pack(pop) +bool inline operator== (const Position& left, const Position& right) noexcept +{ + return left.pos[0] == right.pos[0] && + left.pos[1] == right.pos[1] && + left.pos[2] == right.pos[2] && + left.rot[0] == right.rot[0] && + left.rot[1] == right.rot[1] && + left.rot[2] == right.rot[2]; +} + +bool inline operator!= (const Position& left, const Position& right) noexcept +{ + return left.pos[0] != right.pos[0] || + left.pos[1] != right.pos[1] || + left.pos[2] != right.pos[2] || + left.rot[0] != right.rot[0] || + left.rot[1] != right.rot[1] || + left.rot[2] != right.rot[2]; +} + template struct FourCC { diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index a7a452c82..9709bf4ff 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -26,6 +26,7 @@ void ESM::ObjectState::load (ESMReader &esm) mCount = 1; esm.getHNOT (mCount, "COUN"); + mPosition = mRef.mPos; esm.getHNOT (mPosition, "POS_", 24); if (esm.isNextSub("LROT")) @@ -61,7 +62,7 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mCount!=1) esm.writeHNT ("COUN", mCount); - if (!inInventory) + if (!inInventory && mPosition != mRef.mPos) esm.writeHNT ("POS_", mPosition, 24); if (mFlags != 0) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index d87607e44..76695dbe8 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -4,7 +4,7 @@ #include "esmwriter.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 11; +int ESM::SavedGame::sCurrentFormat = 12; void ESM::SavedGame::load (ESMReader &esm) { From 5d17a4c66e881d78ba4d3ebbd5149ebf14cc5cde Mon Sep 17 00:00:00 2001 From: foobar13372 Date: Sun, 7 Jun 2020 10:10:33 +0200 Subject: [PATCH 26/34] Update CHANGELOG.md Correctly name change (was probably a copy paste error) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..50103697e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -299,7 +299,7 @@ Feature #5147: Show spell magicka cost in spell buying window Feature #5170: Editor: Land shape editing, land selection Feature #5172: Editor: Delete instances/references with keypress in scene window - Feature #5193: Weapon sheathing + Feature #5193: Shields sheathing Feature #5201: Editor: Show tool outline in scene view, when using editmodes Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape From 4afc332a0ccc07db0d737a8d57f966d4a2b826fd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 10:35:33 +0400 Subject: [PATCH 27/34] Add a FogManager --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/fogmanager.cpp | 104 ++++++++++++++++++++++ apps/openmw/mwrender/fogmanager.hpp | 39 ++++++++ apps/openmw/mwrender/renderingmanager.cpp | 86 +++--------------- apps/openmw/mwrender/renderingmanager.hpp | 14 +-- 5 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 apps/openmw/mwrender/fogmanager.cpp create mode 100644 apps/openmw/mwrender/fogmanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a6b68df86..99bd1c2cd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/fogmanager.cpp b/apps/openmw/mwrender/fogmanager.cpp new file mode 100644 index 000000000..837e6ad04 --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.cpp @@ -0,0 +1,104 @@ +#include "fogmanager.hpp" + +#include + +#include +#include +#include +#include + +namespace +{ + float DLLandFogStart; + float DLLandFogEnd; + float DLUnderwaterFogStart; + float DLUnderwaterFogEnd; + float DLInteriorFogStart; + float DLInteriorFogEnd; +} + +namespace MWRender +{ + FogManager::FogManager() + : mLandFogStart(0.f) + , mLandFogEnd(std::numeric_limits::max()) + , mUnderwaterFogStart(0.f) + , mUnderwaterFogEnd(std::numeric_limits::max()) + , mFogColor(osg::Vec4f()) + , mDistantFog(Settings::Manager::getBool("use distant fog", "Fog")) + , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) + , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) + , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) + { + DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); + DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); + DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); + DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); + DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); + DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); + } + + void FogManager::configure(float viewDistance, const ESM::Cell *cell) + { + osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); + + if (mDistantFog) + { + float density = std::max(0.2f, cell->mAmbi.mFogDensity); + mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density; + mLandFogEnd = DLInteriorFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + mFogColor = color; + } + else + configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + } + + void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) + { + if (mDistantFog) + { + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd); + mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + } + else + { + if (fogDepth == 0.0) + { + mLandFogStart = 0.0f; + mLandFogEnd = std::numeric_limits::max(); + } + else + { + mLandFogStart = viewDistance * (1 - fogDepth); + mLandFogEnd = viewDistance; + } + mUnderwaterFogStart = std::min(viewDistance, 6666.f) * (1 - underwaterFog); + mUnderwaterFogEnd = std::min(viewDistance, 6666.f); + } + mFogColor = color; + } + + float FogManager::getFogStart(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogStart : mLandFogStart; + } + + float FogManager::getFogEnd(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd; + } + + osg::Vec4f FogManager::getFogColor(bool isUnderwater) const + { + if (isUnderwater) + { + return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight); + } + + return mFogColor; + } +} diff --git a/apps/openmw/mwrender/fogmanager.hpp b/apps/openmw/mwrender/fogmanager.hpp new file mode 100644 index 000000000..c3efd06ab --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.hpp @@ -0,0 +1,39 @@ +#ifndef OPENMW_MWRENDER_FOGMANAGER_H +#define OPENMW_MWRENDER_FOGMANAGER_H + +#include + +namespace ESM +{ + struct Cell; +} + +namespace MWRender +{ + class FogManager + { + public: + FogManager(); + + void configure(float viewDistance, const ESM::Cell *cell); + void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color); + + osg::Vec4f getFogColor(bool isUnderwater) const; + float getFogStart(bool isUnderwater) const; + float getFogEnd(bool isUnderwater) const; + + private: + float mLandFogStart; + float mLandFogEnd; + float mUnderwaterFogStart; + float mUnderwaterFogEnd; + osg::Vec4f mFogColor; + bool mDistantFog; + + osg::Vec4f mUnderwaterColor; + float mUnderwaterWeight; + float mUnderwaterIndoorFog; + }; +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3eb71db03..462f9fbb6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -69,16 +69,7 @@ #include "navmesh.hpp" #include "actorspaths.hpp" #include "recastmesh.hpp" - -namespace -{ - float DLLandFogStart; - float DLLandFogEnd; - float DLUnderwaterFogStart; - float DLUnderwaterFogEnd; - float DLInteriorFogStart; - float DLInteriorFogEnd; -} +#include "fogmanager.hpp" namespace MWRender { @@ -204,15 +195,7 @@ namespace MWRender , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) , mNavigator(navigator) - , mLandFogStart(0.f) - , mLandFogEnd(std::numeric_limits::max()) - , mUnderwaterFogStart(0.f) - , mUnderwaterFogEnd(std::numeric_limits::max()) - , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) - , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) - , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) - , mDistantFog(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { @@ -282,14 +265,6 @@ namespace MWRender mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); - DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); - DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); - DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); - DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); - DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); - DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders"); @@ -345,8 +320,9 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); + mFog.reset(new FogManager()); + mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mSky->setCamera(mViewer->getCamera()); mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); @@ -602,46 +578,12 @@ namespace MWRender void RenderingManager::configureFog(const ESM::Cell *cell) { - osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - - if(mDistantFog) - { - float density = std::max(0.2f, cell->mAmbi.mFogDensity); - mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density); - mLandFogEnd = DLInteriorFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - mFogColor = color; - } - else - configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + mFog->configure(mViewDistance, cell); } void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - if(mDistantFog) - { - mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd); - mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - } - else - { - if(fogDepth == 0.0) - { - mLandFogStart = 0.0f; - mLandFogEnd = std::numeric_limits::max(); - } - else - { - mLandFogStart = mViewDistance * (1 - fogDepth); - mLandFogEnd = mViewDistance; - } - mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog); - mUnderwaterFogEnd = std::min(mViewDistance, 6666.f); - } - mFogColor = color; + mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color); } SkyManager* RenderingManager::getSkyManager() @@ -670,19 +612,11 @@ namespace MWRender osg::Vec3f focal, cameraPos; mCamera->getPosition(focal, cameraPos); mCurrentCameraPos = cameraPos; - if (mWater->isUnderwater(cameraPos)) - { - setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(mUnderwaterFogStart); - mStateUpdater->setFogEnd(mUnderwaterFogEnd); - } - else - { - setFogColor(mFogColor); - mStateUpdater->setFogStart(mLandFogStart); - mStateUpdater->setFogEnd(mLandFogEnd); - } + bool isUnderwater = mWater->isUnderwater(cameraPos); + mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater)); + mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater)); + setFogColor(mFog->getFogColor(isUnderwater)); } void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) @@ -1331,7 +1265,7 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - if(!mDistantFog) + if(!Settings::Manager::getBool("use distant fog", "Fog")) mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 30826fb38..13f09b359 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,6 +73,7 @@ namespace MWRender class StateUpdater; class EffectManager; + class FogManager; class SkyManager; class NpcAnimation; class Pathgrid; @@ -275,6 +276,7 @@ namespace MWRender std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; std::unique_ptr mSky; + std::unique_ptr mFog; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; osg::ref_ptr mPlayerAnimation; @@ -284,22 +286,12 @@ namespace MWRender osg::ref_ptr mStateUpdater; - float mLandFogStart; - float mLandFogEnd; - float mUnderwaterFogStart; - float mUnderwaterFogEnd; - osg::Vec4f mUnderwaterColor; - float mUnderwaterWeight; - float mUnderwaterIndoorFog; - osg::Vec4f mFogColor; - osg::Vec4f mAmbientColor; float mNightEyeFactor; float mNearClip; float mViewDistance; - bool mDistantFog : 1; - bool mFieldOfViewOverridden : 1; + bool mFieldOfViewOverridden; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; From 3dd4023e8dbd74d6c0ed18acf2d74e9faab8a360 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 8 Jun 2020 11:29:38 +0400 Subject: [PATCH 28/34] Update active spells during rest --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6c88592f4..aa5aac80a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2051,6 +2051,8 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue; From d997842f8d59bc56bdd51175897fea15269c4292 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Jun 2020 10:07:37 +0400 Subject: [PATCH 29/34] Use FourCC to declare all ESM record names --- components/esm/defs.hpp | 90 ++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 61b7b161b..0f9cefab1 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -86,51 +86,51 @@ struct FourCC enum RecNameInts { // format 0 / legacy - REC_ACTI = 0x49544341, - REC_ALCH = 0x48434c41, - REC_APPA = 0x41505041, - REC_ARMO = 0x4f4d5241, - REC_BODY = 0x59444f42, - REC_BOOK = 0x4b4f4f42, - REC_BSGN = 0x4e475342, - REC_CELL = 0x4c4c4543, - REC_CLAS = 0x53414c43, - REC_CLOT = 0x544f4c43, - REC_CNTC = 0x43544e43, - REC_CONT = 0x544e4f43, - REC_CREA = 0x41455243, - REC_CREC = 0x43455243, - REC_DIAL = 0x4c414944, - REC_DOOR = 0x524f4f44, - REC_ENCH = 0x48434e45, - REC_FACT = 0x54434146, - REC_GLOB = 0x424f4c47, - REC_GMST = 0x54534d47, - REC_INFO = 0x4f464e49, - REC_INGR = 0x52474e49, - REC_LAND = 0x444e414c, - REC_LEVC = 0x4356454c, - REC_LEVI = 0x4956454c, - REC_LIGH = 0x4847494c, - REC_LOCK = 0x4b434f4c, - REC_LTEX = 0x5845544c, - REC_MGEF = 0x4645474d, - REC_MISC = 0x4353494d, - REC_NPC_ = 0x5f43504e, - REC_NPCC = 0x4343504e, - REC_PGRD = 0x44524750, - REC_PROB = 0x424f5250, - REC_RACE = 0x45434152, - REC_REGN = 0x4e474552, - REC_REPA = 0x41504552, - REC_SCPT = 0x54504353, - REC_SKIL = 0x4c494b53, - REC_SNDG = 0x47444e53, - REC_SOUN = 0x4e554f53, - REC_SPEL = 0x4c455053, - REC_SSCR = 0x52435353, - REC_STAT = 0x54415453, - REC_WEAP = 0x50414557, + REC_ACTI = FourCC<'A','C','T','I'>::value, + REC_ALCH = FourCC<'A','L','C','H'>::value, + REC_APPA = FourCC<'A','P','P','A'>::value, + REC_ARMO = FourCC<'A','R','M','O'>::value, + REC_BODY = FourCC<'B','O','D','Y'>::value, + REC_BOOK = FourCC<'B','O','O','K'>::value, + REC_BSGN = FourCC<'B','S','G','N'>::value, + REC_CELL = FourCC<'C','E','L','L'>::value, + REC_CLAS = FourCC<'C','L','A','S'>::value, + REC_CLOT = FourCC<'C','L','O','T'>::value, + REC_CNTC = FourCC<'C','N','T','C'>::value, + REC_CONT = FourCC<'C','O','N','T'>::value, + REC_CREA = FourCC<'C','R','E','A'>::value, + REC_CREC = FourCC<'C','R','E','C'>::value, + REC_DIAL = FourCC<'D','I','A','L'>::value, + REC_DOOR = FourCC<'D','O','O','R'>::value, + REC_ENCH = FourCC<'E','N','C','H'>::value, + REC_FACT = FourCC<'F','A','C','T'>::value, + REC_GLOB = FourCC<'G','L','O','B'>::value, + REC_GMST = FourCC<'G','M','S','T'>::value, + REC_INFO = FourCC<'I','N','F','O'>::value, + REC_INGR = FourCC<'I','N','G','R'>::value, + REC_LAND = FourCC<'L','A','N','D'>::value, + REC_LEVC = FourCC<'L','E','V','C'>::value, + REC_LEVI = FourCC<'L','E','V','I'>::value, + REC_LIGH = FourCC<'L','I','G','H'>::value, + REC_LOCK = FourCC<'L','O','C','K'>::value, + REC_LTEX = FourCC<'L','T','E','X'>::value, + REC_MGEF = FourCC<'M','G','E','F'>::value, + REC_MISC = FourCC<'M','I','S','C'>::value, + REC_NPC_ = FourCC<'N','P','C','_'>::value, + REC_NPCC = FourCC<'N','P','C','C'>::value, + REC_PGRD = FourCC<'P','G','R','D'>::value, + REC_PROB = FourCC<'P','R','O','B'>::value, + REC_RACE = FourCC<'R','A','C','E'>::value, + REC_REGN = FourCC<'R','E','G','N'>::value, + REC_REPA = FourCC<'R','E','P','A'>::value, + REC_SCPT = FourCC<'S','C','P','T'>::value, + REC_SKIL = FourCC<'S','K','I','L'>::value, + REC_SNDG = FourCC<'S','N','D','G'>::value, + REC_SOUN = FourCC<'S','O','U','N'>::value, + REC_SPEL = FourCC<'S','P','E','L'>::value, + REC_SSCR = FourCC<'S','S','C','R'>::value, + REC_STAT = FourCC<'S','T','A','T'>::value, + REC_WEAP = FourCC<'W','E','A','P'>::value, // format 0 - saved games REC_SAVE = FourCC<'S','A','V','E'>::value, From 1c50d258535de869989bd542ba534365b3fa6997 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 9 Jun 2020 15:27:38 +0300 Subject: [PATCH 30/34] Minor shader fixes Don't initialize uniform bool to false explicitly Attempt not to calculate specular lighting if the material specular color is black --- files/shaders/objects_fragment.glsl | 5 +++-- files/shaders/terrain_fragment.glsl | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 78cc8c1dd..6ce44cc82 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -49,7 +49,7 @@ uniform vec2 envMapLumaBias; uniform mat2 bumpMapMatrix; #endif -uniform bool simpleWater = false; +uniform bool simpleWater; varying float euclideanDepth; varying float linearDepth; @@ -181,7 +181,8 @@ void main() matSpec = passColor.xyz; #endif - gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; + if (matSpec != vec3(0.0)) + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; #if @radialFog float depth = euclideanDepth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 477b1bf9e..68669dff9 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -90,7 +90,8 @@ void main() matSpec = passColor.xyz; #endif - gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; + if (matSpec != vec3(0.0)) + gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; #if @radialFog float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); From 91514aed5db64af4b40a29de6cd017b9b109e1e6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 9 Jun 2020 21:56:54 +0300 Subject: [PATCH 31/34] Don't deliberately do redundant assignments --- files/shaders/lighting.glsl | 27 +++++++++++++++++---------- files/shaders/objects_fragment.glsl | 8 ++++++-- files/shaders/terrain_fragment.glsl | 4 +++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index e5587a448..1ed162eea 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -29,25 +29,27 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse) #endif { - vec4 diffuse = gl_FrontMaterial.diffuse; - vec3 ambient = gl_FrontMaterial.ambient.xyz; - vec3 emission = gl_FrontMaterial.emission.xyz; + vec4 diffuse; + vec3 ambient; if (colorMode == ColorMode_AmbientAndDiffuse) { diffuse = vertexColor; ambient = vertexColor.xyz; } - else if (colorMode == ColorMode_Ambient) - { - ambient = vertexColor.xyz; - } else if (colorMode == ColorMode_Diffuse) { diffuse = vertexColor; + ambient = gl_FrontMaterial.ambient.xyz; } - else if (colorMode == ColorMode_Emission) + else if (colorMode == ColorMode_Ambient) { - emission = vertexColor.xyz; + diffuse = gl_FrontMaterial.diffuse; + ambient = vertexColor.xyz; + } + else + { + diffuse = gl_FrontMaterial.diffuse; + ambient = gl_FrontMaterial.ambient.xyz; } vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a); @@ -65,7 +67,12 @@ vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadow lightResult.xyz += ambientLight + diffuseLight; } - lightResult.xyz += gl_LightModel.ambient.xyz * ambient + emission; + lightResult.xyz += gl_LightModel.ambient.xyz * ambient; + + if (colorMode == ColorMode_Emission) + lightResult.xyz += vertexColor.xyz; + else + lightResult.xyz += gl_FrontMaterial.emission.xyz; #if @clamp lightResult = clamp(lightResult, vec4(0.0), vec4(1.0)); diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 6ce44cc82..74a0cc80f 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -176,18 +176,22 @@ void main() vec3 matSpec = specTex.xyz; #else float shininess = gl_FrontMaterial.shininess; - vec3 matSpec = gl_FrontMaterial.specular.xyz; + vec3 matSpec; if (colorMode == ColorMode_Specular) matSpec = passColor.xyz; + else + matSpec = gl_FrontMaterial.specular.xyz; #endif if (matSpec != vec3(0.0)) gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; #if @radialFog - float depth = euclideanDepth; + float depth; // For the less detailed mesh of simple water we need to recalculate depth on per-pixel basis if (simpleWater) depth = length(passViewPos); + else + depth = euclideanDepth; float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); #else float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 68669dff9..ffc19fac0 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -85,9 +85,11 @@ void main() vec3 matSpec = vec3(diffuseTex.a); #else float shininess = gl_FrontMaterial.shininess; - vec3 matSpec = gl_FrontMaterial.specular.xyz; + vec3 matSpec; if (colorMode == ColorMode_Specular) matSpec = passColor.xyz; + else + matSpec = gl_FrontMaterial.specular.xyz; #endif if (matSpec != vec3(0.0)) From aacb569acbaac22ee1be3022637de97efdbb015d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 10 Jun 2020 10:30:37 +0400 Subject: [PATCH 32/34] Use more C++11 in tools code --- apps/bsatool/bsatool.cpp | 23 +++++++---------- apps/esmtool/esmtool.cpp | 14 +++++----- apps/esmtool/labels.cpp | 2 +- apps/esmtool/record.cpp | 17 ++++++------ apps/essimporter/converter.cpp | 37 ++++++++++++--------------- apps/essimporter/converter.hpp | 12 ++++----- apps/essimporter/convertinventory.cpp | 13 +++++----- apps/essimporter/convertplayer.cpp | 15 +++++------ apps/essimporter/convertscri.cpp | 8 +++--- apps/essimporter/importer.cpp | 22 ++++++---------- apps/essimporter/importinventory.cpp | 2 -- 11 files changed, 72 insertions(+), 93 deletions(-) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index 54e946cbc..ed86a3a42 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -183,19 +183,19 @@ int list(Bsa::BSAFile& bsa, Arguments& info) { // List all files const Bsa::BSAFile::FileList &files = bsa.getList(); - for(unsigned int i=0; iname; - - std::string extractPath (archivePath); + for (const auto &file : bsa.getList()) + { + std::string extractPath(file.name); replaceAll(extractPath, "\\", "/"); // Get the target path (the path the file will be extracted to) @@ -278,7 +273,7 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info) // Get a stream for the file to extract // (inefficient because getFile iter on the list again) - Files::IStreamPtr data = bsa.getFile(archivePath); + Files::IStreamPtr data = bsa.getFile(file.name); bfs::ofstream out(target, std::ios::binary); // Write the file to disk diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 59095c1df..7618bbeb8 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -352,12 +352,12 @@ int load(Arguments& info) std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; - std::vector m = esm.getGameFiles(); - if (!m.empty()) + std::vector masterData = esm.getGameFiles(); + if (!masterData.empty()) { std::cout << "Masters:" << std::endl; - for(unsigned int i=0;imType = type; @@ -728,10 +728,9 @@ void Record::print() << " (" << mData.mData.mAttribute[0] << ")" << std::endl; std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) << " (" << mData.mData.mAttribute[1] << ")" << std::endl; - for (int i = 0; i < 7; i++) - if (mData.mData.mSkills[i] != -1) - std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) - << " (" << mData.mData.mSkills[i] << ")" << std::endl; + for (int skill : mData.mData.mSkills) + if (skill != -1) + std::cout << " Skill: " << skillLabel(skill) << " (" << skill << ")" << std::endl; for (int i = 0; i != 10; i++) if (!mData.mRanks[i].empty()) { diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 900fbb05c..e0756602d 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -52,9 +52,7 @@ namespace // a dynamically created record e.g. player-enchanted weapon std::string index = indexedRefId.substr(indexedRefId.size()-8); - if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) - return true; - return false; + return index.find_first_not_of("0123456789ABCDEF") == std::string::npos; } void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId) @@ -139,12 +137,12 @@ namespace ESSImport image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); memcpy(image2->data(), &data[0], data.size()); - for (std::set >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it) + for (const auto & exploredCell : mContext->mExploredCells) { - if (it->first > mContext->mGlobalMapState.mBounds.mMaxX - || it->first < mContext->mGlobalMapState.mBounds.mMinX - || it->second > mContext->mGlobalMapState.mBounds.mMaxY - || it->second < mContext->mGlobalMapState.mBounds.mMinY) + if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX + || exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX + || exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY + || exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY) { // out of bounds, I think this could happen, since the original engine had a fixed-size map continue; @@ -152,12 +150,12 @@ namespace ESSImport int imageLeftSrc = mGlobalMapImage->s()/2; int imageTopSrc = mGlobalMapImage->t()/2; - imageLeftSrc += it->first * cellSize; - imageTopSrc -= it->second * cellSize; + imageLeftSrc += exploredCell.first * cellSize; + imageTopSrc -= exploredCell.second * cellSize; int imageLeftDst = width/2; int imageTopDst = height/2; - imageLeftDst += it->first * cellSize; - imageTopDst -= it->second * cellSize; + imageLeftDst += exploredCell.first * cellSize; + imageTopDst -= exploredCell.second * cellSize; for (int x=0; x::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) + for (const auto & cellref : cell.mRefs) { - const CellRef& cellref = *refIt; ESM::CellRef out (cellref); // TODO: use mContext->mCreatures/mNpcs @@ -437,16 +434,16 @@ namespace ESSImport void ConvertCell::write(ESM::ESMWriter &esm) { - for (std::map::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) - writeCell(it->second, esm); + for (const auto & cell : mIntCells) + writeCell(cell.second, esm); - for (std::map, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) - writeCell(it->second, esm); + for (const auto & cell : mExtCells) + writeCell(cell.second, esm); - for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) + for (const auto & marker : mMarkers) { esm.startRecord(ESM::REC_MARK); - it->save(esm); + marker.save(esm); esm.endRecord(ESM::REC_MARK); } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 2694ea570..30d0236aa 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -127,8 +127,8 @@ public: ESM::SpellState::SpellParams empty; // FIXME: player start spells and birthsign spells aren't listed here, // need to fix openmw to account for this - for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) - mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + for (const auto & spell : npc.mSpells.mList) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty; // Clear the list now that we've written it, this prevents issues cropping up with // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal. @@ -434,9 +434,9 @@ public: for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) { std::map, int> owners; - for (std::set::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) + for (const auto & ownerIt : it->second) { - owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second) + owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second) // Since OpenMW doesn't suffer from the owner contamination bug, // it needs a count argument. But for legacy savegames, we don't know // this count, so must assume all items of that ID are stolen, @@ -588,10 +588,10 @@ public: } virtual void write(ESM::ESMWriter &esm) { - for (std::vector::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it) + for (const auto & script : mScripts) { esm.startRecord(ESM::REC_GSCR); - it->save(esm); + script.save(esm); esm.endRecord(ESM::REC_GSCR); } } diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 0799c8d11..79e09488c 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -9,21 +9,20 @@ namespace ESSImport void convertInventory(const Inventory &inventory, ESM::InventoryState &state) { int index = 0; - for (std::vector::const_iterator it = inventory.mItems.begin(); - it != inventory.mItems.end(); ++it) + for (const auto & item : inventory.mItems) { ESM::ObjectState objstate; objstate.blank(); - objstate.mRef = *it; - objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); - objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile + objstate.mRef = item; + objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId); + objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile // openmw handles them differently, so no need to set any flags state.mItems.push_back(objstate); - if (it->mRelativeEquipmentSlot != -1) + if (item.mRelativeEquipmentSlot != -1) // Note we should really write the absolute slot here, which we do not know about // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when // an item could be equipped in two different slots (e.g. equipped two rings) - state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot; + state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot; ++index; } } diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 5e2da2b03..b3ccbca35 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -10,13 +10,13 @@ namespace ESSImport { out.mBirthsign = pcdt.mBirthsign; out.mObject.mNpcStats.mBounty = pcdt.mBounty; - for (std::vector::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) + for (const auto & essFaction : pcdt.mFactions) { ESM::NpcStats::Faction faction; - faction.mExpelled = (it->mFlags & 0x2) != 0; - faction.mRank = it->mRank; - faction.mReputation = it->mReputation; - out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; + faction.mExpelled = (essFaction.mFlags & 0x2) != 0; + faction.mRank = essFaction.mRank; + faction.mReputation = essFaction.mReputation; + out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction; } for (int i=0; i<3; ++i) out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i]; @@ -35,10 +35,9 @@ namespace ESSImport teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled); levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled); - for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); - it != pcdt.mKnownDialogueTopics.end(); ++it) + for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics) { - outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); + outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic)); } controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled; diff --git a/apps/essimporter/convertscri.cpp b/apps/essimporter/convertscri.cpp index dbe35a010..eba48df77 100644 --- a/apps/essimporter/convertscri.cpp +++ b/apps/essimporter/convertscri.cpp @@ -1,18 +1,16 @@ #include "convertscri.hpp" -#include - namespace { template void storeVariables(const std::vector& variables, ESM::Locals& locals, const std::string& scriptname) { - for (typename std::vector::const_iterator it = variables.begin(); it != variables.end(); ++it) + for (const auto& variable : variables) { - ESM::Variant val(*it); + ESM::Variant val(variable); val.setType(VariantType); - locals.mVariables.push_back(std::make_pair(std::string(), val)); + locals.mVariables.emplace_back(std::string(), val); } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 36512579c..706512263 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -16,15 +16,12 @@ #include #include -#include #include #include #include #include #include -#include #include -#include #include @@ -49,7 +46,7 @@ namespace image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE); // need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise - std::vector::const_iterator it = fileHeader.mSCRS.begin(); + auto it = fileHeader.mSCRS.begin(); for (int y=0; y<128; ++y) { for (int x=0; x<128; ++x) @@ -317,10 +314,9 @@ namespace ESSImport std::set unknownRecords; - for (std::map >::const_iterator it = converters.begin(); - it != converters.end(); ++it) + for (const auto & converter : converters) { - it->second->setContext(context); + converter.second->setContext(context); } while (esm.hasMoreRecs()) @@ -328,7 +324,7 @@ namespace ESSImport ESM::NAME n = esm.getRecName(); esm.getRecHeader(); - std::map >::iterator it = converters.find(n.intval); + auto it = converters.find(n.intval); if (it != converters.end()) { it->second->read(esm); @@ -358,17 +354,15 @@ namespace ESSImport writer.setDescription(""); writer.setRecordCount (0); - for (std::vector::const_iterator it = header.mMaster.begin(); - it != header.mMaster.end(); ++it) - writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0 + for (const auto & master : header.mMaster) + writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0 writer.save (stream); ESM::SavedGame profile; - for (std::vector::const_iterator it = header.mMaster.begin(); - it != header.mMaster.end(); ++it) + for (const auto & master : header.mMaster) { - profile.mContentFiles.push_back(it->name); + profile.mContentFiles.push_back(master.name); } profile.mDescription = esm.getDesc(); profile.mInGameTime.mDay = context.mDay; diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index cff114acb..dbf9ce0bd 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -4,8 +4,6 @@ #include -#include - namespace ESSImport { From 0d66369efbe5fe90e542571c88d8833d1eda4e5b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 10 Jun 2020 10:58:07 +0400 Subject: [PATCH 33/34] Use overrides, when needed --- apps/esmtool/record.hpp | 8 ++-- apps/essimporter/converter.hpp | 76 +++++++++++++++--------------- apps/essimporter/importacdt.hpp | 4 +- apps/essimporter/importcellref.hpp | 4 +- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index dca38409f..bbb3dd098 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -74,7 +74,7 @@ namespace EsmTool : mIsDeleted(false) {} - std::string getId() const { + std::string getId() const override { return mData.mId; } @@ -82,15 +82,15 @@ namespace EsmTool return mData; } - void save(ESM::ESMWriter &esm) { + void save(ESM::ESMWriter &esm) override { mData.save(esm, mIsDeleted); } - void load(ESM::ESMReader &esm) { + void load(ESM::ESMReader &esm) override { mData.load(esm, mIsDeleted); } - void print(); + void print() override; }; template<> std::string Record::getId() const; diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 30d0236aa..9a1923c2b 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -79,9 +79,9 @@ template class DefaultConverter : public Converter { public: - virtual int getStage() { return 0; } + int getStage() override { return 0; } - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { T record; bool isDeleted = false; @@ -90,7 +90,7 @@ public: mRecords[record.mId] = record; } - virtual void write(ESM::ESMWriter& esm) + void write(ESM::ESMWriter& esm) override { for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) { @@ -107,7 +107,7 @@ protected: class ConvertNPC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::NPC npc; bool isDeleted = false; @@ -144,7 +144,7 @@ public: class ConvertCREA : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { // See comment in ConvertNPC ESM::Creature creature; @@ -162,7 +162,7 @@ public: class ConvertGlobal : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Global global; bool isDeleted = false; @@ -183,7 +183,7 @@ public: class ConvertClass : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Class class_; bool isDeleted = false; @@ -199,7 +199,7 @@ public: class ConvertBook : public DefaultConverter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { ESM::Book book; bool isDeleted = false; @@ -215,7 +215,7 @@ public: class ConvertNPCC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); NPCC npcc; @@ -235,7 +235,7 @@ public: class ConvertREFR : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { REFR refr; refr.load(esm); @@ -261,7 +261,7 @@ public: } } } - virtual void write(ESM::ESMWriter& esm) + void write(ESM::ESMWriter& esm) override { esm.startRecord(ESM::REC_ASPL); esm.writeHNString("ID__", mSelectedSpell); @@ -280,14 +280,14 @@ public: mLevitationEnabled(true) {} - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { PCDT pcdt; pcdt.load(esm); convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState); } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { esm.startRecord(ESM::REC_ENAB); esm.writeHNT("TELE", mTeleportingEnabled); @@ -306,7 +306,7 @@ private: class ConvertCNTC : public Converter { - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); CNTC cntc; @@ -318,7 +318,7 @@ class ConvertCNTC : public Converter class ConvertCREC : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string id = esm.getHNString("NAME"); CREC crec; @@ -330,8 +330,8 @@ public: class ConvertFMAP : public Converter { public: - virtual void read(ESM::ESMReader &esm); - virtual void write(ESM::ESMWriter &esm); + void read(ESM::ESMReader &esm) override; + void write(ESM::ESMWriter &esm) override; private: osg::ref_ptr mGlobalMapImage; @@ -340,8 +340,8 @@ private: class ConvertCell : public Converter { public: - virtual void read(ESM::ESMReader& esm); - virtual void write(ESM::ESMWriter& esm); + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: struct Cell @@ -362,7 +362,7 @@ private: class ConvertKLST : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { KLST klst; klst.load(esm); @@ -371,7 +371,7 @@ public: mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { esm.startRecord(ESM::REC_DCOU); for (std::map::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) @@ -389,7 +389,7 @@ private: class ConvertFACT : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { ESM::Faction faction; bool isDeleted = false; @@ -409,7 +409,7 @@ public: class ConvertSTLN : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { std::string itemid = esm.getHNString("NAME"); Misc::StringUtils::lowerCaseInPlace(itemid); @@ -428,7 +428,7 @@ public: } } } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { ESM::StolenItems items; for (std::map >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) @@ -467,7 +467,7 @@ private: class ConvertINFO : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { INFO info; info.load(esm); @@ -477,7 +477,7 @@ public: class ConvertDIAL : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { std::string id = esm.getHNString("NAME"); DIAL dial; @@ -485,7 +485,7 @@ public: if (dial.mIndex > 0) mDials[id] = dial; } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { for (std::map::const_iterator it = mDials.begin(); it != mDials.end(); ++it) { @@ -505,7 +505,7 @@ private: class ConvertQUES : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { std::string id = esm.getHNString("NAME"); QUES quest; @@ -516,7 +516,7 @@ public: class ConvertJOUR : public Converter { public: - virtual void read(ESM::ESMReader& esm) + void read(ESM::ESMReader& esm) override { JOUR journal; journal.load(esm); @@ -531,7 +531,7 @@ public: { } - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { mGame.load(esm); mHasGame = true; @@ -551,7 +551,7 @@ public: } } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { if (!mHasGame) return; @@ -578,7 +578,7 @@ private: class ConvertSCPT : public Converter { public: - virtual void read(ESM::ESMReader &esm) + void read(ESM::ESMReader &esm) override { SCPT script; script.load(esm); @@ -586,7 +586,7 @@ public: convertSCPT(script, out); mScripts.push_back(out); } - virtual void write(ESM::ESMWriter &esm) + void write(ESM::ESMWriter &esm) override { for (const auto & script : mScripts) { @@ -603,9 +603,9 @@ private: class ConvertPROJ : public Converter { public: - virtual int getStage() override { return 2; } - virtual void read(ESM::ESMReader& esm) override; - virtual void write(ESM::ESMWriter& esm) override; + int getStage() override { return 2; } + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam); PROJ mProj; @@ -614,8 +614,8 @@ private: class ConvertSPLM : public Converter { public: - virtual void read(ESM::ESMReader& esm) override; - virtual void write(ESM::ESMWriter& esm) override; + void read(ESM::ESMReader& esm) override; + void write(ESM::ESMWriter& esm) override; private: SPLM mSPLM; }; diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index bf48d1f78..354eca32d 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -86,7 +86,9 @@ namespace ESSImport bool mHasANIS; ANIS mANIS; // scripted animation state - void load(ESM::ESMReader& esm); + virtual void load(ESM::ESMReader& esm); + + virtual ~ActorData() = default; }; } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index 556ed19bf..b115628d5 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -25,7 +25,9 @@ namespace ESSImport bool mDeleted; - void load(ESM::ESMReader& esm); + void load(ESM::ESMReader& esm) override; + + virtual ~CellRef() = default; }; } From 4a2fd1e14019b6817fd0737fa5278918c337bc77 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 11 Jun 2020 00:06:37 +0300 Subject: [PATCH 34/34] Revert the changes for "bug" #4551 --- apps/openmw/mwsound/soundmanagerimp.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index eff1cf0fd..162c176e6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -147,10 +147,11 @@ namespace MWSound volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); min = sound->mData.mMinRange; max = sound->mData.mMaxRange; - if (min == 0) + if (min == 0 && max == 0) + { min = fAudioDefaultMinDistance; - if (max == 0) max = fAudioDefaultMaxDistance; + } min *= fAudioMinDistanceMult; max *= fAudioMaxDistanceMult;