From 421a9d2e5097cd129ee39854b4b394bb6684fa34 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 23 Sep 2019 23:56:37 +0300 Subject: [PATCH 01/20] [Regression] Fix pickpocket crashes --- apps/openmw/mwgui/pickpocketitemmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index e85715e87..2a167be2d 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -98,7 +98,7 @@ namespace MWGui if (pickpocket.finish()) { MWBase::Environment::get().getMechanicsManager()->commitCrime( - player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); + player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; } @@ -126,7 +126,7 @@ namespace MWGui if (pickpocket.pick(item, count)) { MWBase::Environment::get().getMechanicsManager()->commitCrime( - player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); + player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); mPickpocketDetected = true; return false; From 48aba76ce904738d428e79f1ee24ce170f2a8309 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 24 Sep 2019 09:30:39 +0400 Subject: [PATCH 02/20] Implement vanilla-style AiActivate (bug #4456) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aiactivate.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 811a276f1..b1ac9fa79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Bug #4383: Bow model obscures crosshair when arrow is drawn Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons Bug #4411: Reloading a saved game while falling prevents damage in some cases + Bug #4456: AiActivate should not be cancelled after target activation Bug #4540: Rain delay when exiting water Bug #4600: Crash when no sound output is available or --no-sound is used. Bug #4639: Black screen after completing first mages guild mission + training diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index e222c82bc..c412a7ea1 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -9,6 +9,7 @@ #include "creaturestats.hpp" #include "movement.hpp" +#include "steering.hpp" namespace MWMechanics { @@ -33,16 +34,18 @@ namespace MWMechanics if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled()) return true; - //Set the target destination for the actor - const osg::Vec3f dest = target.getRefData().getPosition().asVec3(); + // Turn to target and move to it directly, without pathfinding. + const osg::Vec3f targetDir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3(); - if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range + zTurn(actor, std::atan2(targetDir.x(), targetDir.y()), 0.f); + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + actor.getClass().getMovementSettings(actor).mPosition[0] = 0; + + if (MWBase::Environment::get().getWorld()->getMaxActivationDistance() >= targetDir.length()) { - // activate when reached + // Note: we intentionally do not cancel package after activation here for backward compatibility with original engine. MWBase::Environment::get().getWorld()->activate(target, actor); - return true; } - return false; } From 4d381d0804bd089c3a0d5a89243646206e2996ac Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 24 Sep 2019 22:42:04 +0300 Subject: [PATCH 03/20] Use random attack strength if there's no wind up anim (bug #5059) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 811a276f1..e1c51f0aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #5050: Invalid spell effects are not handled gracefully Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame Bug #5056: Calling Cast function on player doesn't equip the spell but casts it + Bug #5059: Modded animation with combined attack keys always does max damage and can double damage Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden Bug #5067: Ranged attacks on unaware opponents ("critical hits") differ from the vanilla engine diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9b6c1e0c3..2dbbfea35 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1629,7 +1629,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) { float attackStrength = complete; - if (!mPtr.getClass().isNpc()) + float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack"); + float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack"); + if (minAttackTime == maxAttackTime) { // most creatures don't actually have an attack wind-up animation, so use a uniform random value // (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings) @@ -1735,7 +1737,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle) { // If actor is already stopped preparing attack, do not play the "min attack -> max attack" part. // Happens if the player did not hold the attack button. - // Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be 1. + // Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be random. float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack"); float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack"); if (mAttackingOrSpell || minAttackTime == maxAttackTime) From 751accad0fc14f47edb98732081df690e318a9b5 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 28 Sep 2019 14:26:52 +0200 Subject: [PATCH 04/20] Remove unused field QuadTreeBuilder::mLodFactor --- components/terrain/quadtreeworld.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index b2f119b88..b2cb7a3b8 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -210,7 +210,6 @@ public: private: Terrain::Storage* mStorage; - float mLodFactor; float mMinX, mMaxX, mMinY, mMaxY; float mMinSize; From ca46da8b048dbc1f88499307ab312589576290d0 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 29 Sep 2019 13:08:52 +0400 Subject: [PATCH 05/20] Do not stack initially added scripted items (bug #5136) --- CHANGELOG.md | 1 + apps/openmw/mwworld/containerstore.cpp | 78 +++++++++++++++----------- apps/openmw/mwworld/containerstore.hpp | 1 + 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1ac9fa79..aaf052d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Bug #5124: Arrow remains attached to actor if pulling animation was cancelled Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5134: Doors rotation by "Lock" console command is inconsistent + Bug #5136: LegionUniform script: can not access local variables Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Bug #5149: Failing lock pick attempts isn't always a crime Bug #5155: Lock/unlock behavior differs from vanilla diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8c17dc0a9..76555de02 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -477,50 +477,64 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: int count, bool topLevel, const std::string& levItem) { if (count == 0) return; //Don't restock with nothing. - try { + try + { ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); - - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + if (ref.getPtr().getClass().getScript(ref.getPtr()).empty()) { - const ESM::ItemLevList* levItemList = ref.getPtr().get()->mBase; - - if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each) - { - for (int i=0; i 0 ? 1 : -1, true, levItemList->mId); - return; - } - else - { - std::string itemId = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); - if (itemId.empty()) - return; - addInitialItem(itemId, owner, count, false, levItemList->mId); - } + addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem); } else { - // A negative count indicates restocking items - // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks - if (!levItem.empty() && count < 0) - { - //If there is no item in map, insert it - std::map, int>::iterator itemInMap = - mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first; - //Update spawned count - itemInMap->second += std::abs(count); - } - count = std::abs(count); - - ref.getPtr().getCellRef().setOwner(owner); - addImp (ref.getPtr(), count); + // Adding just one item per time to make sure there isn't a stack of scripted items + for (int i = 0; i < abs(count); i++) + addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem); } } catch (const std::exception& e) { Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what(); } +} +void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner, + int count, bool topLevel, const std::string& levItem) +{ + if (ptr.getTypeName()==typeid (ESM::ItemLevList).name()) + { + const ESM::ItemLevList* levItemList = ptr.get()->mBase; + + if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each) + { + for (int i=0; i 0 ? 1 : -1, true, levItemList->mId); + return; + } + else + { + std::string itemId = MWMechanics::getLevelledItem(ptr.get()->mBase, false); + if (itemId.empty()) + return; + addInitialItem(itemId, owner, count, false, levItemList->mId); + } + } + else + { + // A negative count indicates restocking items + // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks + if (!levItem.empty() && count < 0) + { + //If there is no item in map, insert it + std::map, int>::iterator itemInMap = + mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first; + //Update spawned count + itemInMap->second += std::abs(count); + } + count = std::abs(count); + + ptr.getCellRef().setOwner(owner); + addImp (ptr, count); + } } void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b06e8b8ce..1e1e4a844 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -94,6 +94,7 @@ namespace MWWorld mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); + void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); template ContainerStoreIterator getState (CellRefList& collection, From 6253d2a7acf78b483baaf53e4aaf5285674175fd Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 29 Sep 2019 10:28:02 +0200 Subject: [PATCH 06/20] Undo door rotation once --- apps/openmw/mwworld/worldimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6f2a6bf4c..c914acfba 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1649,7 +1649,6 @@ namespace MWWorld } // we need to undo the rotation - rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot); reached = false; } } @@ -1671,6 +1670,8 @@ namespace MWWorld if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound)) MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound); } + + rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot); } // the rotation order we want to use From 7fbc696d44e60c4caf24ba25c635cb86f3d5dd8e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 25 Aug 2019 17:21:14 +0200 Subject: [PATCH 07/20] Change angle direction by rolling dice to avoid rotating door --- apps/openmw/mwmechanics/aiavoiddoor.cpp | 42 +++++++++++++++---------- apps/openmw/mwmechanics/aiavoiddoor.hpp | 10 ++++-- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 793bd89ea..c476c9b57 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -1,5 +1,7 @@ #include "aiavoiddoor.hpp" +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -11,8 +13,10 @@ #include "actorutil.hpp" #include "steering.hpp" +static const int MAX_DIRECTIONS = 4; + MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) -: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mLastPos(ESM::Position()), mAdjAngle(0) +: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0) { } @@ -22,25 +26,18 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont ESM::Position pos = actor.getRefData().getPosition(); if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing - mLastPos = pos; + mLastPos = pos.asVec3(); mDuration -= duration; //Update timer - if(mDuration < 0) { - float x = pos.pos[0] - mLastPos.pos[0]; - float y = pos.pos[1] - mLastPos.pos[1]; - float z = pos.pos[2] - mLastPos.pos[2]; - float distance = x * x + y * y + z * z; - if(distance < 10 * 10) { //Got stuck, didn't move - if(mAdjAngle == 0) //Try going in various directions - mAdjAngle = osg::PI / 2; - else if (mAdjAngle == osg::PI / 2) - mAdjAngle = -osg::PI / 2; - else - mAdjAngle = 0; + if (mDuration < 0) + { + if (isStuck(pos.asVec3())) + { + adjustDirection(); mDuration = 1; //reset timer } - else //Not stuck + else return true; // We have tried backing up for more than one second, we've probably cleared it } @@ -54,7 +51,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); // Turn away from the door and move when turn completed - if (zTurn(actor, std::atan2(x,y) + mAdjAngle, osg::DegreesToRadians(5.f))) + if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f))) actor.getClass().getMovementSettings(actor).mPosition[1] = 1; else actor.getClass().getMovementSettings(actor).mPosition[1] = 0; @@ -90,4 +87,17 @@ unsigned int MWMechanics::AiAvoidDoor::getPriority() const return 2; } +bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const +{ + return (actorPos - mLastPos).length2() < 10 * 10; +} +void MWMechanics::AiAvoidDoor::adjustDirection() +{ + mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS); +} + +float MWMechanics::AiAvoidDoor::getAdjustedAngle() const +{ + return 2 * osg::PI / MAX_DIRECTIONS * mDirection; +} diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 7344a1797..4c8be29eb 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -36,8 +36,14 @@ namespace MWMechanics private: float mDuration; MWWorld::ConstPtr mDoorPtr; - ESM::Position mLastPos; - float mAdjAngle; + osg::Vec3f mLastPos; + int mDirection; + + bool isStuck(const osg::Vec3f& actorPos) const; + + void adjustDirection(); + + float getAdjustedAngle() const; }; } #endif From 4efdc88f1ab820f9896181e17c54ea12a6274d7c Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 2 Oct 2019 19:12:06 +0000 Subject: [PATCH 08/20] Change shadow settings pluralisation so it matches the setting section name It used to say *Shadow Settings* as they were the settings that controlled the shadow system, but that keeps confusing people and no other section titles use that convention, so they're now *Shadows Settings* as they're the settings that control shadows. --- docs/source/reference/modding/settings/shadows.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 9b207c448..8bcc0522b 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -1,5 +1,5 @@ -Shadow Settings -############### +Shadows Settings +################ Main settings ************* From bde4a3818141fe55f24d5a52d40d00362844bec1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 3 Oct 2019 23:15:25 +0300 Subject: [PATCH 09/20] Cast Random result to float (bug #5175) --- CHANGELOG.md | 1 + components/compiler/exprparser.cpp | 2 +- components/interpreter/miscopcodes.hpp | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c7d5b36..47e5bb485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,7 @@ Bug #5167: Player can select and cast spells before magic menu is enabled Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn + Bug #5175: Random script function returns an integer value Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index e3a306b8f..017e36373 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -489,7 +489,7 @@ namespace Compiler parseArguments ("l", scanner); Generator::random (mCode); - mOperands.push_back ('l'); + mOperands.push_back ('f'); mNextOperand = false; return true; diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 29d1c063b..a77e0d7d8 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -190,9 +190,7 @@ namespace Interpreter throw std::runtime_error ( "random: argument out of range (Don't be so negative!)"); - Type_Integer value = Misc::Rng::rollDice(limit); // [o, limit) - - runtime[0].mInteger = value; + runtime[0].mFloat = static_cast(Misc::Rng::rollDice(limit)); // [o, limit) } }; From f0b73e0a276eac20fc2047c84dd89647ce4fc9be Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 5 Oct 2019 16:47:41 +0400 Subject: [PATCH 10/20] Do not store owners for items in container stores (bug #1933) --- CHANGELOG.md | 1 + apps/openmw/mwgui/companionitemmodel.cpp | 4 ++-- apps/openmw/mwgui/companionitemmodel.hpp | 2 +- apps/openmw/mwgui/containeritemmodel.cpp | 2 +- apps/openmw/mwgui/containeritemmodel.hpp | 2 +- apps/openmw/mwgui/hud.cpp | 5 ++--- apps/openmw/mwgui/inventoryitemmodel.cpp | 6 +++--- apps/openmw/mwgui/inventoryitemmodel.hpp | 2 +- apps/openmw/mwgui/itemmodel.cpp | 4 ++-- apps/openmw/mwgui/itemmodel.hpp | 4 ++-- apps/openmw/mwworld/containerstore.cpp | 26 +++++------------------- apps/openmw/mwworld/containerstore.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 4 ++-- apps/openmw/mwworld/inventorystore.hpp | 2 +- 14 files changed, 25 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e5bb485..09b4808dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ------ Bug #1515: Opening console masks dialogue, inventory menu + Bug #1933: Actors can have few stocks of the same item Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin Bug #2969: Scripted items can stack Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 0ff0b648e..87adc94c0 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -25,12 +25,12 @@ namespace MWGui { } - MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) + MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count) { if (hasProfit(mActor)) modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); - return InventoryItemModel::copyItem(item, count, setNewOwner); + return InventoryItemModel::copyItem(item, count); } void CompanionItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp index 4c77ee12f..5de5910fb 100644 --- a/apps/openmw/mwgui/companionitemmodel.hpp +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -13,7 +13,7 @@ namespace MWGui public: CompanionItemModel (const MWWorld::Ptr& actor); - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count); bool hasProfit(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 5ba5bb0eb..afb0e2488 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -91,7 +91,7 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) return -1; } -MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) +MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count) { const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index e8d1c5328..a2b14c46f 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -26,7 +26,7 @@ namespace MWGui virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count); virtual void update(); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 8468cf483..996ac4b73 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -38,7 +38,7 @@ namespace MWGui public: WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} virtual ~WorldItemModel() {} - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) { MWBase::World* world = MWBase::Environment::get().getWorld(); @@ -47,8 +47,7 @@ namespace MWGui dropped = world->placeObject(item.mBase, mLeft, mTop, count); else dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); - if (setNewOwner) - dropped.getCellRef().setOwner(""); + dropped.getCellRef().setOwner(""); return dropped; } diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index d74819a89..46094a6b2 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -46,11 +46,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) return -1; } -MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) +MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) throw std::runtime_error("Item to copy needs to be from a different container!"); - return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner); + return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor); } void InventoryItemModel::removeItem (const ItemStack& item, size_t count) @@ -88,7 +88,7 @@ MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, I if (item.mFlags & ItemStack::Flag_Bound) return MWWorld::Ptr(); - MWWorld::Ptr ret = otherModel->copyItem(item, count, false); + MWWorld::Ptr ret = otherModel->copyItem(item, count); removeItem(item, count); return ret; } diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 38730bd4d..f13bf44d2 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -17,7 +17,7 @@ namespace MWGui virtual bool onTakeItem(const MWWorld::Ptr &item, int count); - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count); /// Move items from this model to \a otherModel. diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 08c5bff35..16a0b0748 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -116,9 +116,9 @@ namespace MWGui return mSourceModel->allowedToUseItems(); } - MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) + MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count) { - return mSourceModel->copyItem (item, count, setNewOwner); + return mSourceModel->copyItem (item, count); } void ProxyItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index e8e348a8a..01bc1afb2 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -67,7 +67,7 @@ namespace MWGui /// @param setNewOwner If true, set the copied item's owner to the actor we are copying to, /// otherwise reset owner to "" - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; /// Is the player allowed to use items from this item model? (default true) @@ -97,7 +97,7 @@ namespace MWGui virtual bool onDropItem(const MWWorld::Ptr &item, int count); virtual bool onTakeItem(const MWWorld::Ptr &item, int count); - virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 76555de02..1eca3f4be 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -241,7 +241,6 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) } return ptr1 != ptr2 // an item never stacks onto itself - && ptr1.getCellRef().getOwner() == ptr2.getCellRef().getOwner() && ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul() && ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2) @@ -259,30 +258,14 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); - return add(ref.getPtr(), count, actorPtr, true); + return add(ref.getPtr(), count, actorPtr); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) { Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWWorld::ContainerStoreIterator it = end(); - - // HACK: Set owner on the original item, then reset it after we have copied it - // If we set the owner on the copied item, it would not stack correctly... - std::string oldOwner = itemPtr.getCellRef().getOwner(); - if (!setOwner || actorPtr == MWMechanics::getPlayer()) // No point in setting owner to the player - NPCs will not respect this anyway - { - itemPtr.getCellRef().setOwner(""); - } - else - { - itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId()); - } - - it = addImp(itemPtr, count); - - itemPtr.getCellRef().setOwner(oldOwner); + MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); // The copy of the original item we just made MWWorld::Ptr item = *it; @@ -298,7 +281,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr pos.pos[2] = 0; item.getCellRef().setPosition(pos); - // reset ownership stuff, owner was already handled above + // We do not need to store owners for items in container stores - we do not use it anyway. + item.getCellRef().setOwner(""); item.getCellRef().resetGlobalVariable(); item.getCellRef().setFaction(""); item.getCellRef().setFactionRank(-1); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1e1e4a844..b5daa1f9d 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -131,7 +131,7 @@ namespace MWWorld bool hasVisibleItems() const; - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 65b1186aa..ff58c87e8 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -133,9 +133,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) { - const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if (actorPtr != MWMechanics::getPlayer() diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e99a99bfa..1f98bda64 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -132,7 +132,7 @@ namespace MWWorld virtual InventoryStore* clone() { return new InventoryStore(*this); } - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// From 7f030fc3ee6681496cafbefb5d1868ffb3707ebe Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sun, 6 Oct 2019 13:23:49 +0300 Subject: [PATCH 11/20] copy mWnam record in copy constructor and custom assignment operator --- components/esm/loadland.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 722154757..5673e17b2 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -323,7 +323,12 @@ namespace ESM : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mContext (land.mContext), mDataTypes (land.mDataTypes), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) - {} + { + for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) + { + mWnam[i] = static_cast(land.mWnam[i]); + } + } Land& Land::operator= (Land land) { @@ -340,6 +345,10 @@ namespace ESM std::swap (mContext, land.mContext); std::swap (mDataTypes, land.mDataTypes); std::swap (mLandData, land.mLandData); + for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) + { + std::swap (mWnam[i], land.mWnam[i]); + } } const Land::LandData *Land::getLandData (int flags) const From e35ed960ee217b6ee87c7ea4162cc658ed86bd53 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sun, 6 Oct 2019 21:57:10 +0300 Subject: [PATCH 12/20] for -> std::copy --- components/esm/loadland.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 5673e17b2..00c2efe41 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -324,10 +324,7 @@ namespace ESM mContext (land.mContext), mDataTypes (land.mDataTypes), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) { - for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) - { - mWnam[i] = static_cast(land.mWnam[i]); - } + std::copy(land.mWnam, land.mWnam + LAND_GLOBAL_MAP_LOD_SIZE, mWnam); } Land& Land::operator= (Land land) From 2fdaacf23c78f9f70735e6c2235d9a9588aa0a2a Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sun, 6 Oct 2019 23:50:16 +0300 Subject: [PATCH 13/20] remove for array, just std::swap --- components/esm/loadland.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 00c2efe41..c52483681 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -342,10 +342,7 @@ namespace ESM std::swap (mContext, land.mContext); std::swap (mDataTypes, land.mDataTypes); std::swap (mLandData, land.mLandData); - for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) - { - std::swap (mWnam[i], land.mWnam[i]); - } + std::swap (mWnam, land.mWnam); } const Land::LandData *Land::getLandData (int flags) const From 39e21c618428b71ed36f498f105ef630fe33324c Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Mon, 7 Oct 2019 01:31:49 +0300 Subject: [PATCH 14/20] changelog: Map corruption bugfix --- CHANGELOG.md | 1 + CHANGELOG_PR.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa84ec10a..bbed2fa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,6 +138,7 @@ Bug #5134: Doors rotation by "Lock" console command is inconsistent Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Bug #5149: Failing lock pick attempts isn't always a crime + Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index afd2dafcc..3062cc9d2 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -102,6 +102,7 @@ Editor Bug Fixes: - Cloned, added, or moved instances no longer disappear at load-time (#4748) - "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757) - Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911) +- Minimap doesn't get corrupted, when editing new omwgame (#5177) Miscellaneous: - Upgraded to FFMPEG3 for media decoding (#4686) From f75f9cb337e4929aacd4e3aeadf6d290bac6139d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 5 Oct 2019 16:59:04 +0400 Subject: [PATCH 15/20] Use localized faction name when using TFH --- apps/openmw/mwgui/tooltips.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index d6d51d446..b86eba651 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -652,19 +652,22 @@ namespace MWGui std::string ret; ret += getMiscString(cellref.getOwner(), "Owner"); const std::string factionId = cellref.getFaction(); - ret += getMiscString(factionId, "Faction"); - if (!factionId.empty() && cellref.getFactionRank() >= 0) + if (!factionId.empty()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Faction *fact = store.get().search(factionId); if (fact != nullptr) { - int rank = cellref.getFactionRank(); - const std::string rankName = fact->mRanks[rank]; - if (rankName.empty()) - ret += getValueString(cellref.getFactionRank(), "Rank"); - else - ret += getMiscString(rankName, "Rank"); + ret += getMiscString(fact->mName.empty() ? factionId : fact->mName, "Owner Faction"); + if (cellref.getFactionRank() >= 0) + { + int rank = cellref.getFactionRank(); + const std::string rankName = fact->mRanks[rank]; + if (rankName.empty()) + ret += getValueString(cellref.getFactionRank(), "Rank"); + else + ret += getMiscString(rankName, "Rank"); + } } } From c51aba0b13e2ddd44ad9d9c0ec3d34c54ea2d1b3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 15 Dec 2018 10:23:50 +0400 Subject: [PATCH 16/20] Recharge items outside of player's inventory (bug #4077) --- CHANGELOG.md | 1 + apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 - apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/recharge.cpp | 56 +-------- .../mwmechanics/mechanicsmanagerimp.cpp | 10 -- .../mwmechanics/mechanicsmanagerimp.hpp | 2 - apps/openmw/mwmechanics/recharge.cpp | 92 +++++++++++++++ apps/openmw/mwmechanics/recharge.hpp | 15 +++ apps/openmw/mwworld/cells.cpp | 13 +++ apps/openmw/mwworld/cells.hpp | 2 + apps/openmw/mwworld/cellstore.cpp | 109 +++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 11 ++ apps/openmw/mwworld/containerstore.cpp | 48 +++++++- apps/openmw/mwworld/containerstore.hpp | 10 ++ apps/openmw/mwworld/inventorystore.cpp | 59 ---------- apps/openmw/mwworld/inventorystore.hpp | 14 --- apps/openmw/mwworld/worldimp.cpp | 29 ++++- apps/openmw/mwworld/worldimp.hpp | 1 + 20 files changed, 332 insertions(+), 146 deletions(-) create mode 100644 apps/openmw/mwmechanics/recharge.cpp create mode 100644 apps/openmw/mwmechanics/recharge.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e5bb485..ef908284b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe + Bug #4077: Enchanted items are not recharged if they are not in the player's inventory Bug #4202: Open .omwaddon files without needing toopen openmw-cs first Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect Bug #4270: Closing doors while they are obstructed desyncs closing sfx diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5c893845d..55d6b1af9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -81,7 +81,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actorutil drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe - aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting + aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellsuccess spellcasting disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7c92236ed..4d8d47d51 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -133,6 +133,7 @@ bool OMW::Engine::frame(float frametime) { double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; mEnvironment.getWorld()->advanceTime(hours, true); + mEnvironment.getWorld()->rechargeItems(frametime, true); } } osg::Timer_t afterScriptTick = osg::Timer::instance()->tick(); diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index ae898b24e..411f5fab1 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -73,8 +73,6 @@ namespace MWBase /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). - virtual void advanceTime (float duration) = 0; - virtual void setPlayerName (const std::string& name) = 0; ///< Set player name. diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 261f29608..4df175b89 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -584,6 +584,7 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; virtual void rest(double hours) = 0; + virtual void rechargeItems(double duration, bool activeOnly) = 0; virtual void setPlayerTraveling(bool traveling) = 0; virtual bool isPlayerTraveling() const = 0; diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 63da6092e..f8d89c0cb 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -17,6 +17,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/recharge.hpp" #include "itemwidget.hpp" #include "itemchargeview.hpp" @@ -130,62 +131,9 @@ void Recharge::onItemCancel() void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) { MWWorld::Ptr gem = *mGemIcon->getUserData(); - - if (!gem.getRefData().getCount()) + if (!MWMechanics::rechargeItem(item, gem)) return; - MWWorld::Ptr player = MWMechanics::getPlayer(); - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - - float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); - if (luckTerm < 1|| luckTerm > 10) - luckTerm = 1; - - float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); - - if (intelligenceTerm > 20) - intelligenceTerm = 20; - if (intelligenceTerm < 1) - intelligenceTerm = 1; - - float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); - int roll = Misc::Rng::roll0to99(); - if (roll < x) - { - std::string soul = gem.getCellRef().getSoul(); - const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(soul); - - float restored = creature->mData.mSoul * (roll / x); - - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( - item.getClass().getEnchantment(item)); - item.getCellRef().setEnchantmentCharge( - std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(enchantment->mData.mCharge))); - - MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); - - player.getClass().getContainerStore(player).restack(item); - } - else - { - MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); - } - - player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); - gem.getContainerStore()->remove(gem, 1, player); - - if (gem.getRefData().getCount() == 0) - { - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->mValue.getString(); - message = Misc::StringUtils::format(message, gem.getClass().getName(gem)); - - MWBase::Environment::get().getWindowManager()->messageBox(message); - - // special case: readd Azura's Star - if (Misc::StringUtils::ciEqual(gem.get()->mBase->mId, "Misc_SoulGem_Azura")) - player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); - } - updateView(); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a1ba0a5e7..695abe105 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -287,16 +287,6 @@ namespace MWMechanics mWatched = ptr; } - void MechanicsManager::advanceTime (float duration) - { - // Uses ingame time, but scaled to real time - const float timeScaleFactor = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - if (timeScaleFactor != 0.0f) - duration /= timeScaleFactor; - MWWorld::Ptr player = getPlayer(); - player.getClass().getInventoryStore(player).rechargeItems(duration); - } - 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 0ac9092e8..640bd3bdd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -78,8 +78,6 @@ namespace MWMechanics /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). - virtual void advanceTime (float duration) override; - virtual void setPlayerName (const std::string& name) override; ///< Set player name. diff --git a/apps/openmw/mwmechanics/recharge.cpp b/apps/openmw/mwmechanics/recharge.cpp new file mode 100644 index 000000000..51c78e1e3 --- /dev/null +++ b/apps/openmw/mwmechanics/recharge.cpp @@ -0,0 +1,92 @@ +#include "recharge.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "creaturestats.hpp" +#include "actorutil.hpp" + +namespace MWMechanics +{ + +bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration) +{ + float charge = item.getCellRef().getEnchantmentCharge(); + if (charge == -1 || charge == maxCharge) + return false; + + static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( + "fMagicItemRechargePerSecond")->mValue.getFloat(); + + item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge)); + return true; +} + +bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem) +{ + if (!gem.getRefData().getCount()) + return false; + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + + float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); + if (luckTerm < 1 || luckTerm > 10) + luckTerm = 1; + + float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); + + if (intelligenceTerm > 20) + intelligenceTerm = 20; + if (intelligenceTerm < 1) + intelligenceTerm = 1; + + float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); + int roll = Misc::Rng::roll0to99(); + if (roll < x) + { + std::string soul = gem.getCellRef().getSoul(); + const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(soul); + + float restored = creature->mData.mSoul * (roll / x); + + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( + item.getClass().getEnchantment(item)); + item.getCellRef().setEnchantmentCharge( + std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(enchantment->mData.mCharge))); + + MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); + + player.getClass().getContainerStore(player).restack(item); + } + else + { + MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); + } + + player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); + gem.getContainerStore()->remove(gem, 1, player); + + if (gem.getRefData().getCount() == 0) + { + std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->mValue.getString(); + message = Misc::StringUtils::format(message, gem.getClass().getName(gem)); + + MWBase::Environment::get().getWindowManager()->messageBox(message); + + // special case: readd Azura's Star + if (Misc::StringUtils::ciEqual(gem.get()->mBase->mId, "Misc_SoulGem_Azura")) + player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player); + } + + return true; +} + +} diff --git a/apps/openmw/mwmechanics/recharge.hpp b/apps/openmw/mwmechanics/recharge.hpp new file mode 100644 index 000000000..913f2109b --- /dev/null +++ b/apps/openmw/mwmechanics/recharge.hpp @@ -0,0 +1,15 @@ +#ifndef MWMECHANICS_RECHARGE_H +#define MWMECHANICS_RECHARGE_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration); + + bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem); + +} + +#endif diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index bea5825da..a724c9bdb 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -163,6 +163,19 @@ void MWWorld::Cells::rest (double hours) } } +void MWWorld::Cells::recharge (float duration) +{ + for (auto &interior : mInteriors) + { + interior.second.recharge(duration); + } + + for (auto &exterior : mExteriors) + { + exterior.second.recharge(duration); + } +} + MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) { if (id.mPaged) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index dce42d996..4a6d7abf6 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -61,7 +61,9 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + void rest (double hours); + void recharge (float duration); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index b3264fb7a..3d9c24009 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -21,6 +21,7 @@ #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/recharge.hpp" #include "ptr.hpp" #include "esmstore.hpp" @@ -328,6 +329,7 @@ namespace MWWorld void CellStore::updateMergedRefs() { mMergedRefs.clear(); + mRechargingItemsUpToDate = false; MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); forEachInternal(visitor); visitor.merge(); @@ -345,7 +347,7 @@ namespace MWWorld } CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) - : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) + : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false) { mWaterLevel = cell->mWater; } @@ -992,6 +994,42 @@ namespace MWWorld } } + void CellStore::recharge(float duration) + { + if (duration <= 0) + return; + + if (mState == State_Loaded) + { + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + ptr.getClass().getContainerStore(ptr).rechargeItems(duration); + } + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + ptr.getClass().getContainerStore(ptr).rechargeItems(duration); + } + } + for (CellRefList::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0) + { + ptr.getClass().getContainerStore(ptr).rechargeItems(duration); + } + } + + rechargeItems(duration); + } + } + void CellStore::respawn() { if (mState == State_Loaded) @@ -1027,4 +1065,73 @@ namespace MWWorld } } } + + void MWWorld::CellStore::rechargeItems(float duration) + { + if (!mRechargingItemsUpToDate) + { + updateRechargingItems(); + mRechargingItemsUpToDate = true; + } + for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) + { + MWMechanics::rechargeItem(it->first, it->second, duration); + } + } + + void MWWorld::CellStore::updateRechargingItems() + { + mRechargingItems.clear(); + + for (CellRefList::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + checkItem(ptr); + } + } + for (CellRefList::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + checkItem(ptr); + } + } + for (CellRefList::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + checkItem(ptr); + } + } + for (CellRefList::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + checkItem(ptr); + } + } + } + + void MWWorld::CellStore::checkItem(Ptr ptr) + { + if (ptr.getClass().getEnchantment(ptr).empty()) + return; + + std::string enchantmentId = ptr.getClass().getEnchantment(ptr); + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search(enchantmentId); + if (!enchantment) + { + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << ptr.getCellRef().getRefId(); + return; + } + + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed + || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) + mRechargingItems.emplace_back(ptr.getBase(), static_cast(enchantment->mData.mCharge)); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index baecfb5ea..4872625c1 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -118,6 +118,16 @@ namespace MWWorld /// Repopulate mMergedRefs. void updateMergedRefs(); + // (item, max charge) + typedef std::vector > TRechargingItems; + TRechargingItems mRechargingItems; + + bool mRechargingItemsUpToDate; + + void updateRechargingItems(); + void rechargeItems(float duration); + void checkItem(Ptr ptr); + // helper function for forEachInternal template bool forEachImp (Visitor& visitor, List& list) @@ -184,6 +194,7 @@ namespace MWWorld MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); void rest(double hours); + void recharge(float duration); /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 76555de02..60aa97da2 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -13,6 +13,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/recharge.hpp" #include "manualref.hpp" #include "refdata.hpp" @@ -114,7 +115,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; -MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {} +MWWorld::ContainerStore::ContainerStore() + : mListener(nullptr) + , mRechargingItemsUpToDate(false) + , mCachedWeight (0) + , mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -408,6 +413,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons return it; } +void MWWorld::ContainerStore::rechargeItems(float duration) +{ + if (!mRechargingItemsUpToDate) + { + updateRechargingItems(); + mRechargingItemsUpToDate = true; + } + for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) + { + if (!MWMechanics::rechargeItem(*it->first, it->second, duration)) + continue; + + // attempt to restack when fully recharged + if (it->first->getCellRef().getEnchantmentCharge() == it->second) + it->first = restack(*it->first); + } +} + +void MWWorld::ContainerStore::updateRechargingItems() +{ + mRechargingItems.clear(); + for (ContainerStoreIterator it = begin(); it != end(); ++it) + { + const std::string& enchantmentId = it->getClass().getEnchantment(*it); + if (!enchantmentId.empty()) + { + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search(enchantmentId); + if (!enchantment) + { + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); + continue; + } + + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed + || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) + mRechargingItems.emplace_back(it, static_cast(enchantment->mData.mCharge)); + } + } +} + int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) { int toRemove = count; @@ -633,6 +678,7 @@ void MWWorld::ContainerStore::clear() void MWWorld::ContainerStore::flagAsModified() { mWeightUpToDate = false; + mRechargingItemsUpToDate = false; } float MWWorld::ContainerStore::getWeight() const diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1e1e4a844..9ee4eef7f 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -71,6 +71,12 @@ namespace MWWorld protected: ContainerStoreListener* mListener; + // (item, max charge) + typedef std::vector > TRechargingItems; + TRechargingItems mRechargingItems; + + bool mRechargingItemsUpToDate; + private: MWWorld::CellRefList potions; @@ -108,6 +114,7 @@ namespace MWWorld ESM::InventoryState& inventory, int& index, bool equipable = false) const; + void updateRechargingItems(); virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; @@ -156,6 +163,9 @@ namespace MWWorld /// /// @return the number of items actually removed + void rechargeItems (float duration); + ///< Restore charge on enchanted items. Note this should only be done for the player. + ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1); ///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count). /// diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 65b1186aa..79b41a795 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -101,7 +101,6 @@ MWWorld::InventoryStore::InventoryStore() , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) - , mRechargingItemsUpToDate(false) { initSlots (mSlots); } @@ -114,7 +113,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) , mFirstAutoEquip(store.mFirstAutoEquip) , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) , mSelectedEnchantItem(end()) - , mRechargingItemsUpToDate(false) { copySlots (store); } @@ -709,12 +707,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) mFirstAutoEquip = false; } -void MWWorld::InventoryStore::flagAsModified() -{ - ContainerStore::flagAsModified(); - mRechargingItemsUpToDate = false; -} - bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const { bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); @@ -957,57 +949,6 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito } } -void MWWorld::InventoryStore::updateRechargingItems() -{ - mRechargingItems.clear(); - for (ContainerStoreIterator it = begin(); it != end(); ++it) - { - if (it->getClass().getEnchantment(*it) != "") - { - std::string enchantmentId = it->getClass().getEnchantment(*it); - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search( - enchantmentId); - if (!enchantment) - { - Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); - continue; - } - - if (enchantment->mData.mType == ESM::Enchantment::WhenUsed - || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) - mRechargingItems.push_back(std::make_pair(it, static_cast(enchantment->mData.mCharge))); - } - } -} - -void MWWorld::InventoryStore::rechargeItems(float duration) -{ - if (!mRechargingItemsUpToDate) - { - updateRechargingItems(); - mRechargingItemsUpToDate = true; - } - for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) - { - if (it->first->getCellRef().getEnchantmentCharge() == -1 - || it->first->getCellRef().getEnchantmentCharge() == it->second) - continue; - - static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicItemRechargePerSecond")->mValue.getFloat(); - - if (it->first->getCellRef().getEnchantmentCharge() <= it->second) - { - it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, - it->second)); - - // attempt to restack when fully recharged - if (it->first->getCellRef().getEnchantmentCharge() == it->second) - it->first = restack(*it->first); - } - } -} - void MWWorld::InventoryStore::purgeEffect(short effectId) { for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e99a99bfa..11524b1a8 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -101,18 +101,11 @@ namespace MWWorld // selected magic item (for using enchantments of type "Cast once" or "Cast when used") ContainerStoreIterator mSelectedEnchantItem; - // (item, max charge) - typedef std::vector > TRechargingItems; - TRechargingItems mRechargingItems; - - bool mRechargingItemsUpToDate; - void copySlots (const InventoryStore& store); void initSlots (TSlots& slots_); void updateMagicEffects(const Ptr& actor); - void updateRechargingItems(); void fireEquipmentChangedEvent(const Ptr& actor); @@ -171,10 +164,6 @@ namespace MWWorld const MWMechanics::MagicEffects& getMagicEffects() const; ///< Return magic effects from worn items. - virtual void flagAsModified(); - ///< \attention This function is internal to the world model and should not be called from - /// outside. - virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const; ///< @return true if the two specified objects can stack with each other @@ -216,9 +205,6 @@ namespace MWWorld void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); - void rechargeItems (float duration); - ///< Restore charge on enchanted items. Note this should only be done for the player. - void purgeEffect (short effectId); ///< Remove a magic effect diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c914acfba..42cd1c6dc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -857,7 +857,17 @@ namespace MWWorld void World::advanceTime (double hours, bool incremental) { - MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast(hours * 3600)); + if (!incremental) + { + // When we fast-forward time, we should recharge magic items + // in all loaded cells, using game world time + float duration = hours * 3600; + const float timeScaleFactor = getTimeScaleFactor(); + if (timeScaleFactor != 0.0f) + duration /= timeScaleFactor; + + rechargeItems(duration, false); + } mWeatherManager->advanceTime (hours, incremental); @@ -3305,7 +3315,6 @@ namespace MWWorld closestDistance = distance; closestMarker = marker; } - } return closestMarker; @@ -3316,6 +3325,22 @@ namespace MWWorld mCells.rest(hours); } + void World::rechargeItems(double duration, bool activeOnly) + { + MWWorld::Ptr player = getPlayerPtr(); + player.getClass().getInventoryStore(player).rechargeItems(duration); + + if (activeOnly) + { + for (auto &cell : mWorldScene->getActiveCells()) + { + cell->recharge(duration); + } + } + else + mCells.recharge(duration); + } + void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f803079fb..e67ddbb72 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -574,6 +574,7 @@ namespace MWWorld ///< check if the player is allowed to rest void rest(double hours) override; + void rechargeItems(double duration, bool activeOnly) override; /// \todo Probably shouldn't be here MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; From 301c05662b40c0bde271c15af6aeb12004a1855a Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 9 Oct 2019 02:24:33 +0300 Subject: [PATCH 17/20] assignment to memb. init. list, publics to private, virtual -> final --- .../opencs/view/render/terraintexturemode.cpp | 4 +-- .../opencs/view/render/terraintexturemode.hpp | 33 +++++++++---------- .../view/widget/scenetooltexturebrush.cpp | 8 ++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index b8181eed5..9b09614e6 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -43,7 +43,7 @@ CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceW mBrushTexture("L0#0"), mBrushSize(0), mBrushShape(0), - mTextureBrushScenetool(0), + mTextureBrushScenetool(nullptr), mDragMode(InteractionType_None), mParentNode(parentNode), mIsEditing(false) @@ -82,7 +82,7 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) { toolbar->removeTool (mTextureBrushScenetool); delete mTextureBrushScenetool; - mTextureBrushScenetool = 0; + mTextureBrushScenetool = nullptr; } if (mTerrainTextureSelection) diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 0d670d725..4176abefe 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -51,36 +51,37 @@ namespace CSVRender /// \brief Editmode for terrain texture grid TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - void primaryOpenPressed (const WorldspaceHitResult& hit); + void primaryOpenPressed (const WorldspaceHitResult& hit) final; /// \brief Create single command for one-click texture editing - void primaryEditPressed (const WorldspaceHitResult& hit); + void primaryEditPressed (const WorldspaceHitResult& hit) final; /// \brief Open brush settings window - void primarySelectPressed(const WorldspaceHitResult&); + void primarySelectPressed(const WorldspaceHitResult&) final; - void secondarySelectPressed(const WorldspaceHitResult&); + void secondarySelectPressed(const WorldspaceHitResult&) final; - void activate(CSVWidget::SceneToolbar*); - void deactivate(CSVWidget::SceneToolbar*); + void activate(CSVWidget::SceneToolbar*) final; + void deactivate(CSVWidget::SceneToolbar*) final; /// \brief Start texture editing command macro - virtual bool primaryEditStartDrag (const QPoint& pos); + bool primaryEditStartDrag (const QPoint& pos) final; - virtual bool secondaryEditStartDrag (const QPoint& pos); - virtual bool primarySelectStartDrag (const QPoint& pos); - virtual bool secondarySelectStartDrag (const QPoint& pos); + bool secondaryEditStartDrag (const QPoint& pos) final; + bool primarySelectStartDrag (const QPoint& pos) final; + bool secondarySelectStartDrag (const QPoint& pos) final; /// \brief Handle texture edit behavior during dragging - virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor); + void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final; /// \brief End texture editing command macro - virtual void dragCompleted(const QPoint& pos); + void dragCompleted(const QPoint& pos) final; - virtual void dragAborted(); - virtual void dragWheel (int diff, double speedFactor); - virtual void dragMoveEvent (QDragMoveEvent *event); + void dragAborted() final; + void dragWheel (int diff, double speedFactor) final; + void dragMoveEvent (QDragMoveEvent *event) final; + private: /// \brief Handle brush mechanics, maths regarding worldspace hit etc. void editTerrainTextureGrid (const WorldspaceHitResult& hit); @@ -100,7 +101,6 @@ namespace CSVRender /// \brief Create new cell and land if needed bool allowLandTextureEditing(std::string textureFileName); - private: std::string mCellId; std::string mBrushTexture; int mBrushSize; @@ -113,7 +113,6 @@ namespace CSVRender std::unique_ptr mTerrainTextureSelection; const int cellSize {ESM::Land::REAL_SIZE}; - const int landSize {ESM::Land::LAND_SIZE}; const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; signals: diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index 2208f88a6..1dc1f7e46 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -33,19 +33,19 @@ CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent) - : QGroupBox(title, parent) + : QGroupBox(title, parent), + mLayoutSliderSize(new QHBoxLayout), + mBrushSizeSlider(new QSlider(Qt::Horizontal)), + mBrushSizeSpinBox(new QSpinBox) { - mBrushSizeSlider = new QSlider(Qt::Horizontal); mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides); mBrushSizeSlider->setTickInterval(10); mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); mBrushSizeSlider->setSingleStep(1); - mBrushSizeSpinBox = new QSpinBox; mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); mBrushSizeSpinBox->setSingleStep(1); - mLayoutSliderSize = new QHBoxLayout; mLayoutSliderSize->addWidget(mBrushSizeSlider); mLayoutSliderSize->addWidget(mBrushSizeSpinBox); From fbb9800e89d169dd66c99e715168a73cfa8f301f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 10 Oct 2019 14:21:43 +0300 Subject: [PATCH 18/20] Better terrain texture ID handling --- .../view/widget/scenetooltexturebrush.cpp | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index 1dc1f7e46..b199a2706 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -142,60 +142,61 @@ void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton * void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture) { - mBrushTexture = brushTexture; + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + QUndoStack& undoStack = mDocument.getUndoStack(); CSMWorld::IdCollection& landtexturesCollection = mDocument.getData().getLandTextures(); - int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture); - int columnModification = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Modification); - int index = landtexturesCollection.searchId(mBrushTexture); - // Check if texture exists in current plugin - if(landtexturesCollection.getData(index, columnModification).value() == 0) + int index = 0; + int pluginInDragged = 0; + CSMWorld::LandTexture::parseUniqueRecordId(brushTexture, pluginInDragged, index); + std::string newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, index); + int rowInBase = landtexturesCollection.searchId(brushTexture); + int rowInNew = landtexturesCollection.searchId(newBrushTextureId); + + // Check if texture exists in current plugin, and clone if id found in base, otherwise reindex the texture + // TO-DO: Handle case when texture is not found in neither base or plugin properly (finding new index is not enough) + // TO-DO: Handle conflicting plugins properly + if (rowInNew == -1) { - CSMWorld::IdTable& ltexTable = dynamic_cast ( - *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); - - QUndoStack& undoStack = mDocument.getUndoStack(); - - QVariant textureFileNameVariant; - textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value()); - - std::size_t hashlocation = mBrushTexture.find("#"); - std::string mBrushTexturePlugin = "L0#" + mBrushTexture.substr (hashlocation+1); - int indexPlugin = landtexturesCollection.searchId(mBrushTexturePlugin); - - // Reindex texture if needed - if (indexPlugin != -1 && !landtexturesCollection.getRecord(indexPlugin).isDeleted()) + if (rowInBase == -1) { int counter=0; bool freeIndexFound = false; + const int maxCounter = std::numeric_limits::max() - 1; do { - const size_t maxCounter = std::numeric_limits::max() - 1; - mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter); - if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter; + newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); + if (landtexturesCollection.searchId(brushTexture) != -1 && + landtexturesCollection.getRecord(brushTexture).isDeleted() == 0 && + landtexturesCollection.searchId(newBrushTextureId) != -1 && + landtexturesCollection.getRecord(newBrushTextureId).isDeleted() == 0) + counter = (counter + 1) % maxCounter; else freeIndexFound = true; - } while (freeIndexFound == false); + } while (freeIndexFound == false || counter < maxCounter); } undoStack.beginMacro ("Add land texture record"); - undoStack.push (new CSMWorld::CloneCommand (ltexTable, mBrushTexture, mBrushTexturePlugin, CSMWorld::UniversalId::Type_LandTexture)); + undoStack.push (new CSMWorld::CloneCommand (ltexTable, brushTexture, newBrushTextureId, CSMWorld::UniversalId::Type_LandTexture)); undoStack.endMacro(); - mBrushTexture = mBrushTexturePlugin; - emit passTextureId(mBrushTexture); } if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) { - mBrushTextureLabel = "Selected texture: " + mBrushTexture + " "; + mBrushTextureLabel = "Selected texture: " + newBrushTextureId + " "; mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value()); } else { + newBrushTextureId = ""; mBrushTextureLabel = "No selected texture or invalid texture"; mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel)); } - emit passBrushShape(mBrushShape); // update icon + mBrushTexture = newBrushTextureId; + + emit passTextureId(mBrushTexture); + emit passBrushShape(mBrushShape); // updates the icon tooltip } void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize) From 78f2a5181d941da16674c0f4f915ac79c31af74f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 10 Oct 2019 14:22:58 +0300 Subject: [PATCH 19/20] Default brush size to 1 --- apps/opencs/view/render/terraintexturemode.cpp | 2 +- apps/opencs/view/widget/scenetooltexturebrush.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 9b09614e6..6d682150a 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -41,7 +41,7 @@ CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), mBrushTexture("L0#0"), - mBrushSize(0), + mBrushSize(1), mBrushShape(0), mTextureBrushScenetool(nullptr), mDragMode(InteractionType_None), diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp index b199a2706..408187279 100644 --- a/apps/opencs/view/widget/scenetooltexturebrush.cpp +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -58,7 +58,7 @@ CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *p CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent) : QFrame(parent, Qt::Popup), mBrushShape(0), - mBrushSize(0), + mBrushSize(1), mBrushTexture("L0#0"), mDocument(document) { From 9af8f30819a7b45714773df600ac3d01e59adb75 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 10 Oct 2019 14:30:06 +0300 Subject: [PATCH 20/20] Add changelog --- CHANGELOG.md | 1 + CHANGELOG_PR.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bafad417..0bb5a0448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ Bug #4888: Global variable stray explicit reference calls break script compilation Bug #4896: Title screen music doesn't loop Bug #4902: Using scrollbars in settings causes resolution to change + Bug #4904: Editor: Texture painting with duplicate of a base-version texture Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. Bug #4918: Abilities don't play looping VFX when they're initially applied diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 3062cc9d2..8862a8448 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -101,6 +101,7 @@ Editor Bug Fixes: - Colour fields in interior-cell records now also use the colour picker widget (#4745) - Cloned, added, or moved instances no longer disappear at load-time (#4748) - "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757) +- Terrain texture editing for plugins now correctly handles drags from base file (#4904) - Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911) - Minimap doesn't get corrupted, when editing new omwgame (#5177)