From b1dfb0bc294b5b5580671961e6b6829d852a73b7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Jul 2018 12:57:09 +0200 Subject: [PATCH 001/175] stage1: priorities for event music and other minor improvements to the music system --- docs/openmw-stage1.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/openmw-stage1.md b/docs/openmw-stage1.md index 4707a44354..4290080b8a 100644 --- a/docs/openmw-stage1.md +++ b/docs/openmw-stage1.md @@ -1383,9 +1383,9 @@ We add several new script instructions: * PlayBackgroundMusic (t) * PlaylistBackgroundMusic (pl) * StopBackgroundMusic -* PlayEventMusic (t, loop=0, force=0) -* PlaylistEventMusic (pl, loop=0, force=0) -* StopEventMusic +* PlayEventMusic (t, priority, loop=0, force=0) +* PlaylistEventMusic (pl, priority, loop=0, force=0) +* StopEventMusic (priority) Arguments: @@ -1393,8 +1393,14 @@ Arguments: * pl (string): a playlist (specified by its ID) * loop (integer): looping or not * force (integer): Terminate any other running event music first. If this flag is 0 and there is already event music playing the new event music is ignored. Event music provided by content developers will usually not set this flag. +* priority (integer): Priority for event music. If there are multiple instances of active event music only the one with the highest priority will play. There can only ever be one instance of event music for any priority level. If event music of higher priority stops and there is event music of lower priority the event music of lower priority starts to play. -Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. +Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. Changes in looping behaviour are considered though. + +New games starting with a new format omwgame file can use priorities as the developer sees fit. For old games where automatic injection of scripts and music records take place we define the following priorities: + +* 10: Combat music +* 0: Basic event music ### Transition of Existing System From c79f96d0d2c607c5c31c056d7acb4c1699310c0c Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sun, 29 Jul 2018 19:27:13 +0300 Subject: [PATCH 002/175] Implement ranged crits (feature #3703) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/combat.cpp | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a0..8e21969dde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window + Feature #3703: Ranged sneak attack criticals Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6b45a513be..5910dbacd0 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -209,8 +209,21 @@ namespace MWMechanics adjustWeaponDamage(damage, weapon, attacker); - if(attacker == getPlayer()) + if (attacker == getPlayer()) + { attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0); + const MWMechanics::AiSequence& sequence = victim.getClass().getCreatureStats(victim).getAiSequence(); + + bool unaware = !sequence.isInCombat() + && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim); + + if (unaware) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + } if (victim.getClass().getCreatureStats(victim).getKnockedDown()) damage *= gmst.find("fCombatKODamageMult")->getFloat(); From 0f2c3ecb17fa73de4cf2a83e3a94ac7899cb0f26 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 16:42:45 +0400 Subject: [PATCH 003/175] Rescale player avatar (bug #4539) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 14 ++++++++++++-- apps/openmw/mwgui/inventorywindow.hpp | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a0..31d9134e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode + Bug #4539: Paper Doll is affected by GUI scaling Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index e909ecbba1..ce6d0d522c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -67,7 +67,12 @@ namespace MWGui , mLastYSize(0) , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) + , mScaleFactor(1.0f) { + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale > 1.0) + mScaleFactor = uiScale; + mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreview->rebuild(); @@ -431,10 +436,10 @@ namespace MWGui MyGUI::IntSize size = mAvatarImage->getSize(); int width = std::min(mPreview->getTextureWidth(), size.width); int height = std::min(mPreview->getTextureHeight(), size.height); - mPreview->setViewport(width, height); + mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor)); mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, - width/float(mPreview->getTextureWidth()), height/float(mPreview->getTextureHeight()))); + width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight()))); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -591,6 +596,11 @@ namespace MWGui { // convert to OpenGL lower-left origin y = (mAvatarImage->getHeight()-1) - y; + + // Scale coordinates + x = int(x*mScaleFactor); + y = int(y*mScaleFactor); + int slot = mPreview->getSlotSelected (x, y); if (slot == -1) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 124fe7b0ed..d9cf6870cb 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -101,6 +101,7 @@ namespace MWGui std::unique_ptr mPreview; bool mTrading; + float mScaleFactor; void onItemSelected(int index); void onItemSelectedFromSourceModel(int index); From 469bb29621001bdabf1f8482413afd4fc465bf2e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 21:45:17 +0400 Subject: [PATCH 004/175] Do not try to handle shape controllers as node controllers --- components/nifosg/nifloader.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 8353779a81..4e7f6d511e 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -579,13 +579,11 @@ namespace NifOsg if (composite->getNumControllers() > 0) node->addUpdateCallback(composite); - // Note: NiTriShapes are not allowed to have KeyframeControllers (the vanilla engine just crashes when there is one). // We can take advantage of this constraint for optimizations later. - if (!nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) + if (nifNode->recType != Nif::RC_NiTriShape && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) handleNodeControllers(nifNode, static_cast(node.get()), animflags); - if (nifNode->recType == Nif::RC_NiLODNode) { const Nif::NiLODNode* niLodNode = static_cast(nifNode); From c07cc0dc40dfc8aaf0052dd580c4f0569d09280f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 22:24:25 +0400 Subject: [PATCH 005/175] Reset animation state after weapon unequipping --- apps/openmw/mwmechanics/character.cpp | 75 +++++++++++++++------------ 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f843be66ee..f5d6f8584b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1221,11 +1221,11 @@ bool CharacterController::updateWeaponState() bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; - if(weaptype != mWeaponType && !isKnockedOut() && - !isKnockedDown() && !isRecovery()) + if(!isKnockedOut() && !isKnockedDown() && !isRecovery()) { std::string weapgroup; if ((!isWerewolf || mWeaponType != WeapType_Spell) + && weaptype != mWeaponType && mUpperBodyState != UpperCharState_UnEquipingWeap && !isStillWeapon) { @@ -1250,49 +1250,60 @@ bool CharacterController::updateWeaponState() float complete; bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); + if (!animPlaying || complete >= 1.0f) { - forcestateupdate = true; - mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); - - getWeaponGroup(weaptype, weapgroup); - mAnimation->setWeaponGroup(weapgroup); - - if (!isStillWeapon) + // Weapon is changed, no current animation (e.g. unequipping or attack). + // Start equipping animation now. + if (weaptype != mWeaponType) { - if (weaptype == WeapType_None) + forcestateupdate = true; + mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); + + getWeaponGroup(weaptype, weapgroup); + mAnimation->setWeaponGroup(weapgroup); + + if (!isStillWeapon) { - // Disable current weapon animation manually mAnimation->disable(mCurrentWeapon); + if (weaptype != WeapType_None) + { + mAnimation->showWeapons(false); + mAnimation->play(weapgroup, priorityWeapon, + MWRender::Animation::BlendMask_All, true, + 1.0f, "equip start", "equip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_EquipingWeap; + } } - else - { - mAnimation->showWeapons(false); - mAnimation->play(weapgroup, priorityWeapon, - MWRender::Animation::BlendMask_All, true, - 1.0f, "equip start", "equip stop", 0.0f, 0); - mUpperBodyState = UpperCharState_EquipingWeap; - } - } - if(isWerewolf) - { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Sound *sound = store.get().searchRandom("WolfEquip"); - if(sound) + if(isWerewolf) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfEquip"); + if(sound) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); + } + } + + mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); + + if(!upSoundId.empty() && !isStillWeapon) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); + sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f); } } - mWeaponType = weaptype; - getWeaponGroup(mWeaponType, mCurrentWeapon); - - if(!upSoundId.empty() && !isStillWeapon) + // Make sure that we disabled unequipping animation + if (mUpperBodyState == UpperCharState_UnEquipingWeap) { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f); + mUpperBodyState = UpperCharState_Nothing; + mAnimation->disable(mCurrentWeapon); + mWeaponType = WeapType_None; + getWeaponGroup(mWeaponType, mCurrentWeapon); } } } From 5b929108299a474c7fbc28042346f4f78282a806 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 31 Jul 2018 21:14:16 +0400 Subject: [PATCH 006/175] Limit difficulty scaling, as mentioned in docs --- apps/openmw/mwmechanics/difficultyscaling.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/difficultyscaling.cpp b/apps/openmw/mwmechanics/difficultyscaling.cpp index 693587eda3..53cf47dea2 100644 --- a/apps/openmw/mwmechanics/difficultyscaling.cpp +++ b/apps/openmw/mwmechanics/difficultyscaling.cpp @@ -12,8 +12,10 @@ float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr { const MWWorld::Ptr& player = MWMechanics::getPlayer(); - // [-100, 100] + // [-500, 500] int difficultySetting = Settings::Manager::getInt("difficulty", "Game"); + difficultySetting = std::min(difficultySetting, 500); + difficultySetting = std::max(difficultySetting, -500); static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); From be2e7e9e09ac99d92b08be2b9531468624f69d75 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 1 Aug 2018 02:24:52 +0300 Subject: [PATCH 007/175] Make casting caster-linked on-self effects no-op (bug #4378) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83682c5074..2095599535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Bug #4327: Missing animations during spell/weapon stance switching Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default + Bug #4378: On-self absorb spells restore stats Bug #4393: NPCs walk back to where they were after using ResetActors Bug #4416: Handle exception if we try to play non-music file Bug #4419: MRK NiStringExtraData is handled incorrectly diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 4557a52dfc..0741bedeb4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -420,9 +420,9 @@ namespace MWMechanics if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer)) continue; - // caster needs to be an actor for linked effects (e.g. Absorb) + // caster needs to be an actor that's not the target for linked effects (e.g. Absorb) if (magicEffect->mData.mFlags & ESM::MagicEffect::CasterLinked - && (caster.isEmpty() || !caster.getClass().isActor())) + && (caster.isEmpty() || !caster.getClass().isActor() || caster == target)) continue; // If player is healing someone, show the target's HP bar @@ -554,7 +554,7 @@ namespace MWMechanics // For absorb effects, also apply the effect to the caster - but with a negative // magnitude, since we're transferring stats from the target to the caster - if (!caster.isEmpty() && caster.getClass().isActor()) + if (!caster.isEmpty() && caster != target && caster.getClass().isActor()) { for (int i=0; i<5; ++i) { From 4d48ede6f1673ea295ee17efb5f7ef2c3ae1ed04 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 1 Aug 2018 13:31:17 +0300 Subject: [PATCH 008/175] Add two missing gameplay settings to Advanced tab --- apps/launcher/advancedpage.cpp | 4 ++++ files/ui/advancedpage.ui | 30 +++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 3a4c50e9d9..3e0314607e 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -73,6 +73,8 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); + loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -125,6 +127,8 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); + saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 0e43654f2c..486d410df9 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -21,8 +21,8 @@ 0 0 - 630 - 791 + 641 + 968 @@ -72,6 +72,26 @@ + + + + <html><head/><body><p>Account for the first follower in fast travel cost calculations.</p></body></html> + + + Charge for every follower travelling + + + + + + + <html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html> + + + Enchanted weapons are magical + + + @@ -137,7 +157,7 @@ - -1 + 6 0 @@ -312,7 +332,7 @@ - -1 + 6 0 @@ -379,7 +399,7 @@ - -1 + 6 0 From ab29f9e13f65e492e972bf9483ccc1c5bf8db8fc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 1 Aug 2018 17:31:35 +0300 Subject: [PATCH 009/175] Add permanent barter disposition change option (feature #3103) --- CHANGELOG.md | 1 + apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwbase/dialoguemanager.hpp | 4 ++-- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 +++++- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 ++-- apps/openmw/mwgui/tradewindow.cpp | 2 +- .../source/reference/modding/settings/game.rst | 18 +++++++++++++++--- files/settings-default.cfg | 9 ++++++--- files/ui/advancedpage.ui | 16 +++++++++++++--- 9 files changed, 47 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..219e46f479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ Bug #4539: Paper Doll is affected by GUI scaling Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script + Feature #3103: Provide option for disposition to get increased by successful trade Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window Feature #3703: Ranged sneak attack criticals diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 3e0314607e..ff229e02d9 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -75,6 +75,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -129,6 +130,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 7bace3790b..2ecab1c4cc 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -75,8 +75,8 @@ namespace MWBase virtual void persuade (int type, ResponseCallback* callback) = 0; virtual int getTemporaryDispositionChange () const = 0; - /// @note This change is temporary and gets discarded when dialogue ends. - virtual void applyDispositionChange (int delta) = 0; + /// @note Controlled by an option, gets discarded when dialogue ends by default + virtual void applyBarterDispositionChange (int delta) = 0; virtual int countSavedGameRecords() const = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index fb492ff3b2..df214ce863 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -508,9 +510,11 @@ namespace MWDialogue return static_cast(mTemporaryDispositionChange); } - void DialogueManager::applyDispositionChange(int delta) + void DialogueManager::applyBarterDispositionChange(int delta) { mTemporaryDispositionChange += delta; + if (Settings::Manager::getBool("barter disposition change is permanent", "Game")) + mPermanentDispositionChange += delta; } bool DialogueManager::checkServiceRefused(ResponseCallback* callback) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index f267f7542d..29a90082c7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -94,8 +94,8 @@ namespace MWDialogue virtual void persuade (int type, ResponseCallback* callback); virtual int getTemporaryDispositionChange () const; - /// @note This change is temporary and gets discarded when dialogue ends. - virtual void applyDispositionChange (int delta); + /// @note Controlled by an option, gets discarded when dialogue ends by default + virtual void applyBarterDispositionChange (int delta); virtual int countSavedGameRecords() const; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0a9fe1b4ac..e11147c742 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -334,7 +334,7 @@ namespace MWGui ? gmst.find("iBarterSuccessDisposition")->getInt() : gmst.find("iBarterFailDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyDispositionChange(dispositionDelta); + MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta); } // display message on haggle failure diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 308a295469..b733cc7c49 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -130,7 +130,7 @@ enchanted weapons are magical :Range: True/False :Default: True -Makes enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. +Make enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. This is how original Morrowind behaves. This setting can only be configured by editing the settings configuration file. @@ -142,7 +142,7 @@ prevent merchant equipping :Range: True/False :Default: False -Prevents merchants from equipping items that are sold to them. +Prevent merchants from equipping items that are sold to them. This setting can only be configured by editing the settings configuration file. @@ -153,7 +153,7 @@ followers attack on sight :Range: True/False :Default: False -Makes player followers and escorters start combat with enemies who have started combat with them or the player. +Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first. Please note this setting has not been extensively tested and could have side effects with certain quests. @@ -171,3 +171,15 @@ For example, if the main animation mesh has name Meshes/x.nif, an engine will lo Can be useful if you want to use several animation replacers without merging them. Attention: animations from AnimKit have own format and are not supposed to be directly loaded in-game! This setting can only be configured by editing the settings configuration file. + +barter disposition change is permanent +-------------------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. +This imitates the option Morrowind Code Patch offers. + +This setting can be toggled with a checkbox in Advanced tab of the launcher. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 1390bf8c6d..b0ddd52239 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -203,25 +203,28 @@ show effect duration = false # Account for the first follower in fast travel cost calculations. charge for every follower travelling = false -# Prevents merchants from equipping items that are sold to them. +# Prevent merchants from equipping items that are sold to them. prevent merchant equipping = false # Make enchanted weaponry without Magical flag bypass normal weapons resistance enchanted weapons are magical = true -# Makes player followers and escorters start combat with enemies who have started combat with them +# Make player followers and escorters start combat with enemies who have started combat with them # or the player. Otherwise they wait for the enemies or the player to do an attack first. followers attack on sight = false # Can loot non-fighting actors during death animation can loot during death animation = true -# Makes the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch) +# Make the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch) rebalance soul gem values = false # Allow to load per-group KF-files from Animations folder use additional anim sources = false +# Make the disposition change of merchants caused by barter dealings permanent +barter disposition change is permanent = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 486d410df9..553c244d38 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -22,7 +22,7 @@ 0 0 641 - 968 + 998 @@ -45,7 +45,7 @@ - <html><head/><body><p>Makes player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> + <html><head/><body><p>Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> Followers attack on sight @@ -65,7 +65,7 @@ - <html><head/><body><p>If this setting is true, the value of filled soul gems is dependent only on soul magnitude.</p><p>The default value is false.</p></body></html> + <html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html> Rebalance soul gem values @@ -89,6 +89,16 @@ Enchanted weapons are magical + + + + + + + <html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html> + + + Barter disposition change is permanent From a08048da4ebf4f4dce102f7ef2114e11c40ea111 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 19:30:30 +0400 Subject: [PATCH 010/175] Avoid dereference after null check --- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 9222f1d020..7f36bf001d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -82,7 +82,7 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } - else + else if (mModel) dragItem (NULL, count); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index df2d5ed6d2..c97efed34d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -898,7 +898,8 @@ namespace MWGui mKeyboardNavigation->onFrame(); - mMessageBoxManager->onFrame(frameDuration); + if (mMessageBoxManager) + mMessageBoxManager->onFrame(frameDuration); mToolTips->onFrame(frameDuration); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c162fd57dc..f4c2a75f3d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1180,9 +1180,9 @@ namespace MWWorld addContainerScripts (getPlayerPtr(), newCell); newPtr = getPlayerPtr(); } - else + else if (currCell) { - bool currCellActive = currCell && mWorldScene->isCellActive(*currCell); + bool currCellActive = mWorldScene->isCellActive(*currCell); bool newCellActive = newCell && mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { From 770d86f9bd0c8f0b090da5e1808404da77d14418 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 19:36:55 +0400 Subject: [PATCH 011/175] Initialize cubeSize variable for 360 degrees screenshots correctly --- apps/openmw/mwrender/renderingmanager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3dbf6475c1..95568a57d9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -691,9 +691,6 @@ namespace MWRender int screenshotW = mViewer->getCamera()->getViewport()->width(); int screenshotH = mViewer->getCamera()->getViewport()->height(); int screenshotMapping = 0; - int cubeSize = screenshotMapping == 2 ? - screenshotW: // planet mapping needs higher resolution - screenshotW / 2; std::vector settingArgs; boost::algorithm::split(settingArgs,settingStr,boost::is_any_of(" ")); @@ -717,7 +714,10 @@ namespace MWRender return false; } } - + + // planet mapping needs higher resolution + int cubeSize = screenshotMapping == 2 ? screenshotW : screenshotW / 2; + if (settingArgs.size() > 1) screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str())); From c0bed0fde213d7d45be072982e9c410e001b6387 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 20:17:59 +0400 Subject: [PATCH 012/175] Handle case when index < 0 --- apps/openmw/mwgui/quickkeysmenu.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 457bb5588c..8041c50c59 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -161,6 +161,12 @@ namespace MWGui } } assert(index != -1); + if (index < 0) + { + mSelected = nullptr; + return; + } + mSelected = &mKey[index]; // prevent reallocation of zero key from Type_HandToHand From 12144de8ed55e22f3d9f4983e074b4022d800bc7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 20:18:37 +0400 Subject: [PATCH 013/175] Initialize missing variables --- apps/essimporter/importinventory.cpp | 1 + apps/openmw/engine.cpp | 1 + apps/openmw/mwgui/loadingscreen.cpp | 1 + apps/openmw/mwgui/spellmodel.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwrender/sky.cpp | 1 + components/esmterrain/storage.cpp | 2 ++ 7 files changed, 8 insertions(+) diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index 2c87a9a19d..8c7d07f637 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -20,6 +20,7 @@ namespace ESSImport item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; item.mRelativeEquipmentSlot = -1; + item.mLockLevel = 0; unsigned int itemCount = std::abs(item.mCount); bool separateStacks = false; diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8b6f9d0b3b..bd40dc1abe 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -198,6 +198,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mWindow(NULL) , mEncoding(ToUTF8::WINDOWS_1252) , mEncoder(NULL) + , mScreenCaptureOperation(nullptr) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 99fe777aa7..3df482d684 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -37,6 +37,7 @@ namespace MWGui , mLastRenderTime(0.0) , mLoadingOnTime(0.0) , mImportantLabel(false) + , mVisible(false) , mProgress(0) , mShowWallpaper(true) { diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index 8e29707aed..4aa1f9d2a7 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -26,6 +26,7 @@ namespace MWGui Spell() : mType(Type_Spell) + , mCount(0) , mSelected(false) , mActive(false) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 95568a57d9..f6aa8796dd 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -203,6 +203,7 @@ namespace MWRender , mNightEyeFactor(0.f) , mDistantFog(false) , mDistantTerrain(false) + , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) , mBorders(false) { diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 2e4329f691..24769019b1 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -1117,6 +1117,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana , mWindSpeed(0.f) , mEnabled(true) , mSunEnabled(true) + , mWeatherAlpha(0.f) { osg::ref_ptr skyroot (new CameraRelativeTransform); skyroot->setName("Sky Root"); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index ff3123c5b8..0f5b175025 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -37,6 +37,8 @@ namespace ESMTerrain } LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op) + : mLand(nullptr) + , mLoadFlags(0) { } From 382b68a081e449dd8f229f438e3947ac0a5c91c3 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 1 Aug 2018 19:27:19 +0300 Subject: [PATCH 014/175] Combat AI: take the actual hit chance in account when rating weapon --- apps/openmw/mwmechanics/weaponpriority.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 5a0bd9c154..f3d00c606e 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -103,7 +103,10 @@ namespace MWMechanics int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) - rating *= actor.getClass().getSkill(actor, skill) / 100.f; + { + MWMechanics::SkillValue& value = actor.getClass().getSkill(actor, skill); + rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; + } // There is no need to apply bonus if weapon rating == 0 if (rating == 0.f) From 369ea7e1775e51054c9fec41f2ad302b554d2935 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 20:36:29 +0400 Subject: [PATCH 015/175] Check if a temporary file was successfully closed --- components/crashcatcher/crashcatcher.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index e5377b64f4..f7b8717a6b 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -182,8 +182,10 @@ static void gdb_info(pid_t pid) /* Error creating temp file */ if(fd >= 0) { - close(fd); - remove(respfile); + if (close(fd) == 0) + remove(respfile); + else + std::cerr << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno) << std::endl; } printf("!!! Could not create gdb command file\n"); } From bec47dfb7c63e369bad925fa666705be59f5442a Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 1 Aug 2018 19:41:49 +0300 Subject: [PATCH 016/175] Make ranged weapon bonus a distance-dependent multiplier --- apps/openmw/mwmechanics/weaponpriority.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index f3d00c606e..e1c2eaa344 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -30,7 +30,7 @@ namespace MWMechanics return 0.f; float rating=0.f; - float bonus=0.f; + float rangedMult=1.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { @@ -44,7 +44,8 @@ namespace MWMechanics if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; - bonus+=1.5f; + if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) + rangedMult = 1.5f; } if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) @@ -104,15 +105,11 @@ namespace MWMechanics int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) { - MWMechanics::SkillValue& value = actor.getClass().getSkill(actor, skill); + int value = actor.getClass().getSkill(actor, skill); rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; } - // There is no need to apply bonus if weapon rating == 0 - if (rating == 0.f) - return 0.f; - - return rating + bonus; + return rating * rangedMult; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType) From fa6c205e5dc3f6047bf6d5556886e83c360706dd Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 03:16:33 +0300 Subject: [PATCH 017/175] Make tab autocompletion work with explicit reference calls --- CHANGELOG.md | 1 + apps/openmw/mwgui/console.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..847404bf7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Bug #2835: Player able to slowly move when overencumbered Bug #2852: No murder bounty when a player follower commits murder Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit + Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index e8ac33f6db..dee2d4a316 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -339,6 +339,14 @@ namespace MWGui } } } + + // Erase a possible call to an explicit reference + size_t explicitPos = tmp.find("->"); + if (explicitPos != std::string::npos) + { + tmp.erase(0, explicitPos+2); + } + /* Erase the input from the output string so we can easily append the completed form later. */ output.erase(output.end()-tmp.length(), output.end()); From 80f3bd9f86fd94a3701989de29f1cca72879ef71 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 08:31:45 +0300 Subject: [PATCH 018/175] Don't apply iWereWolfFleeMod to creatures --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aicombataction.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..ee560b680d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4539: Paper Doll is affected by GUI scaling + Bug #4545: Creatures flee from werewolves Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 0ccd0b6ec6..b83f4c331d 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -494,10 +494,13 @@ namespace MWMechanics static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt(); - if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) + if (actor.getClass().isNpc() && enemy.getClass().isNpc()) { - static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); - rating = iWereWolfFleeMod; + if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) + { + static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); + rating += iWereWolfFleeMod; + } } if (rating != 0.0f) From 3ac030d75acfbe6a6bbbf628648205b390086561 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 09:49:53 +0300 Subject: [PATCH 019/175] Handle explicit calls before handling quotes --- apps/openmw/mwgui/console.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index dee2d4a316..b367c6f492 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -303,6 +303,14 @@ namespace MWGui bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ + + /* Erase a possible call to an explicit reference. */ + size_t explicitPos = tmp.find("->"); + if (explicitPos != std::string::npos) + { + tmp.erase(0, explicitPos+2); + } + /* Are there quotation marks? */ if( tmp.find('"') != std::string::npos ) { int numquotes=0; @@ -340,13 +348,6 @@ namespace MWGui } } - // Erase a possible call to an explicit reference - size_t explicitPos = tmp.find("->"); - if (explicitPos != std::string::npos) - { - tmp.erase(0, explicitPos+2); - } - /* Erase the input from the output string so we can easily append the completed form later. */ output.erase(output.end()-tmp.length(), output.end()); From 16af1a6c1c4af593ee56156ea7a4e1f3d0a69d72 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 12:35:55 +0300 Subject: [PATCH 020/175] Replace 0 sound range values separately --- apps/openmw/mwsound/soundmanagerimp.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 0dd95f7732..8c71ab61b1 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -142,16 +142,12 @@ namespace MWSound float volume, min, max; volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); - if(sound->mData.mMinRange == 0 && sound->mData.mMaxRange == 0) - { + min = sound->mData.mMinRange; + max = sound->mData.mMaxRange; + if (min == 0) min = fAudioDefaultMinDistance; + if (max == 0) max = fAudioDefaultMaxDistance; - } - else - { - min = sound->mData.mMinRange; - max = sound->mData.mMaxRange; - } min *= fAudioMinDistanceMult; max *= fAudioMaxDistanceMult; From 73d549671132b73f57e7e2ee5c98ca86678242c3 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 13:01:23 +0300 Subject: [PATCH 021/175] Revert addition change --- apps/openmw/mwmechanics/aicombataction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index b83f4c331d..2685e0e0c2 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -499,7 +499,7 @@ namespace MWMechanics if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) { static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); - rating += iWereWolfFleeMod; + rating = iWereWolfFleeMod; } } From 9d85b7c2d3bdea59933b9f89d4da61aaf8a4d4dd Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 2 Aug 2018 15:20:07 +0300 Subject: [PATCH 022/175] Use the actual damage for deducting weapon rating --- apps/openmw/mwmechanics/weaponpriority.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index e1c2eaa344..6d7cbf07d8 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -50,20 +50,27 @@ namespace MWMechanics if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) { - rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; + float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1]; + MWMechanics::adjustWeaponDamage(rangedDamage, item, actor); + + rating = rangedDamage / 2.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } else { + float meleeDamage = 0.f; + for (int i=0; i<2; ++i) { - rating += weapon->mData.mSlash[i]; - rating += weapon->mData.mThrust[i]; - rating += weapon->mData.mChop[i]; + meleeDamage += weapon->mData.mSlash[i]; + meleeDamage += weapon->mData.mThrust[i]; + meleeDamage += weapon->mData.mChop[i]; } - rating /= 6.f; + + MWMechanics::adjustWeaponDamage(meleeDamage, item, actor); + rating = meleeDamage / 6.f; MWMechanics::resistNormalWeapon(enemy, actor, item, rating); } From c454f1bdad953cdc84af1f4b3242a081146ad090 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 08:58:50 +0400 Subject: [PATCH 023/175] Use log file for editor (feature #4012) --- CHANGELOG.md | 1 + apps/opencs/main.cpp | 59 ++++++++++++++++++++++++++++++++--- apps/openmw/main.cpp | 51 +++++------------------------- components/misc/debugging.hpp | 47 ++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 components/misc/debugging.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..63bd551843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window Feature #3703: Ranged sneak attack criticals + Feature #4012: OpenMW-CS: Create log file like with OpenMW Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index d599f1f961..7b138169bc 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -8,10 +8,13 @@ #include #include -#include "model/doc/messages.hpp" +#include +#include "model/doc/messages.hpp" #include "model/world/universalid.hpp" +#include + #ifdef Q_OS_MAC #include #endif @@ -47,8 +50,43 @@ int main(int argc, char *argv[]) setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); #endif + // Some objects used to redirect cout and cerr + // Scope must be here, so this still works inside the catch block for logging exceptions + std::streambuf* cout_rdbuf = std::cout.rdbuf (); + std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + +#if !(defined(_WIN32) && defined(_DEBUG)) + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; +#endif + + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + + boost::filesystem::ofstream logfile; + + int ret = 0; try - { + { + Files::ConfigurationManager cfgMgr; + +#if defined(_WIN32) && defined(_DEBUG) + // Redirect cout and cerr to VS debug output when running in debug mode + boost::iostreams::stream_buffer sb; + sb.open(Misc::DebugOutput()); + std::cout.rdbuf (&sb); + std::cerr.rdbuf (&sb); +#else + // Redirect cout and cerr to openmw.log + logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw-cs.log")); + + coutsb.open (Misc::Tee(logfile, oldcout)); + cerrsb.open (Misc::Tee(logfile, oldcerr)); + + std::cout.rdbuf (&coutsb); + std::cerr.rdbuf (&cerrsb); +#endif + // To allow background thread drawing in OSG QApplication::setAttribute(Qt::AA_X11InitThreads, true); @@ -74,12 +112,23 @@ int main(int argc, char *argv[]) editor.connectToIPCServer(); return 0; } - return editor.run(); + ret = editor.run(); } catch (std::exception& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - return 0; +#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) + if (!isatty(fileno(stdin))) +#endif + SDL_ShowSimpleMessageBox(0, "OpenMW-CS: Fatal error", e.what(), NULL); + + std::cerr << "\nERROR: " << e.what() << std::endl; + + ret = 1; } + // Restore cout and cerr + std::cout.rdbuf(cout_rdbuf); + std::cerr.rdbuf(cerr_rdbuf); + + return ret; } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index d13a731279..ed76726946 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "engine.hpp" @@ -241,44 +242,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat return true; } -#if defined(_WIN32) && defined(_DEBUG) - -class DebugOutput : public boost::iostreams::sink -{ -public: - std::streamsize write(const char *str, std::streamsize size) - { - // Make a copy for null termination - std::string tmp (str, static_cast(size)); - // Write string to Visual Studio Debug output - OutputDebugString (tmp.c_str ()); - return size; - } -}; -#else -class Tee : public boost::iostreams::sink -{ -public: - Tee(std::ostream &stream, std::ostream &stream2) - : out(stream), out2(stream2) - { - } - - std::streamsize write(const char *str, std::streamsize size) - { - out.write (str, size); - out.flush(); - out2.write (str, size); - out2.flush(); - return size; - } - -private: - std::ostream &out; - std::ostream &out2; -}; -#endif - #ifdef ANDROID extern "C" int SDL_main(int argc, char**argv) #else @@ -295,8 +258,8 @@ int main(int argc, char**argv) std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); #if !(defined(_WIN32) && defined(_DEBUG)) - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; #endif std::ostream oldcout(cout_rdbuf); @@ -313,16 +276,16 @@ int main(int argc, char**argv) #if defined(_WIN32) && defined(_DEBUG) // Redirect cout and cerr to VS debug output when running in debug mode - boost::iostreams::stream_buffer sb; - sb.open(DebugOutput()); + boost::iostreams::stream_buffer sb; + sb.open(Misc::DebugOutput()); std::cout.rdbuf (&sb); std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to openmw.log logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log")); - coutsb.open (Tee(logfile, oldcout)); - cerrsb.open (Tee(logfile, oldcerr)); + coutsb.open (Misc::Tee(logfile, oldcout)); + cerrsb.open (Misc::Tee(logfile, oldcerr)); std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp new file mode 100644 index 0000000000..b367f09123 --- /dev/null +++ b/components/misc/debugging.hpp @@ -0,0 +1,47 @@ +#ifndef MISC_DEBUGGING_H +#define MISC_DEBUGGING_H + +#include + +namespace Misc +{ + #if defined(_WIN32) && defined(_DEBUG) + + class DebugOutput : public boost::iostreams::sink + { + public: + std::streamsize write(const char *str, std::streamsize size) + { + // Make a copy for null termination + std::string tmp (str, static_cast(size)); + // Write string to Visual Studio Debug output + OutputDebugString (tmp.c_str ()); + return size; + } + }; + #else + class Tee : public boost::iostreams::sink + { + public: + Tee(std::ostream &stream, std::ostream &stream2) + : out(stream), out2(stream2) + { + } + + std::streamsize write(const char *str, std::streamsize size) + { + out.write (str, size); + out.flush(); + out2.write (str, size); + out2.flush(); + return size; + } + + private: + std::ostream &out; + std::ostream &out2; + }; + #endif +} + +#endif From 712c9995dbacef89626814bbd8311e4f769df33d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 12:01:31 +0400 Subject: [PATCH 024/175] Rename mIsScripted variable because its name is ambiguous --- apps/openmw/mwmechanics/spellcasting.cpp | 8 ++++---- apps/openmw/mwmechanics/spellcasting.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 4557a52dfc..5f6a9f9679 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -315,14 +315,14 @@ namespace MWMechanics return true; } - CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool isScripted) + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell) : mCaster(caster) , mTarget(target) , mStack(false) , mHitPosition(0,0,0) , mAlwaysSucceed(false) , mFromProjectile(fromProjectile) - , mIsScripted(isScripted) + , mManualSpell(manualSpell) { } @@ -864,7 +864,7 @@ namespace MWMechanics bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); - if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mIsScripted) + if (mCaster.getClass().isActor() && !mAlwaysSucceed && !mManualSpell) { school = getSpellSchool(spell, mCaster); @@ -1037,7 +1037,7 @@ namespace MWMechanics bool CastSpell::spellIncreasesSkill() { - if (mIsScripted) + if (mManualSpell) return false; return MWMechanics::spellIncreasesSkill(mId); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 5e69b4696e..72f6a1f4a0 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -88,10 +88,10 @@ namespace MWMechanics osg::Vec3f mHitPosition; // Used for spawning area orb bool mAlwaysSucceed; // Always succeed spells casted by NPCs/creatures regardless of their chance (default: false) bool mFromProjectile; // True if spell is cast by enchantment of some projectile (arrow, bolt or thrown weapon) - bool mIsScripted; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.) + bool mManualSpell; // True if spell is casted from script and ignores some checks (mana level, success chance, etc.) public: - CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool isScripted=false); + CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool manualSpell=false); bool cast (const ESM::Spell* spell); From ac987979998cf6af73acf18e48bd1ccfe1d846a3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 15:05:13 +0400 Subject: [PATCH 025/175] Add missing file --- components/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b6f390b069..e2e6b97bb1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,7 +85,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng messageformatparser + utf8stream stringops resourcehelpers rng debugging messageformatparser ) IF(NOT WIN32 AND NOT APPLE) From 433c24562e573044fbb7aa46d9e470c4eca7445a Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 3 Aug 2018 14:06:09 +0300 Subject: [PATCH 026/175] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..930e828bb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,9 @@ Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4539: Paper Doll is affected by GUI scaling + Bug #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill + Bug #4549: Weapon priority: use the actual damage in weapon rating calculations + Bug #4550: Weapon priority: make ranged weapon bonus more sensible Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results From 2f44acafe2b761347ac632113a98dc703d07f4ae Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 3 Aug 2018 14:15:03 +0300 Subject: [PATCH 027/175] Fix changelog --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c06a15549..c741c02545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,15 +76,13 @@ Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4539: Paper Doll is affected by GUI scaling Bug #4545: Creatures flee from werewolves - Bug #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill - Bug #4549: Weapon priority: use the actual damage in weapon rating calculations - Bug #4550: Weapon priority: make ranged weapon bonus more sensible Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window Feature #3703: Ranged sneak attack criticals + Feature #4012: Editor: Write a log file if OpenCS crashes Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts @@ -93,9 +91,11 @@ Feature #4444: Per-group KF-animation files support Feature #4466: Editor: Add option to ignore "Base" records when running verifier Feature #4488: Make water shader rougher during rain - Feature #4012: Editor: Write a log file if OpenCS crashes Feature #4509: Show count of enchanted items in stack in the spells list Feature #4512: Editor: Use markers for lights and creatures levelled lists + Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill + Feature #4549: Weapon priority: use the actual damage in weapon rating calculations + Feature #4550: Weapon priority: make ranged weapon bonus more sensible Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test From 1d1eedc0017301a1487a3c19c1d9d48a3ea1dd1c Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 3 Aug 2018 14:15:58 +0300 Subject: [PATCH 028/175] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1923e52377..0a510122cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4539: Paper Doll is affected by GUI scaling + Bug #4551: Replace 0 sound range with default range separately Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results From c2a175c2e01af1e3896b2a7714a8dc43a2de773d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 15:51:17 +0400 Subject: [PATCH 029/175] Move crash catcher wrapper to separate file --- apps/opencs/main.cpp | 124 ++++++++++------------------------ apps/openmw/main.cpp | 96 ++++++-------------------- components/misc/debugging.hpp | 70 ++++++++++++++++++- 3 files changed, 125 insertions(+), 165 deletions(-) diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 7b138169bc..ebc686c233 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -13,8 +13,6 @@ #include "model/doc/messages.hpp" #include "model/world/universalid.hpp" -#include - #ifdef Q_OS_MAC #include #endif @@ -44,91 +42,43 @@ class Application : public QApplication Application (int& argc, char *argv[]) : QApplication (argc, argv) {} }; +int runApplication(int argc, char *argv[]) +{ +#ifdef Q_OS_MAC + setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); +#endif + + // To allow background thread drawing in OSG + QApplication::setAttribute(Qt::AA_X11InitThreads, true); + + Q_INIT_RESOURCE (resources); + + qRegisterMetaType ("std::string"); + qRegisterMetaType ("CSMWorld::UniversalId"); + qRegisterMetaType ("CSMDoc::Message"); + + Application application (argc, argv); + +#ifdef Q_OS_MAC + QDir dir(QCoreApplication::applicationDirPath()); + QDir::setCurrent(dir.absolutePath()); +#endif + + application.setWindowIcon (QIcon (":./openmw-cs.png")); + + CS::Editor editor(argc, argv); + + if(!editor.makeIPCServer()) + { + editor.connectToIPCServer(); + return 0; + } + + return editor.run(); +} + + int main(int argc, char *argv[]) { - #ifdef Q_OS_MAC - setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); - #endif - - // Some objects used to redirect cout and cerr - // Scope must be here, so this still works inside the catch block for logging exceptions - std::streambuf* cout_rdbuf = std::cout.rdbuf (); - std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); - -#if !(defined(_WIN32) && defined(_DEBUG)) - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; -#endif - - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - - boost::filesystem::ofstream logfile; - - int ret = 0; - try - { - Files::ConfigurationManager cfgMgr; - -#if defined(_WIN32) && defined(_DEBUG) - // Redirect cout and cerr to VS debug output when running in debug mode - boost::iostreams::stream_buffer sb; - sb.open(Misc::DebugOutput()); - std::cout.rdbuf (&sb); - std::cerr.rdbuf (&sb); -#else - // Redirect cout and cerr to openmw.log - logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw-cs.log")); - - coutsb.open (Misc::Tee(logfile, oldcout)); - cerrsb.open (Misc::Tee(logfile, oldcerr)); - - std::cout.rdbuf (&coutsb); - std::cerr.rdbuf (&cerrsb); -#endif - - // To allow background thread drawing in OSG - QApplication::setAttribute(Qt::AA_X11InitThreads, true); - - Q_INIT_RESOURCE (resources); - - qRegisterMetaType ("std::string"); - qRegisterMetaType ("CSMWorld::UniversalId"); - qRegisterMetaType ("CSMDoc::Message"); - - Application application (argc, argv); - - #ifdef Q_OS_MAC - QDir dir(QCoreApplication::applicationDirPath()); - QDir::setCurrent(dir.absolutePath()); - #endif - - application.setWindowIcon (QIcon (":./openmw-cs.png")); - - CS::Editor editor(argc, argv); - - if(!editor.makeIPCServer()) - { - editor.connectToIPCServer(); - return 0; - } - ret = editor.run(); - } - catch (std::exception& e) - { -#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) - if (!isatty(fileno(stdin))) -#endif - SDL_ShowSimpleMessageBox(0, "OpenMW-CS: Fatal error", e.what(), NULL); - - std::cerr << "\nERROR: " << e.what() << std::endl; - - ret = 1; - } - - // Restore cout and cerr - std::cout.rdbuf(cout_rdbuf); - std::cerr.rdbuf(cerr_rdbuf); - - return ret; + return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log"); } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index ed76726946..2b4fa9147f 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -7,7 +7,6 @@ #include #include -#include #include "engine.hpp" #include @@ -242,86 +241,33 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat return true; } +int runApplication(int argc, char *argv[]) +{ +#ifdef __APPLE__ + boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0])); + boost::filesystem::current_path(binary_path.parent_path()); + setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); +#endif + + Files::ConfigurationManager cfgMgr; + std::unique_ptr engine; + engine.reset(new OMW::Engine(cfgMgr)); + + if (parseOptions(argc, argv, *engine, cfgMgr)) + { + engine->go(); + } + + return 0; +} + #ifdef ANDROID extern "C" int SDL_main(int argc, char**argv) #else int main(int argc, char**argv) #endif { -#if defined(__APPLE__) - setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); -#endif - - // Some objects used to redirect cout and cerr - // Scope must be here, so this still works inside the catch block for logging exceptions - std::streambuf* cout_rdbuf = std::cout.rdbuf (); - std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); - -#if !(defined(_WIN32) && defined(_DEBUG)) - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; -#endif - - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - - boost::filesystem::ofstream logfile; - - std::unique_ptr engine; - - int ret = 0; - try - { - Files::ConfigurationManager cfgMgr; - -#if defined(_WIN32) && defined(_DEBUG) - // Redirect cout and cerr to VS debug output when running in debug mode - boost::iostreams::stream_buffer sb; - sb.open(Misc::DebugOutput()); - std::cout.rdbuf (&sb); - std::cerr.rdbuf (&sb); -#else - // Redirect cout and cerr to openmw.log - logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log")); - - coutsb.open (Misc::Tee(logfile, oldcout)); - cerrsb.open (Misc::Tee(logfile, oldcerr)); - - std::cout.rdbuf (&coutsb); - std::cerr.rdbuf (&cerrsb); -#endif - - crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string()); - -#ifdef __APPLE__ - boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0])); - boost::filesystem::current_path(binary_path.parent_path()); -#endif - - engine.reset(new OMW::Engine(cfgMgr)); - - if (parseOptions(argc, argv, *engine, cfgMgr)) - { - engine->go(); - } - } - catch (std::exception &e) - { -#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) - if (!isatty(fileno(stdin))) -#endif - SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); - - std::cerr << "\nERROR: " << e.what() << std::endl; - - ret = 1; - } - - // Restore cout and cerr - std::cout.rdbuf(cout_rdbuf); - std::cerr.rdbuf(cerr_rdbuf); - - return ret; + return wrapApplication(&runApplication, argc, argv, "/openmw.log"); } // Platform specific for Windows when there is no console built into the executable. diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp index b367f09123..af13d0cc67 100644 --- a/components/misc/debugging.hpp +++ b/components/misc/debugging.hpp @@ -3,9 +3,11 @@ #include +#include + namespace Misc { - #if defined(_WIN32) && defined(_DEBUG) +#if defined(_WIN32) && defined(_DEBUG) class DebugOutput : public boost::iostreams::sink { @@ -19,7 +21,7 @@ namespace Misc return size; } }; - #else +#else class Tee : public boost::iostreams::sink { public: @@ -41,7 +43,69 @@ namespace Misc std::ostream &out; std::ostream &out2; }; - #endif +#endif +} + +int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& logName) +{ + // Some objects used to redirect cout and cerr + // Scope must be here, so this still works inside the catch block for logging exceptions + std::streambuf* cout_rdbuf = std::cout.rdbuf (); + std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + +#if !(defined(_WIN32) && defined(_DEBUG)) + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; +#endif + + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + + boost::filesystem::ofstream logfile; + + int ret = 0; + try + { + Files::ConfigurationManager cfgMgr; +#if defined(_WIN32) && defined(_DEBUG) + // Redirect cout and cerr to VS debug output when running in debug mode + boost::iostreams::stream_buffer sb; + sb.open(Misc::DebugOutput()); + std::cout.rdbuf (&sb); + std::cerr.rdbuf (&sb); +#else + // Redirect cout and cerr to the log file + boost::filesystem::ofstream logfile; + logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); + + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + + coutsb.open (Misc::Tee(logfile, oldcout)); + cerrsb.open (Misc::Tee(logfile, oldcerr)); + + std::cout.rdbuf (&coutsb); + std::cerr.rdbuf (&cerrsb); +#endif + ret = innerApplication(argc, argv); + } + catch (std::exception& e) + { +#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) + if (!isatty(fileno(stdin))) +#endif + SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); + + std::cerr << "\nERROR: " << e.what() << std::endl; + + ret = 1; + } + + // Restore cout and cerr + std::cout.rdbuf(cout_rdbuf); + std::cerr.rdbuf(cerr_rdbuf); + + return ret; } #endif From e4f862c0b943f5daf4c463482d283dea29c43b75 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 1 Aug 2018 22:22:01 +0400 Subject: [PATCH 030/175] Check if next char exists --- extern/oics/tinyxmlparser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extern/oics/tinyxmlparser.cpp b/extern/oics/tinyxmlparser.cpp index d5bda8fee7..c29e263f49 100644 --- a/extern/oics/tinyxmlparser.cpp +++ b/extern/oics/tinyxmlparser.cpp @@ -1295,9 +1295,10 @@ const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEnc ++p; } - if ( !p ) + if ( !p || !*p ) { - if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + return 0; } if ( *p == '>' ) return p+1; From eeffe2e557376ff7a6df4255faf62b1114ad4045 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 16:40:04 +0400 Subject: [PATCH 031/175] Check if item model exists inside drag and drop functions --- apps/openmw/mwgui/container.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 7f36bf001d..703ba309e5 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -52,7 +52,7 @@ namespace MWGui void ContainerWindow::onItemSelected(int index) { - if (mDragAndDrop->mIsOnDragAndDrop && mModel) + if (mDragAndDrop->mIsOnDragAndDrop) { dropItem(); return; @@ -82,12 +82,15 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } - else if (mModel) + else dragItem (NULL, count); } void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { + if (!mModel) + return; + if (!onTakeItem(mModel->getItem(mSelectedItem), count)) return; @@ -96,6 +99,9 @@ namespace MWGui void ContainerWindow::dropItem() { + if (!mModel) + return; + bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount); if (success) @@ -104,7 +110,7 @@ namespace MWGui void ContainerWindow::onBackgroundSelected() { - if (mDragAndDrop->mIsOnDragAndDrop && mModel) + if (mDragAndDrop->mIsOnDragAndDrop) dropItem(); } From e2519226aa73bfa524aae5d2eac9db845ea17977 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 17:04:07 +0400 Subject: [PATCH 032/175] Move boost include --- apps/openmw/main.cpp | 2 -- components/misc/debugging.hpp | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2b4fa9147f..d9fff69528 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -9,8 +9,6 @@ #include "engine.hpp" -#include - #if defined(_WIN32) // For OutputDebugString #ifndef WIN32_LEAN_AND_MEAN diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp index af13d0cc67..a983a88dfa 100644 --- a/components/misc/debugging.hpp +++ b/components/misc/debugging.hpp @@ -1,6 +1,7 @@ #ifndef MISC_DEBUGGING_H #define MISC_DEBUGGING_H +#include #include #include From dd6cb85783b81e0f66872217e72cf7243d622f24 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 19:31:10 +0400 Subject: [PATCH 033/175] Remove redundant changelog entry --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63bd551843..1923e52377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,7 +79,6 @@ Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window Feature #3703: Ranged sneak attack criticals - Feature #4012: OpenMW-CS: Create log file like with OpenMW Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts From d15dcaff68fcf085ba56d4bf6bfa08b881b581ba Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 3 Aug 2018 19:22:58 +0300 Subject: [PATCH 034/175] Don't adjust weapon rating according to weapon condition twice --- apps/openmw/mwmechanics/weaponpriority.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6d7cbf07d8..26c784c4d3 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -79,7 +79,6 @@ namespace MWMechanics { if (item.getClass().getItemHealth(item) == 0) return 0.f; - rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item)); } if (weapon->mData.mType == ESM::Weapon::MarksmanBow) From b0f2e00e7f060a2a901956db4dcfa8cb6dd2a2e8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 5 Aug 2018 09:31:45 +0400 Subject: [PATCH 035/175] Make forcegreeting a non-op for non-actor objects (bug #4553) --- CHANGELOG.md | 3 ++- apps/openmw/mwgui/dialogue.cpp | 6 ++++++ apps/openmw/mwscript/dialogueextensions.cpp | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9aa35684..cbe1b3ab4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,10 +77,11 @@ Bug #4539: Paper Doll is affected by GUI scaling Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately + Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade - Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results + Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results Feature #3641: Editor: Limit FPS in 3d preview window Feature #3703: Ranged sneak attack criticals Feature #4012: Editor: Write a log file if OpenCS crashes diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index f4fe549172..b0b0c18790 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -408,6 +408,12 @@ namespace MWGui void DialogueWindow::setPtr(const MWWorld::Ptr& actor) { + if (!actor.getClass().isActor()) + { + std::cerr << "Warning: can not talk with non-actor object." << std::endl; + return; + } + bool sameActor = (mPtr == actor); if (!sameActor) { diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 21d8d469b9..4b6ddcf9fb 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -1,3 +1,5 @@ +#include + #include "dialogueextensions.hpp" #include @@ -134,6 +136,14 @@ namespace MWScript if (!ptr.getRefData().isEnabled()) return; + if (!ptr.getClass().isActor()) + { + const std::string error = "Warning: \"forcegreeting\" command works only for actors."; + runtime.getContext().report (error); + std::cerr << error << std::endl; + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, ptr); } }; From 1f4dd3b39354de9c6e446180292a53612e0ec46d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 5 Aug 2018 12:22:45 +0400 Subject: [PATCH 036/175] Make partial binary search case insensitive, as it supposed to be (bug #4558) --- CHANGELOG.md | 1 + apps/openmw_test_suite/misc/test_stringops.cpp | 10 +++++++++- components/misc/stringops.hpp | 11 +++++++++-- components/resource/scenemanager.cpp | 6 +++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9aa35684..be28904af5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Bug #4539: Paper Doll is affected by GUI scaling Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately + Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw_test_suite/misc/test_stringops.cpp b/apps/openmw_test_suite/misc/test_stringops.cpp index 53a33dbf40..081ca8da61 100644 --- a/apps/openmw_test_suite/misc/test_stringops.cpp +++ b/apps/openmw_test_suite/misc/test_stringops.cpp @@ -7,7 +7,7 @@ struct PartialBinarySearchTest : public ::testing::Test std::vector mDataVec; virtual void SetUp() { - const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01" }; + const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01", "Tri Bip01" }; mDataVec = std::vector(data, data+sizeof(data)/sizeof(data[0])); std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess); } @@ -29,7 +29,15 @@ TEST_F(PartialBinarySearchTest, partial_binary_search_test) EXPECT_TRUE( matches("Tri Head 01") ); EXPECT_TRUE( matches("Tri Head") ); EXPECT_TRUE( matches("tri head") ); + EXPECT_TRUE( matches("Tri bip01") ); + EXPECT_TRUE( matches("bip01") ); + EXPECT_TRUE( matches("bip01 head") ); + EXPECT_TRUE( matches("Bip01 L Hand") ); + EXPECT_TRUE( matches("BIp01 r Clavicle") ); + EXPECT_TRUE( matches("Bip01 SpiNe1") ); + EXPECT_FALSE( matches("") ); + EXPECT_FALSE( matches("Node Bip01") ); EXPECT_FALSE( matches("Hea") ); EXPECT_FALSE( matches(" Head") ); EXPECT_FALSE( matches("Tri Head") ); diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 0fde1c96c6..79fa36d1ee 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -146,8 +146,15 @@ public: std::string::const_iterator yit = y.begin(); for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) { - int res = *xit - *yit; - if(res != 0 && toLower(*xit) != toLower(*yit)) + char left = *xit; + char right = *yit; + if (left == right) + continue; + + left = toLower(left); + right = toLower(right); + int res = left - right; + if(res != 0) return (res > 0) ? 1 : -1; } if(len > 0) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 03b850fac1..db8d99ab4e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -393,9 +393,9 @@ namespace Resource static std::vector reservedNames; if (reservedNames.empty()) { - const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", "Left Clavicle", "Weapon Bone", "Tail", - "Bip01 L Hand", "Bip01 R Hand", "Bip01 Head", "Bip01 Spine1", "Bip01 Spine2", "Bip01 L Clavicle", "Bip01 R Clavicle", "bip01", "Root Bone", "Bip01 Neck", - "BoneOffset", "AttachLight", "ArrowBone", "Camera"}; + const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", + "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", + "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "ArrowBone", "Camera"}; reservedNames = std::vector(reserved, reserved + sizeof(reserved)/sizeof(reserved[0])); for (unsigned int i=0; i Date: Sat, 4 Aug 2018 21:12:24 +0300 Subject: [PATCH 037/175] Use special behavior for all topics with reserved names (bug #4557) --- CHANGELOG.md | 1 + apps/openmw/mwgui/dialogue.cpp | 69 +++++++++++++++++----------------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9aa35684..5a2db2d5e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Bug #4539: Paper Doll is affected by GUI scaling Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately + Bug #4557: Topics with reserved names are handled differently from vanilla Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index f4fe549172..7931c8ccc6 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -362,48 +362,49 @@ namespace MWGui if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice()) return; - int separatorPos = 0; - for (unsigned int i=0; igetItemCount(); ++i) - { - if (mTopicsList->getItemNameAt(i) == "") - separatorPos = i; - } + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (id >= separatorPos) + const std::string sPersuasion = gmst.find("sPersuasion")->getString(); + const std::string sCompanionShare = gmst.find("sCompanionShare")->getString(); + const std::string sBarter = gmst.find("sBarter")->getString(); + const std::string sSpells = gmst.find("sSpells")->getString(); + const std::string sTravel = gmst.find("sTravel")->getString(); + const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->getString(); + const std::string sEnchanting = gmst.find("sEnchanting")->getString(); + const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->getString(); + const std::string sRepair = gmst.find("sRepair")->getString(); + + if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter + && topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle + && topic != sEnchanting && topic != sServiceTrainingTitle && topic != sRepair) { onTopicActivated(topic); if (mGoodbyeButton->getEnabled()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); } - else + else if (topic == sPersuasion) + mPersuasionDialog.setVisible(true); + else if (topic == sCompanionShare) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); + else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get())) { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (topic == gmst.find("sPersuasion")->getString()) - mPersuasionDialog.setVisible(true); - else if (topic == gmst.find("sCompanionShare")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); - else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get())) - { - if (topic == gmst.find("sBarter")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); - else if (topic == gmst.find("sSpells")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr); - else if (topic == gmst.find("sTravel")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr); - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr); - else if (topic == gmst.find("sEnchanting")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr); - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr); - else if (topic == gmst.find("sRepair")->getString()) - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr); - } - else - updateTopics(); + if (topic == sBarter) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); + else if (topic == sSpells) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr); + else if (topic == sTravel) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr); + else if (topic == sSpellMakingMenuTitle) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr); + else if (topic == sEnchanting) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr); + else if (topic == sServiceTrainingTitle) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr); + else if (topic == sRepair) + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr); } + else + updateTopics(); } void DialogueWindow::setPtr(const MWWorld::Ptr& actor) From a1e3b2e586db4b4243169393b23adbca6499e6fe Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sun, 5 Aug 2018 16:15:28 +0300 Subject: [PATCH 038/175] Don't render NiTriShapes without NiTexturingProperty (bug #4483) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bacf11f37..a286613727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ Bug #4475: Scripted animations should not cause movement Bug #4479: "Game" category on Advanced page is getting too long Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory + Bug #4483: Shapes without NiTexturingProperty are rendered Bug #4489: Goodbye doesn't block dialogue hyperlinks Bug #4490: PositionCell on player gives "Error: tried to add local script twice" Bug #4494: Training cap based off Base Skill instead of Modified Skill diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 4e7f6d511e..b50d75ee24 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -573,6 +573,10 @@ namespace NifOsg } } + // Make sure we don't render untextured shapes + if (nifNode->recType == Nif::RC_NiTriShape && boundTextures.empty()) + node->setNodeMask(0x1); + if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, rootNode); From bda23c6ad65e737f61ea5e117b64a6d22fb479e5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 7 Aug 2018 20:49:10 +0300 Subject: [PATCH 039/175] Fix nodemask --- components/nifosg/nifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index b50d75ee24..6d2a01f5d1 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -575,7 +575,7 @@ namespace NifOsg // Make sure we don't render untextured shapes if (nifNode->recType == Nif::RC_NiTriShape && boundTextures.empty()) - node->setNodeMask(0x1); + node->setNodeMask(0x0); if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, rootNode); From 2b90504558ec7861a5f0ed6d36339a29772663ba Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 22:57:16 +0400 Subject: [PATCH 040/175] Init SDL2 before Qt4 to avoid crash on Linux (bug #4529) --- apps/launcher/CMakeLists.txt | 2 ++ apps/launcher/graphicspage.cpp | 23 ++++++----------------- apps/launcher/graphicspage.hpp | 7 ++----- apps/launcher/main.cpp | 18 ++++++++++++++++-- apps/launcher/sdlinit.cpp | 25 +++++++++++++++++++++++++ apps/launcher/sdlinit.hpp | 8 ++++++++ 6 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 apps/launcher/sdlinit.cpp create mode 100644 apps/launcher/sdlinit.hpp diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index ec2e963d12..bfc08a7d64 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -1,6 +1,7 @@ set(LAUNCHER datafilespage.cpp graphicspage.cpp + sdlinit.cpp main.cpp maindialog.cpp playpage.cpp @@ -19,6 +20,7 @@ set(LAUNCHER set(LAUNCHER_HEADER datafilespage.hpp graphicspage.hpp + sdlinit.hpp maindialog.hpp playpage.hpp textslotmsgbox.hpp diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 34442fe714..072f1f36f2 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -12,7 +12,6 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED -#include #include #include @@ -48,27 +47,15 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: } -bool Launcher::GraphicsPage::connectToSdl() { - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - SDL_SetMainReady(); - // Required for determining screen resolution and such on the Graphics tab - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - return false; - } - signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, - // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. - - return true; -} - bool Launcher::GraphicsPage::setupSDL() { - bool sdlConnectSuccessful = connectToSdl(); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + bool sdlConnectSuccessful = initSDL(); if (!sdlConnectSuccessful) { return false; } +#endif int displays = SDL_GetNumVideoDisplays(); @@ -89,8 +76,10 @@ bool Launcher::GraphicsPage::setupSDL() screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); } +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) // Disconnect from SDL processes - SDL_Quit(); + quitSDL(); +#endif return true; } diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 0354e5202f..9d943d5e29 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -7,6 +7,8 @@ #include +#include "sdlinit.hpp" + namespace Files { struct ConfigurationManager; } namespace Launcher @@ -37,11 +39,6 @@ namespace Launcher QStringList getAvailableResolutions(int screen); QRect getMaximumResolution(); - /** - * Connect to the SDL so that we can use it to determine graphics - * @return whether or not connecting to SDL is successful - */ - bool connectToSdl(); bool setupSDL(); }; } diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 866ae2aa9c..c4cf568c4b 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -12,11 +12,18 @@ #endif // MAC_OS_X_VERSION_MIN_REQUIRED #include "maindialog.hpp" +#include "sdlinit.hpp" int main(int argc, char *argv[]) { try { +// Note: we should init SDL2 before Qt4 to avoid crashes on Linux, +// but we should init SDL2 after Qt5 to avoid input issues on MacOS X. +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + initSDL(); +#endif + QApplication app(argc, argv); // Now we make sure the current dir is set to application path @@ -33,11 +40,18 @@ int main(int argc, char *argv[]) if (result == Launcher::FirstRunDialogResultContinue) mainWin.show(); - return app.exec(); + int exitCode = app.exec(); + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + // Disconnect from SDL processes + quitSDL(); +#endif + + return exitCode; } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 0; } -} \ No newline at end of file +} diff --git a/apps/launcher/sdlinit.cpp b/apps/launcher/sdlinit.cpp new file mode 100644 index 0000000000..1fe1fd4c2f --- /dev/null +++ b/apps/launcher/sdlinit.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +bool initSDL() +{ + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); + // Required for determining screen resolution and such on the Graphics tab + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + return false; + } + signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, + // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. + + return true; +} + +void quitSDL() +{ + // Disconnect from SDL processes + SDL_Quit(); +} diff --git a/apps/launcher/sdlinit.hpp b/apps/launcher/sdlinit.hpp new file mode 100644 index 0000000000..e6718f0e9c --- /dev/null +++ b/apps/launcher/sdlinit.hpp @@ -0,0 +1,8 @@ +#ifndef SDLINIT_H +#define SDLINIT_H + +bool initSDL(); + +void quitSDL(); + +#endif From bcd9cc4baa06c70150e0ce31eecc418da7db6ff5 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 02:05:12 +0300 Subject: [PATCH 041/175] Check the actor cell instead of the destination cell in fast travel price logic --- CHANGELOG.md | 1 + apps/openmw/mwgui/travelwindow.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a286613727..bb3c6bb7d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7b65eb771c..991b93a62f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -46,7 +46,7 @@ namespace MWGui mSelect->getHeight()); } - void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) + void TravelWindow::addDestination(const std::string& name, ESM::Position pos, bool interior) { int price; @@ -56,7 +56,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if(interior) + if (!mPtr.getCell()->isExterior()) { price = gmst.find("fMagesGuildTravel")->getInt(); } From b7859b3fa979ca7087226392cade5eaf713f86a5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 23:44:21 +0400 Subject: [PATCH 042/175] Cap underwater view distance (bug #4565) --- CHANGELOG.md | 1 + apps/openmw/mwrender/renderingmanager.cpp | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a286613727..17058b25eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4565: Underwater view distance should be limited Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f6aa8796dd..03863000ff 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -554,8 +554,8 @@ namespace MWRender mLandFogStart = mViewDistance * (1 - fogDepth); mLandFogEnd = mViewDistance; } - mUnderwaterFogStart = mViewDistance * (1 - underwaterFog); - mUnderwaterFogEnd = mViewDistance; + mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog); + mUnderwaterFogEnd = std::min(mViewDistance, 6666.f); } mFogColor = color; } @@ -585,8 +585,6 @@ namespace MWRender mCurrentCameraPos = cameraPos; if (mWater->isUnderwater(cameraPos)) { - float viewDistance = mViewDistance; - viewDistance = std::min(viewDistance, 6666.f); setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); mStateUpdater->setFogStart(mUnderwaterFogStart); mStateUpdater->setFogEnd(mUnderwaterFogEnd); From e9e9c0dd6b3873fc8968b11279032072c1a2de2d Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 12:27:18 +0300 Subject: [PATCH 043/175] Fix guild guide fast travelling to exteriors time --- apps/openmw/mwgui/travelwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 991b93a62f..7a4a9293c7 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -168,7 +168,7 @@ namespace MWGui ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); bool interior = _sender->getUserString("interior") == "y"; - if (!interior) + if (mPtr.getCell()->isExterior()) { ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); From 97bc9954d0922d3ae5f480dcc4ecaffb5216d1ff Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 8 Aug 2018 16:36:25 +0200 Subject: [PATCH 044/175] Update appveyor.yml allow msvc2015 to fail --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 97eaa0e262..9877d2105f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,9 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - msvc: 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 +matrix: + allow_failures: + - msvc: 2015 platform: # - Win32 From 85208eff7f1bd938038042e80fbd6fd428c12f0a Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 21:06:12 +0300 Subject: [PATCH 045/175] Revert untextured shapes rendering changes --- CHANGELOG.md | 1 - components/nifosg/nifloader.cpp | 4 ---- 2 files changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a286613727..9bacf11f37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,7 +65,6 @@ Bug #4475: Scripted animations should not cause movement Bug #4479: "Game" category on Advanced page is getting too long Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory - Bug #4483: Shapes without NiTexturingProperty are rendered Bug #4489: Goodbye doesn't block dialogue hyperlinks Bug #4490: PositionCell on player gives "Error: tried to add local script twice" Bug #4494: Training cap based off Base Skill instead of Modified Skill diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 6d2a01f5d1..4e7f6d511e 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -573,10 +573,6 @@ namespace NifOsg } } - // Make sure we don't render untextured shapes - if (nifNode->recType == Nif::RC_NiTriShape && boundTextures.empty()) - node->setNodeMask(0x0); - if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, rootNode); From 6202b4eca93bc7db9df0768d3d6da110ec5a9e66 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 19:17:38 +0400 Subject: [PATCH 046/175] Do not touch GUI modes when taking screenshots (bug #4528) --- apps/openmw/mwgui/loadingscreen.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 3df482d684..28f4b88901 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -170,12 +170,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() + == MWBase::StateManager::State_NoGame); + + if (!visible) + { + draw(); + return; + } + mVisible = visible; mLoadingBox->setVisible(mVisible); - mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); - setVisible(true); if (mShowWallpaper) @@ -184,9 +190,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading); - - if (!mVisible) - draw(); } void LoadingScreen::loadingOff() From 51af72930549ac5fd71382e4329dd8cbd7ecb678 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Aug 2018 23:21:20 +0400 Subject: [PATCH 047/175] Do not use headtracking in the 1st-person view (bug #4573) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a286613727..0b35016ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4573: Player uses headtracking in the 1st-person mode Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 768d9e37a8..5b9782e174 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1353,12 +1353,15 @@ namespace MWMechanics MWWorld::Ptr headTrackTarget; MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + bool firstPersonPlayer = iter->first == player && MWBase::Environment::get().getWorld()->isFirstPerson(); - // Unconsious actor can not track target - // Also actors in combat and pursue mode do not bother to headtrack + // 1. Unconsious actor can not track target + // 2. Actors in combat and pursue mode do not bother to headtrack + // 3. Player character does not use headtracking in the 1st-person view if (!stats.getKnockedDown() && !stats.getAiSequence().isInCombat() && - !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) && + !firstPersonPlayer) { for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { From 348c6f848eac18463e728e15b7520c82ffecac10 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 9 Aug 2018 02:27:33 +0300 Subject: [PATCH 048/175] Fix a bunch of MSVC warnings --- components/misc/debugging.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp index a983a88dfa..c0c3f5a176 100644 --- a/components/misc/debugging.hpp +++ b/components/misc/debugging.hpp @@ -76,12 +76,8 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to the log file - boost::filesystem::ofstream logfile; logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - coutsb.open (Misc::Tee(logfile, oldcout)); cerrsb.open (Misc::Tee(logfile, oldcerr)); From 57e1462417af48daad864aa6408cad0f3c1bdb30 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 9 Aug 2018 11:01:23 +0400 Subject: [PATCH 049/175] Do not use fall-through --- extern/oics/tinyxmlparser.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/extern/oics/tinyxmlparser.cpp b/extern/oics/tinyxmlparser.cpp index c29e263f49..241f85c3ae 100644 --- a/extern/oics/tinyxmlparser.cpp +++ b/extern/oics/tinyxmlparser.cpp @@ -104,25 +104,17 @@ void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* leng output += *length; - // Scary scary fall throughs. - switch (*length) - { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - } + int lengthLeft = *length; + while (lengthLeft > 1) + { + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + --lengthLeft; + } + + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); } From 126b2fdd42836d0c62afc4e9374e4123669cd2c4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 9 Aug 2018 11:16:19 +0400 Subject: [PATCH 050/175] Use the isPlayer variable to do not check if the current actor is player every time --- apps/openmw/mwmechanics/actors.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5b9782e174..e3cd517993 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1298,6 +1298,8 @@ namespace MWMechanics // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + bool isPlayer = iter->first == player; + float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) @@ -1305,7 +1307,7 @@ namespace MWMechanics // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) bool inProcessingRange = distSqr <= sqrAiProcessingDistance; - if (iter->first == player) + if (isPlayer) iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. @@ -1337,14 +1339,14 @@ namespace MWMechanics { if (timerUpdateAITargets == 0) { - if (iter->first != player) + if (!isPlayer) adjustCommandedActor(iter->first); for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { - if (it->first == iter->first || iter->first == player) // player is not AI-controlled + if (it->first == iter->first || isPlayer) // player is not AI-controlled continue; - engageCombat(iter->first, it->first, cachedAllies, it->first == player); + engageCombat(iter->first, it->first, cachedAllies, isPlayer); } } if (timerUpdateHeadTrack == 0) @@ -1353,7 +1355,7 @@ namespace MWMechanics MWWorld::Ptr headTrackTarget; MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - bool firstPersonPlayer = iter->first == player && MWBase::Environment::get().getWorld()->isFirstPerson(); + bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); // 1. Unconsious actor can not track target // 2. Actors in combat and pursue mode do not bother to headtrack From cd92014533a83f0abcfe44dd9294ce7dd53f32f4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 19:17:38 +0400 Subject: [PATCH 051/175] Do not touch GUI modes when taking screenshots (bug #4528) --- apps/openmw/mwgui/loadingscreen.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 3df482d684..28f4b88901 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -170,12 +170,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() + == MWBase::StateManager::State_NoGame); + + if (!visible) + { + draw(); + return; + } + mVisible = visible; mLoadingBox->setVisible(mVisible); - mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); - setVisible(true); if (mShowWallpaper) @@ -184,9 +190,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading); - - if (!mVisible) - draw(); } void LoadingScreen::loadingOff() From 6a03aa6fdb34cf2bf4d440780fc87471629b810f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 12 Jul 2018 12:43:49 +0400 Subject: [PATCH 052/175] Reduce jittering during turning animations for player --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 34 +++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf20..1f3c0f7ac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode + Bug #4574: Player turning animations are twitchy Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f5d6f8584b..054f0b64d8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1992,13 +1992,31 @@ void CharacterController::update(float duration) } } - mTurnAnimationThreshold -= duration; - if (isTurning()) - mTurnAnimationThreshold = 0.05f; - else if (movestate == CharState_None && isTurning() - && mTurnAnimationThreshold > 0) + // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering + if (mPtr == getPlayer()) { - movestate = mMovementState; + float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f; + float complete; + bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete); + if (movestate == CharState_None && isTurning()) + { + if ((animPlaying && complete < threshold) || mJumpState != jumpstate) + movestate = mMovementState; + } + } + else + { + mTurnAnimationThreshold -= duration; + if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || + movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + { + mTurnAnimationThreshold = 0.05f; + } + else if (movestate == CharState_None && isTurning() + && mTurnAnimationThreshold > 0) + { + movestate = mMovementState; + } } if(movestate != CharState_None && !isTurning()) @@ -2028,8 +2046,10 @@ void CharacterController::update(float duration) if (isTurning()) { + // Adjust animation speed from 1.0 to 1.5 multiplier + float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); if (duration > 0) - mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI))); + mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f)); } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { From a0d0e5d2db5ef49d803213c37bd39d09be4ebd6d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Aug 2018 16:52:09 +0400 Subject: [PATCH 053/175] Give jumping animations higher priority than movement ones --- apps/openmw/mwmechanics/character.cpp | 8 +------- apps/openmw/mwmechanics/character.hpp | 4 +++- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 054f0b64d8..c101c2a056 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1980,15 +1980,9 @@ void CharacterController::update(float duration) else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) { if(rot.z() > 0.0f) - { movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - mAnimation->disable(mCurrentJump); - } else if(rot.z() < 0.0f) - { movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; - mAnimation->disable(mCurrentJump); - } } } @@ -2000,7 +1994,7 @@ void CharacterController::update(float duration) bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete); if (movestate == CharState_None && isTurning()) { - if ((animPlaying && complete < threshold) || mJumpState != jumpstate) + if (animPlaying && complete < threshold) movestate = mMovementState; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 84630a4791..754f551f94 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -31,8 +31,10 @@ enum Priority { Priority_WeaponLowerBody, Priority_SneakIdleLowerBody, Priority_SwimIdle, - Priority_Jump, Priority_Movement, + // Note: in vanilla movement anims have higher priority than jump ones. + // It causes issues with landing animations during movement. + Priority_Jump, Priority_Hit, Priority_Weapon, Priority_Block, From 71bcc11ba5b73b0bdc6d2d4aef055b544c8740f3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Jul 2018 20:59:05 +0400 Subject: [PATCH 054/175] Apply only crossbow reload animation to upper body --- apps/openmw/mwmechanics/character.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f5d6f8584b..b63704e764 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1215,6 +1215,7 @@ bool CharacterController::updateWeaponState() MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; + bool forcestateupdate = false; // We should not play equipping animation and sound during weapon->weapon transition @@ -1663,18 +1664,22 @@ bool CharacterController::updateWeaponState() break; } - // Note: apply reload animations only for upper body since blending with movement animations can give weird result. - // Especially noticable with crossbow reload animation. + // Note: apply crossbow reload animation only for upper body + // since blending with movement animations can give weird result. if(!start.empty()) { + int mask = MWRender::Animation::BlendMask_All; + if (mWeaponType == WeapType_Crossbow) + mask = MWRender::Animation::BlendMask_UpperBody; + mAnimation->disable(mCurrentWeapon); if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) mAnimation->play(mCurrentWeapon, priorityWeapon, - MWRender::Animation::BlendMask_UpperBody, true, + mask, true, weapSpeed, start, stop, 0.0f, 0); else mAnimation->play(mCurrentWeapon, priorityWeapon, - MWRender::Animation::BlendMask_UpperBody, false, + mask, false, weapSpeed, start, stop, 0.0f, 0); } } From df577babe9d794e510af2425d52faa65a0cc6747 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Jul 2018 21:00:13 +0400 Subject: [PATCH 055/175] Increase priority of 1st-person weapon animations to avoid issues with animation blending --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf20..a2014e6be3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode + Bug #4575: Weird result of attack animation blending with movement animations Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b63704e764..11056c674f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1212,9 +1212,10 @@ bool CharacterController::updateWeaponState() mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr(); } + // Apply 1st-person weapon animations only for upper body MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; - + if (mPtr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->isFirstPerson()) + priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; From 780648b584df25c3becbe81dd6166479b3099c67 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 25 Jul 2018 18:50:45 +0400 Subject: [PATCH 056/175] Do not reset idle animations if we do not have ammo --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2014e6be3..1e516eaf3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode Bug #4575: Weird result of attack animation blending with movement animations + Bug #4576: Reset of idle animations when attack can not be started Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 11056c674f..9a6026cc37 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1360,14 +1360,7 @@ bool CharacterController::updateWeaponState() { MWWorld::Ptr player = getPlayer(); - // We should reset player's idle animation in the first-person mode. - if (mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) - mIdleState = CharState_None; - - // In other cases we should not break swim and sneak animations - if (mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) - mIdleState = CharState_None; - + bool resetIdle = ammunition; if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block)) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); @@ -1432,6 +1425,11 @@ bool CharacterController::updateWeaponState() 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } + else + { + resetIdle = false; + } + if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); @@ -1502,6 +1500,14 @@ bool CharacterController::updateWeaponState() } } + // We should reset player's idle animation in the first-person mode. + if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) + mIdleState = CharState_None; + + // In other cases we should not break swim and sneak animations + if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) + mIdleState = CharState_None; + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) mAttackStrength = complete; From cde95979d0e8a5ea05a6e433763936aaa86cf17f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 10 Aug 2018 09:29:01 +0400 Subject: [PATCH 057/175] Fix combat engagement for creatures --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e3cd517993..2d535f57b2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1346,7 +1346,7 @@ namespace MWMechanics { if (it->first == iter->first || isPlayer) // player is not AI-controlled continue; - engageCombat(iter->first, it->first, cachedAllies, isPlayer); + engageCombat(iter->first, it->first, cachedAllies, it->first == player); } } if (timerUpdateHeadTrack == 0) From ec9a1b0d0526c9aba7d7cdd0e68789ab0e747d50 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 08:24:30 +0400 Subject: [PATCH 058/175] Handle RootCollisionNode, attached to non-root node (bug #4311) --- CHANGELOG.md | 1 + components/nifbullet/bulletnifloader.cpp | 42 ++++++++++++++++-------- components/nifbullet/bulletnifloader.hpp | 4 +-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a0..1d6a20caca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4307: World cleanup should remove dead bodies only if death animation is finished + Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index be5a7d9d61..6f8c8f2c0d 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -94,13 +94,16 @@ osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& } else { - bool autogenerated = hasAutoGeneratedCollision(node); - // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated const bool isAnimated = pathFileNameStartsWithX(nif->getFilename()); - handleNode(node, 0, autogenerated, isAnimated, autogenerated); + // If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh + const Nif::Node* rootCollisionNode = getCollisionNode(node); + if (rootCollisionNode) + handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false); + else + handleNode(nif->getFilename(), node, 0, true, isAnimated, true); if (mCompoundShape) { @@ -153,25 +156,34 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) return false; } -bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) +const Nif::Node* BulletNifLoader::getCollisionNode(const Nif::Node* rootNode) { const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) { + // If root NiNode has only other NiNode as child, consider it as a wrapper, not as actual root node const Nif::NodeList &list = ninode->children; - for(size_t i = 0;i < list.length();i++) + if (list.length() == 1 && + rootNode->recType == Nif::RC_NiNode && + list[0].getPtr()->recType == Nif::RC_NiNode) { - if(!list[i].empty()) - { - if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) - return false; - } + return getCollisionNode(list[0].getPtr()); + } + + for(size_t i = 0; i < list.length(); i++) + { + if(list[i].empty()) + continue; + + const Nif::Node* childNode = list[i].getPtr(); + if(childNode->recType == Nif::RC_RootCollisionNode) + return childNode; } } - return true; + return nullptr; } -void BulletNifLoader::handleNode(const Nif::Node *node, int flags, +void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all @@ -184,6 +196,9 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); + if (node->recType == Nif::RC_RootCollisionNode && autogenerated) + std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl; + // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; @@ -212,7 +227,6 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. return; } - } } @@ -235,7 +249,7 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); + handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index fff51933fe..8fd9cc2a18 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -55,9 +55,9 @@ public: private: bool findBoundingBox(const Nif::Node* node, int flags = 0); - void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); + void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); - bool hasAutoGeneratedCollision(const Nif::Node *rootNode); + const Nif::Node* getCollisionNode(const Nif::Node* rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); From 3527f3800e73dad9b64698fbc70aa3d423df5083 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 10 Aug 2018 20:07:38 +0300 Subject: [PATCH 059/175] Use the correct spell projectile speed GMST in AI aiming --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aicombat.cpp | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545de..f74af2427e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters + Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes Bug #3993: Terrain texture blending map is not upscaled Bug #3997: Almalexia doesn't pace diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index bc6d223263..cd97098854 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -600,27 +600,26 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t float duration, int weapType, float strength) { float projSpeed; + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); // get projectile speed (depending on weapon type) if (weapType == ESM::Weapon::MarksmanThrown) { - static float fThrownWeaponMinSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMinSpeed")->getFloat(); - static float fThrownWeaponMaxSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMaxSpeed")->getFloat(); + static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); + static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); - projSpeed = - fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; + projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } - else + else if (weapType != 0) { - static float fProjectileMinSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); + static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); + static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); - projSpeed = - fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; + projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; + } + else // weapType is 0 ==> it's a target spell projectile + { + projSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); } // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same From ef631a0961d45e8be83775a84db57337c8846f43 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 20:26:57 +0300 Subject: [PATCH 060/175] Add dependency for GMock --- .travis.yml | 2 - CI/before_install.linux.sh | 8 - CI/before_script.linux.sh | 17 +- CI/build_googletest.sh | 13 + apps/openmw_test_suite/CMakeLists.txt | 8 +- cmake/FindGMock.cmake | 515 ++++++++++++++++++++++++++ 6 files changed, 547 insertions(+), 16 deletions(-) create mode 100755 CI/build_googletest.sh create mode 100644 cmake/FindGMock.cmake diff --git a/.travis.yml b/.travis.yml index c6facef2ba..781f498e2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ addons: packages: [ # Dev cmake, clang-3.6, libunshield-dev, libtinyxml-dev, - # Tests - libgtest-dev, google-mock, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index 2b0e73110d..25d05e619c 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,11 +1,3 @@ #!/bin/sh sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++ - -# build libgtest & libgtest_main -sudo mkdir /usr/src/gtest/build -cd /usr/src/gtest/build -sudo cmake .. -DBUILD_SHARED_LIBS=1 -sudo make -j4 -sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so -sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index 93be1cb48e..dd879989a8 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -1,8 +1,21 @@ -#!/bin/sh +#!/bin/sh -e free -m + +env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh +GOOGLETEST_DIR="$(pwd)/googletest/build" + mkdir build cd build export CODE_COVERAGE=1 if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi -${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE +${ANALYZE}cmake \ + -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \ + -DBUILD_UNITTESTS=1 \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DBINDIR=/usr/games \ + -DCMAKE_BUILD_TYPE="None" \ + -DUSE_SYSTEM_TINYXML=TRUE \ + -DGTEST_ROOT="${GOOGLETEST_DIR}" \ + -DGMOCK_ROOT="${GOOGLETEST_DIR}" \ + .. diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh new file mode 100755 index 0000000000..cd61379b59 --- /dev/null +++ b/CI/build_googletest.sh @@ -0,0 +1,13 @@ +#!/bin/sh -e + +git clone https://github.com/google/googletest.git +cd googletest +mkdir build +cd build +cmake \ + -D CMAKE_BUILD_TYPE="${CONFIGURATION}" \ + -D CMAKE_INSTALL_PREFIX=. \ + -G "${GENERATOR}" \ + .. +cmake --build . --config "${CONFIGURATION}" +cmake --build . --target install --config "${CONFIGURATION}" diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 48c8be4d8f..675da933d4 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -1,7 +1,9 @@ find_package(GTest REQUIRED) +find_package(GMock REQUIRED) -if (GTEST_FOUND) +if (GTEST_FOUND AND GMOCK_FOUND) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) + include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES ../openmw/mwworld/store.cpp @@ -19,11 +21,9 @@ if (GTEST_FOUND) openmw_add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) - target_link_libraries(openmw_test_suite ${GTEST_BOTH_LIBRARIES} components) + target_link_libraries(openmw_test_suite ${GMOCK_LIBRARIES} components) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) endif() endif() - - diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake new file mode 100644 index 0000000000..8d73242423 --- /dev/null +++ b/cmake/FindGMock.cmake @@ -0,0 +1,515 @@ +# Get the Google C++ Mocking Framework. +# (This file is almost an copy of the original FindGTest.cmake file, +# altered to download and compile GMock and GTest if not found +# in GMOCK_ROOT or GTEST_ROOT respectively, +# feel free to use it as it is or modify it for your own needs.) +# +# Defines the following variables: +# +# GMOCK_FOUND - Found or got the Google Mocking framework +# GTEST_FOUND - Found or got the Google Testing framework +# GMOCK_INCLUDE_DIRS - GMock include directory +# GTEST_INCLUDE_DIRS - GTest include direcotry +# +# Also defines the library variables below as normal variables +# +# GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock_main +# GMOCK_LIBRARIES - libgmock +# GMOCK_MAIN_LIBRARIES - libgmock-main +# +# GTEST_BOTH_LIBRARIES - Both libgtest & libgtest_main +# GTEST_LIBRARIES - libgtest +# GTEST_MAIN_LIBRARIES - libgtest_main +# +# Accepts the following variables as input: +# +# GMOCK_ROOT - The root directory of the gmock install prefix +# GTEST_ROOT - The root directory of the gtest install prefix +# GMOCK_SRC_DIR -The directory of the gmock sources +# GMOCK_VER - The version of the gmock sources to be downloaded +# +#----------------------- +# Example Usage: +# +# set(GMOCK_ROOT "~/gmock") +# find_package(GMock REQUIRED) +# include_directories(${GMOCK_INCLUDE_DIRS}) +# +# add_executable(foo foo.cc) +# target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) +# +#============================================================================= +# Copyright (c) 2016 Michel Estermann +# Copyright (c) 2016 Kamil Strzempowicz +# Copyright (c) 2011 Matej Svec +# +# CMake - Cross Platform Makefile Generator +# Copyright 2000-2016 Kitware, Inc. +# Copyright 2000-2011 Insight Software Consortium +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# ------------------------------------------------------------------------------ +# +# The above copyright and license notice applies to distributions of +# CMake in source and binary form. Some source files contain additional +# notices of original copyright by their contributors; see each source +# for details. Third-party software packages supplied with CMake under +# compatible licenses provide their own copyright notices documented in +# corresponding subdirectories. +# +# ------------------------------------------------------------------------------ +# +# CMake was initially developed by Kitware with the following sponsorship: +# +# * National Library of Medicine at the National Institutes of Health +# as part of the Insight Segmentation and Registration Toolkit (ITK). +# +# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel +# Visualization Initiative. +# +# * National Alliance for Medical Image Computing (NAMIC) is funded by the +# National Institutes of Health through the NIH Roadmap for Medical Research, +# Grant U54 EB005149. +# +# * Kitware, Inc. +#============================================================================= +# Thanks to Daniel Blezek for the GTEST_ADD_TESTS code + +function(gtest_add_tests executable extra_args) + if(NOT ARGN) + message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS") + endif() + if(ARGN STREQUAL "AUTO") + # obtain sources used for building that executable + get_property(ARGN TARGET ${executable} PROPERTY SOURCES) + endif() + set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") + set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)") + foreach(source ${ARGN}) + file(READ "${source}" contents) + string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents}) + foreach(hit ${found_tests}) + string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) + + # Parameterized tests have a different signature for the filter + if("x${test_type}" STREQUAL "xTEST_P") + string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTYPED_TEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" test_name ${hit}) + else() + message(WARNING "Could not parse GTest ${hit} for adding to CTest.") + continue() + endif() + add_test(NAME ${test_name} COMMAND ${executable} --gtest_filter=${test_name} ${extra_args}) + endforeach() + endforeach() +endfunction() + +function(_append_debugs _endvar _library) + if(${_library} AND ${_library}_DEBUG) + set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) + else() + set(_output ${${_library}}) + endif() + set(${_endvar} ${_output} PARENT_SCOPE) +endfunction() + +function(_gmock_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + ENV GMOCK_ROOT + ${GMOCK_ROOT} + PATH_SUFFIXES ${_gmock_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + +function(_gtest_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + ENV GTEST_ROOT + ${GTEST_ROOT} + PATH_SUFFIXES ${_gtest_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + +if(NOT DEFINED GMOCK_MSVC_SEARCH) + set(GMOCK_MSVC_SEARCH MD) +endif() + +set(_gmock_libpath_suffixes lib) +set(_gtest_libpath_suffixes lib) +if(MSVC) + if(GMOCK_MSVC_SEARCH STREQUAL "MD") + list(APPEND _gmock_libpath_suffixes + msvc/gmock-md/Debug + msvc/gmock-md/Release) + list(APPEND _gtest_libpath_suffixes + msvc/gtest-md/Debug + msvc/gtest-md/Release) + elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") + list(APPEND _gmock_libpath_suffixes + msvc/gmock/Debug + msvc/gmock/Release) + list(APPEND _gtest_libpath_suffixes + msvc/gtest/Debug + msvc/gtest/Release) + endif() +endif() + +find_path(GMOCK_INCLUDE_DIR gmock/gmock.h + HINTS + $ENV{GMOCK_ROOT}/include + ${GMOCK_ROOT}/include + ) +mark_as_advanced(GMOCK_INCLUDE_DIR) + +find_path(GTEST_INCLUDE_DIR gtest/gtest.h + HINTS + $ENV{GTEST_ROOT}/include + ${GTEST_ROOT}/include + ) +mark_as_advanced(GTEST_INCLUDE_DIR) + +if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") + # The provided /MD project files for Google Mock add -md suffixes to the + # library names. + _gmock_find_library(GMOCK_LIBRARY gmock-md gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) + + _gtest_find_library(GTEST_LIBRARY gtest-md gtest) + _gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd) + _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main) + _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind) +else() + _gmock_find_library(GMOCK_LIBRARY gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) + + _gtest_find_library(GTEST_LIBRARY gtest) + _gtest_find_library(GTEST_LIBRARY_DEBUG gtestd) + _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main) + _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind) +endif() + +if(NOT TARGET GTest::GTest) + add_library(GTest::GTest UNKNOWN IMPORTED) +endif() +if(NOT TARGET GTest::Main) + add_library(GTest::Main UNKNOWN IMPORTED) +endif() + +if(NOT TARGET GMock::GMock) + add_library(GMock::GMock UNKNOWN IMPORTED) +endif() + +if(NOT TARGET GMock::Main) + add_library(GMock::Main UNKNOWN IMPORTED) +endif() + +set(GMOCK_LIBRARY_EXISTS OFF) +set(GTEST_LIBRARY_EXISTS OFF) + +if(EXISTS "${GMOCK_LIBRARY}" OR EXISTS "${GMOCK_LIBRARY_DEBUG}" AND GMOCK_INCLUDE_DIR) + set(GMOCK_LIBRARY_EXISTS ON) +endif() + +if(EXISTS "${GTEST_LIBRARY}" OR EXISTS "${GTEST_LIBRARY_DEBUG}" AND GTEST_INCLUDE_DIR) + set(GTEST_LIBRARY_EXISTS ON) +endif() + +if(NOT (${GMOCK_LIBRARY_EXISTS} AND ${GTEST_LIBRARY_EXISTS})) + + include(ExternalProject) + + if(GTEST_USE_STATIC_LIBS) + set(GTEST_CMAKE_ARGS -Dgtest_force_shared_crt:BOOL=ON -DBUILD_SHARED_LIBS=OFF) + if(BUILD_SHARED_LIBS) + list(APPEND GTEST_CMAKE_ARGS + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -Dgtest_hide_internal_symbols=ON + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON + -DCMAKE_POLICY_DEFAULT_CMP0063=NEW + ) + endif() + set(GTEST_LIBRARY_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) + else() + set(GTEST_CMAKE_ARGS -DBUILD_SHARED_LIBS=ON) + set(GTEST_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) + endif() + if(WIN32) + list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON) + endif() + + if("${GMOCK_SRC_DIR}" STREQUAL "") + message(STATUS "Downloading GMock / GTest version ${GMOCK_VER} from git") + if("${GMOCK_VER}" STREQUAL "1.6.0" OR "${GMOCK_VER}" STREQUAL "1.7.0") + set(GTEST_BIN_DIR "${GMOCK_ROOT}/src/gtest-build") + set(GTEST_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + + externalproject_add( + gtest + GIT_REPOSITORY "https://github.com/google/googletest.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR ${GTEST_BIN_DIR} + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + ) + + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + externalproject_add( + gmock + GIT_REPOSITORY "https://github.com/google/googlemock.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR ${GMOCK_BIN_DIR} + BUILD_BYPRODUCTS + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(gmock gtest) + + add_dependencies(GTest::GTest gtest) + add_dependencies(GTest::Main gtest) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + + externalproject_get_property(gtest source_dir) + set(GTEST_INCLUDE_DIR "${source_dir}/include") + mark_as_advanced(GTEST_INCLUDE_DIR) + externalproject_get_property(gmock source_dir) + set(GMOCK_INCLUDE_DIR "${source_dir}/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + else() #1.8.0 + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + externalproject_add( + gmock + GIT_REPOSITORY "https://github.com/google/googletest.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR "${GMOCK_BIN_DIR}" + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(GTest::GTest gmock) + add_dependencies(GTest::Main gmock) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + + externalproject_get_property(gmock source_dir) + set(GTEST_INCLUDE_DIR "${source_dir}/googletest/include") + set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + mark_as_advanced(GTEST_INCLUDE_DIR) + endif() + + # Prevent CMake from complaining about these directories missing when the libgtest/libgmock targets get used as dependencies + file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) + else() + message(STATUS "Building Gmock / Gtest from dir ${GMOCK_SRC_DIR}") + + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + if(EXISTS "${GMOCK_SRC_DIR}/gtest/include/gtest/gtest.h") + set(GTEST_INCLUDE_DIR "${GMOCK_SRC_DIR}/gtest/include") + mark_as_advanced(GTEST_INCLUDE_DIR) + endif() + if(EXISTS "${GMOCK_SRC_DIR}/include/gmock/gmock.h") + set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + elseif(EXISTS "${GMOCK_SRC_DIR}/../../include/gmock/gmock.h") + set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/../../include") + if(IS_ABSOLUTE "${GMOCK_INCLUDE_DIR}") + get_filename_component(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" ABSOLUTE) + endif() + mark_as_advanced(GMOCK_INCLUDE_DIR) + endif() + + externalproject_add( + gmock + SOURCE_DIR ${GMOCK_SRC_DIR} + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR "${GMOCK_BIN_DIR}" + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(GTest::GTest gmock) + add_dependencies(GTest::Main gmock) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) +find_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) + +include(CMakeFindDependencyMacro) +find_dependency(Threads) + +set_target_properties(GTest::GTest PROPERTIES + INTERFACE_LINK_LIBRARIES "Threads::Threads" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GTEST_LIBRARY}" + ) + +if(GTEST_INCLUDE_DIR) + set_target_properties(GTest::GTest PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" + ) +endif() + +set_target_properties(GTest::Main PROPERTIES + INTERFACE_LINK_LIBRARIES "GTest::GTest" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GTEST_MAIN_LIBRARY}") + +set_target_properties(GMock::GMock PROPERTIES + INTERFACE_LINK_LIBRARIES "Threads::Threads" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GMOCK_LIBRARY}") + +if(GMOCK_INCLUDE_DIR) + set_target_properties(GMock::GMock PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIR}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIR}" + ) + if(GMOCK_VER VERSION_LESS "1.7") + # GMock 1.6 still has GTest as an external link-time dependency, + # so just specify it on the link interface. + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_LINK_LIBRARIES GTest::GTest) + elseif(GTEST_INCLUDE_DIR) + # GMock 1.7 and beyond doesn't have it as a link-time dependency anymore, + # so merge it's compile-time interface (include dirs) with ours. + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") + endif() +endif() + +set_target_properties(GMock::Main PROPERTIES + INTERFACE_LINK_LIBRARIES "GMock::GMock" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GMOCK_MAIN_LIBRARY}") + +if(GTEST_FOUND) + set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR}) + set(GTEST_LIBRARIES GTest::GTest) + set(GTEST_MAIN_LIBRARIES GTest::Main) + set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) + if(VERBOSE) + message(STATUS "GTest includes: ${GTEST_INCLUDE_DIRS}") + message(STATUS "GTest libs: ${GTEST_BOTH_LIBRARIES}") + endif() +endif() + +if(GMOCK_FOUND) + set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) + set(GMOCK_LIBRARIES GMock::GMock) + set(GMOCK_MAIN_LIBRARIES GMock::Main) + set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) + if(VERBOSE) + message(STATUS "GMock includes: ${GMOCK_INCLUDE_DIRS}") + message(STATUS "GMock libs: ${GMOCK_BOTH_LIBRARIES}") + endif() +endif() From 6afc1dc3a05d43c5f56d4378f89eb7de4f545bb2 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 20:27:46 +0300 Subject: [PATCH 061/175] Support build with coverage for components and test suite --- apps/openmw_test_suite/CMakeLists.txt | 5 +++++ components/CMakeLists.txt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 675da933d4..eb3492ed50 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -26,4 +26,9 @@ if (GTEST_FOUND AND GMOCK_FOUND) if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) endif() + + if (BUILD_WITH_CODE_COVERAGE) + add_definitions(--coverage) + target_link_libraries(openmw_test_suite gcov) + endif() endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e2e6b97bb1..a941190998 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -242,6 +242,11 @@ if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) endif() +if (BUILD_WITH_CODE_COVERAGE) + add_definitions(--coverage) + target_link_libraries(components gcov) +endif() + # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) From f2a63bcf3512236d04558ca999a400128d16e21d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 22:22:34 +0300 Subject: [PATCH 062/175] Add unit tests for BulletNifLoader --- apps/openmw_test_suite/CMakeLists.txt | 2 + .../nifloader/testbulletnifloader.cpp | 951 ++++++++++++++++++ .../bullethelpers/processtrianglecallback.hpp | 34 + components/nif/niffile.hpp | 43 +- components/nif/recordptr.hpp | 8 + components/nifbullet/bulletnifloader.cpp | 13 +- components/nifbullet/bulletnifloader.hpp | 2 +- components/resource/bulletshapemanager.cpp | 2 +- 8 files changed, 1037 insertions(+), 18 deletions(-) create mode 100644 apps/openmw_test_suite/nifloader/testbulletnifloader.cpp create mode 100644 components/bullethelpers/processtrianglecallback.hpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index eb3492ed50..bc39bc2be6 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -15,6 +15,8 @@ if (GTEST_FOUND AND GMOCK_FOUND) esm/test_fixed_string.cpp misc/test_stringops.cpp + + nifloader/testbulletnifloader.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp new file mode 100644 index 0000000000..a2311be490 --- /dev/null +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -0,0 +1,951 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace +{ + template + bool compareObjects(const T* lhs, const T* rhs) + { + return (!lhs && !rhs) || (lhs && rhs && *lhs == *rhs); + } + + std::vector getTriangles(const btBvhTriangleMeshShape& shape) + { + std::vector result; + auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { + for (std::size_t i = 0; i < 3; ++i) + result.push_back(triangle[i]); + }); + btVector3 aabbMin; + btVector3 aabbMax; + shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + shape.processAllTriangles(&callback, aabbMin, aabbMax); + return result; + } +} + +static std::ostream& operator <<(std::ostream& stream, const btVector3& value) +{ + return stream << "btVector3 {" + << std::setprecision(std::numeric_limits::max_exponent10) << value.getX() << ", " + << std::setprecision(std::numeric_limits::max_exponent10) << value.getY() << ", " + << std::setprecision(std::numeric_limits::max_exponent10) << value.getZ() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btMatrix3x3& value) +{ + stream << "btMatrix3x3 {"; + for (int i = 0; i < 3; ++i) + stream << value.getRow(i) << ", "; + return stream << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btTransform& value) +{ + return stream << "btTransform {" << value.getBasis() << ", " << value.getOrigin() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value); + +static std::ostream& operator <<(std::ostream& stream, const btCompoundShape& value) +{ + stream << "btCompoundShape {" << value.getLocalScaling() << ", "; + stream << "{"; + for (int i = 0; i < value.getNumChildShapes(); ++i) + stream << value.getChildShape(i) << ", "; + stream << "},"; + stream << "{"; + for (int i = 0; i < value.getNumChildShapes(); ++i) + stream << value.getChildTransform(i) << ", "; + stream << "}"; + return stream << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btBoxShape& value) +{ + return stream << "btBoxShape {" << value.getLocalScaling() << ", " << value.getHalfExtentsWithoutMargin() << "}"; +} + +namespace Resource +{ + +static std::ostream& operator <<(std::ostream& stream, const TriangleMeshShape& value) +{ + stream << "Resource::TriangleMeshShape {" << value.getLocalScaling() << ", " + << value.usesQuantizedAabbCompression() << ", " << value.getOwnsBvh() << ", {"; + auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { + for (std::size_t i = 0; i < 3; ++i) + stream << triangle[i] << ", "; + }); + btVector3 aabbMin; + btVector3 aabbMax; + value.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + value.processAllTriangles(&callback, aabbMin, aabbMax); + return stream << "}}"; +} + +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape& value) +{ + switch (value.getShapeType()) + { + case COMPOUND_SHAPE_PROXYTYPE: + return stream << static_cast(value); + case BOX_SHAPE_PROXYTYPE: + return stream << static_cast(value); + case TRIANGLE_MESH_SHAPE_PROXYTYPE: + if (const auto casted = dynamic_cast(&value)) + return stream << *casted; + break; + } + return stream << "btCollisionShape {" << value.getShapeType() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value) +{ + return value ? stream << "&" << *value : stream << "nullptr"; +} + +namespace osg +{ + static std::ostream& operator <<(std::ostream& stream, const Vec3f& value) + { + return stream << "osg::Vec3f {" + << value.x() << ", " + << value.y() << ", " + << value.z() << "}"; + } +} + +namespace std +{ + static std::ostream& operator <<(std::ostream& stream, const map& value) + { + stream << "std::map {"; + for (const auto& v : value) + stream << "{" << v.first << ", " << v.second << "},"; + return stream << "}"; + } +} + +namespace Resource +{ + static bool operator ==(const Resource::BulletShape& lhs, const Resource::BulletShape& rhs) + { + return compareObjects(lhs.mCollisionShape, rhs.mCollisionShape) + && lhs.mCollisionBoxHalfExtents == rhs.mCollisionBoxHalfExtents + && lhs.mCollisionBoxTranslate == rhs.mCollisionBoxTranslate + && lhs.mAnimatedShapes == rhs.mAnimatedShapes; + } + + static std::ostream& operator <<(std::ostream& stream, const Resource::BulletShape& value) + { + return stream << "Resource::BulletShape {" + << value.mCollisionShape << ", " + << value.mCollisionBoxHalfExtents << ", " + << value.mAnimatedShapes + << "}"; + } +} + +static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs); + +static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs) +{ + if (lhs.getNumChildShapes() != rhs.getNumChildShapes() || lhs.getLocalScaling() != rhs.getLocalScaling()) + return false; + for (int i = 0; i < lhs.getNumChildShapes(); ++i) + { + if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i)) + || !(lhs.getChildTransform(i) == rhs.getChildTransform(i))) + return false; + } + return true; +} + +static bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs) +{ + return lhs.getLocalScaling() == rhs.getLocalScaling() + && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin(); +} + +static bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs) +{ + return lhs.getLocalScaling() == rhs.getLocalScaling() + && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression() + && lhs.getOwnsBvh() == rhs.getOwnsBvh() + && getTriangles(lhs) == getTriangles(rhs); +} + +static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs) +{ + if (lhs.getShapeType() != rhs.getShapeType()) + return false; + switch (lhs.getShapeType()) + { + case COMPOUND_SHAPE_PROXYTYPE: + return static_cast(lhs) == static_cast(rhs); + case BOX_SHAPE_PROXYTYPE: + return static_cast(lhs) == static_cast(rhs); + case TRIANGLE_MESH_SHAPE_PROXYTYPE: + if (const auto lhsCasted = dynamic_cast(&lhs)) + if (const auto rhsCasted = dynamic_cast(&rhs)) + return *lhsCasted == *rhsCasted; + return false; + } + return false; +} + +namespace +{ + using namespace testing; + using NifBullet::BulletNifLoader; + + void init(Nif::Transformation& value) + { + value = Nif::Transformation::getIdentity(); + } + + void init(Nif::Extra& value) + { + value.extra = Nif::ExtraPtr(nullptr); + } + + void init(Nif::Controlled& value) + { + init(static_cast(value)); + value.controller = Nif::ControllerPtr(nullptr); + } + + void init(Nif::Named& value) + { + init(static_cast(value)); + } + + void init(Nif::Node& value) + { + init(static_cast(value)); + value.flags = 0; + init(value.trafo); + value.hasBounds = false; + value.parent = nullptr; + value.isBone = false; + } + + void init(Nif::NiTriShape& value) + { + init(static_cast(value)); + value.recType = Nif::RC_NiTriShape; + value.data = Nif::NiTriShapeDataPtr(nullptr); + value.skin = Nif::NiSkinInstancePtr(nullptr); + } + + void init(Nif::NiSkinInstance& value) + { + value.data = Nif::NiSkinDataPtr(nullptr); + value.root = Nif::NodePtr(nullptr); + } + + void init(Nif::Controller& value) + { + value.next = Nif::ControllerPtr(nullptr); + value.flags = 0; + value.frequency = 0; + value.phase = 0; + value.timeStart = 0; + value.timeStop = 0; + value.target = Nif::ControlledPtr(nullptr); + } + + void copy(const btTransform& src, Nif::Transformation& dst) { + dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z()); + for (int row = 0; row < 3; ++row) + for (int column = 0; column < 3; ++column) + dst.rotation.mValues[column][row] = src.getBasis().getRow(row)[column]; + } + + struct NifFileMock : Nif::File + { + MOCK_CONST_METHOD1(fail, void (const std::string&)); + MOCK_CONST_METHOD1(warn, void (const std::string&)); + MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t)); + MOCK_CONST_METHOD0(numRecords, std::size_t ()); + MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t)); + MOCK_CONST_METHOD0(numRoots, std::size_t ()); + MOCK_METHOD1(setUseSkinning, void (bool)); + MOCK_CONST_METHOD0(getUseSkinning, bool ()); + MOCK_CONST_METHOD0(getFilename, std::string ()); + }; + + struct RecordMock : Nif::Record + { + MOCK_METHOD1(read, void (Nif::NIFStream *nif)); + }; + + struct TestBulletNifLoader : Test + { + BulletNifLoader mLoader; + const StrictMock mNifFile; + Nif::Node mNode; + Nif::Node mNode2; + Nif::NiNode mNiNode; + Nif::NiNode mNiNode2; + Nif::NiNode mNiNode3; + Nif::NiTriShapeData mNiTriShapeData; + Nif::NiTriShape mNiTriShape; + Nif::NiTriShapeData mNiTriShapeData2; + Nif::NiTriShape mNiTriShape2; + Nif::NiSkinInstance mNiSkinInstance; + Nif::NiStringExtraData mNiStringExtraData; + Nif::NiStringExtraData mNiStringExtraData2; + Nif::Controller mController; + btTransform mTransform {btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3)}; + btTransform mResultTransform { + btMatrix3x3( + 1, 0, 0, + 0, 0.82417738437652587890625, 0.56633174419403076171875, + 0, -0.56633174419403076171875, 0.82417738437652587890625 + ), + btVector3(1, 2, 3) + }; + btTransform mResultTransform2 { + btMatrix3x3( + 1, 0, 0, + 0, 0.7951543331146240234375, 0.606407105922698974609375, + 0, -0.606407105922698974609375, 0.7951543331146240234375 + ), + btVector3(4, 8, 12) + }; + + TestBulletNifLoader() + { + init(mNode); + init(mNode2); + init(mNiNode); + init(mNiNode2); + init(mNiNode3); + init(mNiTriShape); + init(mNiTriShape2); + init(mNiSkinInstance); + init(mNiStringExtraData); + init(mNiStringExtraData2); + init(mController); + + mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)}; + mNiTriShapeData.triangles = {0, 1, 2}; + mNiTriShape.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData); + + mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)}; + mNiTriShapeData2.triangles = {0, 1, 2}; + mNiTriShape2.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData2); + } + }; + + TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_not_nif_node_should_return_default) + { + StrictMock record; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&record)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_collision_node_nif_node_should_return_default) + { + mNode.recType = Nif::RC_RootCollisionNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(4, 5, 6); + mNiNode.boundPos = osg::Vec3f(-4, -5, -6); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNode2.hasBounds = true; + mNode2.boundXYZ = osg::Vec3f(4, 5, 6); + mNode2.boundPos = osg::Vec3f(-4, -5, -6); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); + mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds) + { + mNode.hasBounds = true; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNode2.hasBounds = true; + mNode2.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode2.boundXYZ = osg::Vec3f(4, 5, 6); + mNode2.boundPos = osg::Vec3f(-4, -5, -6); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); + mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(4, 5, 6); + expected.mCollisionBoxTranslate = osg::Vec3f(-4, -5, -6); + std::unique_ptr box(new btBoxShape(btVector3(4, 5, 6))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape) + { + mNode.hasBounds = true; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape) + { + mNiTriShape.hasBounds = true; + mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3); + mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiNode2)})); + mNiNode2.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes) + { + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2) + })); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape) + { + mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(3, 3, 3)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + mNiTriShape.parent = &mNiNode; + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(12, 12, 12)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + + copy(mTransform, mNiTriShape2.trafo); + mNiTriShape2.trafo.scale = 3; + + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2), + })); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(3, 3, 3)); + + std::unique_ptr triangles2(new btTriangleMesh(false)); + triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + std::unique_ptr mesh2(new Resource::TriangleMeshShape(triangles2.release(), true)); + mesh2->setLocalScaling(btVector3(3, 3, 3)); + + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform, mesh.release()); + shape->addChildShape(mResultTransform, mesh2.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape) + { + mController.recType = Nif::RC_NiKeyframeController; + mController.flags |= Nif::NiNode::ControllerFlag_Active; + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + mNiTriShape.parent = &mNiNode; + mNiTriShape.controller = Nif::ControllerPtr(&mController); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(12, 12, 12)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape) + { + mController.recType = Nif::RC_NiKeyframeController; + mController.flags |= Nif::NiNode::ControllerFlag_Active; + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + copy(mTransform, mNiTriShape2.trafo); + mNiTriShape2.trafo.scale = 3; + mNiTriShape2.parent = &mNiNode; + mNiTriShape2.controller = Nif::ControllerPtr(&mController); + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2), + })); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(1, 2, 3), btVector3(4, 2, 3), btVector3(4, 4.632747650146484375, 1.56172335147857666015625)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(1, 1, 1)); + + std::unique_ptr triangles2(new btTriangleMesh(false)); + triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + std::unique_ptr mesh2(new Resource::TriangleMeshShape(triangles2.release(), true)); + mesh2->setLocalScaling(btVector3(12, 12, 12)); + + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh2.release()); + shape->addChildShape(btTransform::getIdentity(), mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.recType = Nif::RC_AvoidNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) + { + mNiTriShape.data = Nif::NiTriShapeDataPtr(nullptr); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) + { + mNiTriShape.data->triangles.clear(); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.string = "NC___"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.extra = Nif::ExtraPtr(&mNiStringExtraData2); + mNiStringExtraData2.string = "NC___"; + mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.string = "MRK"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes) + { + mNiStringExtraData.string = "MRK"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode3.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode3.recType = Nif::RC_RootCollisionNode; + mNiNode2.children = Nif::NodeList(std::vector({Nif::NodePtr(nullptr), Nif::NodePtr(&mNiNode3)})); + mNiNode2.recType = Nif::RC_NiNode; + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiNode2)})); + mNiNode.recType = Nif::RC_NiNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } +} diff --git a/components/bullethelpers/processtrianglecallback.hpp b/components/bullethelpers/processtrianglecallback.hpp new file mode 100644 index 0000000000..ee005b459e --- /dev/null +++ b/components/bullethelpers/processtrianglecallback.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H +#define OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H + +#include + +#include + +namespace BulletHelpers +{ + template + class ProcessTriangleCallback : public btTriangleCallback + { + public: + ProcessTriangleCallback(Impl impl) + : mImpl(std::move(impl)) + {} + + void processTriangle(btVector3* triangle, int partId, int triangleIndex) override final + { + return mImpl(triangle, partId, triangleIndex); + } + + private: + Impl mImpl; + }; + + template + ProcessTriangleCallback::type> makeProcessTriangleCallback(Impl&& impl) + { + return ProcessTriangleCallback::type>(std::forward(impl)); + } +} + +#endif diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 9e39a37abf..cab2e98807 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -14,7 +14,30 @@ namespace Nif { -class NIFFile +struct File +{ + virtual ~File() = default; + + virtual void fail(const std::string &msg) const = 0; + + virtual void warn(const std::string &msg) const = 0; + + virtual Record *getRecord(size_t index) const = 0; + + virtual size_t numRecords() const = 0; + + virtual Record *getRoot(size_t index = 0) const = 0; + + virtual size_t numRoots() const = 0; + + virtual void setUseSkinning(bool skinning) = 0; + + virtual bool getUseSkinning() const = 0; + + virtual std::string getFilename() const = 0; +}; + +class NIFFile final : public File { enum NIFVersion { VER_MW = 0x04000002 // Morrowind NIFs @@ -48,14 +71,14 @@ class NIFFile public: /// Used if file parsing fails - void fail(const std::string &msg) const + void fail(const std::string &msg) const override { std::string err = " NIFFile Error: " + msg; err += "\nFile: " + filename; throw std::runtime_error(err); } /// Used when something goes wrong, but not catastrophically so - void warn(const std::string &msg) const + void warn(const std::string &msg) const override { std::cerr << " NIFFile Warning: " << msg < NIFFilePtr; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 25beaf098b..09c3809872 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -23,6 +23,8 @@ class RecordPtrT public: RecordPtrT() : index(-2) {} + RecordPtrT(X* ptr) : ptr(ptr) {} + /// Read the index from the nif void read(NIFStream *nif) { @@ -87,6 +89,12 @@ class RecordListT std::vector list; public: + RecordListT() = default; + + RecordListT(std::vector list) + : list(std::move(list)) + {} + void read(NIFStream *nif) { int len = nif->getInt(); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 6f8c8f2c0d..3f033967cd 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -56,20 +56,20 @@ BulletNifLoader::~BulletNifLoader() { } -osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& nif) +osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; mCompoundShape = NULL; mStaticMesh = NULL; - if (nif->numRoots() < 1) + if (nif.numRoots() < 1) { warn("Found no root nodes in NIF."); return mShape; } - Nif::Record *r = nif->getRoot(0); + Nif::Record *r = nif.getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); @@ -96,14 +96,15 @@ osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& { // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated - const bool isAnimated = pathFileNameStartsWithX(nif->getFilename()); + const auto filename = nif.getFilename(); + const bool isAnimated = pathFileNameStartsWithX(filename); // If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh const Nif::Node* rootCollisionNode = getCollisionNode(node); if (rootCollisionNode) - handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false); + handleNode(filename, rootCollisionNode, 0, false, isAnimated, false); else - handleNode(nif->getFilename(), node, 0, true, isAnimated, true); + handleNode(filename, node, 0, true, isAnimated, true); if (mCompoundShape) { diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 8fd9cc2a18..22d228d106 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -50,7 +50,7 @@ public: abort(); } - osg::ref_ptr load(const Nif::NIFFilePtr& file); + osg::ref_ptr load(const Nif::File& file); private: bool findBoundingBox(const Nif::Node* node, int flags = 0); diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index a3d09311a5..622506d6b0 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -127,7 +127,7 @@ osg::ref_ptr BulletShapeManager::getShape(const std::string & if (ext == "nif") { NifBullet::BulletNifLoader loader; - shape = loader.load(mNifFileManager->get(normalized)); + shape = loader.load(*mNifFileManager->get(normalized)); } else { From 2de38142e249dfc5d7c9e688ea492480d1012393 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 11 Jul 2018 00:13:32 +0300 Subject: [PATCH 063/175] Replace raw pointers by unique_ptr --- components/nifbullet/bulletnifloader.cpp | 31 +++++++++++++++--------- components/nifbullet/bulletnifloader.hpp | 4 +-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 3f033967cd..82d27269f8 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -47,8 +47,8 @@ namespace NifBullet { BulletNifLoader::BulletNifLoader() - : mCompoundShape(NULL) - , mStaticMesh(NULL) + : mCompoundShape() + , mStaticMesh() { } @@ -84,10 +84,11 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { std::unique_ptr compound (new btCompoundShape); - btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)); + std::unique_ptr boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents))); btTransform transform = btTransform::getIdentity(); transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); - compound->addChildShape(transform, boxShape); + compound->addChildShape(transform, boxShape.get()); + boxShape.release(); mShape->mCollisionShape = compound.release(); return mShape; @@ -108,16 +109,20 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) if (mCompoundShape) { - mShape->mCollisionShape = mCompoundShape; if (mStaticMesh) { btTransform trans; trans.setIdentity(); - mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh,true)); + mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh.get(), true)); + mStaticMesh.release(); } + mShape->mCollisionShape = mCompoundShape.release(); } else if (mStaticMesh) - mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh,true); + { + mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true); + mStaticMesh.release(); + } return mShape; } @@ -277,9 +282,9 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, if (isAnimated) { if (!mCompoundShape) - mCompoundShape = new btCompoundShape(); + mCompoundShape.reset(new btCompoundShape); - btTriangleMesh* childMesh = new btTriangleMesh(); + std::unique_ptr childMesh(new btTriangleMesh); const Nif::NiTriShapeData *data = shape->data.getPtr(); @@ -297,7 +302,8 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } - Resource::TriangleMeshShape* childShape = new Resource::TriangleMeshShape(childMesh,true); + std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); + childMesh.release(); float scale = shape->trafo.scale; const Nif::Node* parent = shape; @@ -314,12 +320,13 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); - mCompoundShape->addChildShape(trans, childShape); + mCompoundShape->addChildShape(trans, childShape.get()); + childShape.release(); } else { if (!mStaticMesh) - mStaticMesh = new btTriangleMesh(false); + mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position const Nif::NiTriShapeData *data = shape->data.getPtr(); diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 22d228d106..44134fd238 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -61,9 +61,9 @@ private: void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); - btCompoundShape* mCompoundShape; + std::unique_ptr mCompoundShape; - btTriangleMesh* mStaticMesh; + std::unique_ptr mStaticMesh; osg::ref_ptr mShape; }; From c2b8c7086a7009362e6d512cc9d4f2ab05d616ed Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 12 Aug 2018 00:52:02 +0300 Subject: [PATCH 064/175] Fix race condition Values are accessed from main thread and cell preloader threads. --- components/terrain/material.cpp | 231 ++++++++++++++++++++------------ 1 file changed, 149 insertions(+), 82 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 722df95078..a0f051524b 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -12,66 +12,150 @@ #include +#include + +namespace +{ + class BlendmapTexMat + { + public: + static const osg::ref_ptr& value(const int blendmapScale) + { + static BlendmapTexMat instance; + return instance.get(blendmapScale); + } + + const osg::ref_ptr& get(const int blendmapScale) + { + const std::lock_guard lock(mMutex); + auto texMat = mTexMatMap.find(blendmapScale); + if (texMat == mTexMatMap.end()) + { + osg::Matrixf matrix; + float scale = (blendmapScale/(static_cast(blendmapScale)+1.f)); + matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); + matrix.preMultScale(osg::Vec3f(scale, scale, 1.f)); + matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); + // We need to nudge the blendmap to look like vanilla. + // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently. + matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f)); + + texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first; + } + return texMat->second; + } + + private: + std::mutex mMutex; + std::map> mTexMatMap; + }; + + class LayerTexMat + { + public: + static const osg::ref_ptr& value(const float layerTileSize) + { + static LayerTexMat instance; + return instance.get(layerTileSize); + } + + const osg::ref_ptr& get(const float layerTileSize) + { + const std::lock_guard lock(mMutex); + auto texMat = mTexMatMap.find(layerTileSize); + if (texMat == mTexMatMap.end()) + { + texMat = mTexMatMap.insert(std::make_pair(layerTileSize, + new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))).first; + } + return texMat->second; + } + + private: + std::mutex mMutex; + std::map> mTexMatMap; + }; + + class EqualDepth + { + public: + static const osg::ref_ptr& value() + { + static EqualDepth instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + EqualDepth() + : mValue(new osg::Depth) + { + mValue->setFunction(osg::Depth::EQUAL); + } + }; + + class LequalDepth + { + public: + static const osg::ref_ptr& value() + { + static LequalDepth instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + LequalDepth() + : mValue(new osg::Depth) + { + mValue->setFunction(osg::Depth::LEQUAL); + } + }; + + class BlendFunc + { + public: + static const osg::ref_ptr& value() + { + static BlendFunc instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + BlendFunc() + : mValue(new osg::BlendFunc) + { + mValue->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); + } + }; + + class TexEnvCombine + { + public: + static const osg::ref_ptr& value() + { + static TexEnvCombine instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + TexEnvCombine() + : mValue(new osg::TexEnvCombine) + { + mValue->setCombine_RGB(osg::TexEnvCombine::REPLACE); + mValue->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + } + }; +} namespace Terrain { - - osg::ref_ptr getBlendmapTexMat(int blendmapScale) - { - static std::map > texMatMap; - osg::ref_ptr texMat = texMatMap[blendmapScale]; - if (!texMat) - { - osg::Matrixf matrix; - float scale = (blendmapScale/(static_cast(blendmapScale)+1.f)); - matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); - matrix.preMultScale(osg::Vec3f(scale, scale, 1.f)); - matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); - // We need to nudge the blendmap to look like vanilla. - // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently. - matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f)); - - texMat = new osg::TexMat(matrix); - - texMatMap[blendmapScale] = texMat; - } - return texMat; - } - - osg::ref_ptr getLayerTexMat(float layerTileSize) - { - static std::map > texMatMap; - osg::ref_ptr texMat = texMatMap[layerTileSize]; - if (!texMat) - { - texMat = new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize,layerTileSize,1.f))); - - texMatMap[layerTileSize] = texMat; - } - return texMat; - } - - osg::ref_ptr getEqualDepth() - { - static osg::ref_ptr depth; - if (!depth) - { - depth = new osg::Depth; - depth->setFunction(osg::Depth::EQUAL); - } - return depth; - } - osg::ref_ptr getLequalDepth() - { - static osg::ref_ptr depth; - if (!depth) - { - depth = new osg::Depth; - depth->setFunction(osg::Depth::LEQUAL); - } - return depth; - } - std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { @@ -87,16 +171,9 @@ namespace Terrain if (!firstLayer) { - static osg::ref_ptr blendFunc; - if (!blendFunc) - { - blendFunc= new osg::BlendFunc(); - blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); - } stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - - stateset->setAttributeAndModes(getEqualDepth(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); } // disable fog if we're the first layer of several - supposed to be completely black if (firstLayer && blendmaps.size() > 0) @@ -105,7 +182,7 @@ namespace Terrain fog->setStart(10000000); fog->setEnd(10000000); stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); - stateset->setAttributeAndModes(getLequalDepth(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); } int texunit = 0; @@ -115,7 +192,7 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap); if (layerTileSize != 1.f) - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); @@ -125,8 +202,7 @@ namespace Terrain osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); stateset->setTextureAttributeAndModes(texunit, blendmap.get()); - - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); + stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale)); stateset->addUniform(new osg::Uniform("blendMap", texunit)); } @@ -165,17 +241,8 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, blendmap.get()); // This is to map corner vertices directly to the center of a blendmap texel. - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); - - static osg::ref_ptr texEnvCombine; - if (!texEnvCombine) - { - texEnvCombine = new osg::TexEnvCombine; - texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); - texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - } - - stateset->setTextureAttributeAndModes(texunit, texEnvCombine, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale)); + stateset->setTextureAttributeAndModes(texunit, TexEnvCombine::value(), osg::StateAttribute::ON); ++texunit; } @@ -185,7 +252,7 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, tex.get()); if (layerTileSize != 1.f) - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); } stateset->setRenderBinDetails(passIndex++, "RenderBin"); From 7d6e3673e0a965754a87f9351a25cba204affcf4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 13:19:12 +0400 Subject: [PATCH 065/175] Implement advanced logging system (feature #4581) --- CHANGELOG.md | 1 + apps/opencs/main.cpp | 4 +- apps/openmw/main.cpp | 4 +- components/CMakeLists.txt | 6 +- components/debug/debugging.cpp | 103 +++++++++++++++++++++++++ components/debug/debugging.hpp | 129 ++++++++++++++++++++++++++++++++ components/debug/debuglog.cpp | 8 ++ components/debug/debuglog.hpp | 66 ++++++++++++++++ components/misc/debugging.hpp | 108 -------------------------- components/nifosg/nifloader.cpp | 5 +- 10 files changed, 319 insertions(+), 115 deletions(-) create mode 100644 components/debug/debugging.cpp create mode 100644 components/debug/debugging.hpp create mode 100644 components/debug/debuglog.cpp create mode 100644 components/debug/debuglog.hpp delete mode 100644 components/misc/debugging.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf20..f10318ea6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill Feature #4549: Weapon priority: use the actual damage in weapon rating calculations Feature #4550: Weapon priority: make ranged weapon bonus more sensible + Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index ebc686c233..6058e73f9e 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include "model/doc/messages.hpp" #include "model/world/universalid.hpp" @@ -80,5 +80,5 @@ int runApplication(int argc, char *argv[]) int main(int argc, char *argv[]) { - return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log"); + return wrapApplication(&runApplication, argc, argv, "OpenMW-CS"); } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index d9fff69528..bbe1267b18 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "engine.hpp" @@ -265,7 +265,7 @@ extern "C" int SDL_main(int argc, char**argv) int main(int argc, char**argv) #endif { - return wrapApplication(&runApplication, argc, argv, "/openmw.log"); + return wrapApplication(&runApplication, argc, argv, "OpenMW"); } // Platform specific for Windows when there is no console built into the executable. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e2e6b97bb1..74a11c6c93 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,7 +85,11 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng debugging messageformatparser + utf8stream stringops resourcehelpers rng messageformatparser + ) + +add_component_dir (debug + debugging debuglog ) IF(NOT WIN32 AND NOT APPLE) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp new file mode 100644 index 0000000000..bec97207a4 --- /dev/null +++ b/components/debug/debugging.cpp @@ -0,0 +1,103 @@ +#include "debugging.hpp" + +namespace Debug +{ + std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) + { + // Skip debug level marker + Level level = getLevelMarker(str); + if (level != NoLevel) + { + writeImpl(str+1, size-1, level); + return size; + } + + writeImpl(str, size, NoLevel); + return size; + } + + Level DebugOutputBase::getLevelMarker(const char *str) + { + if (unsigned(*str) <= unsigned(Marker)) + { + return Level(*str); + } + + return NoLevel; + } + + void DebugOutputBase::fillCurrentDebugLevel() + { + const char* env = getenv("OPENMW_DEBUG_LEVEL"); + if (env) + { + std::string value(env); + if (value == "ERROR") + CurrentDebugLevel = Error; + else if (value == "WARNING") + CurrentDebugLevel = Warning; + else if (value == "INFO") + CurrentDebugLevel = Info; + else if (value == "VERBOSE") + CurrentDebugLevel = Verbose; + + return; + } + + CurrentDebugLevel = Verbose; + } +} + +int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName) +{ + // Some objects used to redirect cout and cerr + // Scope must be here, so this still works inside the catch block for logging exceptions + std::streambuf* cout_rdbuf = std::cout.rdbuf (); + std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + + int ret = 0; + try + { + Files::ConfigurationManager cfgMgr; +#if defined(_WIN32) && defined(_DEBUG) + // Redirect cout and cerr to VS debug output when running in debug mode + boost::iostreams::stream_buffer sb; + sb.open(Debug::DebugOutput()); + std::cout.rdbuf (&sb); + std::cerr.rdbuf (&sb); +#else + // Redirect cout and cerr to the log file + const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + boost::filesystem::ofstream logfile; + logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); + + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + coutsb.open (Debug::Tee(logfile, oldcout)); + cerrsb.open (Debug::Tee(logfile, oldcerr)); + + std::cout.rdbuf (&coutsb); + std::cerr.rdbuf (&cerrsb); +#endif + ret = innerApplication(argc, argv); + } + catch (std::exception& e) + { +#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) + if (!isatty(fileno(stdin))) +#endif + SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); + + Log(Debug::Error) << "ERROR: " << e.what(); + + ret = 1; + } + + // Restore cout and cerr + std::cout.rdbuf(cout_rdbuf); + std::cerr.rdbuf(cerr_rdbuf); + + return ret; +} diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp new file mode 100644 index 0000000000..59536d685c --- /dev/null +++ b/components/debug/debugging.hpp @@ -0,0 +1,129 @@ +#ifndef DEBUG_DEBUGGING_H +#define DEBUG_DEBUGGING_H + +#include +#include + +#include + +#include + +#include "debuglog.hpp" + +namespace Debug +{ + // ANSI colors for terminal + enum Color + { + Reset = 0, + DarkGray = 90, + Red = 91, + Yellow = 93 + }; + + class DebugOutputBase : public boost::iostreams::sink + { + public: + DebugOutputBase() + { + if (CurrentDebugLevel == NoLevel) + fillCurrentDebugLevel(); + } + + virtual std::streamsize write(const char *str, std::streamsize size); + + protected: + static Level getLevelMarker(const char *str); + + static void fillCurrentDebugLevel(); + + virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + return size; + } + + char mDebugLevel; + }; + +#if defined(_WIN32) && defined(_DEBUG) + class DebugOutput : public DebugOutputBase + { + public: + std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + // Make a copy for null termination + std::string tmp (str, static_cast(size)); + // Write string to Visual Studio Debug output + OutputDebugString (tmp.c_str ()); + return size; + } + + virtual ~DebugOutput() {} + }; +#else + + class Tee : public DebugOutputBase + { + public: + Tee(std::ostream &stream, std::ostream &stream2) + : out(stream), out2(stream2) + { + // TODO: check which stream is stderr? + mUseColor = useColoredOutput(); + + mColors[Error] = Red; + mColors[Warning] = Yellow; + mColors[Info] = Reset; + mColors[Verbose] = DarkGray; + mColors[NoLevel] = Reset; + } + + virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + out.write (str, size); + out.flush(); + + if(mUseColor) + { + out2 << "\033[0;" << mColors[debugLevel] << "m"; + out2.write (str, size); + out2 << "\033[0;" << Reset << "m"; + } + else + { + out2.write(str, size); + } + out2.flush(); + + return size; + } + + virtual ~Tee() {} + + private: + + static bool useColoredOutput() + { + // Note: cmd.exe in Win10 should support ANSI colors, but in its own way. +#if defined(_WIN32) + return 0; +#else + char *term = getenv("TERM"); + bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr)); + + return useColor; +#endif + } + + std::ostream &out; + std::ostream &out2; + bool mUseColor; + + std::map mColors; + }; +#endif +} + +int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName); + +#endif diff --git a/components/debug/debuglog.cpp b/components/debug/debuglog.cpp new file mode 100644 index 0000000000..510c638614 --- /dev/null +++ b/components/debug/debuglog.cpp @@ -0,0 +1,8 @@ +#include "debuglog.hpp" + +namespace Debug +{ + Level CurrentDebugLevel = Level::NoLevel; +} + +std::mutex Log::sLock; diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp new file mode 100644 index 0000000000..bfd7d1196e --- /dev/null +++ b/components/debug/debuglog.hpp @@ -0,0 +1,66 @@ +#ifndef DEBUG_LOG_H +#define DEBUG_LOG_H + +#include +#include +#include + +namespace Debug +{ + enum Level + { + NoLevel = 0, + Error = 1, + Warning = 2, + Info = 3, + Verbose = 4, + Marker = Verbose + }; + + extern Level CurrentDebugLevel; +} + +class Log +{ + static std::mutex sLock; + + std::unique_lock mLock; +public: + // Locks a global lock while the object is alive + Log(Debug::Level level) : + mLock(sLock), + mLevel(level) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << static_cast(mLevel); + } + + // Perfect forwarding wrappers to give the chain of objects to cout + template + Log& operator<<(T&& rhs) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::forward(rhs); + + return *this; + } + template + Log& operator<<(const T& rhs) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::forward(rhs); + + return *this; + } + + ~Log() + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::endl; + } + +private: + Debug::Level mLevel; +}; + +#endif diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp deleted file mode 100644 index c0c3f5a176..0000000000 --- a/components/misc/debugging.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef MISC_DEBUGGING_H -#define MISC_DEBUGGING_H - -#include -#include - -#include - -namespace Misc -{ -#if defined(_WIN32) && defined(_DEBUG) - - class DebugOutput : public boost::iostreams::sink - { - public: - std::streamsize write(const char *str, std::streamsize size) - { - // Make a copy for null termination - std::string tmp (str, static_cast(size)); - // Write string to Visual Studio Debug output - OutputDebugString (tmp.c_str ()); - return size; - } - }; -#else - class Tee : public boost::iostreams::sink - { - public: - Tee(std::ostream &stream, std::ostream &stream2) - : out(stream), out2(stream2) - { - } - - std::streamsize write(const char *str, std::streamsize size) - { - out.write (str, size); - out.flush(); - out2.write (str, size); - out2.flush(); - return size; - } - - private: - std::ostream &out; - std::ostream &out2; - }; -#endif -} - -int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& logName) -{ - // Some objects used to redirect cout and cerr - // Scope must be here, so this still works inside the catch block for logging exceptions - std::streambuf* cout_rdbuf = std::cout.rdbuf (); - std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); - -#if !(defined(_WIN32) && defined(_DEBUG)) - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; -#endif - - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - - boost::filesystem::ofstream logfile; - - int ret = 0; - try - { - Files::ConfigurationManager cfgMgr; -#if defined(_WIN32) && defined(_DEBUG) - // Redirect cout and cerr to VS debug output when running in debug mode - boost::iostreams::stream_buffer sb; - sb.open(Misc::DebugOutput()); - std::cout.rdbuf (&sb); - std::cerr.rdbuf (&sb); -#else - // Redirect cout and cerr to the log file - logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - - coutsb.open (Misc::Tee(logfile, oldcout)); - cerrsb.open (Misc::Tee(logfile, oldcerr)); - - std::cout.rdbuf (&coutsb); - std::cerr.rdbuf (&cerrsb); -#endif - ret = innerApplication(argc, argv); - } - catch (std::exception& e) - { -#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) - if (!isatty(fileno(stdin))) -#endif - SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); - - std::cerr << "\nERROR: " << e.what() << std::endl; - - ret = 1; - } - - // Restore cout and cerr - std::cout.rdbuf(cout_rdbuf); - std::cerr.rdbuf(cerr_rdbuf); - - return ret; -} - -#endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 4e7f6d511e..5857a39872 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -9,6 +9,7 @@ #include // resource +#include #include #include #include @@ -992,7 +993,7 @@ namespace NifOsg int uvSet = *it; if (uvSet >= (int)data->uvlist.size()) { - std::cerr << "Warning: out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename << std::endl; + Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename; if (!data->uvlist.empty()) geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), &data->uvlist[0][0]), osg::Array::BIND_PER_VERTEX); continue; @@ -1279,7 +1280,7 @@ namespace NifOsg const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; if(tex.texture.empty() && texprop->controller.empty()) { - std::cerr << "Warning: texture layer " << i << " is in use but empty in " << mFilename << std::endl; + Log(Debug::Verbose) << "Texture layer " << i << " is in use but empty in " << mFilename; continue; } From 307e0103dc5ddcdc87d7c5ac5b7a8390b9b6a662 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 19 Jul 2018 16:38:32 +0400 Subject: [PATCH 066/175] Use fallbacks for missing weapon animations (bug #4470) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 123 ++++++++++++++++++++------ apps/openmw/mwmechanics/character.hpp | 3 + 3 files changed, 101 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545de..5b70649d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal + Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour Bug #4474: No fallback when getVampireHead fails Bug #4475: Scripted animations should not cause movement Bug #4479: "Game" category on Advanced page is getting too long diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index db9a53f827..6f9cb941d8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -583,7 +583,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat refreshHitRecoilAnims(); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); - if (!mPtr.getClass().isBipedal(mPtr)) + if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; refreshJumpAnims(weap, jump, force); @@ -592,7 +592,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat // idle handled last as it can depend on the other states // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // the idle animation should be displayed - if ((mUpperBodyState != UpperCharState_Nothing + if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) || (mMovementState != CharState_None && !isTurning()) || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr)) @@ -773,6 +773,20 @@ void CharacterController::playRandomDeath(float startpoint) playDeath(startpoint, mDeathState); } +std::string CharacterController::chooseRandomAttackAnimation() const +{ + std::string result; + bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr); + + if (isSwimming) + result = chooseRandomGroup("swimattack"); + + if (!isSwimming || !mAnimation->hasAnimation(result)) + result = chooseRandomGroup("attack"); + + return result; +} + CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mWeapon(MWWorld::Ptr()) @@ -1123,16 +1137,10 @@ bool CharacterController::updateCreatureState() else mCurrentWeapon = ""; } + if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation { - bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr); - int roll = Misc::Rng::rollDice(3); // [0, 2] - if (roll == 0) - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack1") ? "swimattack1" : "attack1"; - else if (roll == 1) - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack2") ? "swimattack2" : "attack2"; - else - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack3") ? "swimattack3" : "attack3"; + mCurrentWeapon = chooseRandomAttackAnimation(); } if (!mCurrentWeapon.empty()) @@ -1212,9 +1220,10 @@ bool CharacterController::updateWeaponState() mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr(); } - // Apply 1st-person weapon animations only for upper body + // Use blending only with 3d-person movement animations for bipedal actors + bool firstPersonPlayer = (mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()); MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - if (mPtr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->isFirstPerson()) + if (!firstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; @@ -1241,6 +1250,10 @@ bool CharacterController::updateWeaponState() MWRender::Animation::BlendMask_All, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0); mUpperBodyState = UpperCharState_UnEquipingWeap; + + // If we do not have the "unequip detach" key, hide weapon manually. + if (mAnimation->getTextKeyTime(weapgroup+": unequip detach") < 0) + mAnimation->showWeapons(false); } if(!downSoundId.empty()) @@ -1252,7 +1265,6 @@ bool CharacterController::updateWeaponState() float complete; bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - if (!animPlaying || complete >= 1.0f) { // Weapon is changed, no current animation (e.g. unequipping or attack). @@ -1275,6 +1287,13 @@ bool CharacterController::updateWeaponState() MWRender::Animation::BlendMask_All, true, 1.0f, "equip start", "equip stop", 0.0f, 0); mUpperBodyState = UpperCharState_EquipingWeap; + + // If we do not have the "equip attach" key, show weapon manually. + if (weaptype != WeapType_Spell) + { + if (mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0) + mAnimation->showWeapons(true); + } } } @@ -1365,6 +1384,15 @@ bool CharacterController::updateWeaponState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; + + // Randomize attacks for non-bipedal creatures with Weapon flag + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && + !mPtr.getClass().isBipedal(mPtr) && + (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) + { + mCurrentWeapon = chooseRandomAttackAnimation(); + } + if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would @@ -1412,16 +1440,31 @@ bool CharacterController::updateWeaponState() const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation - switch(firstEffect.mRange) + std::string startKey; + std::string stopKey; + if (isRandomAttackAnimation(mCurrentWeapon)) { - case 0: mAttackType = "self"; break; - case 1: mAttackType = "touch"; break; - case 2: mAttackType = "target"; break; + startKey = "start"; + stopKey = "stop"; + MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately + mCastingManualSpell = false; + } + else + { + switch(firstEffect.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } + + startKey = mAttackType+" start"; + stopKey = mAttackType+" stop"; } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, true, - weapSpeed, mAttackType+" start", mAttackType+" stop", + 1, startKey, stopKey, 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } @@ -1469,16 +1512,27 @@ bool CharacterController::updateWeaponState() } else if (ammunition) { - if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || - mWeaponType == WeapType_Thrown) + std::string startKey; + std::string stopKey; + if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + { mAttackType = "shoot"; - else + startKey = mAttackType+" start"; + stopKey = mAttackType+" min attack"; + } + + if (isRandomAttackAnimation(mCurrentWeapon)) + { + startKey = "start"; + stopKey = "stop"; + } + else if (mAttackType != "shoot") { if(mPtr == getPlayer()) { if (isWeapon) { - if (Settings::Manager::getBool("best attack", "Game")) + if (Settings::Manager::getBool("best attack", "Game")) { MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mAttackType = getBestAttack(weapon->get()->mBase); @@ -1487,14 +1541,19 @@ bool CharacterController::updateWeaponState() setAttackTypeBasedOnMovement(); } else - setAttackTypeRandomly(mAttackType); + { + // There is no "best attack" for Hand-to-Hand + setAttackTypeRandomly(mAttackType); + } } // else if (mPtr != getPlayer()) use mAttackType set by AiCombat + startKey = mAttackType+" start"; + stopKey = mAttackType+" min attack"; } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, - weapSpeed, mAttackType+" start", mAttackType+" min attack", + weapSpeed, startKey, stopKey, 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; } @@ -1619,7 +1678,7 @@ bool CharacterController::updateWeaponState() else if(mUpperBodyState == UpperCharState_UnEquipingWeap) mUpperBodyState = UpperCharState_Nothing; } - else if(complete >= 1.0f) + else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon)) { std::string start, stop; switch(mUpperBodyState) @@ -1690,6 +1749,11 @@ bool CharacterController::updateWeaponState() weapSpeed, start, stop, 0.0f, 0); } } + else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon)) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } if (mPtr.getClass().hasInventoryStore(mPtr)) { @@ -2422,6 +2486,13 @@ void CharacterController::setAttackTypeBasedOnMovement() mAttackType = "chop"; } +bool CharacterController::isRandomAttackAnimation(const std::string& group) const +{ + return (group == "attack1" || group == "swimattack1" || + group == "attack2" || group == "swimattack2" || + group == "attack3" || group == "swimattack3"); +} + bool CharacterController::isAttackPrepairing() const { return mUpperBodyState == UpperCharState_StartToMinAttack || @@ -2523,7 +2594,7 @@ void CharacterController::setAttackTypeRandomly(std::string& attackType) bool CharacterController::readyToPrepareAttack() const { return (mHitState == CharState_None || mHitState == CharState_Block) - && mUpperBodyState <= UpperCharState_WeapEquiped; + && mUpperBodyState <= UpperCharState_WeapEquiped; } bool CharacterController::readyToStartAttack() const diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 754f551f94..631f78208a 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -224,6 +224,9 @@ class CharacterController : public MWRender::Animation::TextKeyListener bool updateCreatureState(); void updateIdleStormState(bool inwater); + std::string chooseRandomAttackAnimation() const; + bool isRandomAttackAnimation(const std::string& group) const; + bool isPersistentAnimPlaying(); void updateAnimQueue(); From 8de3383612f83060492ceefb90e6d3313ed58d28 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 12 Aug 2018 17:04:39 +0400 Subject: [PATCH 067/175] Add zero-duration spells effects to effect list (bug #3533) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545de..72dd1df3eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work + Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3876: Landscape texture painting is misaligned diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 09dadcabab..b337fa6b76 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -509,9 +509,18 @@ namespace MWMechanics } else // target.getClass().isActor() == true { + ActiveSpells::ActiveEffect effect; + effect.mEffectId = effectIt->mEffectID; + effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; + effect.mMagnitude = magnitude; + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (hasDuration && effectIt->mDuration == 0) { + // We still should add effect to list to allow GetSpellEffects to detect this spell + effect.mDuration = 0.f; + appliedLastingEffects.push_back(effect); + // duration 0 means apply full magnitude instantly bool wasDead = target.getClass().getCreatureStats(target).isDead(); effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), magnitude); @@ -522,18 +531,15 @@ namespace MWMechanics } else { - // add to list of active effects, to apply in next frame - ActiveSpells::ActiveEffect effect; - effect.mEffectId = effectIt->mEffectID; - effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; + if (!hasDuration) effect.mDuration = 1.0f; else effect.mDuration = static_cast(effectIt->mDuration); - effect.mMagnitude = magnitude; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); + // add to list of active effects, to apply in next frame appliedLastingEffects.push_back(effect); // Unequip all items, if a spell with the ExtraSpell effect was casted From 579f35511afebba2ea3b36c140f793b7efebf6d3 Mon Sep 17 00:00:00 2001 From: James Carty Date: Sun, 12 Aug 2018 22:45:03 +0100 Subject: [PATCH 068/175] Add support for scietific notation flag for MessageBox --- components/compiler/lineparser.cpp | 2 +- components/compiler/lineparser.hpp | 2 +- components/interpreter/miscopcodes.hpp | 8 ++++++-- components/misc/messageformatparser.cpp | 10 ++++++---- components/misc/messageformatparser.hpp | 8 +++++++- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 2d551348de..602bb826fb 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -557,7 +557,7 @@ namespace Compiler mExplicit.clear(); } - void GetArgumentsFromMessageFormat::visitedPlaceholder(Placeholder placeholder, char /*padding*/, int /*width*/, int /*precision*/) + void GetArgumentsFromMessageFormat::visitedPlaceholder(Placeholder placeholder, char /*padding*/, int /*width*/, int /*precision*/, Notation /*notation*/) { switch (placeholder) { diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index d92c4895e2..8f7f64bf2c 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -83,7 +83,7 @@ namespace Compiler std::string mArguments; protected: - virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision); + virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation); virtual void visitedCharacter(char c) {} public: diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 20a1b52bec..8bca0aec13 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -24,7 +24,7 @@ namespace Interpreter Runtime& mRuntime; protected: - virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision) + virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) { std::ostringstream out; out.fill(padding); @@ -58,7 +58,11 @@ namespace Interpreter float value = mRuntime[0].mFloat; mRuntime.pop(); - out << std::fixed << value; + if (notation == FixedNotation) + out << std::fixed << value; + else + out << std::scientific << value; + mFormattedMessage += out.str(); } break; diff --git a/components/misc/messageformatparser.cpp b/components/misc/messageformatparser.cpp index 3a35c83eaf..708d9565d1 100644 --- a/components/misc/messageformatparser.cpp +++ b/components/misc/messageformatparser.cpp @@ -49,11 +49,13 @@ namespace Misc width = (widthSet) ? width : -1; if (m[i] == 'S' || m[i] == 's') - visitedPlaceholder(StringPlaceholder, pad, width, precision); - else if (m[i] == 'g' || m[i] == 'G') - visitedPlaceholder(IntegerPlaceholder, pad, width, precision); + visitedPlaceholder(StringPlaceholder, pad, width, precision, FixedNotation); + else if (m[i] == 'd' || m[i] == 'i') + visitedPlaceholder(IntegerPlaceholder, pad, width, precision, FixedNotation); else if (m[i] == 'f' || m[i] == 'F') - visitedPlaceholder(FloatPlaceholder, pad, width, precision); + visitedPlaceholder(FloatPlaceholder, pad, width, precision, FixedNotation); + else if (m[i] == 'e' || m[i] == 'E') + visitedPlaceholder(FloatPlaceholder, pad, width, precision, ScientificNotation); } } } diff --git a/components/misc/messageformatparser.hpp b/components/misc/messageformatparser.hpp index c12b9352ae..10da624d45 100644 --- a/components/misc/messageformatparser.hpp +++ b/components/misc/messageformatparser.hpp @@ -15,7 +15,13 @@ namespace Misc FloatPlaceholder }; - virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision) = 0; + enum Notation + { + FixedNotation, + ScientificNotation + }; + + virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) = 0; virtual void visitedCharacter(char c) = 0; public: From 9dfd775bf23cf6421408d017f3e3dbd4c0b9611e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 13 Aug 2018 08:30:50 +0400 Subject: [PATCH 069/175] Implement GetPCTraveling console command --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwgui/travelwindow.cpp | 4 ++++ apps/openmw/mwscript/miscextensions.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 17 ++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a886166253..ee1227e0c0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -566,6 +566,9 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void setPlayerTraveling(bool traveling) = 0; + virtual bool isPlayerTraveling() const = 0; + virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0; /// Return terrain height at \a worldPos position. diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7a4a9293c7..cf4fb1b5e0 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -154,6 +154,10 @@ namespace MWGui if (playerGoldsetPlayerTraveling(true); + if (!mPtr.getCell()->isExterior()) // Interior cell -> mages guild transport MWBase::Environment::get().getWindowManager()->playSound("mysticism cast"); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 30d0c6feee..7da1a48333 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1155,8 +1155,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { - /// \todo implement traveling check - runtime.push (0); + runtime.push (MWBase::Environment::get().getWorld()->isPlayerTraveling()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f4c2a75f3d..41ed740008 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -153,7 +153,8 @@ namespace MWWorld mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), - mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mSpellPreloadTimer(0.f) + mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), + mPlayerTraveling(false), mSpellPreloadTimer(0.f) { mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath)); @@ -311,6 +312,7 @@ namespace MWWorld mGoToJail = false; mTeleportEnabled = true; mLevitationEnabled = true; + mPlayerTraveling = false; fillGlobalVariables(); } @@ -1639,6 +1641,9 @@ namespace MWWorld void World::update (float duration, bool paused) { + // Reset "traveling" flag - there was a frame to detect traveling. + mPlayerTraveling = false; + if (mGoToJail && !paused) goToJail(); @@ -3312,6 +3317,16 @@ namespace MWWorld return MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail); } + void World::setPlayerTraveling(bool traveling) + { + mPlayerTraveling = traveling; + } + + bool World::isPlayerTraveling() const + { + return mPlayerTraveling; + } + float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const { return mRendering->getTerrainHeightAt(worldPos); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a2616995ad..1026837680 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -168,6 +168,7 @@ namespace MWWorld bool mLevitationEnabled; bool mGoToJail; int mDaysInPrison; + bool mPlayerTraveling; float mSpellPreloadTimer; @@ -672,6 +673,9 @@ namespace MWWorld bool isPlayerInJail() const override; + void setPlayerTraveling(bool traveling); + bool isPlayerTraveling() const; + /// Return terrain height at \a worldPos position. float getTerrainHeightAt(const osg::Vec3f& worldPos) const override; From 4003fa12962fa0c48a8b2780b340b8068fd552cb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 13 Aug 2018 11:10:01 +0400 Subject: [PATCH 070/175] Keep jailing state for one frame after leaving jail (bug #3788) --- CHANGELOG.md | 1 + apps/openmw/mwworld/worldimp.cpp | 19 ++++++++++++------- apps/openmw/mwworld/worldimp.hpp | 5 +++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545de..f08d05d9d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Bug #3486: [Mod] NPC Commands does not work Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning + Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 41ed740008..874478b710 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -154,7 +154,7 @@ namespace MWWorld mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), - mPlayerTraveling(false), mSpellPreloadTimer(0.f) + mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) { mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath)); @@ -313,6 +313,7 @@ namespace MWWorld mTeleportEnabled = true; mLevitationEnabled = true; mPlayerTraveling = false; + mPlayerInJail = false; fillGlobalVariables(); } @@ -1641,11 +1642,17 @@ namespace MWWorld void World::update (float duration, bool paused) { + if (mGoToJail && !paused) + goToJail(); + // Reset "traveling" flag - there was a frame to detect traveling. mPlayerTraveling = false; - if (mGoToJail && !paused) - goToJail(); + // The same thing for "in jail" flag: reset it if: + // 1. Player was in jail + // 2. Jailing window was closed + if (mPlayerInJail && !mGoToJail && !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail)) + mPlayerInJail = false; updateWeather(duration, paused); @@ -3286,6 +3293,7 @@ namespace MWWorld { // Reset bounty and forget the crime now, but don't change cell yet (the player should be able to read the dialog text first) mGoToJail = true; + mPlayerInJail = true; MWWorld::Ptr player = getPlayerPtr(); @@ -3311,10 +3319,7 @@ namespace MWWorld bool World::isPlayerInJail() const { - if (mGoToJail) - return true; - - return MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail); + return mPlayerInJail; } void World::setPlayerTraveling(bool traveling) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1026837680..2352fd31cb 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -169,6 +169,7 @@ namespace MWWorld bool mGoToJail; int mDaysInPrison; bool mPlayerTraveling; + bool mPlayerInJail; float mSpellPreloadTimer; @@ -673,8 +674,8 @@ namespace MWWorld bool isPlayerInJail() const override; - void setPlayerTraveling(bool traveling); - bool isPlayerTraveling() const; + void setPlayerTraveling(bool traveling) override; + bool isPlayerTraveling() const override; /// Return terrain height at \a worldPos position. float getTerrainHeightAt(const osg::Vec3f& worldPos) const override; From 2cc1b52baf1a484a6c7a3a83581b00a155b142ec Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 24 Nov 2016 00:58:30 +0900 Subject: [PATCH 071/175] Drain and fortify fixes for dynamicStats (Fixes #3049) --- apps/openmw/mwmechanics/actors.cpp | 23 ++++--- apps/openmw/mwmechanics/creaturestats.cpp | 1 + apps/openmw/mwmechanics/stat.cpp | 75 ++++++++++++++++++++--- apps/openmw/mwmechanics/stat.hpp | 23 +++++-- apps/openmw/mwscript/statsextensions.cpp | 1 + 5 files changed, 103 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2d535f57b2..e68a5d3982 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -546,7 +546,7 @@ namespace MWMechanics float diff = (static_cast(magickaFactor*intelligence)) - magicka.getBase(); float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase()); magicka.setModified(magicka.getModified() + diff, 0); - magicka.setCurrent(magicka.getBase() * currentToBaseRatio); + magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true); creatureStats.setMagicka(magicka); } @@ -577,8 +577,14 @@ namespace MWMechanics float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; + + // Current fatigue can be above base value due to a fortify effect. + // In that case stop here and don't try to restore. + DynamicStat fatigue = stats.getFatigue(); + if (fatigue.getCurrent() >= fatigue.getBase()) + return; - // restore fatigue + // Restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); @@ -586,7 +592,6 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); } @@ -598,16 +603,20 @@ namespace MWMechanics MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + // Current fatigue can be above base value due to a fortify effect. + // In that case stop here and don't try to restore. + DynamicStat fatigue = stats.getFatigue(); + if (fatigue.getCurrent() >= fatigue.getBase()) + return; - // restore fatigue + // Restore fatigue + int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; - DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); } @@ -723,7 +732,7 @@ namespace MWMechanics for(int i = 0;i < 3;++i) { DynamicStat stat = creatureStats.getDynamic(i); - stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - + stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(), // Magicka can be decreased below zero due to a fortify effect wearing off // Fatigue can be decreased below zero meaning the actor will be knocked out diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 49823d7a5d..d1128860cf 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -195,6 +195,7 @@ namespace MWMechanics mDead = true; mDynamic[index].setModifier(0); + mDynamic[index].setCurrentModifier(0); mDynamic[index].setCurrent(0); if (MWBase::Environment::get().getWorld()->getGodModeState()) diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 41c5bac5af..f53052a286 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -5,11 +5,11 @@ namespace MWMechanics { template - Stat::Stat() : mBase (0), mModified (0) {} + Stat::Stat() : mBase (0), mModified (0), mCurrentModified (0) {} template - Stat::Stat(T base) : mBase (base), mModified (base) {} + Stat::Stat(T base) : mBase (base), mModified (base), mCurrentModified (base) {} template - Stat::Stat(T base, T modified) : mBase (base), mModified (modified) {} + Stat::Stat(T base, T modified) : mBase (base), mModified (modified), mCurrentModified (modified) {} template const T& Stat::getBase() const @@ -22,23 +22,42 @@ namespace MWMechanics { return std::max(static_cast(0), mModified); } + + template + T Stat::getCurrentModified() const + { + return mCurrentModified; + } + template T Stat::getModifier() const { return mModified-mBase; } + + template + T Stat::getCurrentModifier() const + { + return mCurrentModified - mModified; + } + template void Stat::set (const T& value) { + T diff = value - mBase; mBase = mModified = value; + mCurrentModified += diff; } + template void Stat::setBase (const T& value) { T diff = value - mBase; mBase = value; mModified += diff; + mCurrentModified += diff; } + template void Stat::setModified (T value, const T& min, const T& max) { @@ -57,24 +76,39 @@ namespace MWMechanics mModified = value; mBase += diff; + mCurrentModified += diff; } + + template + void Stat::setCurrentModified(T value) + { + mCurrentModified = value; + } + template void Stat::setModifier (const T& modifier) { mModified = mBase + modifier; } + template + void Stat::setCurrentModifier(const T& modifier) + { + mCurrentModified = mModified + modifier; + } + template void Stat::writeState (ESM::StatState& state) const { state.mBase = mBase; - state.mMod = mModified; + state.mMod = mCurrentModified; } template void Stat::readState (const ESM::StatState& state) { mBase = state.mBase; - mModified = state.mMod; + mModified = state.mBase; + mCurrentModified = state.mMod; } @@ -98,6 +132,12 @@ namespace MWMechanics { return mStatic.getModified(); } + template + T DynamicStat::getCurrentModified() const + { + return mStatic.getCurrentModified(); + } + template const T& DynamicStat::getCurrent() const { @@ -127,14 +167,21 @@ namespace MWMechanics mCurrent = getModified(); } template - void DynamicStat::setCurrent (const T& value, bool allowDecreaseBelowZero) + void DynamicStat::setCurrentModified(T value) + { + mStatic.setCurrentModified(value); + } + template + void DynamicStat::setCurrent (const T& value, bool allowDecreaseBelowZero, bool allowIncreaseAboveModified) { if (value > mCurrent) { // increase - mCurrent = value; - - if (mCurrent > getModified()) + if (value <= getModified() || allowIncreaseAboveModified) + mCurrent = value; + else if (mCurrent > getModified()) + return; + else mCurrent = getModified(); } else if (value > 0 || allowDecreaseBelowZero) @@ -156,6 +203,16 @@ namespace MWMechanics setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero); } + template + void DynamicStat::setCurrentModifier(const T& modifier, bool allowCurrentDecreaseBelowZero) + { + T diff = modifier - mStatic.getCurrentModifier(); + mStatic.setCurrentModifier(modifier); + + // The (modifier > 0) check here allows increase over modified only if the modifier is positive (a fortify effect is active). + setCurrent (getCurrent() + diff, allowCurrentDecreaseBelowZero, (modifier > 0)); + } + template void DynamicStat::writeState (ESM::StatState& state) const { diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 5b58006344..751b80b9c2 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -17,6 +17,7 @@ namespace MWMechanics { T mBase; T mModified; + T mCurrentModified; public: typedef T Type; @@ -28,7 +29,9 @@ namespace MWMechanics const T& getBase() const; T getModified() const; + T getCurrentModified() const; T getModifier() const; + T getCurrentModifier() const; /// Set base and modified to \a value. void set (const T& value); @@ -36,9 +39,15 @@ namespace MWMechanics /// Set base and adjust modified accordingly. void setBase (const T& value); - /// Set modified value an adjust base accordingly. + /// Set modified value and adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); + + /// Set "current modified," used for drain and fortify. Unlike the regular modifier + /// this just adds and subtracts from the current value without changing the maximum. + void setCurrentModified(T value); + void setModifier (const T& modifier); + void setCurrentModifier (const T& modifier); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); @@ -73,6 +82,7 @@ namespace MWMechanics const T& getBase() const; T getModified() const; + T getCurrentModified() const; const T& getCurrent() const; /// Set base, modified and current to \a value. @@ -81,11 +91,16 @@ namespace MWMechanics /// Set base and adjust modified accordingly. void setBase (const T& value); - /// Set modified value an adjust base accordingly. + /// Set modified value and adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); - void setCurrent (const T& value, bool allowDecreaseBelowZero = false); - void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false); + /// Set "current modified," used for drain and fortify. Unlike the regular modifier + /// this just adds and subtracts from the current value without changing the maximum. + void setCurrentModified(T value); + + void setCurrent (const T& value, bool allowDecreaseBelowZero = false, bool allowIncreaseAboveModified = false); + void setModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero=false); + void setCurrentModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero = false); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index fadc99b663..28e6f95dd6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -261,6 +261,7 @@ namespace MWScript .getDynamic (mIndex)); stat.setModified (diff + stat.getModified(), 0); + stat.setCurrentModified (diff + stat.getCurrentModified()); stat.setCurrent (diff + current); From fd89fa415a37c5680c3697135cdffe3ea1541675 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 3 Dec 2017 21:34:17 +0900 Subject: [PATCH 072/175] Do modifiers for dynamic stats before attributes (Fixes #4231) --- apps/openmw/mwmechanics/actors.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e68a5d3982..d53c4407b5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -697,6 +697,19 @@ namespace MWMechanics } } + // dynamic stats + for (int i = 0; i < 3; ++i) + { + DynamicStat stat = creatureStats.getDynamic(i); + stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth + i).getMagnitude() - + effects.get(ESM::MagicEffect::DrainHealth + i).getMagnitude(), + // Magicka can be decreased below zero due to a fortify effect wearing off + // Fatigue can be decreased below zero meaning the actor will be knocked out + i == 1 || i == 2); + + creatureStats.setDynamic(i, stat); + } + // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { @@ -728,19 +741,6 @@ namespace MWMechanics } } - // dynamic stats - for(int i = 0;i < 3;++i) - { - DynamicStat stat = creatureStats.getDynamic(i); - stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - - effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(), - // Magicka can be decreased below zero due to a fortify effect wearing off - // Fatigue can be decreased below zero meaning the actor will be knocked out - i == 1 || i == 2); - - creatureStats.setDynamic(i, stat); - } - // AI setting modifiers int creature = !ptr.getClass().isNpc(); if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Humanoid) From 94d941cabc62d339b34da2487dfd87764416b044 Mon Sep 17 00:00:00 2001 From: Allofich <19624336+Allofich@users.noreply.github.com> Date: Mon, 13 Aug 2018 22:35:45 +0900 Subject: [PATCH 073/175] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd51b8bb6d..1afd16adfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y + Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work @@ -33,6 +34,7 @@ Bug #4215: OpenMW shows book text after last EOL tag Bug #4221: Characters get stuck in V-shaped terrain Bug #4230: AiTravel package issues break some Tribunal quests + Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted From 1c4969805350c0c7f4f7657e2584ec3f38107970 Mon Sep 17 00:00:00 2001 From: James Carty Date: Mon, 13 Aug 2018 20:31:11 +0100 Subject: [PATCH 074/175] Implement 'g' flag --- components/interpreter/miscopcodes.hpp | 26 +++++++++++++++++++++++-- components/misc/messageformatparser.cpp | 2 ++ components/misc/messageformatparser.hpp | 3 ++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 8bca0aec13..03b7e186f5 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -59,11 +59,33 @@ namespace Interpreter mRuntime.pop(); if (notation == FixedNotation) + { out << std::fixed << value; - else + mFormattedMessage += out.str(); + } + else if (notation == ShortestNotation) + { + std::string scientific; + std::string fixed; + out << std::scientific << value; - mFormattedMessage += out.str(); + scientific = out.str(); + + out.str(std::string()); + out.clear(); + + out << std::fixed << value; + + fixed = out.str(); + + mFormattedMessage += fixed.length() < scientific.length() ? fixed : scientific; + } + else + { + out << std::scientific << value; + mFormattedMessage += out.str(); + } } break; default: diff --git a/components/misc/messageformatparser.cpp b/components/misc/messageformatparser.cpp index 708d9565d1..6f0e471325 100644 --- a/components/misc/messageformatparser.cpp +++ b/components/misc/messageformatparser.cpp @@ -56,6 +56,8 @@ namespace Misc visitedPlaceholder(FloatPlaceholder, pad, width, precision, FixedNotation); else if (m[i] == 'e' || m[i] == 'E') visitedPlaceholder(FloatPlaceholder, pad, width, precision, ScientificNotation); + else if (m[i] == 'g' || m[i] == 'G') + visitedPlaceholder(FloatPlaceholder, pad, width, precision, ShortestNotation); } } } diff --git a/components/misc/messageformatparser.hpp b/components/misc/messageformatparser.hpp index 10da624d45..db2a8b0af4 100644 --- a/components/misc/messageformatparser.hpp +++ b/components/misc/messageformatparser.hpp @@ -18,7 +18,8 @@ namespace Misc enum Notation { FixedNotation, - ScientificNotation + ScientificNotation, + ShortestNotation }; virtual void visitedPlaceholder(Placeholder placeholder, char padding, int width, int precision, Notation notation) = 0; From 5e172ed831d797ab14d31689d6e9dcd872d663fb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 10:30:27 +0400 Subject: [PATCH 075/175] Move cerr initialization out of 'try' block --- components/debug/debugging.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index bec97207a4..470df47a7f 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -55,6 +55,14 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); +#if !(defined(_WIN32) && defined(_DEBUG)) + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; +#endif + + const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + boost::filesystem::ofstream logfile; + int ret = 0; try { @@ -67,12 +75,8 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to the log file - const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; - boost::filesystem::ofstream logfile; logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; std::ostream oldcout(cout_rdbuf); std::ostream oldcerr(cerr_rdbuf); coutsb.open (Debug::Tee(logfile, oldcout)); @@ -90,7 +94,7 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); - Log(Debug::Error) << "ERROR: " << e.what(); + Log(Debug::Error) << "Error: " << e.what(); ret = 1; } From 9a5b0162931222900a20d455361c3007f9af53c4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 11:17:05 +0400 Subject: [PATCH 076/175] Move crashcatcher initialization to components --- apps/opencs/editor.cpp | 7 ------- apps/openmw/main.cpp | 1 - components/debug/debugging.cpp | 9 +++++++++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 6ca10e0f66..73208b926a 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,9 +5,6 @@ #include #include - -#include - #include #include @@ -27,10 +24,6 @@ CS::Editor::Editor (int argc, char **argv) mLock(), mMerge (mDocumentManager), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) { - // install the crash handler as soon as possible. note that the log path - // does not depend on config being read. - crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string()); - std::pair > config = readConfig(); setupDataFiles (config.first); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index bbe1267b18..f9cf585444 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 470df47a7f..a4c59c2216 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -1,5 +1,7 @@ #include "debugging.hpp" +#include + namespace Debug { std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) @@ -61,12 +63,14 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log"; boost::filesystem::ofstream logfile; int ret = 0; try { Files::ConfigurationManager cfgMgr; + #if defined(_WIN32) && defined(_DEBUG) // Redirect cout and cerr to VS debug output when running in debug mode boost::iostreams::stream_buffer sb; @@ -85,6 +89,11 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); #endif + + // install the crash handler as soon as possible. note that the log path + // does not depend on config being read. + crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string()); + ret = innerApplication(argc, argv); } catch (std::exception& e) From 7029ed0e8d45010e8d2934073bccfdba4ff3c0c3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 15:36:52 +0300 Subject: [PATCH 077/175] Refactor follower and enemy actor processing Make another exception for wander packages when finding allies (bug #4304) --- apps/openmw/mwmechanics/actors.cpp | 105 +++++++++++++++-------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d53c4407b5..5c59efe308 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1777,38 +1777,35 @@ namespace MWMechanics std::list Actors::getActorsSidingWith(const MWWorld::Ptr& actor) { std::list list; - for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - const CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat packages before the Follow/Escort package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) - { - if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) - { - list.push_back(iter->first); - break; - } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; - } + // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - if (actor != getPlayer()) + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const CreatureStats &stats2 = actor.getClass().getCreatureStats(actor); - for (std::list::const_iterator it2 = stats2.getAiSequence().begin(); it2 != stats2.getAiSequence().end(); ++it2) + const MWWorld::Ptr &target = (*package)->getTarget(); + if ((*package)->sideWithTarget() && !target.isEmpty()) { - if ((*it2)->sideWithTarget() && !(*it2)->getTarget().isEmpty()) + if (iteratedActor == actor) { - list.push_back((*it2)->getTarget()); - break; + list.push_back(target); } - else if ((*it2)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; + else if (target == actor) + { + list.push_back(iteratedActor); + } + break; } + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + break; } } return list; @@ -1819,17 +1816,21 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) - list.push_back(iter->first); - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) + list.push_back(iteratedActor); + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1878,24 +1879,24 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) { - MWWorld::Ptr followTarget = (*it)->getTarget(); - if (followTarget.isEmpty()) - continue; - if (followTarget == actor) - list.push_back(static_cast(*it)->getFollowIndex()); + list.push_back(static_cast(*package)->getFollowIndex()); break; } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1907,14 +1908,14 @@ namespace MWMechanics std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const MWWorld::Class &cls = iter->getClass(); - const CreatureStats &stats = cls.getCreatureStats(*iter); - if (stats.isDead() || *iter == actor) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor) continue; + if (stats.getAiSequence().isInCombat(actor)) - list.push_front(*iter); + list.push_front(*neighbor); } return list; } @@ -1927,14 +1928,16 @@ namespace MWMechanics getObjectsInRange(position, aiProcessingDistance, neighbors); std::list followers = getActorsFollowing(actor); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const CreatureStats &stats = iter->getClass().getCreatureStats(*iter); - if (stats.isDead() || *iter == actor || iter->getClass().isPureWaterCreature(*iter)) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor)) continue; - const bool isFollower = std::find(followers.begin(), followers.end(), *iter) != followers.end(); - if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*iter, actor) && !isFollower)) - list.push_back(*iter); + + const bool isFollower = std::find(followers.begin(), followers.end(), *neighbor) != followers.end(); + + if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower)) + list.push_back(*neighbor); } return list; } From 75bd6e1d2846ab1398b18778b47f392163fb6f2d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 15:41:34 +0300 Subject: [PATCH 078/175] Make search for allies in actorAttacked recursive --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7814b4a91e..e9915397a1 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,6 +1,7 @@ #include "mechanicsmanagerimp.hpp" #include +#include #include @@ -1445,11 +1446,12 @@ namespace MWMechanics if (target == getPlayer() || !attacker.getClass().isActor()) return false; - std::list followersAttacker = getActorsSidingWith(attacker); + std::set followersAttacker; + getActorsSidingWith(attacker, followersAttacker); MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); - if (std::find(followersAttacker.begin(), followersAttacker.end(), target) != followersAttacker.end()) + if (followersAttacker.find(target) != followersAttacker.end()) { statsTarget.friendlyHit(); From 53599290c3ffd2c5f186f1e4cde52ad863959b79 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 16:14:48 +0300 Subject: [PATCH 079/175] Make search for followers in getEnemiesNearby recursive --- apps/openmw/mwmechanics/actors.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5c59efe308..9562654026 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2,7 +2,6 @@ #include #include - #include #include #include @@ -1927,7 +1926,8 @@ namespace MWMechanics osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - std::list followers = getActorsFollowing(actor); + std::set followers; + getActorsFollowing(actor, followers); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); From 22162dcbda39dc3ef568e6316a4c7fa73e1eb2a9 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Tue, 14 Aug 2018 18:14:43 +0300 Subject: [PATCH 080/175] Replace std::find with std::set::find where applicable --- apps/openmw/mwmechanics/actors.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9562654026..3b1f6f5a21 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -403,7 +403,7 @@ namespace MWMechanics std::set playerAllies; getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies); - bool isPlayerFollowerOrEscorter = std::find(playerAllies.begin(), playerAllies.end(), actor1) != playerAllies.end(); + bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end(); // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them // Doesn't apply for player followers/escorters @@ -457,7 +457,7 @@ namespace MWMechanics // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { - if (againstPlayer || std::find(playerAllies.begin(), playerAllies.end(), actor2) != playerAllies.end()) + if (againstPlayer || playerAllies.find(actor2) != playerAllies.end()) { // Player followers and escorters with high fight should not initiate combat with the player or with // other player followers or escorters @@ -1928,13 +1928,13 @@ namespace MWMechanics std::set followers; getActorsFollowing(actor, followers); - for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) + for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor)) continue; - const bool isFollower = std::find(followers.begin(), followers.end(), *neighbor) != followers.end(); + const bool isFollower = followers.find(*neighbor) != followers.end(); if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower)) list.push_back(*neighbor); From 52f70642d0f3e88573aa06e5e74c4a23d52ba91f Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Tue, 14 Aug 2018 18:25:19 +0300 Subject: [PATCH 081/175] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f80aa84b..47b277f893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Bug #4286: Scripted animations can be interrupted Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter + Bug #4304: "Follow" not working as a second AI package Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching From 1452684d9e58c8e59c3d2bf85c837894b2feb6fe Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 19:42:41 +0400 Subject: [PATCH 082/175] Use new logging system for components --- components/crashcatcher/crashcatcher.cpp | 11 +++-- components/debug/debuglog.hpp | 1 - components/esm/cellref.cpp | 6 +-- components/esm/loadcrea.cpp | 4 +- components/esm/loaddial.cpp | 4 +- components/esm/loadscpt.cpp | 12 ++--- components/esmterrain/storage.cpp | 3 +- components/files/configurationmanager.cpp | 9 ++-- components/files/multidircollection.cpp | 6 +-- components/fontloader/fontloader.cpp | 6 ++- components/interpreter/defines.cpp | 6 +-- components/myguiplatform/myguidatamanager.cpp | 4 +- components/myguiplatform/myguitexture.cpp | 3 +- components/nif/niffile.hpp | 5 +- components/nifbullet/bulletnifloader.cpp | 5 +- components/nifbullet/bulletnifloader.hpp | 6 +-- components/nifosg/nifloader.cpp | 48 +++++++++---------- components/nifosg/particle.cpp | 3 +- components/resource/imagemanager.cpp | 9 ++-- components/resource/scenemanager.cpp | 11 +++-- components/sceneutil/attach.cpp | 3 +- components/sceneutil/riggeometry.cpp | 11 +++-- components/sceneutil/skeleton.cpp | 5 +- components/sceneutil/unrefqueue.cpp | 4 +- components/sceneutil/workqueue.cpp | 4 +- components/sdlutil/sdlcursormanager.cpp | 8 ++-- components/sdlutil/sdlinputwrapper.cpp | 6 +-- components/sdlutil/sdlvideowrapper.cpp | 4 +- components/settings/settings.cpp | 26 +++++----- components/shader/shadermanager.cpp | 16 ++++--- components/shader/shadervisitor.cpp | 3 +- components/to_utf8/to_utf8.cpp | 7 ++- components/vfs/filesystemarchive.cpp | 6 +-- components/vfs/registerarchives.cpp | 9 ++-- components/widgets/imagebutton.cpp | 4 +- 35 files changed, 144 insertions(+), 134 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index f7b8717a6b..04b239a7fa 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -185,7 +185,7 @@ static void gdb_info(pid_t pid) if (close(fd) == 0) remove(respfile); else - std::cerr << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno) << std::endl; + Log(Debug::Warning) << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno); } printf("!!! Could not create gdb command file\n"); } @@ -517,8 +517,9 @@ void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1) { - std::cerr << "Installing crash handler failed" << std::endl; - } else - std::cout << "Crash handler installed" << std::endl; + Log(Debug::Warning) << "Installing crash handler failed"; + } + else + Log(Debug::Info) << "Crash handler installed"; } } diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index bfd7d1196e..1ea18aa9b7 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -2,7 +2,6 @@ #define DEBUG_LOG_H #include -#include #include namespace Debug diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 1f7c2b964d..4b9c6b0734 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -1,6 +1,6 @@ #include "cellref.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -48,9 +48,7 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNOString ("NAME"); if (mRefID.empty()) { - std::ios::fmtflags f(std::cerr.flags()); - std::cerr << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset() << std::endl; - std::cerr.flags(f); + Log(Debug::Warning) << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset(); } } diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index ddd49f8df9..83315a5b8f 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -1,6 +1,6 @@ #include "loadcrea.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -89,7 +89,7 @@ namespace ESM { // seems to occur only in .ESS files, unsure of purpose int index; esm.getHT(index); - std::cerr << "Creature::load: Unhandled INDX " << index << std::endl; + Log(Debug::Warning) << "Creature::load: Unhandled INDX " << index; break; default: esm.fail("Unknown subrecord"); diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index 9ef3d3964a..d7e0a6ee17 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -1,6 +1,6 @@ #include "loaddial.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -128,7 +128,7 @@ namespace ESM return; } - std::cerr << "Warning: Failed to insert info " << info.mId << std::endl; + Log(Debug::Warning) << "Warning: Failed to insert info " << info.mId; } void Dialogue::clearDeletedInfos() diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 19246c4c45..474c2b4234 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -1,11 +1,11 @@ #include "loadscpt.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" -#include - namespace ESM { unsigned int Script::sRecordId = REC_SCPT; @@ -45,12 +45,12 @@ namespace ESM // an exeption, just log an error and continue. std::stringstream ss; - ss << "ESM Error: " << "String table overflow"; + ss << "String table overflow"; ss << "\n File: " << esm.getName(); ss << "\n Record: " << esm.getContext().recName.toString(); ss << "\n Subrecord: " << "SCVR"; ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); - std::cerr << ss.str() << std::endl; + Log(Debug::Verbose) << ss.str(); break; } @@ -91,10 +91,10 @@ namespace ESM if (subSize != static_cast(mData.mScriptDataSize)) { std::stringstream ss; - ss << "ESM Warning: Script data size defined in SCHD subrecord does not match size of SCDT subrecord"; + ss << "Script data size defined in SCHD subrecord does not match size of SCDT subrecord"; ss << "\n File: " << esm.getName(); ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); - std::cerr << ss.str() << std::endl; + Log(Debug::Verbose) << ss.str(); } mScriptData.resize(subSize); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 0f5b175025..f77e662765 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -10,6 +10,7 @@ #include +#include #include #include @@ -378,7 +379,7 @@ namespace ESMTerrain const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); if (!ltex) { - std::cerr << "Warning: Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead" << std::endl; + Log(Debug::Warning) << "Warning: Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead"; return defaultTexture; } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 7c3956a293..333ec5ced8 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -1,7 +1,6 @@ #include "configurationmanager.hpp" -#include - +#include #include #include @@ -134,7 +133,7 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (boost::filesystem::is_regular_file(cfgFile)) { if (!mSilent) - std::cout << "Loading config file: " << cfgFile.string() << "... "; + Log(Debug::Info) << "Loading config file: " << cfgFile.string() << "... "; boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile); boost::iostreams::filtering_istream configFileStream; @@ -146,13 +145,13 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, configFileStream, description, true), variables); if (!mSilent) - std::cout << "done." << std::endl; + Log(Debug::Info) << "done."; return true; } else { if (!mSilent) - std::cout << "failed." << std::endl; + Log(Debug::Info) << "failed."; return false; } } diff --git a/components/files/multidircollection.cpp b/components/files/multidircollection.cpp index 93db6834d0..98e25fcc86 100644 --- a/components/files/multidircollection.cpp +++ b/components/files/multidircollection.cpp @@ -1,9 +1,9 @@ #include "multidircollection.hpp" -#include - #include +#include + namespace Files { struct NameEqual @@ -46,7 +46,7 @@ namespace Files { if (!boost::filesystem::is_directory(*iter)) { - std::cout << "Skipping invalid directory: " << (*iter).string() << std::endl; + Log(Debug::Info) << "Skipping invalid directory: " << (*iter).string(); continue; } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 080739bc1d..790df7fa84 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include #include @@ -281,7 +283,7 @@ namespace Gui assert (image->isDataContiguous()); memcpy(image->data(), &textureData[0], textureData.size()); - std::cout << "Writing " << resourceName + ".png" << std::endl; + Log(Debug::Info) << "Writing " << resourceName + ".png"; osgDB::writeImageFile(*image, resourceName + ".png"); } @@ -461,7 +463,7 @@ namespace Gui if (exportToFile) { - std::cout << "Writing " << resourceName + ".xml" << std::endl; + Log(Debug::Info) << "Writing " << resourceName + ".xml"; xmlDocument.createDeclaration(); xmlDocument.save(resourceName + ".xml"); } diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 515c6c7d58..3c6226d9ce 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -3,8 +3,8 @@ #include #include #include -#include +#include #include namespace Interpreter{ @@ -191,8 +191,8 @@ namespace Interpreter{ } catch (std::exception& e) { - std::cerr << "Error: Failed to replace escape character, with the following error: " << e.what() << std::endl; - std::cerr << "Full text below: " << std::endl << text << std::endl; + Log(Debug::Error) << "Error: Failed to replace escape character, with the following error: " << e.what(); + Log(Debug::Error) << "Full text below:\n" << text; } // Not found, or error diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index 485a87ba7f..ddd7ca342d 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace osgMyGUI { @@ -23,7 +23,7 @@ MyGUI::IDataStream *DataManager::getData(const std::string &name) stream->open(fullpath, std::ios::binary); if (stream->fail()) { - std::cerr << "DataManager::getData: Failed to open '" << name << "'" << std::endl; + Log(Debug::Error) << "DataManager::getData: Failed to open '" << name << "'"; return NULL; } return new MyGUI::DataFileStream(stream.release()); diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index a3b70eac97..6c53a96998 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -5,6 +5,7 @@ #include +#include #include namespace osgMyGUI @@ -112,7 +113,7 @@ namespace osgMyGUI void OSGTexture::saveToFile(const std::string &fname) { - std::cerr << "Would save image to file " << fname << std::endl; + Log(Debug::Warning) << "Would save image to file " << fname; } int OSGTexture::getWidth() diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index cab2e98807..0893db72ff 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -5,8 +5,8 @@ #include #include -#include +#include #include #include "record.hpp" @@ -80,8 +80,7 @@ public: /// Used when something goes wrong, but not catastrophically so void warn(const std::string &msg) const override { - std::cerr << " NIFFile Warning: " << msg < #include +#include + #include #include @@ -18,7 +20,6 @@ #include #include - namespace { @@ -203,7 +204,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); if (node->recType == Nif::RC_RootCollisionNode && autogenerated) - std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl; + Log(Debug::Info) << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape."; // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 44134fd238..f970b7f3e5 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -12,6 +11,7 @@ #include #include +#include #include #include @@ -41,12 +41,12 @@ public: void warn(const std::string &msg) { - std::cerr << "NIFLoader: Warn:" << msg << "\n"; + Log(Debug::Warning) << "NIFLoader: Warn:" << msg; } void fail(const std::string &msg) { - std::cerr << "NIFLoader: Fail: "<< msg << std::endl; + Log(Debug::Error) << "NIFLoader: Fail: "<< msg; abort(); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5857a39872..3198e995c5 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -243,7 +243,7 @@ namespace NifOsg callback->setFunction(std::shared_ptr(new NifOsg::ControllerFunction(key))); if (target.mKeyframeControllers.find(strdata->string) != target.mKeyframeControllers.end()) - std::cerr << "Warning: controller " << strdata->string << " present more than once in " << nif->getFilename() << ", ignoring later version" << std::endl; + Log(Debug::Verbose) << "Controller " << strdata->string << " present more than once in " << nif->getFilename() << ", ignoring later version"; else target.mKeyframeControllers[strdata->string] = callback; } @@ -357,20 +357,20 @@ namespace NifOsg { if (nifNode->recType != Nif::RC_NiTextureEffect) { - std::cerr << "Unhandled effect " << nifNode->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled effect " << nifNode->recName << " in " << mFilename; return; } const Nif::NiTextureEffect* textureEffect = static_cast(nifNode); if (textureEffect->textureType != Nif::NiTextureEffect::Environment_Map) { - std::cerr << "Unhandled NiTextureEffect type " << textureEffect->textureType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled NiTextureEffect type " << textureEffect->textureType << " in " << mFilename; return; } if (textureEffect->texture.empty()) { - std::cerr << "NiTextureEffect missing source texture in " << mFilename << std::endl; + Log(Debug::Info) << "NiTextureEffect missing source texture in " << mFilename; return; } @@ -387,7 +387,7 @@ namespace NifOsg texGen->setMode(osg::TexGen::SPHERE_MAP); break; default: - std::cerr << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType << " in " << mFilename; return; } @@ -638,7 +638,7 @@ namespace NifOsg else if(ctrl->recType == Nif::RC_NiGeomMorpherController) {} // handled in handleTriShape else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } @@ -664,7 +664,7 @@ namespace NifOsg handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); } else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } @@ -696,7 +696,7 @@ namespace NifOsg composite->addController(osgctrl); } else - std::cerr << "Unexpected material controller " << ctrl->recType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected material controller " << ctrl->recType << " in " << mFilename; } } @@ -736,7 +736,7 @@ namespace NifOsg composite->addController(callback); } else - std::cerr << "Unexpected texture controller " << ctrl->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected texture controller " << ctrl->recName << " in " << mFilename; } } @@ -769,7 +769,7 @@ namespace NifOsg // unused } else - std::cerr << "Unhandled particle modifier " << affectors->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; } for (; !colliders.empty(); colliders = colliders->extra) { @@ -784,7 +784,7 @@ namespace NifOsg program->addOperator(new SphericalCollider(sphericalcollider)); } else - std::cerr << "Unhandled particle collider " << colliders->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled particle collider " << colliders->recName << " in " << mFilename; } } @@ -881,11 +881,11 @@ namespace NifOsg if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) partctrl = static_cast(ctrl.getPtr()); else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } if (!partctrl) { - std::cerr << "No particle controller found in " << mFilename << std::endl; + Log(Debug::Info) << "No particle controller found in " << mFilename; return; } @@ -925,7 +925,7 @@ namespace NifOsg rootNode->accept(find); if (!find.mFound) { - std::cerr << "can't find emitter node, wrong node order? in " << mFilename << std::endl; + Log(Debug::Info) << "can't find emitter node, wrong node order? in " << mFilename; return; } osg::Group* emitterNode = find.mFound; @@ -1121,7 +1121,7 @@ namespace NifOsg case 9: return osg::BlendFunc::ONE_MINUS_DST_ALPHA; case 10: return osg::BlendFunc::SRC_ALPHA_SATURATE; default: - std::cerr<< "Unexpected blend mode: "<< mode << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected blend mode: "<< mode << " in " << mFilename; return osg::BlendFunc::SRC_ALPHA; } } @@ -1139,7 +1139,7 @@ namespace NifOsg case 6: return osg::AlphaFunc::GEQUAL; case 7: return osg::AlphaFunc::NEVER; default: - std::cerr << "Unexpected blend mode: " << mode << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected blend mode: " << mode << " in " << mFilename; return osg::AlphaFunc::LEQUAL; } } @@ -1157,7 +1157,7 @@ namespace NifOsg case 6: return osg::Stencil::GEQUAL; case 7: return osg::Stencil::NEVER; // NifSkope says this is GL_ALWAYS, but in MW it's GL_NEVER default: - std::cerr << "Unexpected stencil function: " << func << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename; return osg::Stencil::NEVER; } } @@ -1173,7 +1173,7 @@ namespace NifOsg case 4: return osg::Stencil::DECR; case 5: return osg::Stencil::INVERT; default: - std::cerr << "Unexpected stencil operation: " << op << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected stencil operation: " << op << " in " << mFilename; return osg::Stencil::KEEP; } } @@ -1192,7 +1192,7 @@ namespace NifOsg pixelformat = GL_RGBA; break; default: - std::cerr << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; return NULL; } @@ -1210,7 +1210,7 @@ namespace NifOsg size_t mipSize = mip.height * mip.width * pixelData->bpp / 8; if (mipSize + mip.dataOffset > pixelData->data.size()) { - std::cerr << "Warning: Internal texture's mipmap data out of bounds, ignoring texture" << std::endl; + Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; return NULL; } @@ -1225,7 +1225,7 @@ namespace NifOsg if (width <= 0 || height <= 0) { - std::cerr << "Warning: Internal Texture Width and height must be non zero, ignoring texture" << std::endl; + Log(Debug::Info) << "Internal Texture Width and height must be non zero, ignoring texture"; return NULL; } @@ -1267,12 +1267,12 @@ namespace NifOsg { // Not used by the vanilla engine. MCP (Morrowind Code Patch) adds an option to use Gloss maps: // "- Gloss map fix. Morrowind removed gloss map entries from model files after loading them. This stops Morrowind from removing them." - //std::cerr << "NiTexturingProperty::GlossTexture in " << mFilename << " not currently used." << std::endl; + // Log(Debug::Info) << "NiTexturingProperty::GlossTexture in " << mFilename << " not currently used."; continue; } default: { - std::cerr << "Warning: unhandled texture stage " << i << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled texture stage " << i << " in " << mFilename; continue; } } @@ -1478,7 +1478,7 @@ namespace NifOsg break; } default: - std::cerr << "Unhandled " << property->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled " << property->recName << " in " << mFilename; break; } } diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 62360b9d64..03e836a0fd 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -284,7 +285,7 @@ void Emitter::emitParticles(double dt) if (!visitor.mFound) { - std::cerr << "Error: Can't find emitter node" << randomRecIndex << std::endl; + Log(Debug::Info) << "Can't find emitter node" << randomRecIndex; return; } diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 000a833cf7..c1d71ee008 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "objectcache.hpp" @@ -97,7 +98,7 @@ namespace Resource } catch (std::exception& e) { - std::cerr << "Failed to open image: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to open image: " << e.what(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -109,7 +110,7 @@ namespace Resource osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { - std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": no readerwriter for '" << ext << "' found"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -117,7 +118,7 @@ namespace Resource osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { - std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": " << result.message() << " code " << result.status(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -130,7 +131,7 @@ namespace Resource static bool uncompress = (getenv("OPENMW_DECOMPRESS_TEXTURES") != 0); if (!uncompress) { - std::cerr << "Error loading " << filename << ": no S3TC texture compression support installed" << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": no S3TC texture compression support installed"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index db8d99ab4e..8edb3d7657 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -1,6 +1,5 @@ #include "scenemanager.hpp" -#include #include #include @@ -13,6 +12,8 @@ #include #include +#include + #include #include @@ -501,7 +502,7 @@ namespace Resource normalized = "meshes/marker_error." + std::string(sMeshTypes[i]); if (mVFS->exists(normalized)) { - std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl; + Log(Debug::Error) << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead"; Files::IStreamPtr file = mVFS->get(normalized); loaded = load(file, normalized, mImageManager, mNifFileManager); break; @@ -658,12 +659,12 @@ namespace Resource if(magfilter == "nearest") mag = osg::Texture::NEAREST; else if(magfilter != "linear") - std::cerr<< "Warning: Invalid texture mag filter: "< #include +#include #include #include @@ -63,7 +64,7 @@ namespace SceneUtil { osg::ref_ptr node = *it; if (node->getNumParents() > 1) - std::cerr << "Error CopyRigVisitor: node has multiple parents" << std::endl; + Log(Debug::Error) << "Error CopyRigVisitor: node has multiple parents"; while (node->getNumParents()) node->getParent(0)->removeChild(node); diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 7f148cf5e3..ee30f1c85c 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -1,9 +1,10 @@ #include "riggeometry.hpp" #include -#include #include +#include + #include "skeleton.hpp" #include "util.hpp" @@ -96,13 +97,13 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) if (!mSkeleton) { - std::cerr << "Error: A RigGeometry did not find its parent skeleton" << std::endl; + Log(Debug::Error) << "Error: A RigGeometry did not find its parent skeleton"; return false; } if (!mInfluenceMap) { - std::cerr << "Error: No InfluenceMap set on RigGeometry" << std::endl; + Log(Debug::Error) << "Error: No InfluenceMap set on RigGeometry"; return false; } @@ -113,7 +114,7 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) Bone* bone = mSkeleton->getBone(it->first); if (!bone) { - std::cerr << "Error: RigGeometry did not find bone " << it->first << std::endl; + Log(Debug::Error) << "Error: RigGeometry did not find bone " << it->first ; continue; } @@ -166,7 +167,7 @@ void RigGeometry::cull(osg::NodeVisitor* nv) { if (!mSkeleton) { - std::cerr << "Error: RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor" << std::endl; + Log(Debug::Error) << "Error: RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor"; // try to recover anyway, though rendering is likely to be incorrect. if (!initFromParentSkeleton(nv)) return; diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 94ae1f234e..58ed9a27cf 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -3,10 +3,9 @@ #include #include +#include #include -#include - namespace SceneUtil { @@ -183,7 +182,7 @@ void Bone::update(const osg::Matrixf* parentMatrixInSkeletonSpace) { if (!mNode) { - std::cerr << "Error: Bone without node " << std::endl; + Log(Debug::Error) << "Error: Bone without node"; return; } if (parentMatrixInSkeletonSpace) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 7e5646ecb1..42ea311337 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -3,8 +3,8 @@ #include //#include -//#include +//#include #include namespace SceneUtil @@ -20,7 +20,7 @@ namespace SceneUtil //osg::Timer timer; //size_t objcount = mObjects.size(); mObjects.clear(); - //std::cout << "cleared " << objcount << " objects in " << timer.time_m() << std::endl; + //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); } }; diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 2cd1ec806d..37e8563e12 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -1,6 +1,6 @@ #include "workqueue.hpp" -#include +#include namespace SceneUtil { @@ -71,7 +71,7 @@ void WorkQueue::addWorkItem(osg::ref_ptr item, bool front) { if (item->isDone()) { - std::cerr << "Error: trying to add a work item that is already completed" << std::endl; + Log(Debug::Error) << "Error: trying to add a work item that is already completed"; return; } diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 1747c9b941..1560b74b3e 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -17,6 +17,8 @@ #include #include +#include + #include "imagetosurface.hpp" #if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID) @@ -63,7 +65,7 @@ namespace CursorDecompression if (!_gc) { - std::cerr << "Failed to create pbuffer, failing back to normal graphics window." << std::endl; + Log(Debug::Warning) << "Failed to create pbuffer, failing back to normal graphics window."; traits->pbuffer = false; _gc = osg::GraphicsContext::createGraphicsContext(traits.get()); @@ -283,8 +285,8 @@ namespace SDLUtil mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); } catch (std::exception& e) { - std::cerr << e.what() << std::endl; - std::cerr <<"Using default cursor."< +#include #include @@ -160,9 +160,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v break; default: - std::ios::fmtflags f(std::cerr.flags()); - std::cerr << "Unhandled SDL event of type 0x" << std::hex << evt.type << std::endl; - std::cerr.flags(f); + Log(Debug::Info) << "Unhandled SDL event of type 0x" << std::hex << evt.type; break; } } diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index dd89d10724..c2963be861 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -1,6 +1,6 @@ #include "sdlvideowrapper.hpp" -#include +#include #include @@ -65,7 +65,7 @@ namespace SDLUtil red[i] = green[i] = blue[i] = static_cast(value); } if (SDL_SetWindowGammaRamp(mWindow, red, green, blue) < 0) - std::cout << "Couldn't set gamma: " << SDL_GetError() << std::endl; + Log(Debug::Warning) << "Couldn't set gamma: " << SDL_GetError(); } void VideoWrapper::setVideoMode(int width, int height, bool fullscreen, bool windowBorder) diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 15a222d313..66e5dfc045 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,8 +1,8 @@ #include "settings.hpp" #include -#include +#include #include #include @@ -69,7 +69,7 @@ public: mFile = file; boost::filesystem::ifstream stream; stream.open(boost::filesystem::path(file)); - std::cout << "Loading settings file: " << file << std::endl; + Log(Debug::Info) << "Loading settings file: " << file; std::string currentCategory; mLine = 0; while (!stream.eof() && !stream.fail()) @@ -186,8 +186,8 @@ public: // Ensure that all options in the current category have been written. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { - std::cout << "Added new setting: [" << currentCategory << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << currentCategory << "] " + << mit->first.second << " = " << settings[mit->first]; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; @@ -200,7 +200,7 @@ public: // Write the (new) current category to the file. ostream << "[" << currentCategory << "]" << std::endl; - //std::cout << "Wrote category: " << currentCategory << std::endl; + // Log(Debug::Verbose) << "Wrote category: " << currentCategory; // A setting can apparently follow the category on an input line. That's rather // inconvenient, since it makes it more likely to have duplicative sections, @@ -259,8 +259,8 @@ public: finder->second = true; // Did we really change it? if (value != settings[key]) { - std::cout << "Changed setting: [" << currentCategory << "] " - << setting << " = " << settings[key] << std::endl; + Log(Debug::Verbose) << "Changed setting: [" << currentCategory << "] " + << setting << " = " << settings[key]; changed = true; } // No need to write the current line, because we just emitted a replacement. @@ -276,8 +276,8 @@ public: // the current category at the end of the file before moving on to any new categories. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { - std::cout << "Added new setting: [" << mit->first.first << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first]; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; @@ -305,12 +305,12 @@ public: // If the catgory has changed, write a new category header. if (mit->first.first != currentCategory) { currentCategory = mit->first.first; - std::cout << "Created new setting section: " << mit->first.first << std::endl; + Log(Debug::Verbose) << "Created new setting section: " << mit->first.first; ostream << std::endl; ostream << "[" << currentCategory << "]" << std::endl; } - std::cout << "Added new setting: [" << mit->first.first << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first]; // Then write the setting. No need to mark it as written because we're done. ostream << mit->first.second << " = " << settings[mit->first] << std::endl; changed = true; @@ -319,7 +319,7 @@ public: // Now install the newly written file in the requested place. if (changed) { - std::cout << "Updating settings file: " << ipath << std::endl; + Log(Debug::Info) << "Updating settings file: " << ipath; boost::filesystem::ofstream ofstream; ofstream.open(ipath); ofstream << ostream.rdbuf(); diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 5efd1b86ea..28f4300c29 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -11,6 +11,8 @@ #include #include +#include + namespace Shader { @@ -31,13 +33,13 @@ namespace Shader size_t start = source.find('"', foundPos); if (start == std::string::npos || start == source.size()-1) { - std::cerr << "Invalid #include " << std::endl; + Log(Debug::Error) << "Invalid #include"; return false; } size_t end = source.find('"', start+1); if (end == std::string::npos) { - std::cerr << "Invalid #include " << std::endl; + Log(Debug::Error) << "Invalid #include"; return false; } std::string includeFilename = source.substr(start+1, end-(start+1)); @@ -46,7 +48,7 @@ namespace Shader includeFstream.open(includePath); if (includeFstream.fail()) { - std::cerr << "Failed to open " << includePath.string() << std::endl; + Log(Debug::Error) << "Failed to open " << includePath.string(); return false; } @@ -65,7 +67,7 @@ namespace Shader if (includedFiles.insert(includePath).second == false) { - std::cerr << "Detected cyclic #includes" << std::endl; + Log(Debug::Error) << "Detected cyclic #includes"; return false; } } @@ -81,14 +83,14 @@ namespace Shader size_t endPos = source.find_first_of(" \n\r()[].;", foundPos); if (endPos == std::string::npos) { - std::cerr << "Unexpected EOF" << std::endl; + Log(Debug::Error) << "Unexpected EOF"; return false; } std::string define = source.substr(foundPos+1, endPos - (foundPos+1)); ShaderManager::DefineMap::const_iterator defineFound = defines.find(define); if (defineFound == defines.end()) { - std::cerr << "Undefined " << define << std::endl; + Log(Debug::Error) << "Undefined " << define; return false; } else @@ -112,7 +114,7 @@ namespace Shader stream.open(p); if (stream.fail()) { - std::cerr << "Failed to open " << p.string() << std::endl; + Log(Debug::Error) << "Failed to open " << p.string(); return NULL; } std::stringstream buffer; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 9b3876d6c8..cbd950ea3b 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -147,7 +148,7 @@ namespace Shader specularMap = texture; } else - std::cerr << "ShaderVisitor encountered unknown texture " << texture << std::endl; + Log(Debug::Error) << "ShaderVisitor encountered unknown texture " << texture; } } } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 8af0bc5ed8..d4ab00381b 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -2,10 +2,11 @@ #include #include -#include #include #include +#include + /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library is designed to be extened to support more source encodings later, @@ -319,9 +320,7 @@ void Utf8Encoder::copyFromArray2(const char*& chp, char* &out) } } - std::ios::fmtflags f(std::cout.flags()); - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - std::cout.flags(f); + Log(Debug::Info) << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3; *(out++) = ch; // Could not find glyph, just put whatever } diff --git a/components/vfs/filesystemarchive.cpp b/components/vfs/filesystemarchive.cpp index 9738e2a17c..ce4ff020ef 100644 --- a/components/vfs/filesystemarchive.cpp +++ b/components/vfs/filesystemarchive.cpp @@ -1,9 +1,9 @@ #include "filesystemarchive.hpp" -#include - #include +#include + namespace VFS { @@ -41,7 +41,7 @@ namespace VFS std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function); if (!mIndex.insert (std::make_pair (searchable, file)).second) - std::cerr << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases." << std::endl; + Log(Debug::Warning) << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases."; } mBuiltIndex = true; diff --git a/components/vfs/registerarchives.cpp b/components/vfs/registerarchives.cpp index a22e785cd2..80e639f350 100644 --- a/components/vfs/registerarchives.cpp +++ b/components/vfs/registerarchives.cpp @@ -1,9 +1,10 @@ #include "registerarchives.hpp" #include -#include #include +#include + #include #include #include @@ -21,7 +22,7 @@ namespace VFS { // Last BSA has the highest priority const std::string archivePath = collections.getPath(*archive).string(); - std::cout << "Adding BSA archive " << archivePath << std::endl; + Log(Debug::Info) << "Adding BSA archive " << archivePath; vfs->addArchive(new BsaArchive(archivePath)); } @@ -40,12 +41,12 @@ namespace VFS { if (seen.insert(*iter).second) { - std::cout << "Adding data directory " << iter->string() << std::endl; + Log(Debug::Info) << "Adding data directory " << iter->string(); // Last data dir has the highest priority vfs->addArchive(new FileSystemArchive(iter->string())); } else - std::cerr << "Ignoring duplicate data directory " << iter->string() << std::endl; + Log(Debug::Info) << "Ignoring duplicate data directory " << iter->string(); } } diff --git a/components/widgets/imagebutton.cpp b/components/widgets/imagebutton.cpp index a3b0ae28ab..2ea494ebd0 100644 --- a/components/widgets/imagebutton.cpp +++ b/components/widgets/imagebutton.cpp @@ -2,6 +2,8 @@ #include +#include + namespace Gui { @@ -77,7 +79,7 @@ namespace Gui MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(mImageNormal); if (!texture) { - std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; + Log(Debug::Error) << "ImageButton: can't find image " << mImageNormal; return MyGUI::IntSize(0,0); } return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); From c7a5548475f3495d6e1d711e943f4ebbbbf9dcc3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 20:01:09 +0400 Subject: [PATCH 083/175] Use new logging system for editor --- apps/opencs/editor.cpp | 10 +++++----- apps/opencs/main.cpp | 3 +-- apps/opencs/model/doc/document.cpp | 5 +++-- apps/opencs/model/world/refcollection.cpp | 10 +++++----- apps/opencs/view/render/object.cpp | 4 ++-- components/files/configurationmanager.cpp | 7 +++---- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 73208b926a..e733d9924b 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,8 +5,8 @@ #include #include +#include #include - #include #include "model/doc/document.hpp" @@ -294,7 +294,7 @@ bool CS::Editor::makeIPCServer() mLock = boost::interprocess::file_lock(mPid.string().c_str()); if(!mLock.try_lock()) { - std::cerr << "OpenCS already running." << std::endl; + Log(Debug::Error) << "Error: OpenMW-CS is already running."; return false; } @@ -317,17 +317,17 @@ bool CS::Editor::makeIPCServer() if(boost::filesystem::exists(fullPath.toUtf8().constData())) { // TODO: compare pid of the current process with that in the file - std::cout << "Detected unclean shutdown." << std::endl; + Log(Debug::Info) << "Detected unclean shutdown."; // delete the stale file if(remove(fullPath.toUtf8().constData())) - std::cerr << "ERROR removing stale connection file" << std::endl; + Log(Debug::Error) << "Error: can not remove stale connection file."; } } } catch(const std::exception& e) { - std::cerr << "ERROR " << e.what() << std::endl; + Log(Debug::Error) << "Error: " << e.what(); return false; } diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 6058e73f9e..0473291cea 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -1,7 +1,6 @@ #include "editor.hpp" #include -#include #include #include @@ -31,7 +30,7 @@ class Application : public QApplication } catch (const std::exception& exception) { - std::cerr << "An exception has been caught: " << exception.what() << std::endl; + Log(Debug::Error) << "An exception has been caught: " << exception.what(); } return false; diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index e45d13aa9b..233b3e439d 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -13,6 +12,8 @@ #include #endif +#include + void CSMDoc::Document::addGmsts() { for (size_t i=0; i < CSMWorld::DefaultGmsts::FloatCount; ++i) @@ -435,7 +436,7 @@ void CSMDoc::Document::modificationStateChanged (bool clean) void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type) { /// \todo find a better way to get these messages to the user. - std::cout << message.mMessage << std::endl; + Log(Debug::Info) << message.mMessage; } void CSMDoc::Document::operationDone2 (int type, bool failed) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 6b586dcec1..7767cca6dc 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -1,8 +1,8 @@ #include "refcollection.hpp" #include -#include +#include #include #include @@ -58,10 +58,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { - std::cerr << "The Position of moved ref " - << ref.mRefID << " does not match the target cell" << std::endl; - std::cerr << "Position: #" << index.first << " " << index.second - <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; + Log(Debug::Warning) << "Warning: the Position of moved ref " + << ref.mRefID << " does not match the target cell"; + Log(Debug::Warning) << "Position: #" << index.first << " " << index.second + <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1]; stream.clear(); stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 961b6c1c1b..8301f4e9ed 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,7 +1,6 @@ #include "object.hpp" #include -#include #include #include @@ -24,6 +23,7 @@ #include "../../model/world/cellcoordinates.hpp" #include "../../model/prefs/state.hpp" +#include #include #include #include @@ -133,7 +133,7 @@ void CSVRender::Object::update() catch (std::exception& e) { // TODO: use error marker mesh - std::cerr << e.what() << std::endl; + Log(Debug::Error) << e.what(); } } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 333ec5ced8..c58130f963 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -133,7 +133,7 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (boost::filesystem::is_regular_file(cfgFile)) { if (!mSilent) - Log(Debug::Info) << "Loading config file: " << cfgFile.string() << "... "; + Log(Debug::Info) << "Loading config file: " << cfgFile.string(); boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile); boost::iostreams::filtering_istream configFileStream; @@ -144,14 +144,13 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, boost::program_options::store(boost::program_options::parse_config_file( configFileStream, description, true), variables); - if (!mSilent) - Log(Debug::Info) << "done."; return true; } else { if (!mSilent) - Log(Debug::Info) << "failed."; + Log(Debug::Error) << "Loading failed."; + return false; } } From d19cbdb652dd135230714f6b1cb6dd94af678260 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Fri, 27 Jul 2018 21:13:04 -0300 Subject: [PATCH 084/175] Factored strength into hand-to-hand combat --- apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwmechanics/combat.cpp | 6 +++--- files/settings-default.cfg | 3 +++ files/ui/advancedpage.ui | 6 ++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index ff229e02d9..5406201b96 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -76,6 +76,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 5910dbacd0..51e7eefddd 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -388,14 +388,14 @@ namespace MWMechanics void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) { - // Note: MCP contains an option to include Strength in hand-to-hand damage - // calculations. Some mods recommend using it, so we may want to include an - // option for it. const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); + if (Settings::Manager::getBool("strength influences hand to hand", "Game")){ + damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; + } MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); healthdmg = otherstats.isParalyzed() diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b0ddd52239..d974d8b68f 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -225,6 +225,9 @@ use additional anim sources = false # Make the disposition change of merchants caused by barter dealings permanent barter disposition change is permanent = false +# Factors Strength into hand-to-hand combat. Uses the MCP formula (damage * (strength / 40)). +strength influences hand to hand = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 553c244d38..811bc5f868 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -99,6 +99,12 @@ Barter disposition change is permanent + + + <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> + + + Factor strength into hand-to-hand combat From 18e51e0e981d8391d3925b5f18b97469a5be9387 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 10:10:01 -0300 Subject: [PATCH 085/175] Added check for werewolves --- apps/openmw/mwmechanics/combat.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 51e7eefddd..9930c8206a 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -393,14 +393,16 @@ namespace MWMechanics float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); - if (Settings::Manager::getBool("strength influences hand to hand", "Game")){ - damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; - } MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); healthdmg = otherstats.isParalyzed() || otherstats.getKnockedDown(); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); + + if (Settings::Manager::getBool("strength influences hand to hand", "Game") && !isWerewolf) { + damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; + } + if(isWerewolf) { healthdmg = true; From 7e9ce99062e409ce578901f2dabba24a35965a07 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 23:13:56 -0300 Subject: [PATCH 086/175] Made the werewolf check optional --- apps/launcher/advancedpage.cpp | 6 ++++ apps/openmw/mwmechanics/combat.cpp | 7 +++- files/settings-default.cfg | 6 ++-- files/ui/advancedpage.ui | 53 +++++++++++++++++++++++++++--- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 5406201b96..9cb89d2b5d 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -77,6 +77,9 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); + int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); + if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) + unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -133,6 +136,9 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); + int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); + if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) + mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9930c8206a..06ed99f5ae 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -399,7 +399,12 @@ namespace MWMechanics || otherstats.getKnockedDown(); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); - if (Settings::Manager::getBool("strength influences hand to hand", "Game") && !isWerewolf) { + // Options in the launcher's combo box: unarmedFactorsStrengthComboBox + // 0 = Do not factor strength into hand-to-hand combat. + // 1 = Factor into werewolf hand-to-hand combat. + // 2 = Ignore werewolves. + int factorStrength = Settings::Manager::getInt("strength influences hand to hand", "Game"); + if (factorStrength == 1 || (factorStrength == 2 && !isWerewolf)) { damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index d974d8b68f..c2ac2eb1ca 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -225,8 +225,10 @@ use additional anim sources = false # Make the disposition change of merchants caused by barter dealings permanent barter disposition change is permanent = false -# Factors Strength into hand-to-hand combat. Uses the MCP formula (damage * (strength / 40)). -strength influences hand to hand = false +# Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. +# (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and +# 2 means werewolves are ignored) +strength influences hand to hand = 0 [General] diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 811bc5f868..bd5bbf0e77 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -100,12 +100,57 @@ Barter disposition change is permanent + + - <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> - - - Factor strength into hand-to-hand combat + <html><head/><body><p>Factor strength into hand-to-hand damage calculations, as the MCP formula: damage * (strength / 40).</p><p>The default value is Off.</p></body></html> + + + -1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Factor strength into hand-to-hand combat: + + + + + + + 1 + + + + Off + + + + + Affect werewolves + + + + + Do not affect werewolves + + + + + From d6f607e823cc806e4f7f00c88beff464ffb666e4 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 23:47:35 -0300 Subject: [PATCH 087/175] Fixed wrong starting index --- files/ui/advancedpage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index bd5bbf0e77..76c007dc59 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -131,7 +131,7 @@ - 1 + 0 From 640b32da5621d7aa2c4e178d30a04128ab6b6122 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Thu, 2 Aug 2018 21:29:41 -0300 Subject: [PATCH 088/175] Fixed merge conflicts --- apps/launcher/advancedpage.cpp | 2 ++ files/ui/advancedpage.ui | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 9cb89d2b5d..b254e0b012 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -81,6 +81,7 @@ bool Launcher::AdvancedPage::loadSettings() if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); + // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -140,6 +141,7 @@ void Launcher::AdvancedPage::saveSettings() if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); + // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 76c007dc59..b308f21ace 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -22,7 +22,11 @@ 0 0 641 +<<<<<<< HEAD 998 +======= + 968 +>>>>>>> Fixed merge conflicts @@ -89,6 +93,7 @@ Enchanted weapons are magical +<<<<<<< HEAD @@ -100,6 +105,11 @@ Barter disposition change is permanent +======= + + + +>>>>>>> Fixed merge conflicts From 4ff6f64ecddba69325824d555cc80ace63e8f0a2 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 11 Aug 2018 19:24:46 -0300 Subject: [PATCH 089/175] Added entry to the changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f80aa84b..c8eef0d45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill Feature #4549: Weapon priority: use the actual damage in weapon rating calculations Feature #4550: Weapon priority: make ranged weapon bonus more sensible + Feature #4579: Add option for applying Strength into hand to hand damage Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test From ffb103f7c5d66e034b3b6b2c026592571c3c1967 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2018 11:08:05 +0200 Subject: [PATCH 090/175] updated credits file --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d2fd051809..a722ff7237 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -171,6 +171,7 @@ Programmers viadanna Vincent Heuken vocollapse + Yohaulticetl zelurker Documentation From 5a4d0cec3a70e5f6e60ade7547f59cfd0a0c03d5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 23:05:43 +0400 Subject: [PATCH 091/175] Use new logging system for game itself --- apps/openmw/engine.cpp | 34 ++++---- apps/openmw/main.cpp | 8 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 5 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 13 ++- apps/openmw/mwdialogue/scripttest.cpp | 11 +-- apps/openmw/mwgui/charactercreation.cpp | 5 +- apps/openmw/mwgui/class.cpp | 4 +- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwgui/formatting.cpp | 3 +- apps/openmw/mwgui/loadingscreen.cpp | 5 +- apps/openmw/mwgui/messagebox.cpp | 3 +- apps/openmw/mwgui/race.cpp | 3 +- apps/openmw/mwgui/savegamedialog.cpp | 6 +- apps/openmw/mwgui/settingswindow.cpp | 6 +- apps/openmw/mwgui/sortfilteritemmodel.cpp | 6 +- apps/openmw/mwgui/spellmodel.cpp | 4 +- apps/openmw/mwgui/videowidget.cpp | 3 +- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- apps/openmw/mwinput/inputmanagerimp.cpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/aisequence.cpp | 4 +- apps/openmw/mwmechanics/aiwander.cpp | 7 +- apps/openmw/mwmechanics/levelledlist.hpp | 5 +- apps/openmw/mwmechanics/objects.cpp | 4 +- apps/openmw/mwmechanics/summoning.cpp | 4 +- apps/openmw/mwphysics/physicssystem.cpp | 7 +- apps/openmw/mwrender/animation.cpp | 12 +-- apps/openmw/mwrender/bulletdebugdraw.cpp | 6 +- apps/openmw/mwrender/characterpreview.cpp | 4 +- apps/openmw/mwrender/creatureanimation.cpp | 6 +- apps/openmw/mwrender/globalmap.cpp | 12 +-- apps/openmw/mwrender/localmap.cpp | 14 ++-- apps/openmw/mwrender/npcanimation.cpp | 12 +-- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/water.cpp | 9 +- apps/openmw/mwscript/aiextensions.cpp | 19 ++--- apps/openmw/mwscript/containerextensions.cpp | 6 +- apps/openmw/mwscript/dialogueextensions.cpp | 8 +- apps/openmw/mwscript/globalscripts.cpp | 11 ++- apps/openmw/mwscript/locals.cpp | 10 +-- apps/openmw/mwscript/scriptmanagerimp.cpp | 13 +-- apps/openmw/mwscript/statsextensions.cpp | 8 +- .../mwscript/transformationextensions.cpp | 6 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 10 +-- apps/openmw/mwsound/openal_output.cpp | 83 ++++++++++--------- apps/openmw/mwsound/soundmanagerimp.cpp | 28 ++++--- apps/openmw/mwstate/statemanagerimp.cpp | 18 ++-- apps/openmw/mwworld/cellpreloader.cpp | 7 +- apps/openmw/mwworld/cells.cpp | 5 +- apps/openmw/mwworld/cellstore.cpp | 28 +++---- apps/openmw/mwworld/containerstore.cpp | 7 +- apps/openmw/mwworld/contentloader.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 9 +- apps/openmw/mwworld/inventorystore.cpp | 3 +- apps/openmw/mwworld/livecellref.cpp | 10 +-- apps/openmw/mwworld/localscripts.cpp | 14 ++-- apps/openmw/mwworld/player.cpp | 7 +- apps/openmw/mwworld/projectilemanager.cpp | 5 +- apps/openmw/mwworld/scene.cpp | 14 ++-- apps/openmw/mwworld/store.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 14 ++-- 62 files changed, 310 insertions(+), 297 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bd40dc1abe..2941ede1b2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -10,6 +10,8 @@ #include +#include + #include #include @@ -61,7 +63,7 @@ namespace void checkSDLError(int ret) { if (ret != 0) - std::cerr << "SDL error: " << SDL_GetError() << std::endl; + Log(Debug::Error) << "SDL error: " << SDL_GetError(); } } @@ -189,7 +191,7 @@ bool OMW::Engine::frame(float frametime) } catch (const std::exception& e) { - std::cerr << "Error in frame: " << e.what() << std::endl; + Log(Debug::Error) << "Error in frame: " << e.what(); } return true; } @@ -364,7 +366,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) // Try with a lower AA if (antialiasing > 0) { - std::cout << "Note: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2 << std::endl; + Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2; antialiasing /= 2; Settings::Manager::setInt("antialiasing", "Video", antialiasing); checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); @@ -373,7 +375,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) else { std::stringstream error; - error << "Failed to create SDL window: " << SDL_GetError() << std::endl; + error << "Failed to create SDL window: " << SDL_GetError(); throw std::runtime_error(error.str()); } } @@ -420,16 +422,16 @@ void OMW::Engine::setWindowIcon() std::string windowIcon = (mResDir / "mygui" / "openmw.png").string(); windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary); if (windowIconStream.fail()) - std::cerr << "Error: Failed to open " << windowIcon << std::endl; + Log(Debug::Error) << "Error: Failed to open " << windowIcon; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { - std::cerr << "Error: Failed to read window icon, no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Failed to read window icon, no png readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream); if (!result.success()) - std::cerr << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status(); else { osg::ref_ptr image = result.getImage(); @@ -564,21 +566,19 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) { std::pair result = mEnvironment.getScriptManager()->compileAll(); if (result.first) - std::cout + Log(Debug::Info) << "compiled " << result.second << " of " << result.first << " scripts (" << 100*static_cast (result.second)/result.first - << "%)" - << std::endl; + << "%)"; } if (mCompileAllDialogue) { std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode); if (result.first) - std::cout + Log(Debug::Info) << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" << 100*static_cast (result.second)/result.first - << "%)" - << std::endl; + << "%)"; } } @@ -614,14 +614,14 @@ public: osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat); if (!readerwriter) { - std::cerr << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found"; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); if (!result.success()) { - std::cerr << "Error: Can't write screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); } } @@ -636,7 +636,7 @@ void OMW::Engine::go() { assert (!mContentFiles.empty()); - std::cout << "OSG version: " << osgGetVersion() << std::endl; + Log(Debug::Info) << "OSG version: " << osgGetVersion(); // Load settings Settings::Manager settings; @@ -738,7 +738,7 @@ void OMW::Engine::go() // Save user settings settings.saveUser(settingspath); - std::cout << "Quitting peacefully." << std::endl; + Log(Debug::Info) << "Quitting peacefully."; } void OMW::Engine::setCompileAll (bool all) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index f9cf585444..176adfad03 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -202,8 +200,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat StringsVector content = variables["content"].as().toStdStringVector(); if (content.empty()) { - std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; - return false; + Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..."; + return false; } StringsVector::const_iterator it(content.begin()); @@ -217,7 +215,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCell(variables["start"].as().toStdString()); engine.setSkipMenu (variables["skip-menu"].as(), variables["new-game"].as()); if (!variables["skip-menu"].as() && variables["new-game"].as()) - std::cerr << "Warning: new-game used without skip-menu -> ignoring it" << std::endl; + Log(Debug::Warning) << "Warning: new-game used without skip-menu -> ignoring it"; // scripts engine.setCompileAll(variables["script-all"].as()); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 02a8a17ef0..161711751d 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -1,7 +1,7 @@ #include "creature.hpp" #include - +#include #include #include #include @@ -146,7 +146,7 @@ namespace MWClass if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mCreatureStats.getSpells().add (spell); else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility - std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'"; } // inventory diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1c293f1e41..c06c3f67c6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -365,7 +366,7 @@ namespace MWClass if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else - std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'"; } if (!ref->mBase->mFaction.empty()) @@ -395,7 +396,7 @@ namespace MWClass else { /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility - std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'"; } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index df214ce863..c3e56c0bf5 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -5,7 +5,8 @@ #include #include #include -#include + +#include #include #include @@ -203,16 +204,14 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); success = false; } if (!success) { - std::cerr - << "Warning: compiling failed (dialogue script)" << std::endl - << cmd - << std::endl << std::endl; + Log(Debug::Warning) + << "Warning: compiling failed (dialogue script)\n" << cmd << "\n\n"; } return success; @@ -232,7 +231,7 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); } } } diff --git a/apps/openmw/mwdialogue/scripttest.cpp b/apps/openmw/mwdialogue/scripttest.cpp index 54b4d8cd9f..4f0b7422c7 100644 --- a/apps/openmw/mwdialogue/scripttest.cpp +++ b/apps/openmw/mwdialogue/scripttest.cpp @@ -1,7 +1,5 @@ #include "scripttest.hpp" -#include - #include "../mwworld/manualref.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" @@ -12,6 +10,7 @@ #include "../mwscript/compilercontext.hpp" +#include #include #include #include @@ -80,16 +79,14 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler:: } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); success = false; } if (!success) { - std::cerr - << "compiling failed (dialogue script)" << std::endl - << info->mResultScript - << std::endl << std::endl; + Log(Debug::Warning) + << "compiling failed (dialogue script)\n" << info->mResultScript << "\n\n"; } } } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 2deb622151..e3effa995b 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,5 +1,6 @@ #include "charactercreation.hpp" +#include #include #include "../mwbase/environment.hpp" @@ -284,7 +285,7 @@ namespace MWGui } catch (std::exception& e) { - std::cerr << "Error: Failed to create chargen window: " << e.what() << std::endl; + Log(Debug::Error) << "Error: Failed to create chargen window: " << e.what(); } } @@ -602,7 +603,7 @@ namespace MWGui mGenerateClass = "Mage"; else { - std::cout << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + Log(Debug::Warning) << "Failed to deduce class from chosen answers in generate class dialog."; mGenerateClass = "Thief"; } } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 4d2a15c820..6ed7a44915 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -12,6 +12,8 @@ #include "../mwworld/esmstore.hpp" +#include + #include "tooltips.hpp" namespace @@ -924,7 +926,7 @@ namespace MWGui std::string classImage = std::string("textures\\levelup\\") + classId + ".dds"; if (!MWBase::Environment::get().getWindowManager()->textureExists(classImage)) { - std::cout << "No class image for " << classId << ", falling back to default" << std::endl; + Log(Debug::Warning) << "No class image for " << classId << ", falling back to default"; classImage = "textures\\levelup\\warrior.dds"; } imageBox->setImageTexture(classImage); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 7b177fdb03..aeb6dfc0f5 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -411,7 +412,7 @@ namespace MWGui { if (!actor.getClass().isActor()) { - std::cerr << "Warning: can not talk with non-actor object." << std::endl; + Log(Debug::Warning) << "Warning: can not talk with non-actor object."; return; } diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index edcb94eed9..663bd73380 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -300,7 +301,7 @@ namespace MWGui if (!exists) { - std::cerr << "Warning: Could not find \"" << src << "\" referenced by an tag." << std::endl; + Log(Debug::Warning) << "Warning: Could not find \"" << src << "\" referenced by an tag."; break; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 28f4b88901..be3d477e1e 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -10,9 +10,8 @@ #include #include - +#include #include - #include #include @@ -94,7 +93,7 @@ namespace MWGui ++found; } if (mSplashScreens.empty()) - std::cerr << "No splash screens found!" << std::endl; + Log(Debug::Warning) << "Warning: no splash screens found!"; } void LoadingScreen::setLabel(const std::string &label, bool important) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index a79112b9f0..2fbce97d46 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -125,7 +126,7 @@ namespace MWGui { if (mInterMessageBoxe != NULL) { - std::cerr << "Warning: replacing an interactive message box that was not answered yet" << std::endl; + Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet"; mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; mInterMessageBoxe = NULL; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 64609cbe69..96c0d7de4f 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -6,6 +6,7 @@ #include +#include #include #include "../mwworld/esmstore.hpp" @@ -340,7 +341,7 @@ namespace MWGui } catch (std::exception& e) { - std::cerr << "Error creating preview: " << e.what() << std::endl; + Log(Debug::Error) << "Error creating preview: " << e.what(); } } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 43e5111447..45790cbf5f 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -438,14 +440,14 @@ namespace MWGui osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { - std::cerr << "Error: Can't open savegame screenshot, no jpg readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't open savegame screenshot, no jpg readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream); if (!result.success()) { - std::cerr << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status(); return; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 677ddefb3a..80ed9202a0 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -12,6 +12,7 @@ #include +#include #include #include @@ -31,7 +32,8 @@ namespace if (val == "linear") return "Trilinear"; if (val == "nearest") return "Bilinear"; if (val != "none") - std::cerr<< "Warning: Invalid texture mipmap option: "< - #include - +#include #include #include #include @@ -245,7 +243,7 @@ namespace MWGui const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(enchId); if (!ench) { - std::cerr << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId(); return false; } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index a2710b633f..0933737ca2 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -1,6 +1,6 @@ #include "spellmodel.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -94,7 +94,7 @@ namespace MWGui const ESM::Enchantment* enchant = esmStore.get().search(enchantId); if (!enchant) { - std::cerr << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId(); continue; } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 5a4bb981f4..516f5cfcc6 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -37,7 +38,7 @@ void VideoWidget::playVideo(const std::string &video) } catch (std::exception& e) { - std::cerr << "Failed to open video: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to open video: " << e.what(); return; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c97efed34d..e1bf9627de 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include #include @@ -1082,7 +1084,7 @@ namespace MWGui { if (!mStore) { - std::cerr << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'" << std::endl; + Log(Debug::Error) << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'"; return; } const ESM::GameSetting *setting = mStore->get().find(tag); @@ -1788,7 +1790,7 @@ namespace MWGui if (found != mCurrentModals.end()) mCurrentModals.erase(found); else - std::cerr << " warning: can't find modal window " << input << std::endl; + Log(Debug::Warning) << "Warning: can't find modal window " << input; } } if (mCurrentModals.empty()) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 242058c5f7..709c611963 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -120,11 +121,11 @@ namespace MWInput SDL_ControllerDeviceEvent evt; evt.which = i; controllerAdded(mFakeDeviceID, evt); - std::cout << "Detected game controller: " << SDL_GameControllerNameForIndex(i) << std::endl; + Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i); } else { - std::cout << "Detected unusable controller: " << SDL_JoystickNameForIndex(i) << std::endl; + Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2d535f57b2..d1f4e47df6 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -8,7 +8,7 @@ #include #include - +#include #include #include "../mwworld/esmstore.hpp" @@ -1720,7 +1720,7 @@ namespace MWMechanics } else { - std::cerr<< "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId(); return false; } } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 0851748200..753dc240ed 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -1,8 +1,8 @@ #include "aisequence.hpp" #include -#include +#include #include #include "../mwbase/environment.hpp" @@ -282,7 +282,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } catch (std::exception& e) { - std::cerr << "Error during AiSequence::execute: " << e.what() << std::endl; + Log(Debug::Error) << "Error during AiSequence::execute: " << e.what(); } } } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 928b09cf9a..67468453ab 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,10 +1,9 @@ #include "aiwander.hpp" #include -#include +#include #include - #include #include "../mwbase/world.hpp" @@ -24,8 +23,6 @@ #include "coordinateconverter.hpp" #include "actorutil.hpp" - - namespace MWMechanics { static const int COUNT_BEFORE_RESET = 10; @@ -677,7 +674,7 @@ namespace MWMechanics } else { - std::cerr<< "Error: Attempted to play out of range idle animation \""< - +#include #include #include "../mwworld/ptr.hpp" @@ -63,7 +62,7 @@ namespace MWMechanics // Vanilla doesn't fail on nonexistent items in levelled lists if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item))) { - std::cerr << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'"; return std::string(); } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 7d7a5f14ff..d8821276e4 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -1,6 +1,6 @@ #include "objects.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -88,7 +88,7 @@ bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& gro } else { - std::cerr<< "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId(); return false; } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 71c49f9df8..b3f7afc531 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -1,6 +1,6 @@ #include "summoning.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -76,7 +76,7 @@ namespace MWMechanics } catch (std::exception& e) { - std::cerr << "Failed to spawn summoned creature: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to spawn summoned creature: " << e.what(); // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ae8d76a809..13a0f23ac3 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1,6 +1,5 @@ #include "physicssystem.hpp" -#include #include #include @@ -20,7 +19,7 @@ #include #include #include - +#include #include #include #include @@ -638,7 +637,7 @@ namespace MWPhysics mPtr.getRefData().getBaseNode()->accept(visitor); if (!visitor.mFound) { - std::cerr << "Error: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId(); // Remove nonexistent nodes from animated shapes map and early out mShapeInstance->mAnimatedShapes.erase(recIndex); @@ -708,7 +707,7 @@ namespace MWPhysics if (physFramerate > 0) { mPhysicsDt = 1.f / physFramerate; - std::cerr << "Warning: physics framerate was overridden (a new value is " << physFramerate << ")." << std::endl; + Log(Debug::Warning) << "Warning: using custom physics framerate (" << physFramerate << " FPS)."; } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ff31e1cfb2..8e1105b9f1 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -192,7 +194,7 @@ namespace for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) { if (!it->second->removeChild(it->first)) - std::cerr << "error removing " << it->first->getName() << std::endl; + Log(Debug::Error) << "Error removing " << it->first->getName(); } } @@ -612,7 +614,7 @@ namespace MWRender NodeMap::const_iterator found = nodeMap.find(bonename); if (found == nodeMap.end()) { - std::cerr << "Warning: addAnimSource: can't find bone '" + bonename << "' in " << baseModel << " (referenced by " << kfname << ")" << std::endl; + Log(Debug::Warning) << "Warning: addAnimSource: can't find bone '" + bonename << "' in " << baseModel << " (referenced by " << kfname << ")"; continue; } @@ -724,7 +726,7 @@ namespace MWRender } catch (std::exception& e) { - std::cerr << "Error handling text key " << evt << ": " << e.what() << std::endl; + Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what(); } } } @@ -1767,12 +1769,12 @@ namespace MWRender PartHolder::~PartHolder() { if (mNode.get() && !mNode->getNumParents()) - std::cerr << "Error: part has no parents " << std::endl; + Log(Debug::Verbose) << "Part has no parents" ; if (mNode.get() && mNode->getNumParents()) { if (mNode->getNumParents() > 1) - std::cerr << "Error: part has multiple parents " << mNode->getNumParents() << " " << mNode.get() << std::endl; + Log(Debug::Verbose) << "Part has multiple (" << mNode->getNumParents() << ") parents"; mNode->getParent(0)->removeChild(mNode); } } diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index d35c3ac5de..eb3775cb46 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -1,12 +1,12 @@ #include "bulletdebugdraw.hpp" -#include - #include #include #include +#include + #include "vismask.hpp" namespace @@ -91,7 +91,7 @@ void DebugDrawer::drawContactPoint(const btVector3 &PointOnB, const btVector3 &n void DebugDrawer::reportErrorWarning(const char *warningString) { - std::cerr << warningString << std::endl; + Log(Debug::Warning) << warningString; } void DebugDrawer::setDebugMode(int isOn) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index faaa3799eb..2e0249e608 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -1,7 +1,6 @@ #include "characterpreview.hpp" #include -#include #include #include @@ -14,6 +13,7 @@ #include #include +#include #include #include @@ -474,7 +474,7 @@ namespace MWRender mCamera->addUpdateCallback(mUpdateCameraCallback); } else - std::cerr << "Error: Bip01 Head node not found" << std::endl; + Log(Debug::Error) << "Error: Bip01 Head node not found"; } } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 827b576c33..6db223bd54 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,11 +1,9 @@ #include "creatureanimation.hpp" -#include - #include #include - +#include #include #include #include @@ -155,7 +153,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) } catch (std::exception& e) { - std::cerr << "Can not add creature part: " << e.what() << std::endl; + Log(Debug::Error) << "Can not add creature part: " << e.what(); } } diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index af2bb101a8..fae524faa7 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include #include @@ -411,14 +413,14 @@ namespace MWRender osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - std::cerr << "Error: Can't write map overlay: no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't write map overlay: no png readerwriter found"; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mOverlayImage, ostream); if (!result.success()) { - std::cerr << "Error: Can't write map overlay: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Warning) << "Error: Can't write map overlay: " << result.message() << " code " << result.status(); return; } @@ -463,14 +465,14 @@ namespace MWRender osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - std::cerr << "Error: Can't read map overlay: no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't read map overlay: no png readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(istream); if (!result.success()) { - std::cerr << "Error: Can't read map overlay: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Can't read map overlay: " << result.message() << " code " << result.status(); return; } @@ -572,7 +574,7 @@ namespace MWRender CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera); if (found == mActiveCameras.end()) { - std::cerr << "Error: GlobalMap trying to remove an inactive camera" << std::endl; + Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera"; return; } mActiveCameras.erase(found); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0b65a6b13d..5e501ecf86 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -1,6 +1,5 @@ #include "localmap.hpp" -#include #include #include @@ -12,6 +11,7 @@ #include +#include #include #include #include @@ -320,7 +320,7 @@ void LocalMap::markForRemoval(osg::Camera *cam) CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), cam); if (found == mActiveCameras.end()) { - std::cerr << "Error: trying to remove an inactive camera" << std::endl; + Log(Debug::Error) << "Error: trying to remove an inactive camera"; return; } mActiveCameras.erase(found); @@ -492,7 +492,7 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell) // We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same. if (i >= int(fog->mFogTextures.size())) { - std::cout << "Error: fog texture count mismatch" << std::endl; + Log(Debug::Warning) << "Warning: fog texture count mismatch"; break; } @@ -684,7 +684,7 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { - std::cerr << "Error: Unable to load fog, can't find a tga ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter" ; return; } @@ -693,7 +693,7 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in); if (!result.success()) { - std::cerr << "Error: Failed to read fog: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status(); return; } @@ -716,7 +716,7 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { - std::cerr << "Error: Unable to write fog, can't find a tga ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to write fog, can't find a tga ReaderWriter"; return; } @@ -725,7 +725,7 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mFogOfWarImage, ostream); if (!result.success()) { - std::cerr << "Error: Unable to write fog: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Unable to write fog: " << result.message() << " code " << result.status(); return; } mFogOfWarImage->flipVertical(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index baf6cb8229..98f8ce892d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -435,7 +437,7 @@ void NpcAnimation::updateNpcBase() if (bp) mHeadModel = "meshes\\" + bp->mModel; else - std::cerr << "Warning: Failed to load body part '" << mNpc->mHead << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHead << "'"; } mHairModel = ""; @@ -445,7 +447,7 @@ void NpcAnimation::updateNpcBase() if (bp) mHairModel = "meshes\\" + bp->mModel; else - std::cerr << "Warning: Failed to load body part '" << mNpc->mHair << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHair << "'"; } } @@ -758,7 +760,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g } catch (std::exception& e) { - std::cerr << "Error adding NPC part: " << e.what() << std::endl; + Log(Debug::Error) << "Error adding NPC part: " << e.what(); return false; } @@ -845,7 +847,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormFemale << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'"; } if(!bodypart && !part->mMale.empty()) { @@ -860,7 +862,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormMale << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'"; } if(bodypart) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 03863000ff..e17fe3191e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,6 +22,8 @@ #include +#include + #include #include #include @@ -709,7 +711,7 @@ namespace MWRender if (!found) { - std::cerr << "Wrong screenshot type: " << settingArgs[0] << "." << std::endl; + Log(Debug::Warning) << "Wrong screenshot type: " << settingArgs[0] << "."; return false; } } @@ -728,7 +730,7 @@ namespace MWRender if (mCamera->isVanityOrPreviewModeEnabled()) { - std::cerr << "Spherical screenshots are not allowed in preview mode." << std::endl; + Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode."; return false; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 3c617f794e..52832ad874 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -29,7 +31,6 @@ #include - #include #include @@ -193,16 +194,16 @@ osg::ref_ptr readPngImage (const std::string& file) boost::filesystem::ifstream inStream; inStream.open(file, std::ios_base::in | std::ios_base::binary); if (inStream.fail()) - std::cerr << "Error: Failed to open " << file << std::endl; + Log(Debug::Error) << "Error: Failed to open " << file; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { - std::cerr << "Error: Failed to read " << file << ", no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Failed to read " << file << ", no png readerwriter found"; return osg::ref_ptr(); } osgDB::ReaderWriter::ReadResult result = reader->readImage(inStream); if (!result.success()) - std::cerr << "Error: Failed to read " << file << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read " << file << ": " << result.message() << " code " << result.status(); return result.getImage(); } diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index c51a55b508..0bb94a1342 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -1,7 +1,8 @@ #include "aiextensions.hpp" #include -#include + +#include #include #include @@ -50,7 +51,7 @@ namespace MWScript MWMechanics::AiActivate activatePackage(objectID); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr); - std::cout << "AiActivate" << std::endl; + Log(Debug::Info) << "AiActivate"; } }; @@ -78,7 +79,7 @@ namespace MWScript MWMechanics::AiTravel travelPackage(x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr); - std::cout << "AiTravel: " << x << ", " << y << ", " << z << std::endl; + Log(Debug::Info) << "AiTravel: " << x << ", " << y << ", " << z; } }; @@ -112,8 +113,7 @@ namespace MWScript MWMechanics::AiEscort escortPackage(actorID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); - std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -155,8 +155,7 @@ namespace MWScript MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); - std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -316,8 +315,7 @@ namespace MWScript MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); - std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -353,8 +351,7 @@ namespace MWScript MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); - std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration; } }; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 0feadaa59d..dd3e54a88a 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -190,8 +192,8 @@ namespace MWScript if (it == invStore.end()) { it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr); - std::cerr << "Implicitly adding one " << item << " to container " - "to fulfil requirements of Equip instruction" << std::endl; + Log(Debug::Warning) << "Implicitly adding one " << item << " to container " + "to fulfil requirements of Equip instruction"; } if (ptr == MWMechanics::getPlayer()) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 4b6ddcf9fb..135cb787e3 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -1,10 +1,8 @@ -#include - #include "dialogueextensions.hpp" #include #include - +#include #include #include #include @@ -139,8 +137,8 @@ namespace MWScript if (!ptr.getClass().isActor()) { const std::string error = "Warning: \"forcegreeting\" command works only for actors."; - runtime.getContext().report (error); - std::cerr << error << std::endl; + runtime.getContext().report(error); + Log(Debug::Warning) << error; return; } diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 6074a12d0e..a52a4938ad 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -1,8 +1,8 @@ #include "globalscripts.hpp" #include -#include +#include #include #include #include @@ -113,9 +113,9 @@ namespace MWScript } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Failed to add start script " << *iter << " because an exception has " - << "been thrown: " << exception.what() << std::endl; + << "been thrown: " << exception.what(); } } } @@ -169,10 +169,9 @@ namespace MWScript } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Failed to add start script " << script.mId - << " because an exception has been thrown: " << exception.what() - << std::endl; + << " because an exception has been thrown: " << exception.what(); return true; } diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 64f3058eb2..4c55731688 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include #include #include @@ -229,10 +229,10 @@ namespace MWScript } catch (std::exception& e) { - std::cerr << "Failed to read local variable state for script '" - << script << "' (legacy format): " << e.what() - << "\nNum shorts: " << numshorts << " / " << mShorts.size() - << " Num longs: " << numlongs << " / " << mLongs.size() << std::endl; + Log(Debug::Error) << "Failed to read local variable state for script '" + << script << "' (legacy format): " << e.what() + << "\nNum shorts: " << numshorts << " / " << mShorts.size() + << " Num longs: " << numlongs << " / " << mLongs.size(); } } else diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 7c1f9bf4df..80496d6dbf 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -1,11 +1,12 @@ #include "scriptmanagerimp.hpp" #include -#include #include #include #include +#include + #include #include @@ -65,14 +66,14 @@ namespace MWScript } catch (const std::exception& error) { - std::cerr << "Error: An exception has been thrown: " << error.what() << std::endl; + Log(Debug::Error) << "Error: An exception has been thrown: " << error.what(); Success = false; } if (!Success) { - std::cerr - << "Warning: compiling failed: " << name << std::endl; + Log(Debug::Warning) + << "Warning: compiling failed: " << name; } if (Success) @@ -121,8 +122,8 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "Execution of script " << name << " failed:" << std::endl; - std::cerr << e.what() << std::endl; + Log(Debug::Error) << "Execution of script " << name << " failed:"; + Log(Debug::Error) << e.what(); iter->second.first.clear(); // don't execute again. } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index fadc99b663..2ff5035ae3 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1,6 +1,5 @@ #include "statsextensions.hpp" -#include #include #include @@ -9,7 +8,7 @@ #include #include - +#include #include #include #include @@ -246,9 +245,10 @@ namespace MWScript if (R()(runtime, false, true).isEmpty()) { - std::cerr + Log(Debug::Warning) << "Warning: Compensating for broken script in Morrowind.esm by " - << "ignoring remote access to dagoth_ur_1" << std::endl; + << "ignoring remote access to dagoth_ur_1"; + return; } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 611199f723..8f0c725193 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -317,7 +317,7 @@ namespace MWScript { std::string error = "Warning: PositionCell: unknown interior cell (" + cellID + "), moving to exterior instead"; runtime.getContext().report (error); - std::cerr << error << std::endl; + Log(Debug::Warning) << error; } } if(store) @@ -429,7 +429,7 @@ namespace MWScript if(!cell) { runtime.getContext().report ("unknown cell (" + cellID + ")"); - std::cerr << "unknown cell (" << cellID << ")\n"; + Log(Debug::Error) << "Error: unknown cell (" << cellID << ")"; } } if(store) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index f458c0a978..7dffd685a3 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -3,9 +3,9 @@ #include #include -#include #include +#include #include namespace MWSound @@ -28,7 +28,7 @@ int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::writePacket(void *, uint8_t *, int) { - std::cerr<< "can't write to read-only stream" <codec->channels, (*mStream)->codec->channel_layout); - std::cerr<< "Unsupported channel layout: "<codec->channels == 1) { @@ -385,7 +385,7 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { if(!mStream) { - std::cerr<< "No audio stream" < &output) { if(!mStream) { - std::cerr<< "No audio stream" < #include -#include #include #include #include @@ -8,6 +7,7 @@ #include +#include #include #include @@ -44,8 +44,7 @@ ALCenum checkALCError(ALCdevice *device, const char *func, int line) { ALCenum err = alcGetError(device); if(err != ALC_NO_ERROR) - std::cerr<< ">>>>>>>>> ALC error "<>>>>>>>> AL error "<getInfo(&mSampleRate, &chans, &type); mFormat = getALFormat(chans, type); } - catch(std::exception &e) { - std::cerr<< "Failed to get stream info: "<getName()<<"\"" <getName() << "\""; mIsFinished = true; } return !mIsFinished; @@ -593,17 +591,18 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname { deinit(); - std::cout<< "Initializing OpenAL..." < OpenAL_Output::loadSound(const std::string &fname } catch(std::exception &e) { - std::cerr<< "Failed to load audio from "<getIsLooping()) - std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; + Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) @@ -1266,13 +1266,14 @@ bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLou { if(mFreeSources.empty()) { - std::cerr<< "No free sources!" <getIsLooping()) - std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; + Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8c71ab61b1..d6dc2ba99c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -1,6 +1,5 @@ #include "soundmanagerimp.hpp" -#include #include #include #include @@ -8,7 +7,7 @@ #include #include - +#include #include #include "../mwbase/environment.hpp" @@ -81,7 +80,7 @@ namespace MWSound if(!useSound) { - std::cout<< "Sound disabled." <init(devname, hrtfname, hrtfmode)) { - std::cerr<< "Failed to initialize audio output, sound disabled" < names = mOutput->enumerate(); - std::cout <<"Enumerated output devices:\n"; + std::stringstream stream; + + stream << "Enumerated output devices:\n"; for(const std::string &name : names) - std::cout <<" "<enumerateHrtf(); if(!names.empty()) { - std::cout<< "Enumerated HRTF names:\n"; + stream << "Enumerated HRTF names:\n"; for(const std::string &name : names) - std::cout <<" "<isInitialized()) return; - std::cout <<"Playing "< + #include #include #include @@ -157,7 +159,7 @@ void MWState::StateManager::newGame (bool bypass) std::stringstream error; error << "Failed to start new game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); @@ -283,7 +285,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot // Ensure we have written the number of records that was estimated if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record - std::cerr << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount() << std::endl; + Log(Debug::Warning) << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount(); writer.close(); @@ -305,7 +307,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::stringstream error; error << "Failed to save game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); std::vector buttons; buttons.push_back("#{sOk}"); @@ -483,7 +485,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str default: // ignore invalid records - std::cerr << "Warning: Ignoring unknown record: " << n.toString() << std::endl; + Log(Debug::Warning) << "Warning: Ignoring unknown record: " << n.toString(); reader.skipRecord(); } int progressPercent = static_cast(float(reader.getFileOffset())/total*100); @@ -549,7 +551,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::stringstream error; error << "Failed to load saved game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); @@ -625,7 +627,7 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) == selectedContentFiles.end()) { - std::cerr << "Warning: Savegame dependency " << *it << " is missing." << std::endl; + Log(Debug::Warning) << "Warning: Savegame dependency " << *it << " is missing."; notFound = true; } } @@ -653,7 +655,7 @@ void MWState::StateManager::writeScreenshot(std::vector &imageData) const osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { - std::cerr << "Error: Unable to write screenshot, can't find a jpg ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to write screenshot, can't find a jpg ReaderWriter"; return; } @@ -661,7 +663,7 @@ void MWState::StateManager::writeScreenshot(std::vector &imageData) const osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream); if (!result.success()) { - std::cerr << "Error: Unable to write screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Unable to write screenshot: " << result.message() << " code " << result.status(); return; } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 700b91b373..9163f1f2e3 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -1,7 +1,6 @@ #include "cellpreloader.hpp" -#include - +#include #include #include #include @@ -229,12 +228,12 @@ namespace MWWorld { if (!mWorkQueue) { - std::cerr << "Error: can't preload, no work queue set " << std::endl; + Log(Debug::Error) << "Error: can't preload, no work queue set"; return; } if (cell->getState() == CellStore::State_Unloaded) { - std::cerr << "Error: can't preload objects for unloaded cell" << std::endl; + Log(Debug::Error) << "Error: can't preload objects for unloaded cell"; return; } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 752659eb6e..5cac12b9c9 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,7 +1,6 @@ #include "cells.hpp" -#include - +#include #include #include #include @@ -350,7 +349,7 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, catch (...) { // silently drop cells that don't exist anymore - std::cerr << "Warning: Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)"; reader.skipRecord(); return true; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fc3c2e245f..40e904ec2b 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1,8 +1,9 @@ #include "cellstore.hpp" -#include #include +#include + #include #include #include @@ -138,7 +139,7 @@ namespace return; } - std::cerr << "Warning: Dropping reference to " << state.mRef.mRefID << " (invalid content file link)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping reference to " << state.mRef.mRefID << " (invalid content file link)"; return; } @@ -196,9 +197,9 @@ namespace MWWorld } else { - std::cerr + Log(Debug::Warning) << "Warning: could not resolve cell reference '" << ref.mRefID << "'" - << " (dropping reference)" << std::endl; + << " (dropping reference)"; } } @@ -497,7 +498,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "An error occurred listing references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; + Log(Debug::Error) << "An error occurred listing references for cell " << getCell()->getDescription() << ": " << e.what(); } } @@ -553,7 +554,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; + Log(Debug::Error) << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what(); } } @@ -659,11 +660,10 @@ namespace MWWorld case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break; - case 0: std::cerr << "Cell reference '" + ref.mRefID + "' not found!\n"; return; + case 0: Log(Debug::Error) << "Cell reference '" + ref.mRefID + "' not found!"; return; default: - std::cerr - << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; + Log(Debug::Error) << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type"; return; } @@ -756,7 +756,7 @@ namespace MWWorld int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { - std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; + Log(Debug::Warning) << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)"; reader.skipHSubUntil("OBJE"); continue; } @@ -892,7 +892,7 @@ namespace MWWorld if (!visitor.mFound) { - std::cerr << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; continue; } @@ -902,8 +902,8 @@ namespace MWWorld if (otherCell == NULL) { - std::cerr << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() - << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() + << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); @@ -913,7 +913,7 @@ namespace MWWorld if (otherCell == this) { // Should never happen unless someone's tampering with files. - std::cerr << "Found invalid moved ref, ignoring" << std::endl; + Log(Debug::Warning) << "Found invalid moved ref, ignoring"; continue; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 6c369a1548..8fa16ff515 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -502,7 +503,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } catch (const std::exception& e) { - std::cerr << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what() << std::endl; + Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what(); } } @@ -826,10 +827,10 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory) case ESM::REC_WEAP: readEquipmentState (getState (weapons, state), thisIndex, inventory); break; case ESM::REC_LIGH: readEquipmentState (getState (lights, state), thisIndex, inventory); break; case 0: - std::cerr << "Dropping inventory reference to '" << state.mRef.mRefID << "' (object no longer exists)" << std::endl; + Log(Debug::Warning) << "Dropping inventory reference to '" << state.mRef.mRefID << "' (object no longer exists)"; break; default: - std::cerr << "Warning: Invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; + Log(Debug::Warning) << "Warning: Invalid item type in inventory state, refid " << state.mRef.mRefID; break; } } diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp index 0f2d807aae..2069a78fc8 100644 --- a/apps/openmw/mwworld/contentloader.hpp +++ b/apps/openmw/mwworld/contentloader.hpp @@ -2,10 +2,10 @@ #define CONTENTLOADER_HPP #include -#include #include #include +#include #include "components/loadinglistener/loadinglistener.hpp" namespace MWWorld @@ -24,8 +24,8 @@ struct ContentLoader virtual void load(const boost::filesystem::path& filepath, int& index) { - std::cout << "Loading content file " << filepath.string() << std::endl; - mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string())); + Log(Debug::Info) << "Loading content file " << filepath.string(); + mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string())); } protected: diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ac5608b8e2..01d8e4b820 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -1,12 +1,11 @@ #include "esmstore.hpp" #include -#include #include +#include #include - #include #include @@ -84,7 +83,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } else { - std::cerr << "error: info record without dialog" << std::endl; + Log(Debug::Error) << "Error: info record without dialog"; esm.skipRecord(); } } else if (n.intval == ESM::REC_MGEF) { @@ -170,7 +169,7 @@ void ESMStore::validate() const ESM::Faction *fact = mFactions.search(npcFaction); if (!fact) { - std::cerr << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it." << std::endl; + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it."; npc.mFaction = ""; npc.mNpdt.mRank = -1; changed = true; @@ -183,7 +182,7 @@ void ESMStore::validate() const ESM::Class *cls = mClasses.search(npcClass); if (!cls) { - std::cerr << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement." << std::endl; + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement."; npc.mClass = defaultCls; changed = true; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index ba3ea23817..6cfffcde6a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -895,7 +896,7 @@ void MWWorld::InventoryStore::updateRechargingItems() enchantmentId); if (!enchantment) { - std::cerr << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); continue; } diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index b8178f7746..9cf8a0fe04 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -1,7 +1,6 @@ #include "livecellref.hpp" -#include - +#include #include #include "../mwbase/environment.hpp" @@ -38,10 +37,9 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Error: failed to load state for local script " << scriptId - << " because an exception has been thrown: " << exception.what() - << std::endl; + << " because an exception has been thrown: " << exception.what(); } } } @@ -51,7 +49,7 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) if (!mRef.getSoul().empty() && !MWBase::Environment::get().getWorld()->getStore().get().search(mRef.getSoul())) { - std::cerr << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem" << std::endl; + Log(Debug::Warning) << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem"; mRef.setSoul(std::string()); } } diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 4ed83bf11d..ff47d3e564 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -1,14 +1,12 @@ #include "localscripts.hpp" -#include +#include #include "esmstore.hpp" #include "cellstore.hpp" - #include "class.hpp" #include "containerstore.hpp" - namespace { @@ -93,7 +91,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) for (std::list >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter) if (iter->second==ptr) { - std::cerr << "Error: tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Error: tried to add local script twice for " << ptr.getCellRef().getRefId(); remove(ptr); break; } @@ -102,15 +100,15 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "failed to add local script " << scriptName - << " because an exception has been thrown: " << exception.what() << std::endl; + << " because an exception has been thrown: " << exception.what(); } } else - std::cerr + Log(Debug::Warning) << "failed to add local script " << scriptName - << " because the script does not exist." << std::endl; + << " because the script does not exist."; } void MWWorld::LocalScripts::addCell (CellStore *cell) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5439447fd0..44b78336d9 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,7 +1,8 @@ #include "player.hpp" #include -#include + +#include #include #include @@ -364,7 +365,7 @@ namespace MWWorld if (!player.mObject.mEnabled) { - std::cerr << "Warning: Savegame attempted to disable the player." << std::endl; + Log(Debug::Warning) << "Warning: Savegame attempted to disable the player."; player.mObject.mEnabled = true; } @@ -391,7 +392,7 @@ namespace MWWorld } catch (...) { - std::cerr << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists" << std::endl; + Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists"; // Cell no longer exists. The loader will have to choose a default cell. mCellStore = NULL; } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index a4a22ea4a3..f150ff0bab 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -1,11 +1,12 @@ #include "projectilemanager.hpp" #include -#include #include #include +#include + #include #include @@ -647,7 +648,7 @@ namespace MWWorld } catch(...) { - std::cerr << "Warning: Failed to recreate magic projectile from saved data (id \"" << state.mSpellId << "\" no longer exists?)" << std::endl; + Log(Debug::Warning) << "Warning: Failed to recreate magic projectile from saved data (id \"" << state.mSpellId << "\" no longer exists?)"; return true; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 2c2d401d18..ed6dde310c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,8 +1,8 @@ #include "scene.hpp" #include -#include +#include #include #include #include @@ -56,7 +56,7 @@ namespace { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { - std::cerr << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice" << std::endl; + Log(Debug::Warning) << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice"; return; } @@ -160,7 +160,7 @@ namespace catch (const std::exception& e) { std::string error ("failed to render '" + ptr.getCellRef().getRefId() + "': "); - std::cerr << error + e.what() << std::endl; + Log(Debug::Error) << error + e.what(); } } @@ -232,7 +232,7 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { - std::cout << "Unloading cell\n"; + Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription(); ListAndResetObjectsVisitor visitor; (*iter)->forEach(visitor); @@ -270,7 +270,7 @@ namespace MWWorld if(result.second) { - std::cout << "Loading cell " << cell->getCell()->getDescription() << std::endl; + Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription(); float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; @@ -546,7 +546,7 @@ namespace MWWorld return; } - std::cout << "Changing to interior\n"; + Log(Debug::Info) << "Changing to interior"; // unload CellStoreCollection::iterator active = mActiveCells.begin(); @@ -624,7 +624,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "failed to render '" << ptr.getCellRef().getRefId() << "': " << e.what() << std::endl; + Log(Debug::Error) << "failed to render '" << ptr.getCellRef().getRefId() << "': " << e.what(); } } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 6f0a1b49f8..2ac78bb85d 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,5 +1,7 @@ #include "store.hpp" +#include + #include #include @@ -8,7 +10,6 @@ #include #include -#include namespace { @@ -692,7 +693,7 @@ namespace MWWorld if (it_lease != wipecell->mLeasedRefs.end()) wipecell->mLeasedRefs.erase(it_lease); else - std::cerr << "Error: can't find " << it->mRefNum.mIndex << " " << it->mRefNum.mContentFile << " in leasedRefs " << std::endl; + Log(Debug::Error) << "Error: can't find " << it->mRefNum.mIndex << " " << it->mRefNum.mContentFile << " in leasedRefs"; } *itold = *it; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f4c2a75f3d..3e4f3ccd0b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -1794,7 +1796,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "Error updating window manager: " << e.what() << std::endl; + Log(Debug::Error) << "Error updating window manager: " << e.what(); } } @@ -3075,7 +3077,7 @@ namespace MWWorld if ( closestMarker.isEmpty() ) { - std::cerr << "Failed to teleport: no closest marker found" << std::endl; + Log(Debug::Warning) << "Failed to teleport: no closest marker found"; return; } @@ -3250,19 +3252,19 @@ namespace MWWorld MWWorld::ConstPtr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); if ( prisonMarker.isEmpty() ) { - std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found."; return; } std::string prisonName = prisonMarker.getCellRef().getDestCell(); if ( prisonName.empty() ) { - std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior"; return; } MWWorld::CellStore *prison = getInterior( prisonName ); if ( !prison ) { - std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: failed to load cell " << prisonName; return; } @@ -3272,7 +3274,7 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); } else - std::cerr << "Failed to confiscate items: no stolen_goods container found" << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: no stolen_goods container found"; } void World::goToJail() From 4cb9b52b8186fe784c355b7273bc0c85c31b75d7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 14 Aug 2018 23:15:30 +0200 Subject: [PATCH 092/175] fix rebase issue --- files/ui/advancedpage.ui | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index b308f21ace..59e46a56c4 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -22,11 +22,7 @@ 0 0 641 -<<<<<<< HEAD 998 -======= - 968 ->>>>>>> Fixed merge conflicts @@ -93,7 +89,6 @@ Enchanted weapons are magical -<<<<<<< HEAD @@ -104,12 +99,19 @@ Barter disposition change is permanent - -======= ->>>>>>> Fixed merge conflicts + + + + <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> + + + Factor strength into hand-to-hand combat + + + From 5d392d3ef5c02fabfaa079deed8de484b4d20a04 Mon Sep 17 00:00:00 2001 From: Diego Crespo Date: Tue, 14 Aug 2018 17:20:19 -0400 Subject: [PATCH 093/175] fixed some spelling mistakes in .rst files --- docs/source/manuals/openmw-cs/index.rst | 2 +- docs/source/manuals/openmw-cs/record-filters.rst | 2 +- docs/source/manuals/openmw-cs/tour.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/manuals/openmw-cs/index.rst b/docs/source/manuals/openmw-cs/index.rst index f124b526f2..ee82907119 100644 --- a/docs/source/manuals/openmw-cs/index.rst +++ b/docs/source/manuals/openmw-cs/index.rst @@ -9,7 +9,7 @@ few chapters to familiarise yourself with the new interface. .. warning:: OpenMW CS is still software in development. The manual does not cover any of - its shortcomings, it is written as if everything was working as inteded. + its shortcomings, it is written as if everything was working as intended. Please report any software problems as bugs in the software, not errors in the manual. diff --git a/docs/source/manuals/openmw-cs/record-filters.rst b/docs/source/manuals/openmw-cs/record-filters.rst index a579d8dd88..19eee2542c 100644 --- a/docs/source/manuals/openmw-cs/record-filters.rst +++ b/docs/source/manuals/openmw-cs/record-filters.rst @@ -121,7 +121,7 @@ existing filters into more complex ones. Scopes ====== -Every default filter has the prefix ``project``. This is a *scpoe*, a mechanism +Every default filter has the prefix ``project``. This is a *scope*, a mechanism that determines the lifetime of the filter. These are the supported scopes: ``project::`` diff --git a/docs/source/manuals/openmw-cs/tour.rst b/docs/source/manuals/openmw-cs/tour.rst index 5d7034deb3..fbd4fd0478 100644 --- a/docs/source/manuals/openmw-cs/tour.rst +++ b/docs/source/manuals/openmw-cs/tour.rst @@ -236,7 +236,7 @@ a negative number indicating that he will restock again to maintain that level. However, it's an attractive item, so he will probably wear it rather than sell it. So set his stock level too high for him to wear them all (3 works, 2 might do). -Another possibilty, again in Seyda Neen making it easy to access, would be for +Another possibility, again in Seyda Neen making it easy to access, would be for Fargoth to give it to the player in exchange for his healing ring. .. figure:: https://gitlab.com/OpenMW/openmw-docs/raw/master/docs/source/manuals/openmw-cs/_static/images/chapter-1/Ring_to_Fargoth_1.png @@ -360,7 +360,7 @@ the base game. "Modified" status will cover items from the base game which have been modified in this addon. Click on the top of the column to toggle between ascending and descending order - thus between "Added" -and "Modified" at the top. Or put your desired modified status into a filter then sort alpabetically +and "Modified" at the top. Or put your desired modified status into a filter then sort alphabetically on a different column. From 37f9d4518295f5e998f817acfced0c35654f402f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 15 Aug 2018 15:56:31 +0300 Subject: [PATCH 094/175] Remove an outdated checkbox from the advanced page --- apps/launcher/advancedpage.cpp | 4 ---- files/ui/advancedpage.ui | 26 -------------------------- 2 files changed, 30 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index b254e0b012..102463085b 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -76,12 +76,10 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); - // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -136,12 +134,10 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); - // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 59e46a56c4..8395d028f6 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -2,14 +2,6 @@ AdvancedPage - - - 0 - 0 - 671 - 373 - - @@ -17,14 +9,6 @@ true - - - 0 - 0 - 641 - 998 - - @@ -102,16 +86,6 @@ - - - - <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> - - - Factor strength into hand-to-hand combat - - - From 68894320301d1027e55ae3d87a5817197f3024c3 Mon Sep 17 00:00:00 2001 From: James Carty Date: Thu, 16 Aug 2018 00:26:02 +0100 Subject: [PATCH 095/175] Move code to seperate functions for reusability --- apps/openmw/mwmechanics/aisequence.cpp | 15 +++++++++++ apps/openmw/mwmechanics/aisequence.hpp | 3 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 27 ++++++++----------- .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 0851748200..d55ba13dd8 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -19,6 +19,7 @@ #include "aicombataction.hpp" #include "aipursue.hpp" #include "actorutil.hpp" +#include "../mwworld/class.hpp" namespace MWMechanics { @@ -122,6 +123,20 @@ bool AiSequence::isInCombat() const return false; } +bool AiSequence::isEngagedWithActor() const +{ + bool isFightingNpc = false; + for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + { + MWWorld::Ptr target2 = (*it)->getTarget(); + if (!target2.isEmpty() && target2.getClass().isNpc()) + isFightingNpc = true; + } + } +} + bool AiSequence::hasPackage(int typeId) const { for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 5c72bcc4ca..4d0482a985 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -88,6 +88,9 @@ namespace MWMechanics /// Is there any combat package? bool isInCombat () const; + /// Are we in combat with any other actor, who's also engaging us? + bool isEngagedWithActor () const; + /// Does this AI sequence have the given package type? bool hasPackage(int typeId) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7814b4a91e..6ef3587906 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1460,24 +1460,11 @@ namespace MWMechanics } } - // Attacking an NPC that is already in combat with any other NPC is not a crime - AiSequence& seq = statsTarget.getAiSequence(); - bool isFightingNpc = false; - for (std::list::const_iterator it = seq.begin(); it != seq.end(); ++it) - { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) - { - MWWorld::Ptr target2 = (*it)->getTarget(); - if (!target2.isEmpty() && target2.getClass().isNpc()) - isFightingNpc = true; - } - } - - if (target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) - && !isAggressive(target, attacker) && !isFightingNpc - && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (canCommitCrimeAgainst(target, attacker)) commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault); + AiSequence& seq = statsTarget.getAiSequence(); + if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == getPlayer()) && !seq.isInCombat(attacker)) @@ -1504,6 +1491,14 @@ namespace MWMechanics return true; } + bool MechanicsManager::canCommitCrimeAgainst(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker) + { + MWMechanics::AiSequence seq = target.getClass().getCreatureStats(target).getAiSequence(); + return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) + && !isAggressive(target, attacker) && !seq.isEngagedWithActor() + && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); + } + void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) { if (attacker.isEmpty() || victim.isEmpty()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 676a75cafb..85d2e65cfb 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -132,6 +132,8 @@ namespace MWMechanics /// @note No-op for non-player attackers virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); + virtual bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); + /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, From 4b0a6074e7b7024aa54a6bd026a88203093af46a Mon Sep 17 00:00:00 2001 From: James Carty Date: Thu, 16 Aug 2018 00:29:14 +0100 Subject: [PATCH 096/175] Add comment --- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 85d2e65cfb..8c8a9e68f9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -132,6 +132,10 @@ namespace MWMechanics /// @note No-op for non-player attackers virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); + /// Checks if commiting a crime is currently valid + /// @param victim The actor being attacked + /// @param attacker The actor commiting the crime + /// @return true if the victim is a valid target for crime virtual bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Utility to check if taking this item is illegal and calling commitCrime if so From 8a48258b1bd407bcb381e0853163f7c87594fe88 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Aug 2018 14:16:19 +0400 Subject: [PATCH 097/175] Fix attack after shoot --- apps/openmw/mwmechanics/character.cpp | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6f9cb941d8..594a8c9d0d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1385,14 +1385,6 @@ bool CharacterController::updateWeaponState() MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; - // Randomize attacks for non-bipedal creatures with Weapon flag - if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && - !mPtr.getClass().isBipedal(mPtr) && - (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) - { - mCurrentWeapon = chooseRandomAttackAnimation(); - } - if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would @@ -1401,15 +1393,10 @@ bool CharacterController::updateWeaponState() if (mPtr == player) { MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); - } - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - - // For the player, set the spell we want to cast - // This has to be done at the start of the casting animation, - // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) - if (mPtr == player) - { + // For the player, set the spell we want to cast + // This has to be done at the start of the casting animation, + // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); stats.getSpells().setSelectedSpell(selectedSpell); } @@ -1421,6 +1408,7 @@ bool CharacterController::updateWeaponState() MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.get().find(spellid); const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back(); const ESM::MagicEffect *effect; @@ -1520,13 +1508,12 @@ bool CharacterController::updateWeaponState() startKey = mAttackType+" start"; stopKey = mAttackType+" min attack"; } - - if (isRandomAttackAnimation(mCurrentWeapon)) + else if (isRandomAttackAnimation(mCurrentWeapon)) { startKey = "start"; stopKey = "stop"; } - else if (mAttackType != "shoot") + else { if(mPtr == getPlayer()) { From e444b9581c57884e604a2e882bb79b3e07921f06 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Aug 2018 17:33:27 +0400 Subject: [PATCH 098/175] Do not play min attack -> max attack animation when attack strength is 0 (bug #4591) --- apps/openmw/mwmechanics/character.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 594a8c9d0d..1748e3d458 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1670,11 +1670,6 @@ bool CharacterController::updateWeaponState() std::string start, stop; switch(mUpperBodyState) { - case UpperCharState_StartToMinAttack: - start = mAttackType+" min attack"; - stop = mAttackType+" max attack"; - mUpperBodyState = UpperCharState_MinAttackToMaxAttack; - break; case UpperCharState_MinAttackToMaxAttack: //hack to avoid body pos desync when jumping/sneaking in 'max attack' state if(!mAnimation->isPlaying(mCurrentWeapon)) @@ -1682,6 +1677,23 @@ bool CharacterController::updateWeaponState() MWRender::Animation::BlendMask_All, false, 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); break; + case UpperCharState_StartToMinAttack: + { + // If actor is already stopped prepairing 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. + float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack"); + float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack"); + if (mAttackingOrSpell || minAttackTime == maxAttackTime) + { + start = mAttackType+" min attack"; + stop = mAttackType+" max attack"; + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + break; + } + playSwishSound(0.0f); + } + // Fall-through case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { From 15fa47827b5e2c053c1b4830cc31a58fafb5818f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Aug 2018 19:05:57 +0400 Subject: [PATCH 099/175] AiCombat: Avoid jittering when aiming in melee --- apps/openmw/mwmechanics/aicombat.cpp | 27 +++++++++++++++++++-------- apps/openmw/mwmechanics/aicombat.hpp | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index cd97098854..438f4f7c69 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -372,21 +372,32 @@ namespace MWMechanics actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1]; actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2]; - rotateActorOnAxis(actor, 2, actorMovementSettings, storage.mMovement); - rotateActorOnAxis(actor, 0, actorMovementSettings, storage.mMovement); + rotateActorOnAxis(actor, 2, actorMovementSettings, storage); + rotateActorOnAxis(actor, 0, actorMovementSettings, storage); } void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, - MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement) + MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage) { actorMovementSettings.mRotation[axis] = 0; - float& targetAngleRadians = desiredMovement.mRotation[axis]; + float& targetAngleRadians = storage.mMovement.mRotation[axis]; if (targetAngleRadians != 0) { - if (smoothTurn(actor, targetAngleRadians, axis)) + // Some attack animations contain small amount of movement. + // Since we use cone shapes for melee, we can use a threshold to avoid jittering + std::shared_ptr& currentAction = storage.mCurrentAction; + bool isRangedCombat = false; + currentAction->getCombatRange(isRangedCombat); + // Check if the actor now facing desired direction, no need to turn any more + if (isRangedCombat) { - // actor now facing desired direction, no need to turn any more - targetAngleRadians = 0; + if (smoothTurn(actor, targetAngleRadians, axis)) + targetAngleRadians = 0; + } + else + { + if (smoothTurn(actor, targetAngleRadians, axis, osg::DegreesToRadians(3.f))) + targetAngleRadians = 0; } } } @@ -453,7 +464,7 @@ namespace MWMechanics if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) { mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right - mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); + mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 7c9891bcc2..88feba4811 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -129,7 +129,7 @@ namespace MWMechanics /// Transfer desired movement (from AiCombatStorage) to Actor void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, - MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement); + MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage); }; From 3a6c480d414ed51bfa3cc80acfe3aa1cdd57545e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Aug 2018 19:10:15 +0400 Subject: [PATCH 100/175] Do not reset idle animations when turning --- apps/openmw/mwmechanics/character.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 1748e3d458..dd7c15ff6c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -411,8 +411,8 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character if(force || movement != mMovementState) { mMovementState = movement; - - if (movement != CharState_None) + // Turning animations should not interrupt idle ones + if (movement != CharState_None && !isTurning()) mIdleState = CharState_None; std::string movementAnimName; From 66a8402cdf3a3eb443a4609d13ec5e147d1cbb3c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 15 Aug 2018 23:12:38 +0400 Subject: [PATCH 101/175] Add missing changelog entries --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8eef0d45e..54a1a8ea74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Bug #4230: AiTravel package issues break some Tribunal quests Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat + Bug #4271: Scamp flickers when attacking Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted Bug #4291: Non-persistent actors that started the game as dead do not play death animations @@ -92,6 +93,7 @@ Bug #4574: Player turning animations are twitchy Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started + Bug #4591: Attack strength should be 0 if player did not hold the attack button Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade From b7a448cf42833c573612d532547380cede021625 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Aug 2018 11:21:48 +0400 Subject: [PATCH 102/175] Update idle animations after reset of mIdleState (bug #4531) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54a1a8ea74..8d9e9f97a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode + Bug #4531: Movement does not reset idle animations Bug #4539: Paper Doll is affected by GUI scaling Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index dd7c15ff6c..855285b07d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -411,10 +411,6 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character if(force || movement != mMovementState) { mMovementState = movement; - // Turning animations should not interrupt idle ones - if (movement != CharState_None && !isTurning()) - mIdleState = CharState_None; - std::string movementAnimName; MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); @@ -531,7 +527,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) { - if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) + if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) { mIdleState = idle; size_t numLoops = ~0ul; @@ -562,10 +558,12 @@ void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterStat // play until the Loop Stop key 2 to 5 times, then play until the Stop key // this replicates original engine behavior for the "Idle1h" 1st-person animation numLoops = 1 + Misc::Rng::rollDice(4); - } + } } - mAnimation->disable(mCurrentIdle); + if(!mCurrentIdle.empty()) + mAnimation->disable(mCurrentIdle); + mCurrentIdle = idleGroup; if(!mCurrentIdle.empty()) mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false, @@ -2093,7 +2091,15 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { - idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle)); + // Note: turning animations should not interrupt idle ones + if (inwater) + idlestate = CharState_IdleSwim; + else if (sneak && !inJump) + idlestate = CharState_IdleSneak; + else if (movestate != CharState_None && !isTurning()) + idlestate = CharState_None; + else + idlestate = CharState_Idle; } else updateAnimQueue(); From a73d42e711e68b759cc81d51543cbb791209ff73 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Aug 2018 14:37:18 +0400 Subject: [PATCH 103/175] Do not stop idle animation in spellcasting stance --- apps/openmw/mwmechanics/character.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 855285b07d..bdbf204c09 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -561,13 +561,21 @@ void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterStat } } + // There is no need to restart anim if the new and old anims are the same. + // Just update a number of loops. + float startPoint = 0; + if (!mCurrentIdle.empty() && mCurrentIdle == idleGroup) + { + mAnimation->getInfo(mCurrentIdle, &startPoint); + } + if(!mCurrentIdle.empty()) mAnimation->disable(mCurrentIdle); mCurrentIdle = idleGroup; if(!mCurrentIdle.empty()) mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false, - 1.0f, "start", "stop", 0.0f, numLoops, true); + 1.0f, "start", "stop", startPoint, numLoops, true); } } @@ -2091,12 +2099,13 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { - // Note: turning animations should not interrupt idle ones + // Note: turning animations should not interrupt idle ones. + // Also movement should not stop idle animation for spellcasting stance. if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning()) + else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell) idlestate = CharState_None; else idlestate = CharState_Idle; From 16edac8c479660ab511bf78e3653a883c2c2ef97 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 16 Aug 2018 17:47:06 +0400 Subject: [PATCH 104/175] Fix incorrect 'preparing' word spelling --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/actors.cpp | 4 ++-- apps/openmw/mwmechanics/actors.hpp | 2 +- apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 995c8d736c..fe3fc5721b 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -257,7 +257,7 @@ namespace MWBase virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0; virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0; - virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr) = 0; + virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 709c611963..65fc0c0983 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1004,9 +1004,9 @@ namespace MWInput if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"]) return; - // We want to interrupt animation only if attack is prepairing, but still is not triggered + // We want to interrupt animation only if attack is preparing, but still is not triggered // Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice - if (MWBase::Environment::get().getMechanicsManager()->isAttackPrepairing(mPlayer->getPlayer())) + if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(mPlayer->getPlayer())) mPlayer->setAttackingOrSpell(false); else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer())) return; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6a605c056f..e794e0aaa4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -897,14 +897,14 @@ namespace MWMechanics } } - bool Actors::isAttackPrepairing(const MWWorld::Ptr& ptr) + bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr) { PtrActorMap::iterator it = mActors.find(ptr); if (it == mActors.end()) return false; CharacterController* ctrl = it->second->getCharacterController(); - return ctrl->isAttackPrepairing(); + return ctrl->isAttackPreparing(); } bool Actors::isRunning(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 492ff1e2ea..8c94ce45f2 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -118,7 +118,7 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. - bool isAttackPrepairing(const MWWorld::Ptr& ptr); + bool isAttackPreparing(const MWWorld::Ptr& ptr); bool isRunning(const MWWorld::Ptr& ptr); bool isSneaking(const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index bdbf204c09..c7c6b57d0c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1685,7 +1685,7 @@ bool CharacterController::updateWeaponState() break; case UpperCharState_StartToMinAttack: { - // If actor is already stopped prepairing attack, do not play the "min attack -> max attack" part. + // 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. float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack"); @@ -2507,7 +2507,7 @@ bool CharacterController::isRandomAttackAnimation(const std::string& group) cons group == "attack3" || group == "swimattack3"); } -bool CharacterController::isAttackPrepairing() const +bool CharacterController::isAttackPreparing() const { return mUpperBodyState == UpperCharState_StartToMinAttack || mUpperBodyState == UpperCharState_MinAttackToMaxAttack; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 631f78208a..43d26e52f3 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -281,7 +281,7 @@ public: void forceStateUpdate(); - bool isAttackPrepairing() const; + bool isAttackPreparing() const; bool isCastingSpell() const; bool isReadyToBlock() const; bool isKnockedDown() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7814b4a91e..dbc2cd93cd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -435,9 +435,9 @@ namespace MWMechanics return mActors.isActorDetected(actor, observer); } - bool MechanicsManager::isAttackPrepairing(const MWWorld::Ptr& ptr) + bool MechanicsManager::isAttackPreparing(const MWWorld::Ptr& ptr) { - return mActors.isAttackPrepairing(ptr); + return mActors.isAttackPreparing(ptr); } bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 676a75cafb..e9ec14685d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -223,7 +223,7 @@ namespace MWMechanics virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count); - virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr); + virtual bool isAttackPreparing(const MWWorld::Ptr& ptr); virtual bool isRunning(const MWWorld::Ptr& ptr); virtual bool isSneaking(const MWWorld::Ptr& ptr); From 43f1c9163cc254db8fd390604cbaa77a7edb9dfe Mon Sep 17 00:00:00 2001 From: James Carty Date: Thu, 16 Aug 2018 18:58:51 +0100 Subject: [PATCH 105/175] Fix issue in which murder wouldn't be reported after paying fine --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6ef3587906..a46204e668 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1511,11 +1511,10 @@ namespace MWMechanics return; // TODO: implement animal rights const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim); - if (victimStats.getCrimeId() == -1) - return; + const MWWorld::Ptr &player = getPlayer(); + bool canCommit = attacker == player && canCommitCrimeAgainst(attacker, victim); // For now we report only about crimes of player and player's followers - const MWWorld::Ptr &player = getPlayer(); if (attacker != player) { std::set playerFollowers; @@ -1524,6 +1523,9 @@ namespace MWMechanics return; } + if (!canCommit && victimStats.getCrimeId() == -1) + return; + // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. From 372697489bf3af4c636c59c6f6654fb6424c033e Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 17 Aug 2018 02:41:06 +0300 Subject: [PATCH 106/175] Fix Equip command infinite loop (bug #3072) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8eef0d45e..7fb19f2763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue + Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ce6d0d522c..0b35bd9de4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -517,7 +517,7 @@ namespace MWGui // Give the script a chance to run once before we do anything else // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) - if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) + if (!force && !script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) { MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); From ce78a34010f129720c44d04263b36088b8d5a7f1 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 17 Aug 2018 03:32:33 +0300 Subject: [PATCH 107/175] Use container ID in Equip command warning --- apps/openmw/mwscript/containerextensions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index dd3e54a88a..cf12d12c3a 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -192,8 +192,9 @@ namespace MWScript if (it == invStore.end()) { it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr); - Log(Debug::Warning) << "Implicitly adding one " << item << " to container " - "to fulfil requirements of Equip instruction"; + Log(Debug::Warning) << "Implicitly adding one " << item << + " to the inventory store of " << ptr.getCellRef().getRefId() << + " to fulfill the requirements of Equip instruction"; } if (ptr == MWMechanics::getPlayer()) From a492978fe677e0a14e8ff806ee12f96ef704be58 Mon Sep 17 00:00:00 2001 From: David Walley Date: Fri, 17 Aug 2018 08:23:47 +0000 Subject: [PATCH 108/175] Update AUTHORS.md - add Loriel (Documentation) --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index a722ff7237..7252e39304 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -181,6 +181,7 @@ Documentation Alejandro Sanchez (HiPhish) Bodillium Bret Curtis (psi29a) + David Walley (Loriel) Cramal Ryan Tucker (Ravenwing) sir_herrbatka From 8516aee6e0a69d59569218dff74808b83b5e3418 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 16 Jun 2018 10:18:04 +0400 Subject: [PATCH 109/175] Implement getHalfExtents() for non-actor objects --- apps/openmw/mwrender/renderingmanager.cpp | 24 +++++++++++++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 10 +++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e17fe3191e..ce0a41c9a0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -49,6 +49,7 @@ #include #include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" #include "../mwgui/loadingscreen.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -1321,6 +1322,29 @@ namespace MWRender } } + osg::Vec3f RenderingManager::getHalfExtents(const MWWorld::ConstPtr& object) const + { + osg::Vec3f halfExtents(0, 0, 0); + std::string modelName = object.getClass().getModel(object); + if (modelName.empty()) + return halfExtents; + + osg::ref_ptr node = mResourceSystem->getSceneManager()->getTemplate(modelName); + osg::ComputeBoundsVisitor computeBoundsVisitor; + computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); + const_cast(node.get())->accept(computeBoundsVisitor); + osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); + + if (bounds.valid()) + { + halfExtents[0] = std::abs(bounds.xMax() - bounds.xMin()) / 2.f; + halfExtents[1] = std::abs(bounds.yMax() - bounds.yMin()) / 2.f; + halfExtents[2] = std::abs(bounds.zMax() - bounds.zMin()) / 2.f; + } + + return halfExtents; + } + void RenderingManager::resetFieldOfView() { if (mFieldOfViewOverridden == true) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f8e7ba8bcc..b4473c3b4f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -203,6 +203,8 @@ namespace MWRender /// reset a previous overrideFieldOfView() call, i.e. revert to field of view specified in the settings file. void resetFieldOfView(); + osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& object) const; + void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format); LandManager* getLandManager() const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bf829225a5..f9237761d9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3339,12 +3339,16 @@ namespace MWWorld return mRendering->getTerrainHeightAt(worldPos); } - osg::Vec3f World::getHalfExtents(const ConstPtr& actor, bool rendering) const + osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const { + if (!object.getClass().isActor()) + return mRendering->getHalfExtents(object); + + // Handle actors separately because of bodyparts if (rendering) - return mPhysics->getRenderingHalfExtents(actor); + return mPhysics->getRenderingHalfExtents(object); else - return mPhysics->getHalfExtents(actor); + return mPhysics->getHalfExtents(object); } std::string World::exportSceneGraph(const Ptr &ptr) From 31f8bea1dd6e0a93b130b472fa83f1076f41d8a5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 15 Jul 2018 12:44:25 +0400 Subject: [PATCH 110/175] Rework spell effects management --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 76 ++++++-- apps/openmw/mwrender/animation.cpp | 223 ++++++++++++++++++----- apps/openmw/mwrender/animation.hpp | 45 +++-- apps/openmw/mwscript/miscextensions.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 4 +- apps/openmw/mwworld/worldimp.hpp | 2 +- 8 files changed, 273 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c016ca82f3..9efdf02607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ Bug #4574: Player turning animations are twitchy Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started + Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ee1227e0c0..3c46298b0b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -536,7 +536,7 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0; - virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0; + virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0; virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b337fa6b76..76140013d4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -24,6 +24,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwrender/animation.hpp" +#include "../mwrender/vismask.hpp" #include "npcstats.hpp" #include "actorutil.hpp" @@ -327,16 +328,19 @@ namespace MWMechanics } void CastSpell::launchMagicBolt () - { - osg::Vec3f fallbackDirection (0,1,0); + { + osg::Vec3f fallbackDirection(0, 1, 0); + osg::Vec3f offset(0, 0, 0); + if (!mTarget.isEmpty() && mTarget.getClass().isActor()) + offset.z() = MWBase::Environment::get().getWorld()->getHalfExtents(mTarget).z(); // Fall back to a "caster to target" direction if we have no other means of determining it // (e.g. when cast by a non-actor) if (!mTarget.isEmpty()) fallbackDirection = - osg::Vec3f(mTarget.getRefData().getPosition().asVec3())- - osg::Vec3f(mCaster.getRefData().getPosition().asVec3()); - + (mTarget.getRefData().getPosition().asVec3() + offset) - + (mCaster.getRefData().getPosition().asVec3()); + MWBase::Environment::get().getWorld()->launchMagicBolt(mId, mCaster, fallbackDirection); } @@ -999,11 +1003,13 @@ namespace MWMechanics return true; } - void CastSpell::playSpellCastingEffects(const std::string &spellid){ - + void CastSpell::playSpellCastingEffects(const std::string &spellid) + { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.get().find(spellid); + std::vector addedEffects; + for (std::vector::const_iterator iter = spell->mEffects.mList.begin(); iter != spell->mEffects.mList.end(); ++iter) { @@ -1012,18 +1018,56 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); - if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL) + const ESM::Static* castStatic; + + if (!effect->mCasting.empty()) + castStatic = store.get().find (effect->mCasting); + else + castStatic = store.get().find ("VFX_DefaultCast"); + + // check if the effect was already added + if (std::find(addedEffects.begin(), addedEffects.end(), "meshes\\" + castStatic->mModel) != addedEffects.end()) + continue; + + std::string texture = effect->mParticle; + + float scale = 1.0f; + osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3()); + + if (animation && mCaster.getClass().isNpc()) { - const ESM::Static* castStatic; + // For NPC we should take race height as scaling factor + const ESM::NPC *npc = mCaster.get()->mBase; + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); - if (!effect->mCasting.empty()) - castStatic = store.get().find (effect->mCasting); - else - castStatic = store.get().find ("VFX_DefaultCast"); + const ESM::Race *race = + esmStore.get().find(npc->mRace); - std::string texture = effect->mParticle; + scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale; + } + else + { + osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(mCaster); - animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); + // TODO: take a size of particle or NPC with height and weight = 1.0 as scale = 1.0 + float scaleX = halfExtents.x() * 2 / 60.f; + float scaleY = halfExtents.y() * 2 / 60.f; + float scaleZ = halfExtents.z() * 2 / 120.f; + + scale = std::max({ scaleX, scaleY, scaleZ }); + } + + // If the caster has no animation, add the effect directly to the effectManager + if (animation) + { + animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale); + } + else + { + // We should set scale for effect manager manually + float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f; + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale); } if (animation && !mCaster.getClass().isActor()) @@ -1033,6 +1077,8 @@ namespace MWMechanics "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; + addedEffects.push_back("meshes\\" + castStatic->mModel); + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!effect->mCastSound.empty()) sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8e1105b9f1..4da3ec4a8e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -204,6 +205,110 @@ namespace std::vector > mToRemove; }; + class RemoveFinishedCallbackVisitor : public RemoveVisitor + { + public: + RemoveFinishedCallbackVisitor() + : RemoveVisitor() + , mEffectId(-1) + { + } + + RemoveFinishedCallbackVisitor(int effectId) + : RemoveVisitor() + , mEffectId(effectId) + { + } + + virtual void apply(osg::Node &node) + { + traverse(node); + } + + virtual void apply(osg::Group &group) + { + traverse(group); + + osg::Callback* callback = group.getUpdateCallback(); + if (callback) + { + // We should remove empty transformation nodes and finished callbacks here + MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); + bool finished = vfxCallback && vfxCallback->mFinished; + bool toRemove = vfxCallback && mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; + if (finished || toRemove) + { + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + } + } + } + + virtual void apply(osg::MatrixTransform &node) + { + traverse(node); + } + + virtual void apply(osg::Geometry&) + { + } + + private: + int mEffectId; + }; + + class FindVfxCallbacksVisitor : public osg::NodeVisitor + { + public: + + std::vector mCallbacks; + + FindVfxCallbacksVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mEffectId(-1) + { + } + + FindVfxCallbacksVisitor(int effectId) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mEffectId(effectId) + { + } + + virtual void apply(osg::Node &node) + { + traverse(node); + } + + virtual void apply(osg::Group &group) + { + osg::Callback* callback = group.getUpdateCallback(); + if (callback) + { + MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); + if (vfxCallback) + { + if (mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId) + { + mCallbacks.push_back(vfxCallback); + } + } + } + traverse(group); + } + + virtual void apply(osg::MatrixTransform &node) + { + traverse(node); + } + + virtual void apply(osg::Geometry&) + { + } + + private: + int mEffectId; + }; + // Removes all drawables from a graph. class CleanObjectRootVisitor : public RemoveVisitor { @@ -287,7 +392,6 @@ namespace } } }; - } namespace MWRender @@ -432,6 +536,42 @@ namespace MWRender const std::multimap& getTextKeys() const; }; + void UpdateVfxCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) + { + traverse(node, nv); + + if (mFinished) + return; + + double newTime = nv->getFrameStamp()->getSimulationTime(); + if (mStartingTime == 0) + { + mStartingTime = newTime; + return; + } + + double duration = newTime - mStartingTime; + mStartingTime = newTime; + + mParams.mAnimTime->addTime(duration); + if (mParams.mAnimTime->getTime() >= mParams.mMaxControllerLength) + { + if (mParams.mLoop) + { + // Start from the beginning again; carry over the remainder + // Not sure if this is actually needed, the controller function might already handle loops + float remainder = mParams.mAnimTime->getTime() - mParams.mMaxControllerLength; + mParams.mAnimTime->resetTime(remainder); + } + else + { + // Remove effect immediately + mParams.mObjects.reset(); + mFinished = true; + } + } + } + class ResetAccumRootCallback : public osg::NodeCallback { public: @@ -1436,15 +1576,22 @@ namespace MWRender useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue); } - void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) + void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture, float scale) { if (!mObjectRoot.get()) return; // Early out if we already have this effect - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename) + FindVfxCallbacksVisitor visitor(effectId); + mInsert->accept(visitor); + + for (std::vector::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end(); ++it) + { + UpdateVfxCallback* callback = *it; + + if (loop && !callback->mFinished && callback->mParams.mLoop && callback->mParams.mBoneName == bonename) return; + } EffectParams params; params.mModelName = model; @@ -1459,83 +1606,64 @@ namespace MWRender parentNode = found->second; } - osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model, parentNode); + osg::ref_ptr trans = new osg::PositionAttitudeTransform; + trans->setScale(osg::Vec3f(scale, scale, scale)); + parentNode->addChild(trans); + + osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model, trans); node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - params.mObjects = PartHolderPtr(new PartHolder(node)); - SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor; node->accept(findMaxLengthVisitor); // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; node->accept(disableFreezeOnCullVisitor); - - params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); - node->setNodeMask(Mask_Effect); + params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; - + params.mObjects = PartHolderPtr(new PartHolder(node)); params.mAnimTime = std::shared_ptr(new EffectAnimationTime); + trans->addUpdateCallback(new UpdateVfxCallback(params)); SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr(params.mAnimTime)); node->accept(assignVisitor); overrideFirstRootTexture(texture, mResourceSystem, node); - - // TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box. - - mEffects.push_back(params); } void Animation::removeEffect(int effectId) { - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - { - if (it->mEffectId == effectId) - { - mEffects.erase(it); - return; - } - } + RemoveFinishedCallbackVisitor visitor(effectId); + mInsert->accept(visitor); + visitor.remove(); } void Animation::getLoopingEffects(std::vector &out) const { - for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) + FindVfxCallbacksVisitor visitor; + mInsert->accept(visitor); + + for (std::vector::iterator it = visitor.mCallbacks.begin(); it != visitor.mCallbacks.end(); ++it) { - if (it->mLoop) - out.push_back(it->mEffectId); + UpdateVfxCallback* callback = *it; + + if (callback->mParams.mLoop && !callback->mFinished) + out.push_back(callback->mParams.mEffectId); } } void Animation::updateEffects(float duration) { - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) - { - it->mAnimTime->addTime(duration); - - if (it->mAnimTime->getTime() >= it->mMaxControllerLength) - { - if (it->mLoop) - { - // Start from the beginning again; carry over the remainder - // Not sure if this is actually needed, the controller function might already handle loops - float remainder = it->mAnimTime->getTime() - it->mMaxControllerLength; - it->mAnimTime->resetTime(remainder); - } - else - { - it = mEffects.erase(it); - continue; - } - } - ++it; - } + // TODO: objects without animation still will have + // transformation nodes with finished callbacks + RemoveFinishedCallbackVisitor visitor; + mInsert->accept(visitor); + visitor.remove(); } bool Animation::upperBodyReady() const @@ -1778,5 +1906,4 @@ namespace MWRender mNode->getParent(0)->removeChild(mNode); } } - } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 43a6268994..1da1fe4efb 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -71,6 +71,17 @@ private: }; typedef std::shared_ptr PartHolderPtr; +struct EffectParams +{ + std::string mModelName; // Just here so we don't add the same effect twice + PartHolderPtr mObjects; + std::shared_ptr mAnimTime; + float mMaxControllerLength; + int mEffectId; + bool mLoop; + std::string mBoneName; +}; + class Animation : public osg::Referenced { public: @@ -247,19 +258,6 @@ protected: osg::Vec3f mAccumulate; - struct EffectParams - { - std::string mModelName; // Just here so we don't add the same effect twice - PartHolderPtr mObjects; - std::shared_ptr mAnimTime; - float mMaxControllerLength; - int mEffectId; - bool mLoop; - std::string mBoneName; - }; - - std::vector mEffects; - TextKeyListener* mTextKeyListener; osg::ref_ptr mHeadController; @@ -369,7 +367,7 @@ public: * @param texture override the texture specified in the model's materials - if empty, do not override * @note Will not add an effect twice. */ - void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = ""); + void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f); void removeEffect (int effectId); void getLoopingEffects (std::vector& out) const; @@ -489,5 +487,24 @@ public: ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight); }; +class UpdateVfxCallback : public osg::NodeCallback +{ +public: + UpdateVfxCallback(EffectParams& params) + : mFinished(false) + , mParams(params) + , mStartingTime(0) + { + } + + bool mFinished; + EffectParams mParams; + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + +private: + double mStartingTime; +}; + } #endif diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 7da1a48333..3d1978d625 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1082,6 +1082,7 @@ namespace MWScript MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWMechanics::CastSpell cast(ptr, target, false, true); + cast.playSpellCastingEffects(spell->mId); cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f9237761d9..c2ebac8fc5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3407,9 +3407,9 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false); } - void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos) + void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos, float scale, bool isMagicVFX) { - mRendering->spawnEffect(model, textureOverride, worldPos); + mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX); } void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType, diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2352fd31cb..432991059a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -646,7 +646,7 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override; - void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) override; + void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override; void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName, From d7ca087f592eb90e9dfbeeb779ad04bc00b9bd3e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 16 Jul 2018 11:23:39 +0400 Subject: [PATCH 111/175] AiCast: fix aiming --- apps/openmw/mwmechanics/aicast.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index 48cb17f6d7..948ffb3aac 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -42,7 +42,19 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac return false; } - osg::Vec3f dir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3(); + osg::Vec3f targetPos = target.getRefData().getPosition().asVec3(); + if (target.getClass().isActor()) + { + osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target); + targetPos.z() += halfExtents.z() * 2 * 0.75f; + } + + osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); + osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + actorPos.z() += halfExtents.z() * 2 * 0.75f; + + osg::Vec3f dir = targetPos - actorPos; + bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f)); turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f)); From 76932ebfc5447f8f0dca05dffe3a90844b1c42e0 Mon Sep 17 00:00:00 2001 From: Diego Crespo Date: Fri, 17 Aug 2018 08:39:50 -0400 Subject: [PATCH 112/175] Updated AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d2fd051809..9fc092c8e1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -183,6 +183,7 @@ Documentation Cramal Ryan Tucker (Ravenwing) sir_herrbatka + Diego Crespo Packagers --------- From 331016c782a3ac8582919165f79a6898df09ff49 Mon Sep 17 00:00:00 2001 From: James Carty Date: Fri, 17 Aug 2018 13:48:02 +0100 Subject: [PATCH 113/175] Update Authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d2fd051809..bc22b230f8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -172,6 +172,7 @@ Programmers Vincent Heuken vocollapse zelurker + James Carty (MrTopCat) Documentation ------------- From 99b465eede0a76058e5fdd498b9c079d6a89a4a2 Mon Sep 17 00:00:00 2001 From: James Carty Date: Fri, 17 Aug 2018 13:54:58 +0100 Subject: [PATCH 114/175] Update authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d2fd051809..bc22b230f8 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -172,6 +172,7 @@ Programmers Vincent Heuken vocollapse zelurker + James Carty (MrTopCat) Documentation ------------- From 513e99148e135fbf60309db718b3853d12898918 Mon Sep 17 00:00:00 2001 From: James Carty Date: Fri, 17 Aug 2018 20:17:26 +0100 Subject: [PATCH 115/175] Fix function with no return value --- apps/openmw/mwmechanics/aisequence.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index d55ba13dd8..9a42168d63 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -125,16 +125,16 @@ bool AiSequence::isInCombat() const bool AiSequence::isEngagedWithActor() const { - bool isFightingNpc = false; for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) { if ((*it)->getTypeId() == AiPackage::TypeIdCombat) { MWWorld::Ptr target2 = (*it)->getTarget(); if (!target2.isEmpty() && target2.getClass().isNpc()) - isFightingNpc = true; + return true; } } + return false; } bool AiSequence::hasPackage(int typeId) const From 4489838ea386f16975646c1366ef3025b69469e3 Mon Sep 17 00:00:00 2001 From: James Carty Date: Fri, 17 Aug 2018 22:35:04 +0100 Subject: [PATCH 116/175] Fix incorrect function call --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a46204e668..42e46016a5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1512,7 +1512,7 @@ namespace MWMechanics const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim); const MWWorld::Ptr &player = getPlayer(); - bool canCommit = attacker == player && canCommitCrimeAgainst(attacker, victim); + bool canCommit = attacker == player && canCommitCrimeAgainst(victim, attacker); // For now we report only about crimes of player and player's followers if (attacker != player) From a06c9c767d1beb2cc1222e3d2ed61a4c8f2cf4e4 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sun, 19 Aug 2018 18:36:43 +0300 Subject: [PATCH 117/175] Treat <> and << operators as < and >< and >> as > in scripts (bug #4597) --- CHANGELOG.md | 1 + components/compiler/scanner.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78eb..4b573766a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button + Bug #4597: <> operator causes a compile error Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index bb0fb93740..f159b8b8bd 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -502,6 +502,11 @@ namespace Compiler if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( putback (c); } + else if (c == '<' || c == '>') // Treat <> and << as < + { + special = S_cmpLT; + mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc); + } else { putback (c); @@ -525,6 +530,11 @@ namespace Compiler if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( putback (c); } + else if (c == '<' || c == '>') // Treat >< and >> as > + { + special = S_cmpGT; + mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc); + } else { putback (c); From e1afa1c2c38a5b18e4fd493b7b9682569aab7792 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Mon, 20 Aug 2018 12:49:26 -0500 Subject: [PATCH 118/175] Adding common problems that were previous on the site FAQ The [OpenMW FAQ](https://openmw.org/faq/) currently has two problems on it: a black screen and pink textures due to ST3C issues. Given that these are less common now, and that we have a dedicated space for "common problems" anyway, I figure that we should just move this to these questions to there. --- .../manuals/installation/common-problems.rst | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/source/manuals/installation/common-problems.rst b/docs/source/manuals/installation/common-problems.rst index 543b8ec9b9..a44f2d5813 100644 --- a/docs/source/manuals/installation/common-problems.rst +++ b/docs/source/manuals/installation/common-problems.rst @@ -47,4 +47,33 @@ Installing game files via Steam on macOS: DISK WRITE ERROR [other entries] } - Restart the Steam client. The download should now proceed. \ No newline at end of file + Restart the Steam client. The download should now proceed. + +In-game textures show up as pink +################################ + +:Symptoms: + Some textures don't show up and are replaced by pink "filler" textures. + +:Cause: + Textures shipped with Morrowind are compressed with S3TC, a texture compression algorithm that was patented in + the United States until October 2017. Software drivers and operating system distributions released before that date + may not include support for this algorithm. + +:Fix: + Upgrade your graphics drivers and/or operating system the latest version. + +Music plays, but only a black screen is shown +############################################# + +:Symptoms: + When launching OpenMW, audio is heard but there is only a black screen. + +:Cause: + This can occur when you did not import the Morrowind.ini file when the launcher asked for it. + +:Fix: + To fix it, you need to delete the `launcher.cfg` file from your configuration path + ([Path description on Wiki](https://wiki.openmw.org/index.php?title=Paths)), then start the launcher, select your + Morrowind installation, and when the launcher asks whether the `Morrowind.ini` file should be imported, make sure + to select "Yes". \ No newline at end of file From 74229490e44e74f9b59cd49f02895091acfce825 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 20 Aug 2018 21:38:57 +0300 Subject: [PATCH 119/175] Use the correct skill for creature AI weapon hit chance rating --- apps/openmw/mwmechanics/weaponpriority.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 26c784c4d3..9030d6254a 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -108,11 +108,19 @@ namespace MWMechanics } } - int skill = item.getClass().getEquipmentSkill(item); - if (skill != -1) + if (actor.getClass().isNpc()) { - int value = actor.getClass().getSkill(actor, skill); - rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; + int skill = item.getClass().getEquipmentSkill(item); + if (skill != -1) + { + int value = actor.getClass().getSkill(actor, skill); + rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; + } + } + else + { + MWWorld::LiveCellRef *ref = actor.get(); + rating *= MWMechanics::getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; } return rating * rangedMult; From 9b10fe0edb0bc940eea8addecacc696b7f6b2869 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Tue, 21 Aug 2018 07:03:55 -0500 Subject: [PATCH 120/175] Addiong missing "to" word --- docs/source/manuals/installation/common-problems.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/manuals/installation/common-problems.rst b/docs/source/manuals/installation/common-problems.rst index a44f2d5813..0967ea9eff 100644 --- a/docs/source/manuals/installation/common-problems.rst +++ b/docs/source/manuals/installation/common-problems.rst @@ -61,7 +61,7 @@ In-game textures show up as pink may not include support for this algorithm. :Fix: - Upgrade your graphics drivers and/or operating system the latest version. + Upgrade your graphics drivers and/or operating system to the latest version. Music plays, but only a black screen is shown ############################################# From b77d733c3eb50623e8f7ab0407b6feca1be16e3c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 21 Aug 2018 16:47:29 +0300 Subject: [PATCH 121/175] Fix freeze in getActorsSidingWith --- apps/openmw/mwmechanics/actors.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6f5b4eeb8d..c28f6df28d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1790,14 +1790,13 @@ namespace MWMechanics // Actors that are targeted by this actor's Follow or Escort packages also side with them for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const MWWorld::Ptr &target = (*package)->getTarget(); - if ((*package)->sideWithTarget() && !target.isEmpty()) + if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) { if (iteratedActor == actor) { - list.push_back(target); + list.push_back((*package)->getTarget()); } - else if (target == actor) + else if ((*package)->getTarget() == actor) { list.push_back(iteratedActor); } From 910065f38fffce09f8db68d6598e262ad9b25c86 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 21 Aug 2018 17:02:56 +0300 Subject: [PATCH 122/175] Make some more optimizations to actor processing loops --- apps/openmw/mwmechanics/actors.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c28f6df28d..d203dccf40 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1782,6 +1782,8 @@ namespace MWMechanics if (iteratedActor == getPlayer()) continue; + const bool sameActor = (iteratedActor == actor); + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; @@ -1792,7 +1794,7 @@ namespace MWMechanics { if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) { - if (iteratedActor == actor) + if (sameActor) { list.push_back((*package)->getTarget()); } @@ -1815,7 +1817,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1878,7 +1880,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { const MWWorld::Ptr &iteratedActor = iter->first; - if (iteratedActor == getPlayer()) + if (iteratedActor == getPlayer() || iteratedActor == actor) continue; const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); @@ -1908,8 +1910,11 @@ namespace MWMechanics getObjectsInRange(position, aiProcessingDistance, neighbors); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { + if (*neighbor == actor) + continue; + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); - if (stats.isDead() || *neighbor == actor) + if (stats.isDead()) continue; if (stats.getAiSequence().isInCombat(actor)) From 2b45fd84ead5ee55aa5e00e6bddf61d24ea4bacb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 19 Aug 2018 20:41:09 +0300 Subject: [PATCH 123/175] Play landing sound manually and ignore land soundgen textkeys (bug #2256) --- CHANGELOG.md | 1 + apps/openmw/mwclass/npc.cpp | 7 +++---- apps/openmw/mwmechanics/character.cpp | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78eb..a019489436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Bug #1990: Sunrise/sunset not set correct Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards + Bug #2256: Landing sound not playing when jumping immediately after landing Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c06c3f67c6..95d7fa66d0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1241,11 +1241,10 @@ namespace MWClass { MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) + if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) return "DefaultLandWater"; - if(world->isOnGround(ptr)) - return "DefaultLand"; - return ""; + + return "DefaultLand"; } if(name == "swimleft") return "Swim Left"; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c7c6b57d0c..be9daee887 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -939,10 +939,10 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); - if(!sound.empty()) + if(!sound.empty() && evt.compare(10, evt.size()-10, "land") != 0) // Don't play landing sounds here { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) + if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. @@ -2027,6 +2027,12 @@ void CharacterController::update(float duration) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } + + // Play landing sound + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); + if (!sound.empty()) + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } else { From 5a2e9868c1e65862f6c139da338de41f9e30aa98 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Tue, 21 Aug 2018 22:13:05 -0500 Subject: [PATCH 124/175] Fixes #3681 This changes the way that the popup works to use [`QtColorDialog::getColor()`](http://doc.qt.io/archives/qt-4.8/qcolordialog.html#getColor) instead of the problematic open() function. Also makes the button change to create the modal dialog when pushed, rather than being a checkbox of sorts --- apps/opencs/view/widget/coloreditor.cpp | 19 +------------------ apps/opencs/view/widget/coloreditor.hpp | 1 - apps/opencs/view/widget/colorpickerpopup.cpp | 13 +++---------- apps/opencs/view/widget/colorpickerpopup.hpp | 2 -- 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp index 5f82f8e26d..82ca2b0c9c 100644 --- a/apps/opencs/view/widget/coloreditor.cpp +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -1,11 +1,9 @@ #include "coloreditor.hpp" #include -#include #include #include #include -#include #include #include "colorpickerpopup.hpp" @@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart) mColorPicker(new ColorPickerPopup(this)), mPopupOnStart(popupOnStart) { - setCheckable(true); connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); - connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); } @@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt) void CSVWidget::ColorEditor::showPicker() { - if (isChecked()) - { - mColorPicker->showPicker(calculatePopupPosition(), mColor); - } - else - { - mColorPicker->hide(); - } -} - -void CSVWidget::ColorEditor::pickerHid() -{ - setChecked(false); - emit pickingFinished(); + mColorPicker->showPicker(calculatePopupPosition(), mColor); } void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp index 668f22cc94..368896e422 100644 --- a/apps/opencs/view/widget/coloreditor.hpp +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -45,7 +45,6 @@ namespace CSVWidget private slots: void showPicker(); - void pickerHid(); void pickerColorChanged(const QColor &color); signals: diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp index 8e71ce39e4..47aab89817 100644 --- a/apps/opencs/view/widget/colorpickerpopup.cpp +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) mColorPicker->setWindowFlags(Qt::Widget); mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); mColorPicker->installEventFilter(this); - mColorPicker->open(); connect(mColorPicker, SIGNAL(currentColorChanged(const QColor &)), this, @@ -39,8 +37,9 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo geometry.moveTo(position); setGeometry(geometry); - mColorPicker->setCurrentColor(initialColor); - show(); + // Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel + QColor color = mColorPicker->getColor(initialColor); + mColorPicker->setCurrentColor(color); } void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) @@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) QFrame::mousePressEvent(event); } -void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) -{ - QFrame::hideEvent(event); - emit hid(); -} - bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) { if (object == mColorPicker && event->type() == QEvent::KeyPress) diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp index 602bbdb6da..eb5653f469 100644 --- a/apps/opencs/view/widget/colorpickerpopup.hpp +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -20,11 +20,9 @@ namespace CSVWidget protected: virtual void mousePressEvent(QMouseEvent *event); - virtual void hideEvent(QHideEvent *event); virtual bool eventFilter(QObject *object, QEvent *event); signals: - void hid(); void colorChanged(const QColor &color); }; } From ae0a6a22b3a0cdcfda40dec66e1963e90fdb1930 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 14:43:12 +0300 Subject: [PATCH 125/175] Move "land" check earlier --- apps/openmw/mwmechanics/character.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index be9daee887..686d0924ad 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -913,7 +913,8 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } - if(evt.compare(0, 10, "soundgen: ") == 0) + if(evt.compare(0, 10, "soundgen: ") == 0 + && evt.compare(10, evt.size()-10, "land") != 0) // Morrowind ignores land soundgen for some reason { std::string soundgen = evt.substr(10); @@ -939,7 +940,7 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); - if(!sound.empty() && evt.compare(10, evt.size()-10, "land") != 0) // Don't play landing sounds here + if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) From f74ebb64af6e9d64e6d180de9b49badc424762da Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 15:26:21 +0300 Subject: [PATCH 126/175] Correct special case soundgen comparisons --- apps/openmw/mwmechanics/character.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 686d0924ad..49e2c4e6d9 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -913,8 +913,7 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); return; } - if(evt.compare(0, 10, "soundgen: ") == 0 - && evt.compare(10, evt.size()-10, "land") != 0) // Morrowind ignores land soundgen for some reason + if(evt.compare(0, 10, "soundgen: ") == 0) { std::string soundgen = evt.substr(10); @@ -939,11 +938,14 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } + if (soundgen == "land") // Morrowind ignores land soundgen for some reason + return; + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) + if(soundgen == "left" || soundgen == "right") { // Don't make foot sounds local for the player, it makes sense to keep them // positioned on the ground. From 3f76f1d3ed4d45e53fb72fdc923dd5389e79a69b Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 22 Aug 2018 22:30:46 +0300 Subject: [PATCH 127/175] Fix gold count calculation in pickupObject (bug #4604) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b573766a0..cf1bf91d44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button Bug #4597: <> operator causes a compile error + Bug #4604: Picking up gold from the ground only makes 1 grabbed Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0b35bd9de4..523409ceb4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -681,6 +681,8 @@ namespace MWGui return; int count = object.getRefData().getCount(); + if (object.getClass().isGold(object)) + count *= object.getClass().getValue(object); MWWorld::Ptr player = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->breakInvisibility(player); From 3d4f5536d238d0957e8e53843af4a55eb76f3ee6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 24 Aug 2018 15:03:54 +0400 Subject: [PATCH 128/175] Check for impact immediately when launch a projectile (bug #3059) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwrender/weaponanimation.cpp | 9 ++++--- apps/openmw/mwworld/worldimp.cpp | 30 +++++++++++++++++++++--- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4c96cf92..1b8afe14ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue + Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c46298b0b..5561d13ca8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -490,8 +490,8 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0; virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; - virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; + virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0; virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 0b3d000f34..054ce1b1d3 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -123,7 +123,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength); showWeapon(false); @@ -149,9 +150,11 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; - MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength); + MWWorld::Ptr weaponPtr = *weapon; + MWWorld::Ptr ammoPtr = *ammo; + MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength); - inv.remove(*ammo, 1, actor); + inv.remove(ammoPtr, 1, actor); mAmmunition.reset(); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c2ebac8fc5..ae00595bbe 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2892,10 +2892,34 @@ namespace MWWorld } } - void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) + void World::launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) { - mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); + // An initial position of projectile can be outside shooter's collision box, so any object between shooter and launch position will be ignored. + // To avoid this issue, we should check for impact immediately before launch the projectile. + // So we cast a 1-yard-length ray from shooter to launch position and check if there are collisions in this area. + // TODO: as a better solutuon we should handle projectiles during physics update, not during world update. + const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0,-1,0) * 64.f; + + // Early out if the launch position is underwater + bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos); + if (underwater) + { + mRendering->emitWaterRipple(worldPos); + return; + } + + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer()) + actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); + + // Check for impact, if yes, handle hit, if not, launch projectile + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile); + if (result.mHit) + MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength); + else + mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); } void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 432991059a..1c055cbf71 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -607,8 +607,8 @@ namespace MWWorld void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override; void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override; - void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, - const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override; + void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile, + const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override; void applyLoopingParticles(const MWWorld::Ptr& ptr) override; From a560a9e00d93b1bdd00f4971a72b0645c969944c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 18 Aug 2018 16:12:01 +0400 Subject: [PATCH 129/175] Allow messageboxes arguments to have newline characters (bug #3836) --- CHANGELOG.md | 1 + components/compiler/scanner.cpp | 32 +++++++++++++++++++++++++++----- components/compiler/scanner.hpp | 6 ++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c016ca82f3..945d114b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla + Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index bb0fb93740..257c3314ed 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -306,10 +306,22 @@ namespace Compiler int i = 0; std::string lowerCase = Misc::StringUtils::lowerCase(name); - + bool isKeyword = false; for (; sKeywords[i]; ++i) if (lowerCase==sKeywords[i]) + { + isKeyword = true; break; + } + + // Russian localization and some mods use a quirk - add newline character directly + // to compiled bytecode via HEX-editor to implement multiline messageboxes. + // Of course, original editor will not compile such script. + // Allow messageboxes to bybass the "incomplete string or name" error. + if (lowerCase == "messagebox") + enableIgnoreNewlines(); + else if (isKeyword) + mIgnoreNewline = false; if (sKeywords[i]) { @@ -357,9 +369,14 @@ namespace Compiler // } else if (c=='\n') { - error = true; - mErrorHandler.error ("incomplete string or name", mLoc); - break; + if (mIgnoreNewline) + mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc); + else + { + error = true; + mErrorHandler.error ("incomplete string or name", mLoc); + break; + } } } else if (!(c=='"' && name.empty())) @@ -578,7 +595,7 @@ namespace Compiler const Extensions *extensions) : mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions), mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0), - mStrictKeywords (false), mTolerantNames (false) + mStrictKeywords (false), mTolerantNames (false), mIgnoreNewline(false) { } @@ -631,6 +648,11 @@ namespace Compiler mExtensions->listKeywords (keywords); } + void Scanner::enableIgnoreNewlines() + { + mIgnoreNewline = true; + } + void Scanner::enableStrictKeywords() { mStrictKeywords = true; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 49fbaa96a2..a431cabb29 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -39,6 +39,7 @@ namespace Compiler TokenLoc mPutbackLoc; bool mStrictKeywords; bool mTolerantNames; + bool mIgnoreNewline; public: @@ -126,6 +127,11 @@ namespace Compiler void listKeywords (std::vector& keywords); ///< Append all known keywords to \a keywords. + /// Treat newline character as a part of script command. + /// + /// \attention This mode lasts only until the next keyword is reached. + void enableIgnoreNewlines(); + /// Do not accept keywords in quotation marks anymore. /// /// \attention This mode lasts only until the next newline is reached. From 4dd9386c4f04e1e45c1d377e9ac560d9ac0539e2 Mon Sep 17 00:00:00 2001 From: Sophie Kirschner Date: Fri, 24 Aug 2018 16:41:52 +0300 Subject: [PATCH 130/175] Fix error: member access into incomplete type 'SceneUtil::UnrefWorkItem' Fixes compile error encountered on OSX 10.9 with g++ sophie:build pineapple$ g++ --version Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn) Target: x86_64-apple-darwin13.4.0 Thread model: posix The compilation error: [ 24%] Building CXX object apps/openmw/CMakeFiles/openmw.dir/mwrender/renderingmanager.cpp.o In file included from /Users/pineapple/git/openmw/apps/openmw/mwrender/renderingmanager.cpp:1: In file included from /Users/pineapple/git/openmw/apps/openmw/mwrender/renderingmanager.hpp:4: /Users/pineapple/git/openmw/openmw-deps/include/osg/ref_ptr:35:36: error: member access into incomplete type 'SceneUtil::UnrefWorkItem' ~ref_ptr() { if (_ptr) _ptr->unref(); _ptr = 0; } ^ /Users/pineapple/git/openmw/./components/sceneutil/unrefqueue.hpp:14:11: note: in instantiation of member function 'osg::ref_ptr::~ref_ptr' requested here class UnrefQueue : public osg::Referenced ^ /Users/pineapple/git/openmw/./components/sceneutil/unrefqueue.hpp:10:11: note: forward declaration of 'SceneUtil::UnrefWorkItem' class UnrefWorkItem; --- components/sceneutil/unrefqueue.cpp | 22 ++++++---------------- components/sceneutil/unrefqueue.hpp | 12 +++++++++++- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 42ea311337..271c17af2a 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -1,28 +1,18 @@ #include "unrefqueue.hpp" -#include - //#include //#include -#include namespace SceneUtil { - - class UnrefWorkItem : public SceneUtil::WorkItem + void UnrefWorkItem::doWork() { - public: - std::deque > mObjects; - - virtual void doWork() - { - //osg::Timer timer; - //size_t objcount = mObjects.size(); - mObjects.clear(); - //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); - } - }; + //osg::Timer timer; + //size_t objcount = mObjects.size(); + mObjects.clear(); + //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); + } UnrefQueue::UnrefQueue() { diff --git a/components/sceneutil/unrefqueue.hpp b/components/sceneutil/unrefqueue.hpp index 4a2724927f..7155e669c6 100644 --- a/components/sceneutil/unrefqueue.hpp +++ b/components/sceneutil/unrefqueue.hpp @@ -1,13 +1,23 @@ #ifndef OPENMW_COMPONENTS_UNREFQUEUE_H #define OPENMW_COMPONENTS_UNREFQUEUE_H +#include + #include #include +#include + namespace SceneUtil { class WorkQueue; - class UnrefWorkItem; + + class UnrefWorkItem : public SceneUtil::WorkItem + { + public: + std::deque > mObjects; + virtual void doWork(); + }; /// @brief Handles unreferencing of objects through the WorkQueue. Typical use scenario /// would be the main thread pushing objects that are no longer needed, and the background thread deleting them. From 67055b18c41a2599128f9e7bac1348467dd18a27 Mon Sep 17 00:00:00 2001 From: Thunderforge Date: Fri, 24 Aug 2018 12:51:18 -0500 Subject: [PATCH 131/175] Adding Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78eb..b77c5be1f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning + Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye From de08c1cb1b024761671a00a27621268d66c0fef6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 25 Aug 2018 10:34:33 +0400 Subject: [PATCH 132/175] Make Move and MoveWorld console commands move actors standing on moving object (bug #2274) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 1 + .../mwscript/transformationextensions.cpp | 35 +++++++++++++------ apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 1 + 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4c96cf92..0fe2edb0b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards Bug #2256: Landing sound not playing when jumping immediately after landing + Bug #2274: Thin platform clips through player character instead of lifting Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c46298b0b..115d2a2171 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -412,6 +412,7 @@ namespace MWBase /// @note throws an exception when invoked on a teleport door virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; + virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) = 0; ///< get a list of actors standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8f0c725193..16d7e80364 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -29,6 +29,18 @@ namespace MWScript { namespace Transformation { + void moveStandingActors(const MWWorld::Ptr &ptr, const osg::Vec3f& diff) + { + std::vector actors; + MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); + for (auto& actor : actors) + { + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + actorPos += diff; + MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z()); + } + } + template class OpSetScale : public Interpreter::Opcode0 { @@ -666,6 +678,10 @@ namespace MWScript osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange; osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3()); worldPos += diff; + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()); } }; @@ -688,22 +704,21 @@ namespace MWScript runtime.pop(); const float *objPos = ptr.getRefData().getPosition().pos; + osg::Vec3f diff; - MWWorld::Ptr updated; if (axis == "x") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); - } + diff.x() += movement; else if (axis == "y") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); - } + diff.y() += movement; else if (axis == "z") - { - updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); - } + diff.z() += movement; else throw std::runtime_error ("invalid movement axis: " + axis); + + // We should move actors, standing on moving object, too. + // This approach can be used to create elevators. + moveStandingActors(ptr, diff); + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c2ebac8fc5..3ae5fd317d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2377,6 +2377,11 @@ namespace MWWorld return !actors.empty(); } + void World::getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) + { + mPhysics->getActorsStandingOn(object, actors); + } + bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object) { MWWorld::Ptr player = getPlayerPtr(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 432991059a..ca87ae5e3c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -525,6 +525,7 @@ namespace MWWorld /// @note throws an exception when invoked on a teleport door void activateDoor(const MWWorld::Ptr& door, int state) override; + void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors); ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object From 4e3ae85c110591094745f7197cc445d913ec6dc3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 24 Aug 2018 01:54:50 -0700 Subject: [PATCH 133/175] Set the OpenAL source offset after setting the buffer This is to work around a bug in the Rapture3D driver. --- apps/openmw/mwsound/openal_output.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3569cd3da3..076162fde4 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1145,14 +1145,24 @@ bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset) initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); @@ -1175,14 +1185,24 @@ bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset) initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getUseEnv()); + alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } - alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcePlay(source); if(getALError() != AL_NO_ERROR) + { + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + alGetError(); return false; + } mFreeSources.pop_front(); sound->mHandle = MAKE_PTRID(source); From c2b3ca9638199a50f4d2f50cc3fdbdc26496088a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 24 Aug 2018 05:48:14 -0700 Subject: [PATCH 134/175] Update some comments It wasn't actually a bug in OSX like the comment said, but intended behavior. --- apps/openmw/mwsound/openal_output.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 076162fde4..42dd6b5fda 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1217,9 +1217,8 @@ void OpenAL_Output::finishSound(Sound *sound) ALuint source = GET_PTRID(sound->mHandle); sound->mHandle = 0; - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); @@ -1322,9 +1321,8 @@ void OpenAL_Output::finishStream(Stream *sound) sound->mHandle = 0; mStreamThread->remove(stream); - // Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state, - // which works around a bug in the MacOS OpenAL implementation which would otherwise think - // the initial queue already played when it hasn't. + // Rewind the stream to put the source back into an AL_INITIAL state, for + // the next time it's used. alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); From 77bdd0ea662d34f75bb4291e46de55b0e08a5b58 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 25 Aug 2018 01:24:19 -0700 Subject: [PATCH 135/175] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb77be8d4..ef417facb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4606: Support Rapture3D's OpenAL driver 0.44.0 ------ From dd01c4d2240acc21a220f1fd60ac6e6081b1cb05 Mon Sep 17 00:00:00 2001 From: Sophie Kirschner Date: Sat, 25 Aug 2018 11:53:43 +0300 Subject: [PATCH 136/175] Fix: 'sizeof' to an incomplete type 'Video::VideoPlayer' Alternate solution to same problem reported in https://github.com/OpenMW/openmw/pull/1888 --- apps/openmw/mwgui/videowidget.cpp | 2 ++ apps/openmw/mwgui/videowidget.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 516f5cfcc6..28432b811d 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -22,6 +22,8 @@ VideoWidget::VideoWidget() setNeedKeyFocus(true); } +VideoWidget::~VideoWidget() = default; + void VideoWidget::setVFS(const VFS::Manager *vfs) { mVFS = vfs; diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index ac240e69d5..20af579a25 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -25,6 +25,8 @@ namespace MWGui MYGUI_RTTI_DERIVED(VideoWidget) VideoWidget(); + + ~VideoWidget(); /// Set the VFS (virtual file system) to find the videos on. void setVFS(const VFS::Manager* vfs); From c6dcfd1fcec013b32f47c60d64e2d0aebeab669e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 25 Aug 2018 17:26:17 +0400 Subject: [PATCH 137/175] Do not apply scale twice for animated collision nodes (bug #4607) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/physicssystem.cpp | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb77be8d4..735d7683d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,7 @@ Bug #4591: Attack strength should be 0 if player did not hold the attack button Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed + Bug #4607: Scaling for animated collision shapes is applied twice Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 13a0f23ac3..2a7b65cac0 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -650,7 +650,6 @@ namespace MWPhysics osg::NodePath& nodePath = nodePathFound->second; osg::Matrixf matrix = osg::computeLocalToWorld(nodePath); - osg::Vec3f scale = matrix.getScale(); matrix.orthoNormalize(matrix); btTransform transform; @@ -659,8 +658,8 @@ namespace MWPhysics for (int j=0; j<3; ++j) transform.getBasis()[i][j] = matrix(j,i); // NB column/row major difference - if (compound->getLocalScaling() * toBullet(scale) != compound->getChildShape(shapeIndex)->getLocalScaling()) - compound->getChildShape(shapeIndex)->setLocalScaling(compound->getLocalScaling() * toBullet(scale)); + // Note: we can not apply scaling here for now since we treat scaled shapes + // as new shapes (btScaledBvhTriangleMeshShape) with 1.0 scale for now if (!(transform == compound->getChildTransform(shapeIndex))) compound->updateChildTransform(shapeIndex, transform); } From ff241fb7873ce412282eba10cc6c42a5b87b017e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 22 Aug 2018 12:48:05 +0400 Subject: [PATCH 138/175] Optimize skinning (task #4605) --- CHANGELOG.md | 1 + components/sceneutil/riggeometry.cpp | 77 ++++++++++++++-------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b573766a0..b7f3272d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,7 @@ Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4605: Optimize skinning 0.44.0 ------ diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index ee30f1c85c..c409bcd5cc 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -8,6 +8,31 @@ #include "skeleton.hpp" #include "util.hpp" +namespace +{ + inline void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result) + { + osg::Matrixf m = invBindMatrix * matrix; + float* ptr = m.ptr(); + float* ptrresult = result.ptr(); + ptrresult[0] += ptr[0] * weight; + ptrresult[1] += ptr[1] * weight; + ptrresult[2] += ptr[2] * weight; + + ptrresult[4] += ptr[4] * weight; + ptrresult[5] += ptr[5] * weight; + ptrresult[6] += ptr[6] * weight; + + ptrresult[8] += ptr[8] * weight; + ptrresult[9] += ptr[9] * weight; + ptrresult[10] += ptr[10] * weight; + + ptrresult[12] += ptr[12] * weight; + ptrresult[13] += ptr[13] * weight; + ptrresult[14] += ptr[14] * weight; + } +} + namespace SceneUtil { @@ -141,28 +166,6 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) return true; } -void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result) -{ - osg::Matrixf m = invBindMatrix * matrix; - float* ptr = m.ptr(); - float* ptrresult = result.ptr(); - ptrresult[0] += ptr[0] * weight; - ptrresult[1] += ptr[1] * weight; - ptrresult[2] += ptr[2] * weight; - - ptrresult[4] += ptr[4] * weight; - ptrresult[5] += ptr[5] * weight; - ptrresult[6] += ptr[6] * weight; - - ptrresult[8] += ptr[8] * weight; - ptrresult[9] += ptr[9] * weight; - ptrresult[10] += ptr[10] * weight; - - ptrresult[12] += ptr[12] * weight; - ptrresult[13] += ptr[13] * weight; - ptrresult[14] += ptr[14] * weight; -} - void RigGeometry::cull(osg::NodeVisitor* nv) { if (!mSkeleton) @@ -173,7 +176,8 @@ void RigGeometry::cull(osg::NodeVisitor* nv) return; } - if ((!mSkeleton->getActive() && mLastFrameNumber != 0) || mLastFrameNumber == nv->getTraversalNumber()) + unsigned int traversalNumber = nv->getTraversalNumber(); + if (mLastFrameNumber == traversalNumber || (mLastFrameNumber != 0 && !mSkeleton->getActive())) { osg::Geometry& geom = *getGeometry(mLastFrameNumber); nv->pushOntoNodePath(&geom); @@ -181,10 +185,10 @@ void RigGeometry::cull(osg::NodeVisitor* nv) nv->popFromNodePath(); return; } - mLastFrameNumber = nv->getTraversalNumber(); + mLastFrameNumber = traversalNumber; osg::Geometry& geom = *getGeometry(mLastFrameNumber); - mSkeleton->updateBoneMatrices(nv->getTraversalNumber()); + mSkeleton->updateBoneMatrices(traversalNumber); // skinning const osg::Vec3Array* positionSrc = static_cast(mSourceGeometry->getVertexArray()); @@ -195,34 +199,31 @@ void RigGeometry::cull(osg::NodeVisitor* nv) osg::Vec3Array* normalDst = static_cast(geom.getNormalArray()); osg::Vec4Array* tangentDst = static_cast(geom.getTexCoordArray(7)); - for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it) + for (auto &pair : mBone2VertexMap) { - osg::Matrixf resultMat (0, 0, 0, 0, + osg::Matrixf resultMat (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - for (std::vector::const_iterator weightIt = it->first.begin(); weightIt != it->first.end(); ++weightIt) + for (auto &weight : pair.first) { - Bone* bone = weightIt->first.first; - const osg::Matrix& invBindMatrix = weightIt->first.second; - float weight = weightIt->second; - const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace; - accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat); + accumulateMatrix(weight.first.second, weight.first.first->mMatrixInSkeletonSpace, weight.second, resultMat); } + if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - for (std::vector::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt) + for (auto &vertex : pair.second) { - unsigned short vertex = *vertexIt; (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); if (normalDst) - (*normalDst)[vertex] = osg::Matrix::transform3x3((*normalSrc)[vertex], resultMat); + (*normalDst)[vertex] = osg::Matrixf::transform3x3((*normalSrc)[vertex], resultMat); + if (tangentDst) { - osg::Vec4f srcTangent = (*tangentSrc)[vertex]; - osg::Vec3f transformedTangent = osg::Matrix::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); + const osg::Vec4f& srcTangent = (*tangentSrc)[vertex]; + osg::Vec3f transformedTangent = osg::Matrixf::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat); (*tangentDst)[vertex] = osg::Vec4f(transformedTangent, srcTangent.w()); } } From c412f99963f03a4305271dfe00bfd03b500ba274 Mon Sep 17 00:00:00 2001 From: Sophie Kirschner Date: Sun, 26 Aug 2018 11:08:06 +0300 Subject: [PATCH 139/175] Remove commented lines in UnrefWorkItem::doWork --- components/sceneutil/unrefqueue.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 271c17af2a..21ba836c0c 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -8,10 +8,7 @@ namespace SceneUtil { void UnrefWorkItem::doWork() { - //osg::Timer timer; - //size_t objcount = mObjects.size(); mObjects.clear(); - //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); } UnrefQueue::UnrefQueue() From dc68b2ff2681e64d9e4ae61289562b7e1f88065d Mon Sep 17 00:00:00 2001 From: Sophie Kirschner Date: Sun, 26 Aug 2018 11:10:25 +0300 Subject: [PATCH 140/175] Add Sophie Kirschner (me) to AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 3422f28a92..5320a24625 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -153,6 +153,7 @@ Programmers Siimacore sir_herrbatka smbas + Sophie Kirschner (pineapplemachine) spycrab Stefan Galowicz (bogglez) Stanislav Bobrov (Jiub) From 9e253f6a46263f92e8a5939459d66a2d541b4e4c Mon Sep 17 00:00:00 2001 From: Sophie Kirschner Date: Sun, 26 Aug 2018 12:02:49 +0300 Subject: [PATCH 141/175] Add issue #4613 to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d8df78eb..a8a0721051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,7 @@ Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test + Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 0.44.0 ------ From 60698e6f8aab18b612653dc7acd4c242cdf3b3d1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 26 Aug 2018 21:02:14 +0400 Subject: [PATCH 142/175] Optimize new magic effects update system --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 3 --- apps/openmw/mwrender/animation.cpp | 32 ++++++++++++++++++++++----- apps/openmw/mwrender/animation.hpp | 3 ++- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d203dccf40..ba8f62e2fa 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1679,7 +1679,7 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); if (animation) - animation->updateEffects(duration); + animation->updateEffects(); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 49e2c4e6d9..9d5db08b09 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2219,9 +2219,6 @@ void CharacterController::update(float duration) moved *= (l / newLength); } - if (mSkipAnim) - mAnimation->updateEffects(duration); - if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) moved.z() = 1.0; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4da3ec4a8e..1ace2f8b4b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -208,8 +208,11 @@ namespace class RemoveFinishedCallbackVisitor : public RemoveVisitor { public: + bool mHasMagicEffects; + RemoveFinishedCallbackVisitor() : RemoveVisitor() + , mHasMagicEffects(false) , mEffectId(-1) { } @@ -234,11 +237,14 @@ namespace { // We should remove empty transformation nodes and finished callbacks here MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); - bool finished = vfxCallback && vfxCallback->mFinished; - bool toRemove = vfxCallback && mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; - if (finished || toRemove) + if (vfxCallback) { - mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + bool finished = vfxCallback->mFinished; + bool toRemove = mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; + if (finished || toRemove) + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + else + mHasMagicEffects = true; } } } @@ -610,6 +616,7 @@ namespace MWRender , mTextKeyListener(NULL) , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) + , mHasMagicEffects(false) , mAlpha(1.f) { for(size_t i = 0;i < sNumBlendMasks;i++) @@ -1321,7 +1328,7 @@ namespace MWRender ++stateiter; } - updateEffects(duration); + updateEffects(); if (mHeadController) { @@ -1633,6 +1640,9 @@ namespace MWRender SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr(params.mAnimTime)); node->accept(assignVisitor); + // Notify that this animation has attached magic effects + mHasMagicEffects = true; + overrideFirstRootTexture(texture, mResourceSystem, node); } @@ -1641,10 +1651,14 @@ namespace MWRender RemoveFinishedCallbackVisitor visitor(effectId); mInsert->accept(visitor); visitor.remove(); + mHasMagicEffects = visitor.mHasMagicEffects; } void Animation::getLoopingEffects(std::vector &out) const { + if (!mHasMagicEffects) + return; + FindVfxCallbacksVisitor visitor; mInsert->accept(visitor); @@ -1657,13 +1671,19 @@ namespace MWRender } } - void Animation::updateEffects(float duration) + void Animation::updateEffects() { + // We do not need to visit scene every frame. + // We can use a bool flag to check in spellcasting effect found. + if (!mHasMagicEffects) + return; + // TODO: objects without animation still will have // transformation nodes with finished callbacks RemoveFinishedCallbackVisitor visitor; mInsert->accept(visitor); visitor.remove(); + mHasMagicEffects = visitor.mHasMagicEffects; } bool Animation::upperBodyReady() const diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1da1fe4efb..e10d2c995a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -263,6 +263,7 @@ protected: osg::ref_ptr mHeadController; float mHeadYawRadians; float mHeadPitchRadians; + bool mHasMagicEffects; osg::ref_ptr mGlowLight; osg::ref_ptr mGlowUpdater; @@ -450,7 +451,7 @@ public: void setLoopingEnabled(const std::string &groupname, bool enabled); /// This is typically called as part of runAnimation, but may be called manually if needed. - void updateEffects(float duration); + void updateEffects(); /// Return a node with the specified name, or NULL if not existing. /// @note The matching is case-insensitive. From d448b802ef28d8a5343db3c79a0388490720c1f4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 26 Aug 2018 22:57:08 +0400 Subject: [PATCH 143/175] Add a small threshold for player turning animations --- apps/openmw/mwmechanics/character.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 49e2c4e6d9..46676c0763 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2045,6 +2045,12 @@ void CharacterController::update(float duration) inJump = false; + // Do not play turning animation for player if rotation speed is very slow. + // Actual threshold should take framerate in account. + float rotationThreshold = 0; + if (mPtr == getPlayer()) + rotationThreshold = 0.015 * 60 * duration; + if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) { if(vec.x() > 0.0f) @@ -2069,9 +2075,9 @@ void CharacterController::update(float duration) } else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) { - if(rot.z() > 0.0f) + if(rot.z() > rotationThreshold) movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - else if(rot.z() < 0.0f) + else if(rot.z() < -rotationThreshold) movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; } } From 8fa6c6f726ac27b54b621795d27208d17c7c7b1d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 27 Aug 2018 09:37:08 +0400 Subject: [PATCH 144/175] Update pinned windows in-game (bug #4560) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 17 +++++++++++++++++ apps/openmw/mwgui/inventorywindow.hpp | 1 + apps/openmw/mwgui/spellwindow.cpp | 4 ++++ 4 files changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34f4dbbad2..91e11fb80b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4560: OpenMW does not update pinned windows properly Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 523409ceb4..0d37bbff3b 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -68,6 +68,7 @@ namespace MWGui , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer())) , mTrading(false) , mScaleFactor(1.0f) + , mUpdateTimer(0.f) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale > 1.0) @@ -631,6 +632,22 @@ namespace MWGui void InventoryWindow::onFrame(float dt) { updateEncumbranceBar(); + + if (mPinned) + { + mUpdateTimer += dt; + if (0.1f < mUpdateTimer) + { + mUpdateTimer = 0; + + // Update pinned inventory in-game + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + mItemView->update(); + notifyContentChanged(); + } + } + } } void InventoryWindow::setTrading(bool trading) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index d9cf6870cb..c60e373630 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -102,6 +102,7 @@ namespace MWGui bool mTrading; float mScaleFactor; + float mUpdateTimer; void onItemSelected(int index); void onItemSelectedFromSourceModel(int index); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 3fe171e4e0..38de9288bf 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -86,6 +86,10 @@ namespace MWGui mUpdateTimer = 0; mSpellView->incrementalUpdate(); } + + // Update effects in-game too if the window is pinned + if (mPinned && !MWBase::Environment::get().getWindowManager()->isGuiMode()) + mSpellIcons->updateWidgets(mEffectBox, false); } void SpellWindow::updateSpells() From 2564fcc37e810a4e9314946172c9c65b454aeabc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Aug 2018 03:29:00 +0300 Subject: [PATCH 145/175] Apply sneaking offset to camera while the character is in air (bug #4617) --- CHANGELOG.md | 1 + apps/openmw/mwworld/worldimp.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0584445c8e..023ec49379 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,7 @@ Bug #4591: Attack strength should be 0 if player did not hold the attack button Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed + Bug #4617: First person sneaking offset is not applied while the character is in air Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a3d7873d7f..6194e831f6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1706,11 +1706,10 @@ namespace MWWorld // Sink the camera while sneaking bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool inair = !isOnGround(player); bool swimming = isSwimming(player); static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->getFloat(); - if (sneaking && !(swimming || inair)) + if (sneaking && !swimming) mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta); else mRendering->getCamera()->setSneakOffset(0.f); From c677f7ca2768262164531385d81b6d2ac29d6f60 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 28 Aug 2018 12:05:27 +0400 Subject: [PATCH 146/175] Rework pulsing light sources (bug #4615) --- CHANGELOG.md | 1 + components/sceneutil/lightcontroller.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34f4dbbad2..f5c990b4c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,7 @@ Bug #4591: Attack strength should be 0 if player did not hold the attack button Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed + Bug #4615: Flicker effects for light sources are handled incorrectly Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/components/sceneutil/lightcontroller.cpp b/components/sceneutil/lightcontroller.cpp index e3ea93843c..b89cfc4bc7 100644 --- a/components/sceneutil/lightcontroller.cpp +++ b/components/sceneutil/lightcontroller.cpp @@ -73,17 +73,15 @@ namespace SceneUtil float cycle_time; float time_distortion; - const float pi = 3.14159265359; - if(mType == LT_Pulse || mType == LT_PulseSlow) { - cycle_time = 2.0f * pi; - time_distortion = mType == LT_Pulse ? 20.0f : 4.f; + cycle_time = 2.0f * osg::PI; + time_distortion = 3.0f; } else { - static const float fa = 0.785398f; - static const float phase_wavelength = 120.0f * pi / fa; + static const float fa = osg::PI / 4.0f; + static const float phase_wavelength = 120.0f * osg::PI / fa; cycle_time = 500.0f; mPhase = std::fmod(mPhase + dt, phase_wavelength); @@ -94,12 +92,14 @@ namespace SceneUtil if(mDirection > 0 && mDeltaCount > +cycle_time) { mDirection = -1.0f; - mDeltaCount = 2.0f*cycle_time - mDeltaCount; + float extra = mDeltaCount - cycle_time; + mDeltaCount -= 2*extra; } if(mDirection < 0 && mDeltaCount < -cycle_time) { mDirection = +1.0f; - mDeltaCount = -2.0f*cycle_time - mDeltaCount; + float extra = cycle_time - mDeltaCount; + mDeltaCount += 2*extra; } static const float fast = 4.0f/1.0f; From 0ddd0e4edc7f5fc9c5cff13117518453965aff47 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 28 Aug 2018 13:41:44 +0400 Subject: [PATCH 147/175] Fix light flicker amplitude calculation --- components/sceneutil/lightcontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/lightcontroller.cpp b/components/sceneutil/lightcontroller.cpp index b89cfc4bc7..199b408d74 100644 --- a/components/sceneutil/lightcontroller.cpp +++ b/components/sceneutil/lightcontroller.cpp @@ -26,7 +26,7 @@ namespace float v = 0.0f; for(int i = 0;i < 3;++i) - v += std::sin(fb*time*f[i] + o[1])*m[i]; + v += std::sin(fb*time*f[i] + o[i])*m[i]; return v * s; } From 079b60c1ea25e78445c82edad83af5616303430c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Aug 2018 14:28:27 +0300 Subject: [PATCH 148/175] Don't allow actors to use sneaking while flying (bug #4618) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 023ec49379..e34dee8331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4617: First person sneaking offset is not applied while the character is in air + Bug #4618: Sneaking is possible while the character is flying Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 49e2c4e6d9..a7ef7c816f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1825,9 +1825,9 @@ void CharacterController::update(float duration) { bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); - bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); - // Can't run while flying (see speed formula in Npc/Creature::getSpeed) + // Can't run and sneak while flying (see speed formula in Npc/Creature::getSpeed) + bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying; bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying; CreatureStats &stats = cls.getCreatureStats(mPtr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6194e831f6..0bc952e897 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1707,9 +1707,10 @@ namespace MWWorld // Sink the camera while sneaking bool sneaking = player.getClass().getCreatureStats(getPlayerPtr()).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool swimming = isSwimming(player); + bool flying = isFlying(player); static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->getFloat(); - if (sneaking && !swimming) + if (sneaking && !swimming && !flying) mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta); else mRendering->getCamera()->setSneakOffset(0.f); From 7f459f06108b651012daff256e680aa07f0bfbf3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 28 Aug 2018 16:42:15 +0300 Subject: [PATCH 149/175] Knockdown and godmode fixes Make sure an incapacitated player is not able to jump Cleanup of redundant player and godmode checks in creature class Make sure the player is not knocked down while in godmode --- apps/openmw/mwclass/creature.cpp | 12 ++---------- apps/openmw/mwclass/npc.cpp | 6 +++++- apps/openmw/mwmechanics/character.cpp | 6 ++++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 161711751d..7f4694af2e 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -253,7 +253,7 @@ namespace MWClass // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; - if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) + if (!ptr.isEmpty() && ptr.getClass().isActor()) ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors); @@ -325,9 +325,6 @@ namespace MWClass if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) damage = 0; - if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()) - damage = 0; - MWMechanics::diseaseContact(victim, ptr); victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); @@ -376,11 +373,6 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - bool godmode = object == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); - - if (godmode) - damage = 0; - if (!successful) { // Missed @@ -415,7 +407,7 @@ namespace MWClass if(ishealth) { - if (!attacker.isEmpty() && !godmode) + if (!attacker.isEmpty()) { damage = scaleDamage(damage, attacker, ptr); MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 95d7fa66d0..0b8b60feb0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -992,6 +992,10 @@ namespace MWClass if(getEncumbrance(ptr) > getCapacity(ptr)) return 0.f; + const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) + return 0.f; + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); @@ -1013,7 +1017,7 @@ namespace MWClass x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64; x *= encumbranceTerm; - if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) + if(stats.getStance(MWMechanics::CreatureStats::Stance_Run)) x *= gmst.fJumpRunMultiplier->getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 49e2c4e6d9..2bd3dadace 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1824,6 +1824,7 @@ void CharacterController::update(float duration) else if(!cls.getCreatureStats(mPtr).isDead()) { bool onground = world->isOnGround(mPtr); + bool incapacitated = (cls.getCreatureStats(mPtr).isParalyzed() || cls.getCreatureStats(mPtr).getKnockedDown()); bool inwater = world->isSwimming(mPtr); bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); @@ -1942,7 +1943,7 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).setFatigue(fatigue); } - if(sneak || inwater || flying) + if(sneak || inwater || flying || incapacitated) vec.z() = 0.0f; bool inJump = true; @@ -2021,7 +2022,8 @@ void CharacterController::update(float duration) const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { - cls.getCreatureStats(mPtr).setKnockedDown(true); + if (!godmode) + cls.getCreatureStats(mPtr).setKnockedDown(true); } else { From 6c47f95677e748e1e2aea4324ab6f1d95824a72a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 27 Aug 2018 02:17:49 +0300 Subject: [PATCH 150/175] Make RemoveSpellEffects affect permanent spells (bug #3920) Also make it remove the effects but not the spells themselves --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/activespells.cpp | 8 ++++++-- apps/openmw/mwmechanics/spells.cpp | 13 +++++++++++++ apps/openmw/mwmechanics/spells.hpp | 2 ++ apps/openmw/mwscript/statsextensions.cpp | 1 + 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5453ffb497..f147c75362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters + Bug #3920: RemoveSpellEffects doesn't remove constant effects Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes Bug #3993: Terrain texture blending map is not upscaled diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 90d29f686d..9e523a8453 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -197,8 +197,12 @@ namespace MWMechanics void ActiveSpells::removeEffects(const std::string &id) { - mSpells.erase(Misc::StringUtils::lowerCase(id)); - mSpellsChanged = true; + TContainer::iterator spell(mSpells.find(id)); + if (spell != end()); + { + spell->second.mEffects.clear(); + mSpellsChanged = true; + } } void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index ff397f69d8..25f3011189 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -279,6 +279,19 @@ namespace MWMechanics } } + void Spells::removeEffects(const std::string &id) + { + if (isSpellActive(id)) + { + TContainer::iterator spellIt = mSpells.find(getSpell(id)); + for (long unsigned int i = 0; i != spellIt->first->mEffects.mList.size(); i++) + { + spellIt->second.mPurgedEffects.insert(i); + mSpellsChanged = true; + } + } + } + void Spells::visitEffectSources(EffectSourceVisitor &visitor) const { if (mSpellsChanged) { diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index c1705b38a1..e635f4f56d 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -124,6 +124,8 @@ namespace MWMechanics bool hasBlightDisease() const; + void removeEffects(const std::string& id); + void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void readState (const ESM::SpellState& state); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5df767da83..29160d47e4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -501,6 +501,7 @@ namespace MWScript runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().removeEffects(spellid); + ptr.getClass().getCreatureStats (ptr).getSpells().removeEffects(spellid); } }; From ed1f8f7be7b43d1ca6b1154c7b9f33633835d6c6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 13:40:37 +0300 Subject: [PATCH 151/175] Remove effects from all active spells with the same ID --- apps/openmw/mwmechanics/activespells.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 9e523a8453..57b009689c 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -197,11 +197,13 @@ namespace MWMechanics void ActiveSpells::removeEffects(const std::string &id) { - TContainer::iterator spell(mSpells.find(id)); - if (spell != end()); + for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell) { - spell->second.mEffects.clear(); - mSpellsChanged = true; + if (spell->first == id) + { + spell->second.mEffects.clear(); + mSpellsChanged = true; + } } } From b8ba9092cb1657df934ece8c7d93dae135dab6ea Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 14:09:43 +0300 Subject: [PATCH 152/175] Purge effects from all permanent spells with the same ID --- apps/openmw/mwmechanics/spells.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 25f3011189..0a11ed6414 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -283,12 +283,18 @@ namespace MWMechanics { if (isSpellActive(id)) { - TContainer::iterator spellIt = mSpells.find(getSpell(id)); - for (long unsigned int i = 0; i != spellIt->first->mEffects.mList.size(); i++) + for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell) { - spellIt->second.mPurgedEffects.insert(i); - mSpellsChanged = true; + if (spell->first == getSpell(id)) + { + for (long unsigned int i = 0; i != spell->first->mEffects.mList.size(); i++) + { + spell->second.mPurgedEffects.insert(i); + } + } } + + mSpellsChanged = true; } } From 23834b5ed81123fc1426c1490d5bb884d2e437f4 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 15:09:03 +0300 Subject: [PATCH 153/175] Don't apply falling damage twice (bug #4608) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5453ffb497..528e8963a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice + Bug #4608: Falling damage is applied twice Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aca3dde7f5..79527f8860 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2012,10 +2012,7 @@ void CharacterController::update(float duration) // inflict fall damages if (!godmode) { - DynamicStat health = cls.getCreatureStats(mPtr).getHealth(); float realHealthLost = static_cast(healthLost * (1.0f - 0.25f * fatigueTerm)); - health.setCurrent(health.getCurrent() - realHealthLost); - cls.getCreatureStats(mPtr).setHealth(health); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } From b0ac0b0b225355066a8ba66deb0109efd0d32d55 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 27 Aug 2018 13:33:50 +0400 Subject: [PATCH 154/175] Do not initialize map every call --- CHANGELOG.md | 1 + components/esm/loadmgef.cpp | 139 +++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5453ffb497..fec409d48d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,6 +138,7 @@ Task #4605: Optimize skinning Task #4606: Support Rapture3D's OpenAL driver Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 + Task #4621: Optimize combat AI 0.44.0 ------ diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 9f8ad94e1e..75a94f828a 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -274,43 +274,46 @@ short MagicEffect::getResistanceEffect(short effect) // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // - std::map effects; - effects[DisintegrateArmor] = Sanctuary; - effects[DisintegrateWeapon] = Sanctuary; - - for (int i=0; i<5; ++i) - effects[DrainAttribute+i] = ResistMagicka; - for (int i=0; i<5; ++i) - effects[DamageAttribute+i] = ResistMagicka; - for (int i=0; i<5; ++i) - effects[AbsorbAttribute+i] = ResistMagicka; - for (int i=0; i<10; ++i) - effects[WeaknessToFire+i] = ResistMagicka; - - effects[Burden] = ResistMagicka; - effects[Charm] = ResistMagicka; - effects[Silence] = ResistMagicka; - effects[Blind] = ResistMagicka; - effects[Sound] = ResistMagicka; - - for (int i=0; i<2; ++i) + static std::map effects; + if (effects.empty()) { - effects[CalmHumanoid+i] = ResistMagicka; - effects[FrenzyHumanoid+i] = ResistMagicka; - effects[DemoralizeHumanoid+i] = ResistMagicka; - effects[RallyHumanoid+i] = ResistMagicka; + effects[DisintegrateArmor] = Sanctuary; + effects[DisintegrateWeapon] = Sanctuary; + + for (int i=0; i<5; ++i) + effects[DrainAttribute+i] = ResistMagicka; + for (int i=0; i<5; ++i) + effects[DamageAttribute+i] = ResistMagicka; + for (int i=0; i<5; ++i) + effects[AbsorbAttribute+i] = ResistMagicka; + for (int i=0; i<10; ++i) + effects[WeaknessToFire+i] = ResistMagicka; + + effects[Burden] = ResistMagicka; + effects[Charm] = ResistMagicka; + effects[Silence] = ResistMagicka; + effects[Blind] = ResistMagicka; + effects[Sound] = ResistMagicka; + + for (int i=0; i<2; ++i) + { + effects[CalmHumanoid+i] = ResistMagicka; + effects[FrenzyHumanoid+i] = ResistMagicka; + effects[DemoralizeHumanoid+i] = ResistMagicka; + effects[RallyHumanoid+i] = ResistMagicka; + } + + effects[TurnUndead] = ResistMagicka; + + effects[FireDamage] = ResistFire; + effects[FrostDamage] = ResistFrost; + effects[ShockDamage] = ResistShock; + effects[Vampirism] = ResistCommonDisease; + effects[Corprus] = ResistCorprusDisease; + effects[Poison] = ResistPoison; + effects[Paralyze] = ResistParalysis; } - effects[TurnUndead] = ResistMagicka; - - effects[FireDamage] = ResistFire; - effects[FrostDamage] = ResistFrost; - effects[ShockDamage] = ResistShock; - effects[Vampirism] = ResistCommonDisease; - effects[Corprus] = ResistCorprusDisease; - effects[Poison] = ResistPoison; - effects[Paralyze] = ResistParalysis; - if (effects.find(effect) != effects.end()) return effects[effect]; else @@ -319,42 +322,44 @@ short MagicEffect::getResistanceEffect(short effect) short MagicEffect::getWeaknessEffect(short effect) { - std::map effects; - - for (int i=0; i<5; ++i) - effects[DrainAttribute+i] = WeaknessToMagicka; - for (int i=0; i<5; ++i) - effects[DamageAttribute+i] = WeaknessToMagicka; - for (int i=0; i<5; ++i) - effects[AbsorbAttribute+i] = WeaknessToMagicka; - for (int i=0; i<10; ++i) - effects[WeaknessToFire+i] = WeaknessToMagicka; - - effects[Burden] = WeaknessToMagicka; - effects[Charm] = WeaknessToMagicka; - effects[Silence] = WeaknessToMagicka; - effects[Blind] = WeaknessToMagicka; - effects[Sound] = WeaknessToMagicka; - - for (int i=0; i<2; ++i) + static std::map effects; + if (effects.empty()) { - effects[CalmHumanoid+i] = WeaknessToMagicka; - effects[FrenzyHumanoid+i] = WeaknessToMagicka; - effects[DemoralizeHumanoid+i] = WeaknessToMagicka; - effects[RallyHumanoid+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[DrainAttribute+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[DamageAttribute+i] = WeaknessToMagicka; + for (int i=0; i<5; ++i) + effects[AbsorbAttribute+i] = WeaknessToMagicka; + for (int i=0; i<10; ++i) + effects[WeaknessToFire+i] = WeaknessToMagicka; + + effects[Burden] = WeaknessToMagicka; + effects[Charm] = WeaknessToMagicka; + effects[Silence] = WeaknessToMagicka; + effects[Blind] = WeaknessToMagicka; + effects[Sound] = WeaknessToMagicka; + + for (int i=0; i<2; ++i) + { + effects[CalmHumanoid+i] = WeaknessToMagicka; + effects[FrenzyHumanoid+i] = WeaknessToMagicka; + effects[DemoralizeHumanoid+i] = WeaknessToMagicka; + effects[RallyHumanoid+i] = WeaknessToMagicka; + } + + effects[TurnUndead] = WeaknessToMagicka; + + effects[FireDamage] = WeaknessToFire; + effects[FrostDamage] = WeaknessToFrost; + effects[ShockDamage] = WeaknessToShock; + effects[Vampirism] = WeaknessToCommonDisease; + effects[Corprus] = WeaknessToCorprusDisease; + effects[Poison] = WeaknessToPoison; + + effects[Paralyze] = -1; } - effects[TurnUndead] = WeaknessToMagicka; - - effects[FireDamage] = WeaknessToFire; - effects[FrostDamage] = WeaknessToFrost; - effects[ShockDamage] = WeaknessToShock; - effects[Vampirism] = WeaknessToCommonDisease; - effects[Corprus] = WeaknessToCorprusDisease; - effects[Poison] = WeaknessToPoison; - - effects[Paralyze] = -1; - if (effects.find(effect) != effects.end()) return effects[effect]; else From 4c0ef4ddb6b1739d3fc614c877519cf316a21840 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 29 Aug 2018 17:19:31 +0400 Subject: [PATCH 155/175] Do not initialize magic schools map every time we access it --- apps/openmw/mwmechanics/spellcasting.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 76140013d4..77b5bc1232 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -34,13 +34,17 @@ namespace MWMechanics { ESM::Skill::SkillEnum spellSchoolToSkill(int school) { - std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = ESM::Skill::Alteration; - schoolSkillMap[1] = ESM::Skill::Conjuration; - schoolSkillMap[3] = ESM::Skill::Illusion; - schoolSkillMap[2] = ESM::Skill::Destruction; - schoolSkillMap[4] = ESM::Skill::Mysticism; - schoolSkillMap[5] = ESM::Skill::Restoration; + static std::map schoolSkillMap; // maps spell school to skill id + if (schoolSkillMap.empty()) + { + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; + } + assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } From 2cac8b59b1b70d166315d79e26c81ca53fb131dd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 27 Aug 2018 13:35:49 +0400 Subject: [PATCH 156/175] Use square distance to target --- apps/openmw/mwmechanics/aisequence.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index b64b3568f8..cf572abc0f 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -228,7 +228,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac { if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; - MWWorld::Ptr target = static_cast(*it)->getTarget(); + MWWorld::Ptr target = (*it)->getTarget(); // target disappeared (e.g. summoned creatures) if (target.isEmpty()) @@ -242,11 +242,11 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac const ESM::Position &targetPos = target.getRefData().getPosition(); - float distTo = (targetPos.asVec3() - vActorPos).length(); + float distTo = (targetPos.asVec3() - vActorPos).length2(); // Small threshold for changing target if (it == mPackages.begin()) - distTo = std::max(0.f, distTo - 50.f); + distTo = std::max(0.f, distTo - 2500.f); // if a target has higher priority than current target or has same priority but closer if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating)) From 5d54214acb18b92afb676466d61966068c7d8258 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 27 Aug 2018 13:38:53 +0400 Subject: [PATCH 157/175] Optimize combat action iteration --- apps/openmw/mwmechanics/aicombataction.cpp | 22 ++++------------------ apps/openmw/mwmechanics/spellcasting.cpp | 4 ++++ apps/openmw/mwmechanics/spellcasting.hpp | 2 ++ apps/openmw/mwmechanics/spellpriority.cpp | 5 ++--- apps/openmw/mwmechanics/weaponpriority.cpp | 3 +++ 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 2685e0e0c2..421e5a99f9 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -190,11 +190,6 @@ namespace MWMechanics for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; - if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) - == equipmentSlots.end()) - continue; - float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { @@ -215,14 +210,12 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = it->first; - - float rating = rateSpell(spell, actor, enemy); + float rating = rateSpell(it->first, actor, enemy); if (rating > bestActionRating) { bestActionRating = rating; - bestAction.reset(new ActionSpell(spell->mId)); - antiFleeRating = vanillaRateSpell(spell, actor, enemy); + bestAction.reset(new ActionSpell(it->first->mId)); + antiFleeRating = vanillaRateSpell(it->first, actor, enemy); } } @@ -265,11 +258,6 @@ namespace MWMechanics for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - std::vector equipmentSlots = it->getClass().getEquipmentSlots(*it).first; - if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight) - == equipmentSlots.end()) - continue; - float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); if (rating > bestActionRating) { @@ -280,9 +268,7 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - const ESM::Spell* spell = it->first; - - float rating = rateSpell(spell, actor, enemy); + float rating = rateSpell(it->first, actor, enemy); if (rating > bestActionRating) { bestActionRating = rating; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 77b5bc1232..1d7b1783eb 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -52,7 +52,11 @@ namespace MWMechanics float calcEffectCost(const ESM::ENAMstruct& effect) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); + return calcEffectCost(effect, magicEffect); + } + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) + { int minMagn = 1; int maxMagn = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 72f6a1f4a0..2844e7f23f 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" @@ -25,6 +26,7 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); float calcEffectCost(const ESM::ENAMstruct& effect); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); bool isSummoningEffect(int effectId); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 7bedb1e375..afe6329a21 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -515,8 +515,6 @@ namespace MWMechanics return 0.f; } - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - // Underwater casting not possible if (effect.mRange == ESM::RT_Target) { @@ -530,6 +528,7 @@ namespace MWMechanics return 0.f; } + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) { rating *= -1.f; @@ -565,7 +564,7 @@ namespace MWMechanics } } - rating *= calcEffectCost(effect); + rating *= calcEffectCost(effect, magicEffect); // Currently treating all "on target" or "on touch" effects to target the enemy actor. // Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors. diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 9030d6254a..81bd08626f 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -29,6 +29,9 @@ namespace MWMechanics if (type != -1 && weapon->mData.mType != type) return 0.f; + if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt)) + return 0.f; + float rating=0.f; float rangedMult=1.f; From 7ef6fa9f61ad174bbc74c3cbce19756608e183c9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 18:38:12 +0300 Subject: [PATCH 158/175] Remove deprecated GMST get* functions --- apps/openmw/mwclass/armor.cpp | 8 +- apps/openmw/mwclass/creature.cpp | 22 ++-- apps/openmw/mwclass/creaturelevlist.cpp | 4 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 64 +++++------ apps/openmw/mwclass/weapon.cpp | 6 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 +- apps/openmw/mwgui/dialogue.cpp | 40 +++---- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/hud.cpp | 4 +- apps/openmw/mwgui/jailscreen.cpp | 10 +- apps/openmw/mwgui/merchantrepair.cpp | 4 +- apps/openmw/mwgui/recharge.cpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 10 +- apps/openmw/mwgui/spellicons.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.cpp | 6 +- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwgui/travelwindow.cpp | 6 +- apps/openmw/mwgui/waitdialog.cpp | 6 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 52 ++++----- apps/openmw/mwmechanics/aibreathe.cpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 18 +-- apps/openmw/mwmechanics/aicombataction.cpp | 28 ++--- apps/openmw/mwmechanics/aiwander.cpp | 6 +- apps/openmw/mwmechanics/alchemy.cpp | 12 +- apps/openmw/mwmechanics/autocalcspell.cpp | 18 +-- apps/openmw/mwmechanics/character.cpp | 34 +++--- apps/openmw/mwmechanics/combat.cpp | 54 ++++----- apps/openmw/mwmechanics/creaturestats.cpp | 4 +- apps/openmw/mwmechanics/difficultyscaling.cpp | 2 +- apps/openmw/mwmechanics/disease.hpp | 4 +- apps/openmw/mwmechanics/enchanting.cpp | 14 +-- .../mwmechanics/mechanicsmanagerimp.cpp | 106 +++++++++--------- apps/openmw/mwmechanics/npcstats.cpp | 28 ++--- apps/openmw/mwmechanics/pickpocket.cpp | 6 +- apps/openmw/mwmechanics/repair.cpp | 4 +- apps/openmw/mwmechanics/security.cpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 14 +-- apps/openmw/mwmechanics/spellpriority.cpp | 4 +- apps/openmw/mwmechanics/trading.cpp | 6 +- apps/openmw/mwmechanics/weaponpriority.cpp | 6 +- apps/openmw/mwphysics/physicssystem.cpp | 8 +- apps/openmw/mwrender/weaponanimation.cpp | 8 +- apps/openmw/mwsound/soundmanagerimp.cpp | 16 +-- apps/openmw/mwworld/cellstore.cpp | 4 +- apps/openmw/mwworld/inventorystore.cpp | 6 +- apps/openmw/mwworld/player.cpp | 8 +- apps/openmw/mwworld/projectilemanager.cpp | 2 +- apps/openmw/mwworld/weather.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 16 +-- components/esm/loadgmst.cpp | 15 --- components/esm/loadgmst.hpp | 11 -- 56 files changed, 356 insertions(+), 382 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 0d267046fe..b90c1ec581 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -142,14 +142,14 @@ namespace MWClass const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float iWeight = floor(gmst.find(typeGmst)->getFloat()); + float iWeight = floor(gmst.find(typeGmst)->mValue.getFloat()); float epsilon = 0.0005f; - if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon) + if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->mValue.getFloat() + epsilon) return ESM::Skill::LightArmor; - if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon) + if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->mValue.getFloat() + epsilon) return ESM::Skill::MediumArmor; else @@ -285,7 +285,7 @@ namespace MWClass int armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); - int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->getInt(); + int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); if(ref->mBase->mData.mWeight == 0) return ref->mBase->mData.mArmor; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 7f4694af2e..6999558ae9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -247,7 +247,7 @@ namespace MWClass MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); - float dist = gmst.find("fCombatDistance")->getFloat(); + float dist = gmst.find("fCombatDistance")->mValue.getFloat(); if (!weapon.isEmpty()) dist *= weapon.get()->mBase->mData.mReach; @@ -394,9 +394,9 @@ namespace MWClass if (!attacker.isEmpty()) { // Check for knockdown - float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat(); float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() - * getGmst().iKnockDownOddsMult->getInt() * 0.01f + getGmst().iKnockDownOddsBase->getInt(); + * getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) stats.setKnockedDown(true); else @@ -516,8 +516,8 @@ namespace MWClass const GMST& gmst = getGmst(); - float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); + float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -536,9 +536,9 @@ namespace MWClass { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); - flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat()); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -548,8 +548,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - gmst.fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * + gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } else if(running) @@ -814,8 +814,8 @@ namespace MWClass return; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 4a4d9793ee..b8c4cbb620 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -56,8 +56,8 @@ namespace MWClass else if (creatureStats.isDead()) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay); if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp()) diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index df7c61ebf1..c133b6a266 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -138,7 +138,7 @@ namespace MWClass int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); static const float fWortChanceValue = - MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); MWGui::Widgets::SpellEffectList list; for (int i=0; i<4; ++i) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0b8b60feb0..c247ee543e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -372,9 +372,9 @@ namespace MWClass if (!ref->mBase->mFaction.empty()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("iAutoRepFacMod")->getInt(); + .find("iAutoRepFacMod")->mValue.getInteger(); static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("iAutoRepLevMod")->getInt(); + .find("iAutoRepLevMod")->mValue.getInteger(); int rank = ref->mBase->getFactionRank(); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); @@ -528,7 +528,7 @@ namespace MWClass const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - return store.find("sWerewolfPopup")->getString(); + return store.find("sWerewolfPopup")->mValue.getString(); } const MWWorld::LiveCellRef *ref = ptr.get(); @@ -565,10 +565,10 @@ namespace MWClass MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); - const float fCombatDistance = store.find("fCombatDistance")->getFloat(); + const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat(); float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : - store.find("fHandToHandReach")->getFloat()); + store.find("fHandToHandReach")->mValue.getFloat()); // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; @@ -638,14 +638,14 @@ namespace MWClass && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); if(unaware) { - damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); + damage *= store.find("fCombatCriticalStrikeMult")->mValue.getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } } if (othercls.getCreatureStats(victim).getKnockedDown()) - damage *= store.find("fCombatKODamageMult")->getFloat(); + damage *= store.find("fCombatKODamageMult")->mValue.getFloat(); // Apply "On hit" enchanted weapons MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition); @@ -737,14 +737,14 @@ namespace MWClass const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const GMST& gmst = getGmst(); - int chance = store.get().find("iVoiceHitOdds")->getInt(); + int chance = store.get().find("iVoiceHitOdds")->mValue.getInteger(); if (Misc::Rng::roll0to99() < chance) MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); // Check for knockdown - float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); + float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat(); float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() - * gmst.iKnockDownOddsMult->getInt() * 0.01f + gmst.iKnockDownOddsBase->getInt(); + * gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger(); if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) stats.setKnockedDown(true); else @@ -772,7 +772,7 @@ namespace MWClass float unmitigatedDamage = damage; float x = damage / (damage + getArmorRating(ptr)); - damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); + damage *= std::max(gmst.fCombatArmorMinMult->mValue.getFloat(), x); int damageDiff = static_cast(unmitigatedDamage - damage); damage = std::max(1.f, damage); damageDiff = std::max(1, damageDiff); @@ -941,15 +941,15 @@ namespace MWClass bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); - float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); - walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; + float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* + (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); if(sneaking) - walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); + walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * - gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); + gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); float moveSpeed; if(getEncumbrance(ptr) > getCapacity(ptr)) @@ -959,8 +959,8 @@ namespace MWClass { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); - flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -970,8 +970,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* - gmst.fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } else if(running && !sneaking) @@ -982,7 +982,7 @@ namespace MWClass moveSpeed *= 0.75f; if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) - moveSpeed *= gmst.fWereWolfRunMult->getFloat(); + moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat(); return moveSpeed; } @@ -999,8 +999,8 @@ namespace MWClass const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); - const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + - gmst.fJumpEncumbranceMultiplier->getFloat() * + const float encumbranceTerm = gmst.fJumpEncumbranceBase->mValue.getFloat() + + gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); float a = static_cast(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); @@ -1011,14 +1011,14 @@ namespace MWClass a = 50.0f; } - float x = gmst.fJumpAcrobaticsBase->getFloat() + - std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); - x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); + float x = gmst.fJumpAcrobaticsBase->mValue.getFloat() + + std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->mValue.getFloat()); + x += 3.0f * b * gmst.fJumpAcroMultiplier->mValue.getFloat(); x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64; x *= encumbranceTerm; if(stats.getStance(MWMechanics::CreatureStats::Stance_Run)) - x *= gmst.fJumpRunMultiplier->getFloat(); + x *= gmst.fJumpRunMultiplier->mValue.getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ x /= 3.0f; @@ -1083,7 +1083,7 @@ namespace MWClass float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->getFloat(); + static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->mValue.getFloat(); return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult; } @@ -1126,8 +1126,8 @@ namespace MWClass MWMechanics::NpcStats &stats = getNpcStats(ptr); const MWWorld::InventoryStore &invStore = getInventoryStore(ptr); - float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); - float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); + float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); + float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); float ratings[MWWorld::InventoryStore::Slots]; @@ -1367,8 +1367,8 @@ namespace MWClass return; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); - static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); + static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat(); + static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat(); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bb1ca09a34..294aebd94b 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -287,8 +287,8 @@ namespace MWClass std::string type = mapping[ref->mBase->mData.mType].first; std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; - text += store.get().find(type)->getString() + - ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->getString() : ""); + text += store.get().find(type)->mValue.getString() + + ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->mValue.getString() : ""); // weapon damage if (ref->mBase->mData.mType >= 9) @@ -326,7 +326,7 @@ namespace MWClass if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) { // 64 game units = 1 yard = 3 ft, display value in feet - const float combatDistance = store.get().find("fCombatDistance")->getFloat() * ref->mBase->mData.mReach; + const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); text += " #{sFeet}"; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index c3e56c0bf5..dcc33d1c32 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -261,7 +261,7 @@ namespace MWDialogue const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); - title = gmsts.find (modifiedTopic)->getString(); + title = gmsts.find (modifiedTopic)->mValue.getString(); } else title = topic; @@ -537,7 +537,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - callback->addResponse(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + callback->addResponse(gmsts.find ("sServiceRefusal")->mValue.getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript, mActor); return true; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index aeb6dfc0f5..6aceccaff5 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -365,15 +365,15 @@ namespace MWGui const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - const std::string sPersuasion = gmst.find("sPersuasion")->getString(); - const std::string sCompanionShare = gmst.find("sCompanionShare")->getString(); - const std::string sBarter = gmst.find("sBarter")->getString(); - const std::string sSpells = gmst.find("sSpells")->getString(); - const std::string sTravel = gmst.find("sTravel")->getString(); - const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->getString(); - const std::string sEnchanting = gmst.find("sEnchanting")->getString(); - const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->getString(); - const std::string sRepair = gmst.find("sRepair")->getString(); + const std::string sPersuasion = gmst.find("sPersuasion")->mValue.getString(); + const std::string sCompanionShare = gmst.find("sCompanionShare")->mValue.getString(); + const std::string sBarter = gmst.find("sBarter")->mValue.getString(); + const std::string sSpells = gmst.find("sSpells")->mValue.getString(); + const std::string sTravel = gmst.find("sTravel")->mValue.getString(); + const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->mValue.getString(); + const std::string sEnchanting = gmst.find("sEnchanting")->mValue.getString(); + const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->mValue.getString(); + const std::string sRepair = gmst.find("sRepair")->mValue.getString(); if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter && topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle @@ -458,7 +458,7 @@ namespace MWGui void DialogueWindow::restock() { MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); - float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); + float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->mValue.getFloat(); // Gold is restocked every 24h if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) @@ -503,31 +503,31 @@ namespace MWGui MWBase::Environment::get().getWorld()->getStore().get(); if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + mTopicsList->addItem(gmst.find("sPersuasion")->mValue.getString()); if (services & ESM::NPC::AllItems) - mTopicsList->addItem(gmst.find("sBarter")->getString()); + mTopicsList->addItem(gmst.find("sBarter")->mValue.getString()); if (services & ESM::NPC::Spells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); + mTopicsList->addItem(gmst.find("sSpells")->mValue.getString()); if (travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); + mTopicsList->addItem(gmst.find("sTravel")->mValue.getString()); if (services & ESM::NPC::Spellmaking) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->mValue.getString()); if (services & ESM::NPC::Enchanting) - mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + mTopicsList->addItem(gmst.find("sEnchanting")->mValue.getString()); if (services & ESM::NPC::Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->mValue.getString()); if (services & ESM::NPC::Repair) - mTopicsList->addItem(gmst.find("sRepair")->getString()); + mTopicsList->addItem(gmst.find("sRepair")->mValue.getString()); if (isCompanion()) - mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); + mTopicsList->addItem(gmst.find("sCompanionShare")->mValue.getString()); if (mTopicsList->getItemCount() > 0) mTopicsList->addSeparator(); @@ -592,7 +592,7 @@ namespace MWGui Goodbye* link = new Goodbye(); link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated); mLinks.push_back(link); - std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); + std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->mValue.getString(); BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, textColours.answerPressed, TypesetBook::InteractiveId(link)); diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index f7764e0f14..8fbfa65c68 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -341,7 +341,7 @@ namespace MWGui MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr)) { - std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); + std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->mValue.getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); MWBase::Environment::get().getWindowManager()->messageBox(msg); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1841303f25..91467d490e 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -611,7 +611,7 @@ namespace MWGui // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) mEnemyHealth->setProgressPosition(static_cast(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100)); - static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->getFloat(); + static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarFade")->mValue.getFloat(); if (fNPCHealthBarFade > 0.f) mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade))); @@ -620,7 +620,7 @@ namespace MWGui void HUD::setEnemy(const MWWorld::Ptr &enemy) { mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); - mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->getFloat(); + mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCHealthBarTime")->mValue.getFloat(); if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mEnemyHealth->setVisible(true); diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 9fa5927ac9..1761e13460 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -99,9 +99,9 @@ namespace MWGui std::string message; if (mDays == 1) - message = gmst.find("sNotifyMessage42")->getString(); + message = gmst.find("sNotifyMessage42")->mValue.getString(); else - message = gmst.find("sNotifyMessage43")->getString(); + message = gmst.find("sNotifyMessage43")->mValue.getString(); std::stringstream dayStr; dayStr << mDays; @@ -110,12 +110,12 @@ namespace MWGui for (std::set::iterator it = skills.begin(); it != skills.end(); ++it) { - std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); + std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->mValue.getString(); std::stringstream skillValue; skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); - std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); + std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString(); if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) - skillMsg = gmst.find("sNotifyMessage39")->getString(); + skillMsg = gmst.find("sNotifyMessage39")->mValue.getString(); if (skillMsg.find("%s") != std::string::npos) skillMsg.replace(skillMsg.find("%s"), 2, skillName); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index d3504ebc3a..9974c6d16e 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -57,7 +57,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int basePrice = iter->getClass().getValue(*iter); float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fRepairMult")->getFloat(); + .find("fRepairMult")->mValue.getFloat(); float p = static_cast(std::max(1, basePrice)); float r = static_cast(std::max(1, static_cast(maxDurability / p))); @@ -71,7 +71,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) std::string name = iter->getClass().getName(*iter) + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() - .find("sgp")->getString(); + .find("sgp")->mValue.getString(); MyGUI::Button* button = diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 26a364f726..3133dc0cb2 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -181,7 +181,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) if (gem.getRefData().getCount() == 0) { - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->getString(); + std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage51")->mValue.getString(); message = boost::str(boost::format(message) % gem.getClass().getName(gem)); MWBase::Environment::get().getWindowManager()->messageBox(message); diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index d9c3a5f169..9876013f13 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -44,7 +44,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int price = static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->getFloat()); + int price = static_cast(spell.mData.mCost*store.get().find("fSpellValueMult")->mValue.getFloat()); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MWWorld::Ptr player = MWMechanics::getPlayer(); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 4aa208d8f6..65d66f9e2e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -32,8 +32,8 @@ namespace const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - return gmst.find(ESM::MagicEffect::effectIdToString (id1))->getString() - < gmst.find(ESM::MagicEffect::effectIdToString (id2))->getString(); + return gmst.find(ESM::MagicEffect::effectIdToString (id1))->mValue.getString() + < gmst.find(ESM::MagicEffect::effectIdToString (id2))->mValue.getString(); } void init(ESM::ENAMstruct& effect) @@ -469,7 +469,7 @@ namespace MWGui mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); float fSpellMakingValueMult = - store.get().find("fSpellMakingValueMult")->getFloat(); + store.get().find("fSpellMakingValueMult")->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast(y * fSpellMakingValueMult),true); @@ -547,7 +547,7 @@ namespace MWGui for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get().find( - ESM::MagicEffect::effectIdToString (*it))->getString()); + ESM::MagicEffect::effectIdToString (*it))->mValue.getString()); mButtonMapping[i] = *it; ++i; } @@ -557,7 +557,7 @@ namespace MWGui for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { std::string name = MWBase::Environment::get().getWorld ()->getStore ().get().find( - ESM::MagicEffect::effectIdToString (*it))->getString(); + ESM::MagicEffect::effectIdToString (*it))->mValue.getString(); MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); ToolTips::createMagicEffectToolTip (w, *it); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 27896381ef..c0f56e654e 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -75,7 +75,7 @@ namespace MWGui std::string sourcesDescription; - static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->getFloat(); + static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get().find("fMagicStartIconBlink")->mValue.getFloat(); for (std::vector::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index d3d487579d..db73351bfd 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -61,7 +61,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { - setText (names[i][0], store.get().find (names[i][1])->getString()); + setText (names[i][0], store.get().find (names[i][1])->mValue.getString()); } getWidget(mSkillView, "SkillView"); @@ -306,7 +306,7 @@ namespace MWGui MyGUI::Widget* levelWidget; for (int i=0; i<2; ++i) { - int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); + int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->mValue.getInteger(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index e11147c742..3871baa09d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -313,7 +313,7 @@ namespace MWGui { if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr)) { - std::string msg = gmst.find("sNotifyMessage49")->getString(); + std::string msg = gmst.find("sNotifyMessage49")->mValue.getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); MWBase::Environment::get().getWindowManager()->messageBox(msg); @@ -331,8 +331,8 @@ namespace MWGui // apply disposition change if merchant is NPC if ( mPtr.getClass().isNpc() ) { int dispositionDelta = offerAccepted - ? gmst.find("iBarterSuccessDisposition")->getInt() - : gmst.find("iBarterFailDisposition")->getInt(); + ? gmst.find("iBarterSuccessDisposition")->mValue.getInteger() + : gmst.find("iBarterFailDisposition")->mValue.getInteger(); MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta); } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 630ba8ce1d..b309da27d0 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -98,7 +98,7 @@ namespace MWGui for (int i=0; i<3; ++i) { int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer - (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); + (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger(),true); MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -136,7 +136,7 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); + int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->mValue.getInteger(); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index cf4fb1b5e0..23a5b322fd 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -58,13 +58,13 @@ namespace MWGui if (!mPtr.getCell()->isExterior()) { - price = gmst.find("fMagesGuildTravel")->getInt(); + price = gmst.find("fMagesGuildTravel")->mValue.getInteger(); } else { ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2)); - price = static_cast(d / gmst.find("fTravelMult")->getFloat()); + price = static_cast(d / gmst.find("fTravelMult")->mValue.getFloat()); } price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); @@ -176,7 +176,7 @@ namespace MWGui { ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); - int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); + int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->mValue.getFloat()); for(int i = 0;i < hours;i++) { MWBase::Environment::get().getMechanicsManager ()->rest (true); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 52575e25c1..c5a06a12c9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -183,10 +183,10 @@ 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(); + float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->mValue.getFloat(); if (x < fSleepRandMod * hoursToWait) { - float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); + float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->mValue.getFloat(); int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait); if (interruptAtHoursRemaining != 0) { @@ -252,7 +252,7 @@ namespace MWGui // trigger levelup if possible const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) + if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->mValue.getInteger()) { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e1bf9627de..45897b88cf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -358,7 +358,7 @@ namespace MWGui mGuiModeStates[GM_Journal].mCloseSound = "book close"; mGuiModeStates[GM_Journal].mOpenSound = "book open"; - mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->getFloat()); + mMessageBoxManager = new MessageBoxManager(mStore->get().find("fMessageTimePerChar")->mValue.getFloat()); SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow(); mWindows.push_back(spellBuyingWindow); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 65fc0c0983..b24c8cc149 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1192,7 +1192,7 @@ namespace MWInput void InputManager::updateIdleTime(float dt) { static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() - .find("fVanityDelay")->getFloat(); + .find("fVanityDelay")->mValue.getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; if (mTimeIdle > vanityDelay) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ba8f62e2fa..0ee49ac45e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -54,22 +54,22 @@ int getBoundItemSlot (const std::string& itemId) static std::map boundItemsMap; if (boundItemsMap.empty()) { - std::string boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundBootsID")->getString(); + std::string boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundBootsID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundCuirassID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundCuirassID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundLeftGauntletID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundRightGauntletID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundRightGauntletID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundHelmID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundHelmID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet; - boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundShieldID")->getString(); + boundId = MWBase::Environment::get().getWorld()->getStore().get().find("sMagicBoundShieldID")->mValue.getString(); boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft; } @@ -138,7 +138,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float magicka = 0; if (!stunted) { - float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); + float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); } } @@ -179,7 +179,7 @@ namespace MWMechanics if (caster.isEmpty() || !caster.getClass().isActor()) return; - static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); + static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->mValue.getFloat(); int creatureSoulValue = mCreature.get()->mBase->mData.mSoul; if (creatureSoulValue == 0) @@ -313,9 +313,9 @@ namespace MWMechanics MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) { static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get() - .find("fMaxHeadTrackDistance")->getFloat(); + .find("fMaxHeadTrackDistance")->mValue.getFloat(); static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fInteriorHeadTrackMult")->getFloat(); + .find("fInteriorHeadTrackMult")->mValue.getFloat(); float maxDistance = fMaxHeadTrackDistance; const ESM::Cell* currentCell = actor.getCell()->getCell(); if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) @@ -470,7 +470,7 @@ namespace MWMechanics if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc()) { // Check if the creature is too far - static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->getFloat(); + static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->mValue.getFloat(); if (sqrDist > fAlarmRadius * fAlarmRadius) return; @@ -534,9 +534,9 @@ namespace MWMechanics float base = 1.f; if (ptr == getPlayer()) - base = MWBase::Environment::get().getWorld()->getStore().get().find("fPCbaseMagickaMult")->getFloat(); + base = MWBase::Environment::get().getWorld()->getStore().get().find("fPCbaseMagickaMult")->mValue.getFloat(); else - base = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCbaseMagickaMult")->getFloat(); + base = MWBase::Environment::get().getWorld()->getStore().get().find("fNPCbaseMagickaMult")->mValue.getFloat(); double magickaFactor = base + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1; @@ -584,9 +584,9 @@ namespace MWMechanics return; // Restore fatigue - float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); - float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); - float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); + float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); + float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); + float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; @@ -611,8 +611,8 @@ namespace MWMechanics // Restore fatigue int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); - static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); + static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; @@ -853,14 +853,14 @@ namespace MWMechanics std::string itemGmst = it->second; std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( - itemGmst)->getString(); + itemGmst)->mValue.getString(); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); if (it->first == ESM::MagicEffect::BoundGloves) { item = MWBase::Environment::get().getWorld()->getStore().get().find( - "sMagicBoundRightGauntletID")->getString(); + "sMagicBoundRightGauntletID")->mValue.getString(); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); } } @@ -936,7 +936,7 @@ namespace MWMechanics NpcStats &stats = ptr.getClass().getNpcStats(ptr); // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST - static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->getFloat(); + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); @@ -970,7 +970,7 @@ namespace MWMechanics if(timeLeft == 0.0f && !godmode) { // If drowning, apply 3 points of damage per second - static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->getFloat(); + static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->mValue.getFloat(); DynamicStat health = stats.getHealth(); health.setCurrent(health.getCurrent() - fSuffocationDamage*duration); stats.setHealth(health); @@ -1096,7 +1096,7 @@ namespace MWMechanics && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - static const int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); + static const int cutoff = esmStore.get().find("iCrimeThreshold")->mValue.getInteger(); // Force dialogue on sight if bounty is greater than the cutoff // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff @@ -1104,7 +1104,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); + static const int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->mValue.getInteger(); if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); @@ -1487,9 +1487,9 @@ namespace MWMechanics static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - const int radius = esmStore.get().find("fSneakUseDist")->getInt(); + const int radius = esmStore.get().find("fSneakUseDist")->mValue.getInteger(); - static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->getFloat(); + static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->mValue.getFloat(); if (sneakTimer >= fSneakUseDelay) sneakTimer = 0.f; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 4e0076824b..36acc75d52 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -19,7 +19,7 @@ MWMechanics::AiBreathe::AiBreathe() bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { - static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->getFloat(); + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->mValue.getFloat(); const MWWorld::Class& actorClass = actor.getClass(); if (actorClass.isNpc()) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 438f4f7c69..9824347e32 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -351,7 +351,7 @@ namespace MWMechanics case AiCombatStorage::FleeState_RunToDestination: { - static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->getFloat(); + static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fFleeDistance")->mValue.getFloat(); float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); if ((dist > fFleeDistance && !storage.mLOS) @@ -520,13 +520,13 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float baseDelay = store.get().find("fCombatDelayCreature")->getFloat(); + float baseDelay = store.get().find("fCombatDelayCreature")->mValue.getFloat(); if (actor.getClass().isNpc()) { - baseDelay = store.get().find("fCombatDelayNPC")->getFloat(); + baseDelay = store.get().find("fCombatDelayNPC")->mValue.getFloat(); //say a provoking combat phrase - int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int chance = store.get().find("iVoiceAttackOdds")->mValue.getInteger(); if (Misc::Rng::roll0to99() < chance) { MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); @@ -616,21 +616,21 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t // get projectile speed (depending on weapon type) if (weapType == ESM::Weapon::MarksmanThrown) { - static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); - static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat(); + static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat(); projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } else if (weapType != 0) { - static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat(); + static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; } else // weapType is 0 ==> it's a target spell projectile { - projSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); + projSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat(); } // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 2685e0e0c2..52da1417ed 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -23,8 +23,8 @@ namespace MWMechanics { float suggestCombatRange(int rangeTypes) { - static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->getFloat(); - static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->mValue.getFloat(); + static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->mValue.getFloat(); // This distance is a possible distance of melee attack static float distance = fCombatDistance * std::max(2.f, fHandToHandReach); @@ -114,13 +114,13 @@ namespace MWMechanics { isRanged = false; - static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->getFloat(); - static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); + static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->mValue.getFloat(); + static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->mValue.getFloat(); if (mWeapon.isEmpty()) { static float fHandToHandReach = - MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->mValue.getFloat(); return fHandToHandReach * fCombatDistance; } @@ -336,7 +336,7 @@ namespace MWMechanics float dist = 1.0f; if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty()) { - static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat(); + static const float fHandToHandReach = gmst.find("fHandToHandReach")->mValue.getFloat(); dist = fHandToHandReach; } else if (stats.getDrawState() == MWMechanics::DrawState_Spell) @@ -375,7 +375,7 @@ namespace MWMechanics } } - static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); + static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat(); dist *= std::max(1000.0f, fTargetSpellMaxSpeed); } else if (!activeWeapon.isEmpty()) @@ -383,7 +383,7 @@ namespace MWMechanics const ESM::Weapon* esmWeap = activeWeapon.get()->mBase; if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) { - static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); dist = fTargetSpellMaxSpeed; if (!activeAmmo.isEmpty()) { @@ -399,8 +399,8 @@ namespace MWMechanics dist = (dist > 0.f) ? dist : 1.0f; - static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); - static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat(); + static const float fCombatDistance = gmst.find("fCombatDistance")->mValue.getFloat(); + static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->mValue.getFloat(); float combatDistance = fCombatDistance; if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) @@ -485,20 +485,20 @@ namespace MWMechanics if (flee >= 100) return flee; - static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat(); - static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat(); + static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->mValue.getFloat(); + static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->mValue.getFloat(); float healthPercentage = (stats.getHealth().getModified() == 0.0f) ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified(); float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult; - static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt(); + static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->mValue.getInteger(); if (actor.getClass().isNpc() && enemy.getClass().isNpc()) { if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) { - static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); + static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->mValue.getInteger(); rating = iWereWolfFleeMod; } } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 67468453ab..d36c5930f8 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -495,7 +495,7 @@ namespace MWMechanics MWWorld::Ptr player = getPlayer(); static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() - .get().find("fVoiceIdleOdds")->getFloat(); + .get().find("fVoiceIdleOdds")->mValue.getFloat(); float roll = Misc::Rng::rollProbability() * 10000.0f; @@ -522,7 +522,7 @@ namespace MWMechanics int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = static_cast(hello); static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() - .get().find("iGreetDistanceMultiplier")->getInt(); + .get().find("iGreetDistanceMultiplier")->mValue.getInteger(); helloDistance *= iGreetDistanceMultiplier; @@ -700,7 +700,7 @@ namespace MWMechanics for(unsigned int counter = 0; counter < mIdle.size(); counter++) { static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore() - .get().find("fIdleChanceMultiplier")->getFloat(); + .get().find("fIdleChanceMultiplier")->mValue.getFloat(); unsigned short idleChance = static_cast(fIdleChanceMultiplier * mIdle[counter]); unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier)); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 48705dc72b..b8f8203070 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -140,11 +140,11 @@ void MWMechanics::Alchemy::updateEffects() float x = getAlchemyFactor(); x *= mTools[ESM::Apparatus::MortarPestle].get()->mBase->mData.mQuality; - x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->getFloat(); + x *= MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionStrengthMult")->mValue.getFloat(); // value mValue = static_cast ( - x * MWBase::Environment::get().getWorld()->getStore().get().find ("iAlchemyMod")->getFloat()); + x * MWBase::Environment::get().getWorld()->getStore().get().find ("iAlchemyMod")->mValue.getFloat()); // build quantified effect list for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) @@ -160,13 +160,13 @@ void MWMechanics::Alchemy::updateEffects() } float fPotionT1MagMul = - MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->mValue.getFloat(); if (fPotionT1MagMul<=0) throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); float fPotionT1DurMult = - MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->mValue.getFloat(); if (fPotionT1DurMult<=0) throw std::runtime_error ("invalid gmst: fPotionT1DurMult"); @@ -449,7 +449,7 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); static const float fWortChanceValue = - MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->getFloat(); + MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) || (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2) || (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3) @@ -503,5 +503,5 @@ std::string MWMechanics::Alchemy::suggestPotionName() int effectId = effects.begin()->mId; return MWBase::Environment::get().getWorld()->getStore().get().find( - ESM::MagicEffect::effectIdToString(effectId))->getString(); + ESM::MagicEffect::effectIdToString(effectId))->mValue.getString(); } diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index f655a68b48..a52fcc059d 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -25,7 +25,7 @@ namespace MWMechanics std::vector autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); + static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat(); float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; static const std::string schools[] = { @@ -38,7 +38,7 @@ namespace MWMechanics for (int i=0; i<6; ++i) { const std::string& gmstName = "iAutoSpell" + schools[i] + "Max"; - iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt(); + iAutoSpellSchoolMax[i] = gmst.find(gmstName)->mValue.getInteger(); } init = true; } @@ -70,7 +70,7 @@ namespace MWMechanics continue; if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) continue; - static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt(); + static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger(); if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) continue; @@ -89,7 +89,7 @@ namespace MWMechanics if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) continue; - static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat(); + static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat(); if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) continue; @@ -146,7 +146,7 @@ namespace MWMechanics { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->getFloat(); + static const float fPCbaseMagickaMult = esmStore.get().find("fPCbaseMagickaMult")->mValue.getFloat(); float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; @@ -173,7 +173,7 @@ namespace MWMechanics if (baseMagicka < spell->mData.mCost) continue; - static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->getFloat(); + static const float fAutoPCSpellChance = esmStore.get().find("fAutoPCSpellChance")->mValue.getFloat(); if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) continue; @@ -206,7 +206,7 @@ namespace MWMechanics weakestSpell = spell; minCost = weakestSpell->mData.mCost; } - static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->getInt(); + static const unsigned int iAutoPCSpellMax = esmStore.get().find("iAutoPCSpellMax")->mValue.getInteger(); if (selectedSpells.size() == iAutoPCSpellMax) reachedLimit = true; } @@ -221,7 +221,7 @@ namespace MWMechanics for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectID); - static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->getInt(); + static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get().find("iAutoSpellAttSkillMin")->mValue.getInteger(); if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) { @@ -278,7 +278,7 @@ namespace MWMechanics duration = effect.mDuration; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() - .get().find("fEffectCostMult")->getFloat(); + .get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); x *= 0.1 * magicEffect->mData.mBaseCost; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aca3dde7f5..9d3d6a80ef 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -123,16 +123,16 @@ float getFallDamage(const MWWorld::Ptr& ptr, float fallHeight) MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); + const float fallDistanceMin = store.find("fFallDamageDistanceMin")->mValue.getFloat(); if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics)); const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude(); - const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); - const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); - const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); - const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); + const float fallAcroBase = store.find("fFallAcroBase")->mValue.getFloat(); + const float fallAcroMult = store.find("fFallAcroMult")->mValue.getFloat(); + const float fallDistanceBase = store.find("fFallDistanceBase")->mValue.getFloat(); + const float fallDistanceMult = store.find("fFallDistanceMult")->mValue.getFloat(); float x = fallHeight - fallDistanceMin; x -= (1.5f * acrobaticsSkill) + jumpSpellBonus; @@ -1907,14 +1907,14 @@ void CharacterController::update(float duration) // reduce fatigue const MWWorld::Store &gmst = world->getStore().get(); float fatigueLoss = 0; - static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); - static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); - static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); - static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); - static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); - static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); - static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); - static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); + static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->mValue.getFloat(); + static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->mValue.getFloat(); + static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->mValue.getFloat(); + static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->mValue.getFloat(); + static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->mValue.getFloat(); + static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->mValue.getFloat(); + static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->mValue.getFloat(); + static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->mValue.getFloat(); if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr)) { @@ -1954,8 +1954,8 @@ void CharacterController::update(float duration) forcestateupdate = (mJumpState != JumpState_InAir); jumpstate = JumpState_InAir; - static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); - static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); + static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->mValue.getFloat(); + static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->mValue.getFloat(); float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; factor = std::min(1.f, factor); vec.x() *= factor; @@ -1982,8 +1982,8 @@ void CharacterController::update(float duration) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); // decrease fatigue - const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); - const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); + const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); + const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat(); float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 06ed99f5ae..be55b681f1 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -82,9 +82,9 @@ namespace MWMechanics osg::Vec3f(0,0,1))); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->getFloat()) + if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->mValue.getFloat()) return false; - if (angleDegrees > gmst.find("fCombatBlockRightAngle")->getFloat()) + if (angleDegrees > gmst.find("fCombatBlockRightAngle")->mValue.getFloat()) return false; MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); @@ -92,11 +92,11 @@ namespace MWMechanics float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() + 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); float enemySwing = attackStrength; - float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); + float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->mValue.getFloat() + gmst.find("fSwingBlockBase")->mValue.getFloat(); float blockerTerm = blockTerm * swingTerm; if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) - blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); + blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); int attackerSkill = 0; @@ -109,8 +109,8 @@ namespace MWMechanics attackerTerm *= attackerStats.getFatigueTerm(); int x = int(blockerTerm - attackerTerm); - int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); - int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); + int iBlockMaxChance = gmst.find("iBlockMaxChance")->mValue.getInteger(); + int iBlockMinChance = gmst.find("iBlockMinChance")->mValue.getInteger(); x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); if (Misc::Rng::roll0to99() < x) @@ -126,9 +126,9 @@ namespace MWMechanics inv.unequipItem(*shield, blocker); } // Reduce blocker fatigue - const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); - const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); - const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); + const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->mValue.getFloat(); + const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->mValue.getFloat(); + const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->mValue.getFloat(); MWMechanics::DynamicStat fatigue = blockerStats.getFatigue(); float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); @@ -166,7 +166,7 @@ namespace MWMechanics if ((weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) - damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->getFloat(); + damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->mValue.getFloat(); if (damage == 0 && attacker == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); @@ -219,14 +219,14 @@ namespace MWMechanics if (unaware) { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + damage *= gmst.find("fCombatCriticalStrikeMult")->mValue.getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } } if (victim.getClass().getCreatureStats(victim).getKnockedDown()) - damage *= gmst.find("fCombatKODamageMult")->getFloat(); + damage *= gmst.find("fCombatKODamageMult")->mValue.getFloat(); } reduceWeaponCondition(damage, validVictim, weapon, attacker); @@ -241,7 +241,7 @@ namespace MWMechanics // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory if (victim != getPlayer() && !appliedEnchantment) { - float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); + float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->mValue.getFloat(); if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f) victim.getClass().getContainerStore(victim).add(projectile, 1, victim); } @@ -273,10 +273,10 @@ namespace MWMechanics defenseTerm = victimStats.getEvasion(); } defenseTerm += std::min(100.f, - gmst.find("fCombatInvisoMult")->getFloat() * + gmst.find("fCombatInvisoMult")->mValue.getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude()); defenseTerm += std::min(100.f, - gmst.find("fCombatInvisoMult")->getFloat() * + gmst.find("fCombatInvisoMult")->mValue.getFloat() * victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude()); } float attackTerm = skillValue + @@ -322,7 +322,7 @@ namespace MWMechanics x = std::min(100.f, x + elementResistance); - static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); + static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->mValue.getFloat(); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); // Note swapped victim and attacker, since the attacker takes the damage here. @@ -352,7 +352,7 @@ namespace MWMechanics // weapon condition does not degrade when godmode is on if (!godmode) { - const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->getFloat(); + const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get().find("fWeaponDamageMult")->mValue.getFloat(); float x = std::max(1.f, fWeaponDamageMult * damage); weaphealth -= std::min(int(x), weaphealth); @@ -379,9 +379,9 @@ namespace MWMechanics } static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get() - .find("fDamageStrengthBase")->getFloat(); + .find("fDamageStrengthBase")->mValue.getFloat(); static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fDamageStrengthMult")->getFloat(); + .find("fDamageStrengthMult")->mValue.getFloat(); damage *= fDamageStrengthBase + (attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f); } @@ -389,8 +389,8 @@ namespace MWMechanics void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) { const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); - float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); + float minstrike = store.get().find("fMinHandToHandMult")->mValue.getFloat(); + float maxstrike = store.get().find("fMaxHandToHandMult")->mValue.getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); @@ -415,7 +415,7 @@ namespace MWMechanics damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult"); } if(healthdmg) - damage *= store.get().find("fHandtoHandHealthPer")->getFloat(); + damage *= store.get().find("fHandtoHandHealthPer")->mValue.getFloat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(isWerewolf) @@ -432,9 +432,9 @@ namespace MWMechanics { // somewhat of a guess, but using the weapon weight makes sense const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); - const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); - const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); - const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); + const float fFatigueAttackBase = store.find("fFatigueAttackBase")->mValue.getFloat(); + const float fFatigueAttackMult = store.find("fFatigueAttackMult")->mValue.getFloat(); + const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->mValue.getFloat(); CreatureStats& stats = attacker.getClass().getCreatureStats(attacker); MWMechanics::DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker); @@ -459,9 +459,9 @@ namespace MWMechanics float d = (pos1 - pos2).length(); static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( - "iFightDistanceBase")->getInt(); + "iFightDistanceBase")->mValue.getInteger(); static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDistanceMultiplier")->getFloat(); + "fFightDistanceMultiplier")->mValue.getFloat(); return (iFightDistanceBase - fFightDistanceMultiplier * d); } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index d1128860cf..2994eac289 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -48,8 +48,8 @@ namespace MWMechanics const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fFatigueBase = gmst.find("fFatigueBase")->getFloat(); - static const float fFatigueMult = gmst.find("fFatigueMult")->getFloat(); + static const float fFatigueBase = gmst.find("fFatigueBase")->mValue.getFloat(); + static const float fFatigueMult = gmst.find("fFatigueMult")->mValue.getFloat(); return fFatigueBase - fFatigueMult * (1-normalised); } diff --git a/apps/openmw/mwmechanics/difficultyscaling.cpp b/apps/openmw/mwmechanics/difficultyscaling.cpp index 53cf47dea2..2376989745 100644 --- a/apps/openmw/mwmechanics/difficultyscaling.cpp +++ b/apps/openmw/mwmechanics/difficultyscaling.cpp @@ -17,7 +17,7 @@ float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr difficultySetting = std::min(difficultySetting, 500); difficultySetting = std::max(difficultySetting, -500); - static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); + static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->mValue.getFloat(); float difficultyTerm = 0.01f * difficultySetting; diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index 5746af2a2f..fec3bdd376 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -28,7 +28,7 @@ namespace MWMechanics float fDiseaseXferChance = MWBase::Environment::get().getWorld()->getStore().get().find( - "fDiseaseXferChance")->getFloat(); + "fDiseaseXferChance")->mValue.getFloat(); MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects(); @@ -59,7 +59,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).getSpells().add(it->first); std::string msg = "sMagicContractDisease"; - msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->mValue.getString(); if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, spell->mName); MWBase::Environment::get().getWindowManager()->messageBox(msg); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 08f30aba1b..c0bffc4cb5 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -107,7 +107,7 @@ namespace MWMechanics } const bool powerfulSoul = getGemCharge() >= \ - MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); + MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->mValue.getInteger(); if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) { // Armor or Clothing switch(mCastStyle) @@ -184,7 +184,7 @@ namespace MWMechanics float magnitudeCost = (magMin + magMax) * baseCost * 0.05f; if (mCastStyle == ESM::Enchantment::ConstantEffect) { - magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->getFloat(); + magnitudeCost *= store.get().find("fEnchantmentConstantDurationMult")->mValue.getFloat(); } else { @@ -193,7 +193,7 @@ namespace MWMechanics float areaCost = area * 0.05f * baseCost; - const float fEffectCostMult = store.get().find("fEffectCostMult")->getFloat(); + const float fEffectCostMult = store.get().find("fEffectCostMult")->mValue.getFloat(); cost += (magnitudeCost + areaCost) * fEffectCostMult; @@ -230,7 +230,7 @@ namespace MWMechanics if(mEnchanter.isEmpty()) return 0; - float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); + float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->mValue.getFloat(); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast(getEnchantPoints() * priceMultipler), true); return price; } @@ -256,7 +256,7 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - return static_cast(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->getFloat()); + return static_cast(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->mValue.getFloat()); } bool Enchanting::soulEmpty() const { @@ -288,8 +288,8 @@ namespace MWMechanics const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? - gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1.0f )) + float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->mValue.getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? + gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat() : 1.0f )) * getEnchantPoints(); return (chance1-chance2); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index e7343e23a7..859b2e5229 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -36,7 +36,7 @@ namespace float getFightDispositionBias(float disposition) { static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fFightDispMult")->getFloat(); + "fFightDispMult")->mValue.getFloat(); return ((50.f - disposition) * fFightDispMult); } @@ -45,11 +45,11 @@ namespace const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); - float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); - float repTerm = stats.getReputation() * gmst.find("fReputationMod")->getFloat(); + float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->mValue.getFloat(); + float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->mValue.getFloat(); + float repTerm = stats.getReputation() * gmst.find("fReputationMod")->mValue.getFloat(); float fatigueTerm = stats.getFatigueTerm(); - float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->getFloat(); + float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->mValue.getFloat(); rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; @@ -342,7 +342,7 @@ namespace MWMechanics if(timeToDrown != mWatchedTimeToStartDrowning) { static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get() - .find("fHoldBreathTime")->getFloat(); + .find("fHoldBreathTime")->mValue.getFloat(); mWatchedTimeToStartDrowning = timeToDrown; @@ -543,12 +543,12 @@ namespace MWMechanics const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fDispRaceMod = gmst.find("fDispRaceMod")->getFloat(); + static const float fDispRaceMod = gmst.find("fDispRaceMod")->mValue.getFloat(); if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace)) x += fDispRaceMod; - static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->getFloat(); - static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->getFloat(); + static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->mValue.getFloat(); + static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->mValue.getFloat(); x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase); float reaction = 0; @@ -592,20 +592,20 @@ namespace MWMechanics rank = 0; } - static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->getFloat(); - static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->getFloat(); - static const float fDispFactionMod = gmst.find("fDispFactionMod")->getFloat(); + static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->mValue.getFloat(); + static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->mValue.getFloat(); + static const float fDispFactionMod = gmst.find("fDispFactionMod")->mValue.getFloat(); x += (fDispFactionRankMult * rank + fDispFactionRankBase) * fDispFactionMod * reaction; - static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->getFloat(); - static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->getFloat(); + static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->mValue.getFloat(); + static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->mValue.getFloat(); x -= fDispCrimeMod * playerStats.getBounty(); if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) x += fDispDiseaseMod; - static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->getFloat(); + static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->mValue.getFloat(); if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += fDispWeaponDrawn; @@ -673,16 +673,16 @@ namespace MWMechanics float target2 = d * (playerRating2 - npcRating2 + 50); float bribeMod; - if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); - else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); - else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); + if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->mValue.getFloat(); + else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->mValue.getFloat(); + else bribeMod = gmst.find("fBribe1000Mod")->mValue.getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; - float iPerMinChance = floor(gmst.find("iPerMinChance")->getFloat()); - float iPerMinChange = floor(gmst.find("iPerMinChange")->getFloat()); - float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); - float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); + float iPerMinChance = floor(gmst.find("iPerMinChance")->mValue.getFloat()); + float iPerMinChange = floor(gmst.find("iPerMinChange")->mValue.getFloat()); + float fPerDieRollMult = gmst.find("fPerDieRollMult")->mValue.getFloat(); + float fPerTempMult = gmst.find("fPerTempMult")->mValue.getFloat(); float x = 0; float y = 0; @@ -867,7 +867,7 @@ namespace MWMechanics continue; // All sMagicBound* GMST's should be of type string - std::string currentGMSTValue = currentSetting.getString(); + std::string currentGMSTValue = currentSetting.mValue.getString(); Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); boundItemIDCache.insert(currentGMSTValue); @@ -1163,7 +1163,7 @@ namespace MWMechanics osg::Vec3f from (player.getRefData().getPosition().asVec3()); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + float radius = esmStore.get().find("fAlarmRadius")->mValue.getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -1261,29 +1261,29 @@ namespace MWMechanics float disp = 0.f, dispVictim = 0.f; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) { - arg = store.find("iCrimeTresspass")->getInt(); - disp = dispVictim = store.find("iDispTresspass")->getFloat(); + arg = store.find("iCrimeTresspass")->mValue.getInteger(); + disp = dispVictim = store.find("iDispTresspass")->mValue.getFloat(); } else if (type == OT_Pickpocket) { - arg = store.find("iCrimePickPocket")->getInt(); - disp = dispVictim = store.find("fDispPickPocketMod")->getFloat(); + arg = store.find("iCrimePickPocket")->mValue.getInteger(); + disp = dispVictim = store.find("fDispPickPocketMod")->mValue.getFloat(); } else if (type == OT_Assault) { - arg = store.find("iCrimeAttack")->getInt(); - disp = store.find("iDispAttackMod")->getFloat(); - dispVictim = store.find("fDispAttacking")->getFloat(); + arg = store.find("iCrimeAttack")->mValue.getInteger(); + disp = store.find("iDispAttackMod")->mValue.getFloat(); + dispVictim = store.find("fDispAttacking")->mValue.getFloat(); } else if (type == OT_Murder) { - arg = store.find("iCrimeKilling")->getInt(); - disp = dispVictim = store.find("iDispKilling")->getFloat(); + arg = store.find("iCrimeKilling")->mValue.getInteger(); + disp = dispVictim = store.find("iDispKilling")->mValue.getFloat(); } else if (type == OT_Theft) { - disp = dispVictim = store.find("fDispStealing")->getFloat() * arg; - arg = static_cast(arg * store.find("fCrimeStealing")->getFloat()); + disp = dispVictim = store.find("fDispStealing")->mValue.getFloat() * arg; + arg = static_cast(arg * store.find("fCrimeStealing")->mValue.getFloat()); arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } @@ -1293,7 +1293,7 @@ namespace MWMechanics const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); osg::Vec3f from (player.getRefData().getPosition().asVec3()); - float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + float radius = esmStore.get().find("fAlarmRadius")->mValue.getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -1307,21 +1307,21 @@ namespace MWMechanics // Controls whether witnesses will engage combat with the criminal. int fight = 0, fightVictim = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - fight = fightVictim = esmStore.get().find("iFightTrespass")->getInt(); + fight = fightVictim = esmStore.get().find("iFightTrespass")->mValue.getInteger(); else if (type == OT_Pickpocket) { - fight = esmStore.get().find("iFightPickpocket")->getInt(); - fightVictim = esmStore.get().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki + fight = esmStore.get().find("iFightPickpocket")->mValue.getInteger(); + fightVictim = esmStore.get().find("iFightPickpocket")->mValue.getInteger() * 4; // *4 according to research wiki } else if (type == OT_Assault) { - fight = esmStore.get().find("iFightAttacking")->getInt(); - fightVictim = esmStore.get().find("iFightAttack")->getInt(); + fight = esmStore.get().find("iFightAttacking")->mValue.getInteger(); + fightVictim = esmStore.get().find("iFightAttack")->mValue.getInteger(); } else if (type == OT_Murder) - fight = fightVictim = esmStore.get().find("iFightKilling")->getInt(); + fight = fightVictim = esmStore.get().find("iFightKilling")->mValue.getInteger(); else if (type == OT_Theft) - fight = fightVictim = esmStore.get().find("fFightStealing")->getInt(); + fight = fightVictim = esmStore.get().find("fFightStealing")->mValue.getInteger(); bool reported = false; @@ -1552,8 +1552,8 @@ namespace MWMechanics && !MWBase::Environment::get().getWorld()->isSwimming(ptr) && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { - static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); - static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); + static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); + static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); @@ -1568,8 +1568,8 @@ namespace MWMechanics sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult; } - static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); - static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); + static float fSneakDistBase = store.find("fSneakDistanceBase")->mValue.getFloat(); + static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->mValue.getFloat(); osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3()); @@ -1587,8 +1587,8 @@ namespace MWMechanics float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; // is ptr behind the observer? - static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); - static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); + static float fSneakNoViewMult = store.find("fSneakNoViewMult")->mValue.getFloat(); + static float fSneakViewMult = store.find("fSneakViewMult")->mValue.getFloat(); float y = 0; osg::Vec3f vec = pos1 - pos2; if (observer.getRefData().getBaseNode()) @@ -1748,7 +1748,7 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf"))) { const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get().find("iWerewolfFightMod"); - fight += iWerewolfFightMod->getInt(); + fight += iWerewolfFightMod->mValue.getInteger(); } } @@ -1841,7 +1841,7 @@ namespace MWMechanics // Witnesses of the player's transformation will make them a globally known werewolf std::vector closeActors; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->getFloat(), closeActors); + getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->mValue.getFloat(), closeActors); bool detected = false, reported = false; for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) @@ -1866,7 +1866,7 @@ namespace MWMechanics if (reported) { npcStats.setBounty(npcStats.getBounty()+ - gmst.find("iWereWolfBounty")->getInt()); + gmst.find("iWereWolfBounty")->mValue.getInteger()); windowManager->messageBox("#{sCrimeMessage}"); } } @@ -1878,7 +1878,7 @@ namespace MWMechanics const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); - stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->mValue.getInteger()); } void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr &caster, int creatureActorId) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index f15152759f..cb9ef54773 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -149,12 +149,12 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - float typeFactor = gmst.find ("fMiscSkillBonus")->getFloat(); + float typeFactor = gmst.find ("fMiscSkillBonus")->mValue.getFloat(); for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][0]==skillIndex) { - typeFactor = gmst.find ("fMinorSkillBonus")->getFloat(); + typeFactor = gmst.find ("fMinorSkillBonus")->mValue.getFloat(); break; } @@ -162,7 +162,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const for (int i=0; i<5; ++i) if (class_.mData.mSkills[i][1]==skillIndex) { - typeFactor = gmst.find ("fMajorSkillBonus")->getFloat(); + typeFactor = gmst.find ("fMajorSkillBonus")->mValue.getFloat(); break; } @@ -178,7 +178,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const MWBase::Environment::get().getWorld()->getStore().get().find (skillIndex); if (skill->mData.mSpecialization==class_.mData.mSpecialization) { - specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat(); + specialisationFactor = gmst.find ("fSpecialSkillBonus")->mValue.getFloat(); if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); @@ -227,21 +227,21 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWorld()->getStore().get(); // is this a minor or major skill? - int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo + int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][0] == skillIndex) { - mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); - increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); + mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger(); + increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger(); } } for (int k=0; k<5; ++k) { if (class_.mData.mSkills[k][1] == skillIndex) { - mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); - increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); + mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger(); + increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger(); } } @@ -249,7 +249,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWorld ()->getStore ().get().find(skillIndex); mSkillIncreases[skill->mData.mAttribute] += increase; - mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->getInt(); + mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); // Play sound & skill progress notification /// \todo check if character is the player, if levelling is ever implemented for NPCs @@ -266,7 +266,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); - if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) + if (mLevelProgress >= gmst.find("iLevelUpTotal")->mValue.getInteger()) { // levelup is possible now MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); @@ -287,7 +287,7 @@ void MWMechanics::NpcStats::levelUp() const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); + mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger(); mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console for (int i=0; igetFloat()); + setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat()); setLevel(getLevel()+1); } @@ -324,7 +324,7 @@ int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const std::stringstream gmst; gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; - return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->getInt(); + return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->mValue.getInteger(); } int MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp index eca24606e6..ed64691065 100644 --- a/apps/openmw/mwmechanics/pickpocket.cpp +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -37,9 +37,9 @@ namespace MWMechanics float pcSneak = static_cast(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak)); int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() - .find("iPickMinChance")->getInt(); + .find("iPickMinChance")->mValue.getInteger(); int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() - .find("iPickMaxChance")->getInt(); + .find("iPickMaxChance")->mValue.getInteger(); int roll = Misc::Rng::roll0to99(); if (t < pcSneak / iPickMinChance) @@ -57,7 +57,7 @@ namespace MWMechanics { float stackValue = static_cast(item.getClass().getValue(item) * count); float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() - .find("fPickPocketMod")->getFloat(); + .find("fPickPocketMod")->mValue.getFloat(); float valueTerm = 10 * fPickPocketMod * stackValue; return getDetected(valueTerm); diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 5c6123e822..3ebef36bf1 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -42,7 +42,7 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fRepairAmountMult")->getFloat(); + .find("fRepairAmountMult")->mValue.getFloat(); float toolQuality = ref->mBase->mData.mQuality; @@ -87,7 +87,7 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) store.remove(mTool, 1, player); std::string message = MWBase::Environment::get().getWorld()->getStore().get() - .find("sNotifyMessage51")->getString(); + .find("sNotifyMessage51")->mValue.getString(); MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % mTool.getClass().getName(mTool)).str()); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 3e38bc5219..c8a4dd7706 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -40,7 +40,7 @@ namespace MWMechanics float pickQuality = lockpick.get()->mBase->mData.mQuality; - float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); + float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->mValue.getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x *= pickQuality * mFatigueTerm; @@ -81,7 +81,7 @@ namespace MWMechanics const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().getTrap()); int trapSpellPoints = trapSpell->mData.mCost; - float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); + float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->mValue.getFloat(); float x = 0.2f * mAgility + 0.1f * mLuck + mSecuritySkill; x += fTrapCostMult * trapSpellPoints; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 76140013d4..9e163f132c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -62,7 +62,7 @@ namespace MWMechanics duration = effect.mDuration; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() - .get().find("fEffectCostMult")->getFloat(); + .get().find("fEffectCostMult")->mValue.getFloat(); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); x *= 0.1 * magicEffect->mData.mBaseCost; @@ -93,7 +93,7 @@ namespace MWMechanics if (it->mRange == ESM::RT_Target) x *= 1.5f; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->getFloat(); + "fEffectCostMult")->mValue.getFloat(); x *= fEffectCostMult; float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); @@ -883,8 +883,8 @@ namespace MWMechanics if (!godmode) { // Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss) - static const float fFatigueSpellBase = store.get().find("fFatigueSpellBase")->getFloat(); - static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->getFloat(); + static const float fFatigueSpellBase = store.get().find("fFatigueSpellBase")->mValue.getFloat(); + static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->mValue.getFloat(); DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster); @@ -968,7 +968,7 @@ namespace MWMechanics if (roll > x) { // "X has no effect on you" - std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->getString(); + std::string message = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage50")->mValue.getString(); message = boost::str(boost::format(message) % ingredient->mName); MWBase::Environment::get().getWindowManager()->messageBox(message); return false; @@ -1251,7 +1251,7 @@ namespace MWMechanics float damageScale = 1.f - timeDiff / 7.f; // When cloudy, the sun damage effect is halved static float fMagicSunBlockedMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicSunBlockedMult")->getFloat(); + "fMagicSunBlockedMult")->mValue.getFloat(); int weather = MWBase::Environment::get().getWorld()->getCurrentWeather(); if (weather > 1) @@ -1348,7 +1348,7 @@ namespace MWMechanics if (it == summonMap.end()) return std::string(); else - return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); + return MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->mValue.getString(); } void ApplyLoopingParticlesVisitor::visit (MWMechanics::EffectKey key, diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 7bedb1e375..9a366a916f 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -593,8 +593,8 @@ namespace MWMechanics { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat(); - static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat(); + static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->mValue.getFloat(); + static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->mValue.getFloat(); float mult = fAIMagicSpellMult; diff --git a/apps/openmw/mwmechanics/trading.cpp b/apps/openmw/mwmechanics/trading.cpp index eee5e64499..b824d7c450 100644 --- a/apps/openmw/mwmechanics/trading.cpp +++ b/apps/openmw/mwmechanics/trading.cpp @@ -50,11 +50,11 @@ namespace MWMechanics float e1 = 0.1f * merchantStats.getAttribute(ESM::Attribute::Luck).getModified(); float f1 = 0.2f * merchantStats.getAttribute(ESM::Attribute::Personality).getModified(); - float dispositionTerm = gmst.find("fDispositionMod")->getFloat() * (clampedDisposition - 50); + float dispositionTerm = gmst.find("fDispositionMod")->mValue.getFloat() * (clampedDisposition - 50); float pcTerm = (dispositionTerm + a1 + b1 + c1) * playerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * merchantStats.getFatigueTerm(); - float x = gmst.find("fBargainOfferMulti")->getFloat() * d - + gmst.find("fBargainOfferBase")->getFloat() + float x = gmst.find("fBargainOfferMulti")->mValue.getFloat() * d + + gmst.find("fBargainOfferBase")->mValue.getFloat() + int(pcTerm - npcTerm); int roll = Misc::Rng::rollDice(100) + 1; diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 9030d6254a..7e33784c17 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -157,9 +157,9 @@ namespace MWMechanics { const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat(); - static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat(); - static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat(); + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->mValue.getFloat(); + static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); if (weapon.isEmpty()) return 0.f; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2a7b65cac0..90668914f2 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -303,7 +303,7 @@ namespace MWPhysics position.z() += halfExtents.z(); static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() - .find("fSwimHeightScale")->getFloat(); + .find("fSwimHeightScale")->mValue.getFloat(); float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); ActorTracer tracer; @@ -339,7 +339,7 @@ namespace MWPhysics osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length()))); static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() - .find("fStromWalkMult")->getFloat(); + .find("fStromWalkMult")->mValue.getFloat(); velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f)); } @@ -860,8 +860,8 @@ namespace MWPhysics // Use cone shape as fallback const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); - btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->getFloat()/2.0f), queryDistance); - shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->getFloat()/2.0f) / + btConeShape shape (osg::DegreesToRadians(store.find("fCombatAngleXY")->mValue.getFloat()/2.0f), queryDistance); + shape.setLocalScaling(btVector3(1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->mValue.getFloat()/2.0f) / shape.getRadius())); // The shape origin is its center, so we have to move it forward by half the length. The diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 054ce1b1d3..32990482a2 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -119,8 +119,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); - float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); - float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat(); + float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat(); float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength; MWWorld::Ptr weaponPtr = *weapon; @@ -146,8 +146,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength) return; osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans(); - float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); - float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat(); + float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat(); float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength; MWWorld::Ptr weaponPtr = *weapon; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d6dc2ba99c..9c12584d71 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -139,10 +139,10 @@ namespace MWSound Sound_Buffer *SoundManager::insertSound(const std::string &soundId, const ESM::Sound *sound) { MWBase::World* world = MWBase::Environment::get().getWorld(); - static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->getFloat(); - static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->getFloat(); - static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); - static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); + static const float fAudioDefaultMinDistance = world->getStore().get().find("fAudioDefaultMinDistance")->mValue.getFloat(); + static const float fAudioDefaultMaxDistance = world->getStore().get().find("fAudioDefaultMaxDistance")->mValue.getFloat(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->mValue.getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->mValue.getFloat(); float volume, min, max; volume = static_cast(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); @@ -296,10 +296,10 @@ namespace MWSound Stream *SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) { MWBase::World* world = MWBase::Environment::get().getWorld(); - static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); - static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->getFloat(); - static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->getFloat(); - static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->getFloat(); + static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->mValue.getFloat(); + static const float fAudioMaxDistanceMult = world->getStore().get().find("fAudioMaxDistanceMult")->mValue.getFloat(); + static const float fAudioVoiceDefaultMinDistance = world->getStore().get().find("fAudioVoiceDefaultMinDistance")->mValue.getFloat(); + static const float fAudioVoiceDefaultMaxDistance = world->getStore().get().find("fAudioVoiceDefaultMaxDistance")->mValue.getFloat(); static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 40e904ec2b..05ff1e3260 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -944,7 +944,7 @@ namespace MWWorld void clearCorpse(const MWWorld::Ptr& ptr) { const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); - static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get().find("fCorpseClearDelay")->getFloat(); + static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get().find("fCorpseClearDelay")->mValue.getFloat(); if (creatureStats.isDead() && creatureStats.isDeathAnimationFinished() && !ptr.getClass().isPersistent(ptr) && @@ -958,7 +958,7 @@ namespace MWWorld { if (mState == State_Loaded) { - static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); + static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->mValue.getInteger(); if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) { mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 6cfffcde6a..7f3018c559 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -252,8 +252,8 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) const MWWorld::Store &store = world->getStore().get(); MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); - static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); - static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); + static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); + static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); @@ -921,7 +921,7 @@ void MWWorld::InventoryStore::rechargeItems(float duration) continue; static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( - "fMagicItemRechargePerSecond")->getFloat(); + "fMagicItemRechargePerSecond")->mValue.getFloat(); if (it->first->getCellRef().getEnchantmentCharge() <= it->second) { diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 44b78336d9..389f599832 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -64,7 +64,7 @@ namespace MWWorld MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer()); MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer()); MWMechanics::DynamicStat health = creatureStats.getDynamic(0); - creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->getFloat())); + creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->mValue.getFloat())); for (int i=0; i health = creatureStats.getDynamic(0); - creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->getFloat())); + creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->mValue.getFloat())); for(size_t i = 0;i < ESM::Attribute::Length;++i) { // Oh, Bethesda. It's "Intelligence". @@ -85,7 +85,7 @@ namespace MWWorld ESM::Attribute::sAttributeNames[i]); MWMechanics::AttributeValue value = npcStats.getAttribute(i); - value.setBase(int(gmst.find(name)->getFloat())); + value.setBase(int(gmst.find(name)->mValue.getFloat())); npcStats.setAttribute(i, value); } @@ -100,7 +100,7 @@ namespace MWWorld ESM::Skill::sSkillNames[i]); MWMechanics::SkillValue value = npcStats.getSkill(i); - value.setBase(int(gmst.find(name)->getFloat())); + value.setBase(int(gmst.find(name)->mValue.getFloat())); npcStats.setSkill(i, value); } } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index f150ff0bab..21218fab01 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -385,7 +385,7 @@ namespace MWWorld { osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() - .find("fTargetSpellMaxSpeed")->getFloat(); + .find("fTargetSpellMaxSpeed")->mValue.getFloat(); float speed = fTargetSpellMaxSpeed * it->mSpeed; osg::Vec3f direction = orient * osg::Vec3f(0,1,0); direction.normalize(); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index d12e633beb..2a9e8d7cc0 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -911,7 +911,7 @@ inline void WeatherManager::addWeather(const std::string& name, float dlFactor, float dlOffset, const std::string& particleEffect) { - static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); + static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->mValue.getFloat(); Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0bc952e897..48cb1dda3c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -190,7 +190,7 @@ namespace MWWorld mStore.setUp(true); mStore.movePlayerRecord(); - mSwimHeightScale = mStore.get().find("fSwimHeightScale")->getFloat(); + mSwimHeightScale = mStore.get().find("fSwimHeightScale")->mValue.getFloat(); mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mFallback, mStore)); @@ -1026,7 +1026,7 @@ namespace MWWorld if (mActivationDistanceOverride >= 0) return static_cast(mActivationDistanceOverride); - static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->getInt(); + static const int iMaxActivateDist = getStore().get().find("iMaxActivateDist")->mValue.getInteger(); return static_cast(iMaxActivateDist); } @@ -1709,7 +1709,7 @@ namespace MWWorld bool swimming = isSwimming(player); bool flying = isFlying(player); - static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->getFloat(); + static const float i1stPersonSneakDelta = getStore().get().find("i1stPersonSneakDelta")->mValue.getFloat(); if (sneaking && !swimming && !flying) mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta); else @@ -2803,7 +2803,7 @@ namespace MWWorld if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell) stats.getAiSequence().getCombatTargets(targetActors); - const float fCombatDistance = getStore().get().find("fCombatDistance")->getFloat(); + const float fCombatDistance = getStore().get().find("fCombatDistance")->mValue.getFloat(); osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); @@ -3267,8 +3267,8 @@ namespace MWWorld int bounty = player.getClass().getNpcStats(player).getBounty(); int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId); - float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); - float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); + float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->mValue.getFloat(); + float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->mValue.getFloat(); int discount = static_cast(bounty * fCrimeGoldDiscountMult); int turnIn = static_cast(bounty * fCrimeGoldTurnInMult); @@ -3333,7 +3333,7 @@ namespace MWWorld mPlayer->recordCrimeId(); confiscateStolenItems(player); - int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->getInt(); + int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->mValue.getInteger(); mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod); return; @@ -3391,7 +3391,7 @@ namespace MWWorld { const ESM::CreatureLevList* list = getStore().get().find(creatureList); - int iNumberCreatures = getStore().get().find("iNumberCreatures")->getInt(); + int iNumberCreatures = getStore().get().find("iNumberCreatures")->mValue.getInteger(); int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures] for (int i=0; i Date: Thu, 30 Aug 2018 23:52:33 +0300 Subject: [PATCH 159/175] Grant Enchant experience even if recharging fails (bug #4622) --- CHANGELOG.md | 1 + apps/openmw/mwgui/recharge.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5453ffb497..8ae6088de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,7 @@ Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying + Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 26a364f726..8bd8e250b0 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -169,14 +169,13 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); player.getClass().getContainerStore(player).restack(item); - - player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); } 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) From 374e98d665f303f2a981b4d2dfcc5601e073c48d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 23:04:02 +0300 Subject: [PATCH 160/175] Make rateWeapon more sensible and account for weapon speed --- apps/openmw/mwmechanics/weaponpriority.cpp | 71 +++++++++------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index ac01a07146..8d09bd8d19 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -21,13 +21,19 @@ namespace MWMechanics float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type, float arrowRating, float boltRating) { - if (item.getTypeName() != typeid(ESM::Weapon).name()) + if (enemy.isEmpty() || item.getTypeName() != typeid(ESM::Weapon).name()) + return 0.f; + + if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) == 0) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; if (type != -1 && weapon->mData.mType != type) return 0.f; + + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWWorld::Store& gmst = world->getStore().get(); if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt)) return 0.f; @@ -37,54 +43,34 @@ namespace MWMechanics if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { - // Range weapon is useless under water - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)) - return 0.f; - - if (enemy.isEmpty()) - return 0.f; - - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) + // Underwater ranged combat is impossible + if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f) + || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) - rangedMult = 1.5f; + { + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); + rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + } } + const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) - { - float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1]; - MWMechanics::adjustWeaponDamage(rangedDamage, item, actor); - - rating = rangedDamage / 2.f; - - if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); - } + rating = chop; else { - float meleeDamage = 0.f; - - for (int i=0; i<2; ++i) - { - meleeDamage += weapon->mData.mSlash[i]; - meleeDamage += weapon->mData.mThrust[i]; - meleeDamage += weapon->mData.mChop[i]; - } - - MWMechanics::adjustWeaponDamage(meleeDamage, item, actor); - rating = meleeDamage / 6.f; - - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); + const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f; + const float thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1]) / 2.f; + rating = (slash * slash + thrust * thrust + chop * chop) / (slash + thrust + chop); } - if (item.getClass().hasItemHealth(item)) - { - if (item.getClass().getItemHealth(item) == 0) - return 0.f; - } + adjustWeaponDamage(rating, item, actor); - if (weapon->mData.mType == ESM::Weapon::MarksmanBow) + if (weapon->mData.mType != ESM::Weapon::MarksmanBow && weapon->mData.mType != ESM::Weapon::MarksmanCrossbow) + resistNormalWeapon(enemy, actor, item, rating); + else if (weapon->mData.mType == ESM::Weapon::MarksmanBow) { if (arrowRating <= 0.f) rating = 0.f; @@ -101,7 +87,7 @@ namespace MWMechanics if (!weapon->mEnchant.empty()) { - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(weapon->mEnchant); + const ESM::Enchantment* enchantment = world->getStore().get().find(weapon->mEnchant); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { int castCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), actor); @@ -111,19 +97,22 @@ namespace MWMechanics } } + if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + rating *= weapon->mData.mSpeed; + if (actor.getClass().isNpc()) { int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) { int value = actor.getClass().getSkill(actor, skill); - rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; + rating *= getHitChance(actor, enemy, value) / 100.f; } } else { MWWorld::LiveCellRef *ref = actor.get(); - rating *= MWMechanics::getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; + rating *= getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; } return rating * rangedMult; From 00c847db195e41036510681796d0279db0cbdf02 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 23:36:47 +0300 Subject: [PATCH 161/175] Make AI Blind, Sound and Silence effect rating more logical --- apps/openmw/mwmechanics/spellpriority.cpp | 54 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index a4bf467698..b437561627 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -232,20 +232,59 @@ namespace MWMechanics case ESM::MagicEffect::CommandHumanoid: return 0.f; + case ESM::MagicEffect::Blind: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't attack + if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + return 0.f; + + // Enemy doesn't attack + if (stats.getDrawState() != MWMechanics::DrawState_Weapon) + return 0.f; + + break; + } + case ESM::MagicEffect::Sound: { if (enemy.isEmpty()) return 0.f; - // there is no need to cast sound if enemy is not able to cast spells - CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + // Enemy can't cast spells if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0) return 0.f; if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) return 0.f; + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; + + break; + } + + case ESM::MagicEffect::Silence: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't cast spells + if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + return 0.f; + + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; break; } @@ -353,9 +392,10 @@ namespace MWMechanics int priority = 1; if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) priority = 10; - const DynamicStat& current = actor.getClass().getCreatureStats(actor). - getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); - float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration; + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const DynamicStat& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); + const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f; + const float toHeal = magnitude * effect.mDuration; // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) @@ -363,8 +403,8 @@ namespace MWMechanics return 10000.f * priority - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion } - else - return -10000.f * priority; // Save for later + + return 0.f; } break; From 533b72eff609dc9cdc3af1de9591dcfc410981eb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 00:36:30 +0300 Subject: [PATCH 162/175] Cache weapon type strings --- apps/openmw/mwclass/weapon.cpp | 37 ++++++++++++---------- apps/openmw/mwmechanics/spellpriority.cpp | 4 +-- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 294aebd94b..466ae47162 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -268,24 +268,27 @@ namespace MWClass { text += "\n#{sType} "; - std::map > mapping; - mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); - mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); - mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); - mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); - mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); - mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + static std::map > mapping; + if (mapping.empty()) + { + mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); + mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); + mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); + mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); + mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); + mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + } - std::string type = mapping[ref->mBase->mData.mType].first; - std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; + const std::string type = mapping[ref->mBase->mData.mType].first; + const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; text += store.get().find(type)->mValue.getString() + ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->mValue.getString() : ""); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index b437561627..f55b30d7cd 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -403,8 +403,8 @@ namespace MWMechanics return 10000.f * priority - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion } - - return 0.f; + else + return -10000.f * priority; // Save for later } break; diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 8d09bd8d19..61d348e14f 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -31,7 +31,7 @@ namespace MWMechanics if (type != -1 && weapon->mData.mType != type) return 0.f; - + const MWBase::World* world = MWBase::Environment::get().getWorld(); const MWWorld::Store& gmst = world->getStore().get(); From fa3e45fa7dfb4a1f2733e8b02b73c6c6aa596623 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 01:54:59 +0300 Subject: [PATCH 163/175] Slight cleanup --- apps/openmw/mwmechanics/weaponpriority.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 61d348e14f..6b96163593 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -100,21 +100,21 @@ namespace MWMechanics if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) rating *= weapon->mData.mSpeed; + int value = 50.f; if (actor.getClass().isNpc()) { int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) - { - int value = actor.getClass().getSkill(actor, skill); - rating *= getHitChance(actor, enemy, value) / 100.f; - } + value = actor.getClass().getSkill(actor, skill); } else { MWWorld::LiveCellRef *ref = actor.get(); - rating *= getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; + value = ref->mBase->mData.mCombat; } + rating *= getHitChance(actor, enemy, value) / 100.f; + return rating * rangedMult; } From e66be02e2e2f4a6ebd8fb883e58402aa0bc17cdf Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 02:06:29 +0300 Subject: [PATCH 164/175] Account for enemy armor rating in weapon rating --- apps/openmw/mwmechanics/weaponpriority.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6b96163593..6861a1b779 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -97,8 +97,11 @@ namespace MWMechanics } } - if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) - rating *= weapon->mData.mSpeed; + if (enemy.getClass().isNpc()) + { + static const float fCombatArmorMinMult = gmst.find("fCombatArmorMinMult")->mValue.getFloat(); + rating *= std::max(fCombatArmorMinMult, rating / (rating + enemy.getClass().getArmorRating(enemy))); + } int value = 50.f; if (actor.getClass().isNpc()) @@ -115,6 +118,9 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; + if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + rating *= weapon->mData.mSpeed; + return rating * rangedMult; } From 58bd35c70de83a46ffabacf28d7a0cc7c30be142 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:00:18 +0300 Subject: [PATCH 165/175] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab7d88d3f..f84ccf0500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,6 +136,9 @@ Feature #4550: Weapon priority: make ranged weapon bonus more sensible Feature #4579: Add option for applying Strength into hand to hand damage Feature #4581: Use proper logging system + Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment + Feature #4625: Weapon priority: use weighted mean for melee damage rating + Feature #4626: Weapon priority: account for weapon speed Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning From 2965373ed69c31be87aa3674c5dd035d3d82f53d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:24:13 +0300 Subject: [PATCH 166/175] Avoid potential zero division --- apps/openmw/mwmechanics/weaponpriority.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6861a1b779..b2a9c8dedf 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -52,7 +52,10 @@ namespace MWMechanics { static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); - rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + if (fAIMeleeWeaponMult != 0) + rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + else + rangedMult = fAIRangeMeleeWeaponMult; } } From ceb6121b33536b371f7edfb07d6425761f7c0139 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:44:29 +0300 Subject: [PATCH 167/175] Better checks for enemy incapacitation --- apps/openmw/mwmechanics/spellpriority.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index f55b30d7cd..f9d19ca28b 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -240,7 +240,7 @@ namespace MWMechanics const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); // Enemy can't attack - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't attack @@ -261,7 +261,7 @@ namespace MWMechanics if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0) return 0.f; - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't cast spells @@ -279,7 +279,7 @@ namespace MWMechanics const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); // Enemy can't cast spells - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't cast spells From d758b573e2c5adfd25f5b46935944c19736346de Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sat, 1 Sep 2018 12:07:26 +0300 Subject: [PATCH 168/175] Fix erroneous assumption that ranged weaponry has speed Restrict speed mult to melee weaponry --- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index b2a9c8dedf..6ce064fe44 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -121,7 +121,7 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; - if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + if (weapon->mData.mType < ESM::Weapon::MarksmanBow) rating *= weapon->mData.mSpeed; return rating * rangedMult; From 7e2bda459b530680887798486caf435198f0f605 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 2 Sep 2018 17:34:01 +0400 Subject: [PATCH 169/175] Check if there are textures in FlipController to avoid division by zero (bug #4614) --- CHANGELOG.md | 1 + components/nifosg/controller.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab7d88d3f..72a6fa40cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice Bug #4608: Falling damage is applied twice + Bug #4614: Crash due to division by zero when FlipController has no textures Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 262966e957..83841e0e52 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -409,7 +409,7 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { - if (hasInput() && mDelta != 0) + if (hasInput() && mDelta != 0 && !mTextures.empty()) { int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); From 65ff346b619c5121a02f3f065dea0c4f1b1119f7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 3 Sep 2018 16:30:21 +0300 Subject: [PATCH 170/175] Make NPC record reputation, disposition and faction rank have unsigned char type --- CHANGELOG.md | 1 + components/esm/loadnpc.hpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72a6fa40cc..f964ca327c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails + Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 5f567d9990..fbe1dca1f1 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -90,7 +90,7 @@ struct NPC char mFactionID; unsigned short mHealth, mMana, mFatigue; - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown; int mGold; }; // 52 bytes @@ -101,7 +101,7 @@ struct NPC { short mLevel; // see above - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown1, mUnknown2, mUnknown3; int mGold; }; // 12 bytes From aed7c1b2bbd47d56614ce0cdba66f38b86e6893f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:37:43 +0400 Subject: [PATCH 171/175] Fix a couple of Clang warnings --- apps/openmw/mwscript/miscextensions.cpp | 8 +++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 3d1978d625..8d592f29c6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1064,7 +1064,13 @@ namespace MWScript runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find (spellId); - if (spell && spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) + if (!spell) + { + runtime.getContext().report("spellcasting failed: can not find spell \""+spellId+"\""); + return; + } + + if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) { runtime.getContext().report("spellcasting failed: you can cast only spells and powers."); return; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2f732ef976..536a40468a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -525,7 +525,7 @@ namespace MWWorld /// @note throws an exception when invoked on a teleport door void activateDoor(const MWWorld::Ptr& door, int state) override; - void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors); ///< get a list of actors standing on \a object + void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) override; ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object From d3defd83fc3f24968c43bfa5b53ab03377869175 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:58:34 +0400 Subject: [PATCH 172/175] Disable C4643 MSVC warning, caused by boost --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 678f3e16b6..34e1a8f4bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,7 +624,7 @@ if (WIN32) # Warnings that aren't enabled normally and don't need to be enabled # They're unneeded and sometimes completely retarded warnings that /Wall enables # Not going to bother commenting them as they tend to warn on every standard library file - 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 + 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045 # Warnings that are thrown on standard libraries and not OpenMW @@ -643,6 +643,7 @@ if (WIN32) # caused by boost 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) + 4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files) # caused by MyGUI 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' From 5693ceca749e91d222667e930d521630dba8b256 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 17:56:19 +0400 Subject: [PATCH 173/175] Remove redundant declaration --- components/debug/debuglog.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index 1ea18aa9b7..f4a8e17bef 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -43,14 +43,6 @@ public: return *this; } - template - Log& operator<<(const T& rhs) - { - if (mLevel <= Debug::CurrentDebugLevel) - std::cout << std::forward(rhs); - - return *this; - } ~Log() { From 6529883527b54409ba04b8750f304a1b284471ac Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 18:14:51 +0400 Subject: [PATCH 174/175] Fix MSVC warning C4389 --- apps/opencs/model/tools/pathgridcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 6427bb1199..c25845885e 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -109,7 +109,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) { - std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); + std::vector::const_iterator it = find(duplList.begin(), duplList.end(), static_cast(i)); if (it == duplList.end()) { std::ostringstream ss; From 9408876b5891a38fbf7757cc9f3d4a48cf5f633f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 6 Sep 2018 17:17:30 +0300 Subject: [PATCH 175/175] Utilize AI GMSTs for priority rating (feature #4632) Fix on-target effect rating calculation --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellpriority.cpp | 12 +++++++++--- apps/openmw/mwmechanics/weaponpriority.cpp | 12 +++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03036e1e4b..c11d92900b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,7 @@ Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment Feature #4625: Weapon priority: use weighted mean for melee damage rating Feature #4626: Weapon priority: account for weapon speed + Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index f9d19ca28b..807b56608e 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -617,13 +617,19 @@ namespace MWMechanics float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) { // NOTE: enemy may be empty + float rating = 0.f; + float ratingMult = 1.f; // NB: this multiplier is applied to the effect rating, not the final rating + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->mValue.getFloat(); + static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->mValue.getFloat(); + for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { - rating += rateEffect(*it, actor, enemy); + ratingMult = (it->mRange == ESM::RT_Target) ? fAIRangeMagicSpellMult : fAIMagicSpellMult; - if (it->mRange == ESM::RT_Target) - rating *= 1.5f; + rating += rateEffect(*it, actor, enemy) * ratingMult; } return rating; } diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6ce064fe44..818065ae44 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -39,7 +39,8 @@ namespace MWMechanics return 0.f; float rating=0.f; - float rangedMult=1.f; + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + float ratingMult = fAIMeleeWeaponMult; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { @@ -48,14 +49,11 @@ namespace MWMechanics || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; + // Use a higher rating multiplier if the actor is out of enemy's reach, use the normal mult otherwise if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) { - static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); - if (fAIMeleeWeaponMult != 0) - rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; - else - rangedMult = fAIRangeMeleeWeaponMult; + ratingMult = fAIRangeMeleeWeaponMult; } } @@ -124,7 +122,7 @@ namespace MWMechanics if (weapon->mData.mType < ESM::Weapon::MarksmanBow) rating *= weapon->mData.mSpeed; - return rating * rangedMult; + return rating * ratingMult; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType)