From 86f0e505e956583e9d62f13fd565a30f4a8f388f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jul 2015 23:27:25 +0200 Subject: [PATCH 01/15] Add a fixPosition for the --start exterior cell (Fixes #2790) --- apps/openmw/mwworld/worldimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 15c8cbeb2..a97c8f0d4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -228,6 +228,7 @@ namespace MWWorld if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos); + fixPosition(getPlayerPtr()); } else { From 0d8f07d5634a8127c350d3418e5b9dd836e67aa2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jul 2015 20:55:31 +0200 Subject: [PATCH 02/15] Properly apply changes when backing out of chargen dialogs (Fixes #2627) --- apps/openmw/mwgui/charactercreation.cpp | 107 +++++++++++------------- apps/openmw/mwgui/charactercreation.hpp | 5 ++ 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index d0a050526..6585a0dd0 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -312,7 +312,7 @@ namespace MWGui }; } - void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) + void CharacterCreation::selectPickedClass() { if (mPickClassDialog) { @@ -332,20 +332,18 @@ namespace MWGui } updatePlayerHealth(); + } + + void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) + { + selectPickedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } void CharacterCreation::onPickClassDialogBack() { - if (mPickClassDialog) - { - const std::string classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - } + selectPickedClass(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); @@ -390,29 +388,7 @@ namespace MWGui handleDialogDone(CSE_NameChosen, GM_Race); } - void CharacterCreation::onRaceDialogBack() - { - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); - } - MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); - } - - void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) + void CharacterCreation::selectRace() { if (mRaceDialog) { @@ -433,11 +409,24 @@ namespace MWGui } updatePlayerHealth(); + } + + void CharacterCreation::onRaceDialogBack() + { + selectRace(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + } + + void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) + { + selectRace(); handleDialogDone(CSE_RaceChosen, GM_Class); } - void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) + void CharacterCreation::selectBirthSign() { if (mBirthSignDialog) { @@ -449,24 +438,24 @@ namespace MWGui } updatePlayerHealth(); + } + + void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) + { + selectBirthSign(); handleDialogDone(CSE_BirthSignChosen, GM_Review); } void CharacterCreation::onBirthSignDialogBack() { - if (mBirthSignDialog) - { - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); - MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } + selectBirthSign(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } - void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) + void CharacterCreation::selectCreatedClass() { if (mCreateClassDialog) { @@ -495,19 +484,23 @@ namespace MWGui mPlayerClass = klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); - // Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later + // Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later mCreateClassDialog->setVisible(false); } - updatePlayerHealth(); + } + + void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) + { + selectCreatedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } void CharacterCreation::onCreateClassDialogBack() { - // Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later - mCreateClassDialog->setVisible(false); + // not done in MW, but we do it for consistency with the other dialogs + selectCreatedClass(); MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); @@ -631,18 +624,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); } - void CharacterCreation::onGenerateClassBack() - { - MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); - } - - void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) + void CharacterCreation::selectGeneratedClass() { MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; @@ -656,6 +638,19 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); updatePlayerHealth(); + } + + void CharacterCreation::onGenerateClassBack() + { + selectGeneratedClass(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) + { + selectGeneratedClass(); handleDialogDone(CSE_ClassChosen, GM_Birth); } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index f6e7c6c92..7fb67caf6 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -83,6 +83,7 @@ namespace MWGui //Race dialog void onRaceDialogDone(WindowBase* parWindow); void onRaceDialogBack(); + void selectRace(); //Class dialogs void onClassChoice(int _index); @@ -94,10 +95,14 @@ namespace MWGui void onClassQuestionChosen(int _index); void onGenerateClassBack(); void onGenerateClassDone(WindowBase* parWindow); + void selectGeneratedClass(); + void selectCreatedClass(); + void selectPickedClass(); //Birthsign dialog void onBirthSignDialogDone(WindowBase* parWindow); void onBirthSignDialogBack(); + void selectBirthSign(); //Review dialog void onReviewDialogDone(WindowBase* parWindow); From 211deeb63e2c5f70dd3a8a78dcb8f16cd17b2c5d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Aug 2015 17:33:34 +0200 Subject: [PATCH 03/15] Don't attempt to lock or unlock unsuitable objects (Fixes #2826) --- apps/openmw/mwclass/container.cpp | 13 +++++++------ apps/openmw/mwclass/container.hpp | 2 ++ apps/openmw/mwclass/door.cpp | 5 +++++ apps/openmw/mwclass/door.hpp | 2 ++ apps/openmw/mwmechanics/security.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 2 ++ 8 files changed, 27 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 862ae6c5d..830493627 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -281,9 +281,12 @@ namespace MWClass ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } + bool Container::canLock(const MWWorld::Ptr &ptr) const + { + return true; + } - MWWorld::Ptr - Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + MWWorld::Ptr Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -291,8 +294,7 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const + void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { const ESM::ContainerState& state2 = dynamic_cast (state); @@ -307,8 +309,7 @@ namespace MWClass readState (state2.mInventory); } - void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const + void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const { ESM::ContainerState& state2 = dynamic_cast (state); diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 3268d45d1..3541937d1 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -57,6 +57,8 @@ namespace MWClass virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object + virtual bool canLock(const MWWorld::Ptr &ptr) const; + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 5a8c736d9..5062cc557 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -207,6 +207,11 @@ namespace MWClass ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative } + bool Door::canLock(const MWWorld::Ptr &ptr) const + { + return true; + } + std::string Door::getScript (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 9cfb46509..db5a7f32a 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -47,6 +47,8 @@ namespace MWClass virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object + virtual bool canLock(const MWWorld::Ptr &ptr) const; + virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 9eab5bfef..97878db87 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -31,7 +31,7 @@ namespace MWMechanics void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, std::string& resultMessage, std::string& resultSound) { - if (!(lock.getCellRef().getLockLevel() > 0)) //If it's unlocked back out immediately + if (!(lock.getCellRef().getLockLevel() > 0) || !lock.getClass().canLock(lock)) //If it's unlocked back out immediately return; int lockStrength = lock.getCellRef().getLockLevel(); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1613300d7..675177508 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -562,7 +562,7 @@ namespace MWMechanics void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) { short effectId = effect.mId; - if (!target.getClass().isActor()) + if (target.getClass().canLock(target)) { if (effectId == ESM::MagicEffect::Lock) { @@ -570,7 +570,7 @@ namespace MWMechanics { if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); - target.getCellRef().setLockLevel(static_cast(magnitude)); + target.getClass().lock(target, static_cast(magnitude)); } } else if (effectId == ESM::MagicEffect::Open) @@ -586,7 +586,7 @@ namespace MWMechanics if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}"); } - target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); + target.getClass().unlock(target); } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 180cba332..18200e33b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -144,6 +144,11 @@ namespace MWWorld throw std::runtime_error ("class does not support unlocking"); } + bool Class::canLock(const Ptr &ptr) const + { + return false; + } + void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const { throw std::runtime_error ("class does not support time-based uses"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cd05b471b..1157db670 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -161,6 +161,8 @@ namespace MWWorld virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) + virtual bool canLock (const Ptr& ptr) const; + virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; ///< Sets the remaining duration of the object, such as an equippable light /// source. (default implementation: throw an exception) From 1e18a73b1c7ba1551a2eecbd1c4b45fd4298dd9a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Aug 2015 17:55:38 +0200 Subject: [PATCH 04/15] Don't play magic effect sounds & visual effects for unsuitable targets (Fixes #2811) --- apps/openmw/mwmechanics/spellcasting.cpp | 43 ++++++++++++++++-------- apps/openmw/mwmechanics/spellcasting.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 4 --- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 675177508..eacd674ed 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -489,8 +489,8 @@ namespace MWMechanics if (!wasDead && isDead) MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster); } - else - applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); + else if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude)) + continue; } // Re-casting a summon effect will remove the creature from previous castings of that effect. @@ -559,7 +559,7 @@ namespace MWMechanics target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) + bool CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) { short effectId = effect.mId; if (target.getClass().canLock(target)) @@ -572,6 +572,7 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); target.getClass().lock(target, static_cast(magnitude)); } + return true; } else if (effectId == ESM::MagicEffect::Open) { @@ -590,43 +591,55 @@ namespace MWMechanics } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); + return true; } } - else + else if (target.getClass().isActor()) { - if (effectId == ESM::MagicEffect::CurePoison) + switch (effectId) + { + case ESM::MagicEffect::CurePoison: target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); - else if (effectId == ESM::MagicEffect::CureParalyzation) + return true; + case ESM::MagicEffect::CureParalyzation: target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); - else if (effectId == ESM::MagicEffect::CureCommonDisease) + return true; + case ESM::MagicEffect::CureCommonDisease: target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease(); - else if (effectId == ESM::MagicEffect::CureBlightDisease) + return true; + case ESM::MagicEffect::CureBlightDisease: target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease(); - else if (effectId == ESM::MagicEffect::CureCorprusDisease) + return true; + case ESM::MagicEffect::CureCorprusDisease: target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); - else if (effectId == ESM::MagicEffect::Dispel) + return true; + case ESM::MagicEffect::Dispel: target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); - else if (effectId == ESM::MagicEffect::RemoveCurse) + return true; + case ESM::MagicEffect::RemoveCurse: target.getClass().getCreatureStats(target).getSpells().purgeCurses(); + return true; + } if (target != MWBase::Environment::get().getWorld()->getPlayerPtr()) - return; - if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) - return; + return false; if (effectId == ESM::MagicEffect::DivineIntervention) { MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker"); + return true; } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker"); + return true; } else if (effectId == ESM::MagicEffect::Mark) { MWBase::Environment::get().getWorld()->getPlayer().markPosition( target.getCell(), target.getRefData().getPosition()); + return true; } else if (effectId == ESM::MagicEffect::Recall) { @@ -640,8 +653,10 @@ namespace MWMechanics markedPosition, false); action.execute(target); } + return true; } } + return false; } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 418e9f56d..5b48bd4a8 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -97,7 +97,8 @@ namespace MWMechanics const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); /// @note \a caster can be any type of object, or even an empty object. - void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); + /// @return was the target suitable for the effect? + bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a97c8f0d4..fb2778940 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2656,10 +2656,6 @@ namespace MWWorld target = result.mHitObject; hitPosition = result.mHitPos; - // don't allow casting on non-activatable objects - if (!target.isEmpty() && !target.getClass().isActor() && target.getClass().getName(target).empty()) - target = MWWorld::Ptr(); - std::string selectedSpell = stats.getSpells().getSelectedSpell(); MWMechanics::CastSpell cast(actor, target); From e8c9d3ea2ae72fe72ae974e69a24490ab9c361a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Aug 2015 23:29:19 +0200 Subject: [PATCH 05/15] Fix cell names on world map not always being translated (Fixes #2832) --- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/mapwindow.hpp | 3 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index e26c076e7..2df1c8f3c 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -783,7 +783,7 @@ namespace MWGui MyGUI::Widget* markerWidget = mGlobalMap->createWidget("MarkerButton", widgetCoord, MyGUI::Align::Default); - markerWidget->setUserString("Caption_TextOneLine", name); + markerWidget->setUserString("Caption_TextOneLine", "#{sCell=" + name + "}"); setGlobalMapMarkerTooltip(markerWidget, x, y); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index a5594bee8..a41d5d527 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -190,7 +190,8 @@ namespace MWGui void renderGlobalMap(Loading::Listener* loadingListener); - // adds the marker to the global map + /// adds the marker to the global map + /// @param name The ESM::Cell::mName void addVisitedLocation(const std::string& name, int x, int y); // reveals this cell's map on the global map diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 4552c42f2..db5593704 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -993,7 +993,7 @@ namespace MWGui if (cell->getCell()->isExterior()) { if (!cell->getCell()->mName.empty()) - mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->getCell()->getGridX (), cell->getCell()->getGridY ()); + mMap->addVisitedLocation (name, cell->getCell()->getGridX (), cell->getCell()->getGridY ()); mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY()); } From d76fb2d26601bf6d082762b68b157860d41fec0a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Aug 2015 13:38:39 +0200 Subject: [PATCH 06/15] Apply disintegrate only to weapons and armor (Fixes #2853) --- apps/openmw/mwmechanics/spellcasting.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index eacd674ed..b98bf9f96 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -941,7 +941,8 @@ namespace MWMechanics MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator item = inv.getSlot(slot); - if (item != inv.end()) + + if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon)) { if (!item->getClass().hasItemHealth(*item)) return false; From c57e72fe03c35e3c888e7e894f718914ef71744d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Aug 2015 16:09:53 +0200 Subject: [PATCH 07/15] Adjust the sleep interruption chance (Fixes #2781) --- apps/openmw/mwgui/waitdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 4aeec146f..37a827745 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -157,7 +157,7 @@ namespace MWGui // figure out if player will be woken while sleeping int x = Misc::Rng::rollDice(hoursToWait); float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->getFloat(); - if (x < fSleepRandMod * hoursToWait) + if (x < static_cast(fSleepRandMod * hoursToWait)) { float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait); From 58cd2b1a84828f406e2970b7d688fc1e01b0ba38 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Aug 2015 19:01:21 +0200 Subject: [PATCH 08/15] Remove "Tri Bip*" nodes in creature meshes (meant for debugging)? (Fixes #2148) --- apps/openmw/mwrender/animation.cpp | 43 +++++++++++++++++++++- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ee0ce6df1..c1ae5fed5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -208,6 +208,38 @@ namespace std::vector mToRemove; }; + class RemoveTriBipVisitor : public osg::NodeVisitor + { + public: + RemoveTriBipVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + virtual void apply(osg::Geode &node) + { + const std::string toFind = "tri bip"; + if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) + { + // Not safe to remove in apply(), since the visitor is still iterating the child list + mToRemove.push_back(&node); + } + } + + void remove() + { + for (std::vector::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) + { + osg::Node* node = *it; + if (node->getNumParents()) + node->getParent(0)->removeChild(node); + } + } + + private: + std::vector mToRemove; + }; + } namespace MWRender @@ -898,7 +930,7 @@ namespace MWRender return movement; } - void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly) + void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) { if (mObjectRoot) { @@ -933,6 +965,13 @@ namespace MWRender removeDrawableVisitor.remove(); } + if (isCreature) + { + RemoveTriBipVisitor removeTriBipVisitor; + mObjectRoot->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); + } + NodeMapVisitor visitor; mObjectRoot->accept(visitor); mNodeMap = visitor.getNodeMap(); @@ -1308,7 +1347,7 @@ namespace MWRender { if (!model.empty()) { - setObjectRoot(model, false, false); + setObjectRoot(model, false, false, false); if (animated) addAnimSource(model); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 23f807238..30b6d156a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -273,7 +273,7 @@ protected: * @param baseonly If true, then any meshes or particle systems in the model are ignored * (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files). */ - void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly); + void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature); /* Adds the keyframe controllers in the specified model as a new animation source. Note that * the filename portion of the provided model name will be prepended with 'x', and the .nif diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 35294b1b5..f46736a39 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr, if(!model.empty()) { - setObjectRoot(model, false, false); + setObjectRoot(model, false, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\xbase_anim.nif"); @@ -42,7 +42,7 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const if(!model.empty()) { - setObjectRoot(model, true, false); + setObjectRoot(model, true, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\xbase_anim.nif"); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6417bc812..c5a107837 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -378,7 +378,7 @@ void NpcAnimation::updateNpcBase() : "meshes\\wolf\\skin.1st.nif"); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); - setObjectRoot(smodel, true, true); + setObjectRoot(smodel, true, true, false); if(mViewMode != VM_FirstPerson) { From 7dd09dd637c317d3d94497b9d1cca4e1c4e54420 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Aug 2015 20:44:15 +0200 Subject: [PATCH 09/15] Fix being able to flip journal pages with the mousewheel when the options overlay is active (Fixes #2855) --- apps/openmw/mwgui/journalwindow.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 4cfcc8064..8238f6585 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -61,6 +61,7 @@ namespace DisplayStateStack mStates; Book mTopicIndexBook; bool mQuestMode; + bool mOptionsMode; bool mAllQuests; template @@ -182,6 +183,7 @@ namespace mQuestMode = false; mAllQuests = false; + mOptionsMode = false; } void adjustButton (char const * name, bool optional = false) @@ -244,6 +246,7 @@ namespace void setBookMode () { + mOptionsMode = false; setVisible (OptionsBTN, true); setVisible (OptionsOverlay, false); @@ -253,6 +256,8 @@ namespace void setOptionsMode () { + mOptionsMode = true; + setVisible (OptionsBTN, false); setVisible (OptionsOverlay, true); @@ -508,6 +513,8 @@ namespace void notifyNextPage(MyGUI::Widget* _sender) { + if (mOptionsMode) + return; if (!mStates.empty ()) { unsigned int & page = mStates.top ().mPage; @@ -523,6 +530,8 @@ namespace void notifyPrevPage(MyGUI::Widget* _sender) { + if (mOptionsMode) + return; if (!mStates.empty ()) { unsigned int & page = mStates.top ().mPage; From 82419763687c7afea51ec807324099bff925d77a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Aug 2015 23:27:37 +0200 Subject: [PATCH 10/15] Don't attempt to play non-existing hit animations (Fixes #2856) --- apps/openmw/mwmechanics/character.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 22ddf1fb7..3959c413a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -247,15 +247,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { - if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0 + if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0 || mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0) + && mAnimation->hasAnimation("knockout")) { mHitState = CharState_KnockOut; mCurrentHit = "knockout"; mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul); mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); } - else if(knockdown) + else if(knockdown && mAnimation->hasAnimation("knockdown")) { mHitState = CharState_KnockDown; mCurrentHit = "knockdown"; @@ -263,11 +264,15 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } else if (recovery) { - mHitState = CharState_Hit; - mCurrentHit = chooseRandomGroup("hit"); - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); + std::string anim = chooseRandomGroup("hit"); + if (mAnimation->hasAnimation(anim)) + { + mHitState = CharState_Hit; + mCurrentHit = anim; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); + } } - else if (block) + else if (block && mAnimation->hasAnimation("shield")) { mHitState = CharState_Block; mCurrentHit = "shield"; From c25dacb4802c97761e66b9d5f157d847951774b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Aug 2015 01:50:44 +0200 Subject: [PATCH 11/15] Fix "Level" string in the save/load menu not being localised (Bug #2840) --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index d490d49cd..ae36cbacc 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -347,7 +347,7 @@ namespace MWGui char buffer[size]; if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) text << buffer << "\n"; - text << "Level " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; + text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; text << mCurrentSlot->mProfile.mPlayerCell << "\n"; // text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; From afb3d94ba41263127d90416302ed26f97885fc98 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Aug 2015 01:55:14 +0200 Subject: [PATCH 12/15] Fix cell names in the save/load menu not being localised (Fixes #2840) --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index ae36cbacc..82dd0d8a9 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -348,7 +348,7 @@ namespace MWGui if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) text << buffer << "\n"; text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; - text << mCurrentSlot->mProfile.mPlayerCell << "\n"; + text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCell << "}\n"; // text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour); From 8426d376f02765a5e446c818a1f2d0b40c481f3a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Aug 2015 02:06:04 +0200 Subject: [PATCH 13/15] Use separate touch spell raycasts for actors and objects (Fixes #2849) --- apps/openmw/mwworld/worldimp.cpp | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fb2778940..f1bcec731 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2624,12 +2624,10 @@ namespace MWWorld { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - // Get the target to use for "on touch" effects + // Get the target to use for "on touch" effects, using the facing direction from Head node MWWorld::Ptr target; float distance = 192.f; // ?? osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); - - // For NPCs use facing direction from Head node osg::Vec3f origin(actor.getRefData().getPosition().asVec3()); MWRender::Animation* anim = mRendering->getAnimation(actor); @@ -2652,9 +2650,33 @@ namespace MWWorld osg::Vec3f direction = orient * osg::Vec3f(0,1,0); osg::Vec3f dest = origin + direction * distance; - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(origin, dest, actor); - target = result.mHitObject; - hitPosition = result.mHitPos; + // For actor targets, we want to use bounding boxes (physics raycast). + // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise. + // For object targets, we want the detailed shapes (rendering raycast). + // If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf. + + MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, MWPhysics::CollisionType_Actor); + + MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true); + + float dist1 = FLT_MAX; + float dist2 = FLT_MAX; + + if (result1.mHit) + dist1 = (origin - result1.mHitPos).length(); + if (result2.mHit) + dist2 = (origin - result2.mHitPointWorld).length(); + + if (dist1 <= dist2 && result1.mHit) + { + target = result1.mHitObject; + hitPosition = result1.mHitPos; + } + else if (result2.mHit) + { + target = result2.mHitObject; + hitPosition = result2.mHitPointWorld; + } std::string selectedSpell = stats.getSpells().getSelectedSpell(); From c8d6679a25130a3085a8563c3d7411aa46ead54c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Aug 2015 17:40:20 +0200 Subject: [PATCH 14/15] Remove an unused function --- apps/openmw/mwgui/sortfilteritemmodel.cpp | 4 ---- apps/openmw/mwgui/sortfilteritemmodel.hpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 183ab07ff..1d086f2ee 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -71,7 +71,6 @@ namespace MWGui SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel) : mCategory(Category_All) , mFilter(0) - , mShowEquipped(true) , mSortByType(true) { mSourceModel = sourceModel; @@ -91,9 +90,6 @@ namespace MWGui { MWWorld::Ptr base = item.mBase; - if (item.mType == ItemStack::Type_Equipped && !mShowEquipped) - return false; - int category = 0; if (base.getTypeName() == typeid(ESM::Armor).name() || base.getTypeName() == typeid(ESM::Clothing).name()) diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 1b68bdd4f..064f62e77 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -24,7 +24,6 @@ namespace MWGui void setCategory (int category); void setFilter (int filter); - void setShowEquipped (bool show) { mShowEquipped = show; } /// Use ItemStack::Type for sorting? void setSortByType(bool sort) { mSortByType = sort; } @@ -49,7 +48,6 @@ namespace MWGui int mCategory; int mFilter; - bool mShowEquipped; bool mSortByType; }; From 9fad33cd14b33c7fde4523a1189801b7e6689e1b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Aug 2015 17:49:37 +0200 Subject: [PATCH 15/15] Don't reset the item model's sort/filter options in updatePlayer (Fixes #2863) --- apps/openmw/mwgui/inventorywindow.cpp | 7 ++++++- apps/openmw/mwgui/itemmodel.cpp | 19 +++++++++++++++++++ apps/openmw/mwgui/itemmodel.hpp | 4 ++++ apps/openmw/mwgui/itemview.cpp | 4 ++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 9c9138ed5..580e583c9 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -119,7 +119,12 @@ namespace MWGui { mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); - mSortModel = new SortFilterItemModel(mTradeModel); + + if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings + mSortModel->setSourceModel(mTradeModel); + else + mSortModel = new SortFilterItemModel(mTradeModel); + mItemView->setModel(mSortModel); mPreview->updatePtr(mPtr); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 9fce6e84d..a1e36bce6 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -120,6 +120,11 @@ namespace MWGui } + ProxyItemModel::ProxyItemModel() + : mSourceModel(NULL) + { + } + ProxyItemModel::~ProxyItemModel() { delete mSourceModel; @@ -164,4 +169,18 @@ namespace MWGui return mSourceModel->getIndex(item); } + void ProxyItemModel::setSourceModel(ItemModel *sourceModel) + { + if (mSourceModel == sourceModel) + return; + + if (mSourceModel) + { + delete mSourceModel; + mSourceModel = NULL; + } + + mSourceModel = sourceModel; + } + } diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 53c7018e2..2019c1042 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -80,11 +80,15 @@ namespace MWGui class ProxyItemModel : public ItemModel { public: + ProxyItemModel(); virtual ~ProxyItemModel(); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); + /// @note Takes ownership of the passed pointer. + void setSourceModel(ItemModel* sourceModel); + ModelIndex mapToSource (ModelIndex index); ModelIndex mapFromSource (ModelIndex index); protected: diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index df44eb33c..2cdfcada4 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -30,8 +30,12 @@ ItemView::~ItemView() void ItemView::setModel(ItemModel *model) { + if (mModel == model) + return; + delete mModel; mModel = model; + update(); }