From 90b2709d6c0fa1997faf64127b3c85990e3610fd Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Jul 2014 19:56:06 +0200 Subject: [PATCH 01/17] Allow absorption of non-harmful spells (Fixes #1693) Also fix absorption being calculated for each effect rather than the whole spell. --- apps/openmw/mwmechanics/spellcasting.cpp | 48 ++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 67da992009..2566bf1896 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -287,6 +287,27 @@ namespace MWMechanics bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player"); + // Try absorbing if it's a spell + // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure + // if that is worth replicating. + bool absorbed = false; + if (spell && caster != target && target.getClass().isActor()) + { + int absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).mMagnitude; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + absorbed = (roll < absorb); + if (absorbed) + { + const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); + MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( + "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); + // Magicka is increased by cost of spell + DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); + magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + target.getClass().getCreatureStats(target).setMagicka(magicka); + } + } + for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) { @@ -326,31 +347,13 @@ namespace MWMechanics { anyHarmfulEffect = true; + if (absorbed) // Absorbed, and we know there was a harmful effect (figuring that out is the only reason we are in this loop) + break; + // If player is attempting to cast a harmful spell, show the target's HP bar if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); - // Try absorbing if it's a spell - // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure - // if that is worth replicating. - if (spell && caster != target) - { - int absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).mMagnitude; - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - bool isAbsorbed = (roll < absorb); - if (isAbsorbed) - { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::Reflect, false, ""); - // Magicka is increased by cost of spell - DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); - target.getClass().getCreatureStats(target).setMagicka(magicka); - magnitudeMult = 0; - } - } - // Try reflecting if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) { @@ -382,8 +385,7 @@ namespace MWMechanics } } - - if (magnitudeMult > 0) + if (magnitudeMult > 0 && !absorbed) { float random = std::rand() / static_cast(RAND_MAX); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; From 4a1e5610153eb1be6f72dcd730084b609b83753c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Jul 2014 23:03:34 +0200 Subject: [PATCH 02/17] Properly assign effect attribute/skill in potion creation (Fixes #1704) --- apps/openmw/mwmechanics/alchemy.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 2e03122d5b..a097446a0a 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -181,7 +181,13 @@ void MWMechanics::Alchemy::updateEffects() ESM::ENAMstruct effect; effect.mEffectID = iter->mId; - effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work + effect.mAttribute = -1; + effect.mSkill = -1; + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + effect.mSkill = iter->mArg; + else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + effect.mAttribute = iter->mArg; effect.mRange = 0; effect.mArea = 0; From b370c0f7b5c29a45fca9f923d78649651ba96fbd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 00:57:49 +0200 Subject: [PATCH 03/17] Enchanting: Don't check price on self-enchanting (Bug #1701) --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 92221977b1..145602eb04 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -299,7 +299,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (mEnchanting.getEnchantPrice() > playerGold) + if (mPtr != player && mEnchanting.getEnchantPrice() > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; From 09607f992ea2f725e886dc193813f88b3bbe7fd9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 00:59:35 +0200 Subject: [PATCH 04/17] Enchanting: fix inverted self-enchant success chance (Fixes #1701) --- apps/openmw/mwmechanics/enchanting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index f3f6795db8..18732514bd 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -62,7 +62,7 @@ namespace MWMechanics if(mSelfEnchanting) { - if(getEnchantChance() (RAND_MAX)*100) + if(std::rand()/static_cast (RAND_MAX)*100 < getEnchantChance()) return false; mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2); From 9c60e4d8265c0e1dd54499790de491c7e3929e7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 01:03:27 +0200 Subject: [PATCH 05/17] Change button caption from "Buy" to "Create" when self-enchanting --- apps/openmw/mwgui/enchantingdialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 145602eb04..1fef34371e 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -144,6 +144,8 @@ namespace MWGui mEnchanting.setSelfEnchanting(false); mEnchanting.setEnchanter(actor); + mBuyButton->setCaptionWithReplacing("#{sBuy}"); + mPtr = actor; startEditing (); @@ -156,6 +158,8 @@ namespace MWGui mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(player); + mBuyButton->setCaptionWithReplacing("#{sCreate}"); + mPtr = player; startEditing(); mEnchanting.setSoulGem(soulgem); From 16b089cdc833e332bba1243cfb7ee9328aa75324 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 01:07:32 +0200 Subject: [PATCH 06/17] Fix invisible enchanting price when self-enchanting was previously used. --- apps/openmw/mwgui/enchantingdialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 1fef34371e..d7e79e7a2a 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -149,6 +149,9 @@ namespace MWGui mPtr = actor; startEditing (); + mPrice->setVisible(true); + mPriceText->setVisible(true); + updateLabels(); } void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) @@ -162,7 +165,6 @@ namespace MWGui mPtr = player; startEditing(); - mEnchanting.setSoulGem(soulgem); setSoulGem(soulgem); From 31d058b98cf7a17a9081768d36b60309afabbd6b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 02:23:42 +0200 Subject: [PATCH 07/17] Add workaround for ScrollView messing up canvas size (Fixes #1700) TODO: Create fixed ScrollView widget? --- apps/openmw/mwgui/itemview.cpp | 6 ++++++ apps/openmw/mwgui/journalwindow.cpp | 3 +++ apps/openmw/mwgui/list.cpp | 4 ++++ apps/openmw/mwgui/merchantrepair.cpp | 3 +++ apps/openmw/mwgui/quickkeysmenu.cpp | 5 +++-- apps/openmw/mwgui/recharge.cpp | 4 ++++ apps/openmw/mwgui/repair.cpp | 3 +++ apps/openmw/mwgui/review.cpp | 3 +++ apps/openmw/mwgui/scrollwindow.cpp | 3 +++ apps/openmw/mwgui/settingswindow.cpp | 3 +++ apps/openmw/mwgui/spellbuyingwindow.cpp | 5 +++++ apps/openmw/mwgui/spellcreationdialog.cpp | 3 +++ apps/openmw/mwgui/spellwindow.cpp | 3 +++ apps/openmw/mwgui/statswindow.cpp | 6 ++++++ apps/openmw/mwgui/travelwindow.cpp | 3 +++ 15 files changed, 55 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index b86f610341..c4c6c65458 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -115,7 +115,13 @@ void ItemView::update() } x += 42; MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mScrollView->setVisibleVScroll(false); + mScrollView->setVisibleHScroll(false); mScrollView->setCanvasSize(size); + mScrollView->setVisibleVScroll(true); + mScrollView->setVisibleHScroll(true); dragArea->setSize(size); } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index fa27b4ef06..b588ba3e14 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -400,7 +400,10 @@ namespace getPage (pageId)->showPage (book, 0); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + getWidget (listId)->setVisibleVScroll(false); getWidget (listId)->setCanvasSize (size.first, size.second); + getWidget (listId)->setVisibleVScroll(true); } void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index b0c514b9d3..e88d43534b 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -93,7 +93,11 @@ namespace MWGui } ++i; } + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mScrollView->setVisibleVScroll(false); mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height)); + mScrollView->setVisibleVScroll(true); if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 0d1a570008..013a400172 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -91,7 +91,10 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); } } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mList->setVisibleVScroll(false); mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); + mList->setVisibleVScroll(true); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 328ef21fb7..84150c3226 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -654,9 +654,10 @@ namespace MWGui mHeight += spellHeight; } - + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mMagicList->setVisibleVScroll(false); mMagicList->setCanvasSize (mWidth, std::max(mMagicList->getHeight(), mHeight)); - + mMagicList->setVisibleVScroll(true); } void MagicSelectionDialog::addGroup(const std::string &label, const std::string& label2) diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 9c43b14163..1b994d5843 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -119,7 +119,11 @@ void Recharge::updateView() currentY += 32 + 4; } + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mView->setVisibleVScroll(false); mView->setCanvasSize (MyGUI::IntSize(mView->getWidth(), std::max(mView->getHeight(), currentY))); + mView->setVisibleVScroll(true); } void Recharge::onCancel(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 986e27243a..019f341b57 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -126,7 +126,10 @@ void Repair::updateRepairView() currentY += 32 + 4; } } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mRepairView->setVisibleVScroll(false); mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY))); + mRepairView->setVisibleVScroll(true); } void Repair::onCancel(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index e27e40ae64..072411707e 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -320,7 +320,10 @@ namespace MWGui if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + mSkillView->setVisibleVScroll(true); } // widget controls diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 3d07511143..867bef0d79 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -57,10 +57,13 @@ namespace MWGui BookTextParser parser; MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mTextView->setVisibleVScroll(false); if (size.height > mTextView->getSize().height) mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); else mTextView->setCanvasSize(410, mTextView->getSize().height); + mTextView->setVisibleVScroll(true); mTextView->setViewOffset(MyGUI::IntPoint(0,0)); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 09d2fc19c9..abeb6b86cf 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -473,7 +473,10 @@ namespace MWGui curH += h; } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mControlsBox->setVisibleVScroll(false); mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + mControlsBox->setVisibleVScroll(true); } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 8d9f35daaa..d6c716453a 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -50,6 +50,8 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + // TODO: refactor to use MyGUI::ListBox + MyGUI::Button* toAdd = mSpellsView->createWidget( "SandTextButton", @@ -106,7 +108,10 @@ namespace MWGui updateLabels(); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSpellsView->setVisibleVScroll(false); mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY))); + mSpellsView->setVisibleVScroll(true); } bool SpellBuyingWindow::playerHasSpell(const std::string &id) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 030b8bf37c..f1ca3c802a 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -637,7 +637,10 @@ namespace MWGui ++i; } + // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mUsedEffectsView->setVisibleHScroll(false); mUsedEffectsView->setCanvasSize(size); + mUsedEffectsView->setVisibleHScroll(true); notifyEffectsChanged(); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 77da56fa4f..15b5bf2800 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -261,7 +261,10 @@ namespace MWGui mHeight += spellHeight; } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSpellView->setVisibleVScroll(false); mSpellView->setCanvasSize(mSpellView->getWidth(), std::max(mSpellView->getHeight(), mHeight)); + mSpellView->setVisibleVScroll(true); } void SpellWindow::addGroup(const std::string &label, const std::string& label2) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index b11258f1cd..7f3b66d83e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -82,7 +82,10 @@ namespace MWGui { mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), mSkillView->getCanvasSize().height); + mSkillView->setVisibleVScroll(true); } void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) @@ -578,7 +581,10 @@ namespace MWGui mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + mSkillView->setVisibleVScroll(true); } void StatsWindow::onPinToggled() diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 9aa75173a6..d874e8c0ce 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -126,7 +126,10 @@ namespace MWGui } updateLabels(); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mDestinationsView->setVisibleVScroll(false); mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY))); + mDestinationsView->setVisibleVScroll(true); } void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) From fdc6dd6985959a24b4c4f0b29e16e7717201f79a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 15:34:42 +0200 Subject: [PATCH 08/17] Shader compatibility fixes for GLSL ES --- files/materials/atmosphere.shader | 2 +- files/materials/clouds.shader | 2 +- files/materials/mygui.shader | 2 +- files/materials/objects.shader | 16 ++++++++-------- files/materials/terrain.shader | 24 ++++++++++++------------ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index f02c5766fe..c8a8e02203 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -33,7 +33,7 @@ SH_START_PROGRAM { - shOutputColour(0) = alphaFade * atmosphereColour + (1.f - alphaFade) * horizonColour; + shOutputColour(0) = alphaFade * atmosphereColour + (1.0 - alphaFade) * horizonColour; } #endif diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index d3e5f083c7..9e9b102564 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -25,7 +25,7 @@ shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); UV = uv0; - alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; + alphaFade = (shInputPosition.z <= 200.0) ? ((shInputPosition.z <= 100.0) ? 0.0 : 0.25) : 1.0; } #else diff --git a/files/materials/mygui.shader b/files/materials/mygui.shader index 014558d972..4d12eba90e 100644 --- a/files/materials/mygui.shader +++ b/files/materials/mygui.shader @@ -14,7 +14,7 @@ SH_START_PROGRAM { - shOutputPosition = float4(shInputPosition.xyz, 1.f); + shOutputPosition = float4(shInputPosition.xyz, 1.0); #if TEXTURE UV.xy = uv0; #endif diff --git a/files/materials/objects.shader b/files/materials/objects.shader index ed75babdd4..bacf3f9121 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -210,11 +210,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #endif #if @shIterator == 0 @@ -432,11 +432,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colourPassthrough.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #endif #if @shIterator == 0 @@ -504,8 +504,8 @@ #if ENV_MAP // Everything looks better with fresnel - float facing = 1.0 - max(abs(dot(-eyeDir, normal)), 0); - float envFactor = shSaturate(0.25 + 0.75 * pow(facing, 1)); + float facing = 1.0 - max(abs(dot(-eyeDir, normal)), 0.0); + float envFactor = shSaturate(0.25 + 0.75 * pow(facing, 1.0)); shOutputColour(0).xyz += shSample(envMap, UV.zw).xyz * envFactor * env_map_color; #endif @@ -513,7 +513,7 @@ #if SPECULAR float3 light0Dir = normalize(lightPosObjSpace0.xyz); - float NdotL = max(dot(normal, light0Dir), 0); + float NdotL = max(dot(normal, light0Dir), 0.0); float3 halfVec = normalize (light0Dir + eyeDir); float shininess = matShininess; @@ -522,7 +522,7 @@ shininess *= (specTex.a); #endif - float3 specular = pow(max(dot(normal, halfVec), 0), shininess) * lightSpec0 * matSpec; + float3 specular = pow(max(dot(normal, halfVec), 0.0), shininess) * lightSpec0 * matSpec; #if SPEC_MAP specular *= specTex.xyz; #else diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 1436de0c35..8384588e4f 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -175,7 +175,7 @@ lightResult.xyz += lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normal.xyz, lightDir), 0); + * max(dot(normal.xyz, lightDir), 0.0); #if @shIterator == 0 directionalResult = lightResult.xyz; @@ -310,7 +310,7 @@ shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) #if !IS_FIRST_PASS // Opacity the previous passes should have, i.e. 1 - (opacity of this pass) -float previousAlpha = 1.f; +float previousAlpha = 1.0; #endif @@ -334,7 +334,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float4 albedo = float4(0,0,0,1); - float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents + float2 layerUV = float2(UV.x, 1.0-UV.y) * 16.0; // Reverse Y, required to get proper tangents float2 thisLayerUV; float4 normalTex; float4 diffuseTex; @@ -349,9 +349,9 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #if @shPropertyBool(use_normal_map_@shIterator) normalTex = shSample(normalMap@shIterator, thisLayerUV); #if @shIterator == 0 && IS_FIRST_PASS - TSnormal = normalize(normalTex.xyz * 2 - 1); + TSnormal = normalize(normalTex.xyz * 2.0 - 1.0); #else - TSnormal = shLerp(TSnormal, normalTex.xyz * 2 - 1, blendValues@shPropertyString(blendmap_component_@shIterator)); + TSnormal = shLerp(TSnormal, normalTex.xyz * 2.0 - 1.0, blendValues@shPropertyString(blendmap_component_@shIterator)); #endif #endif @@ -361,7 +361,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; diffuseTex = shSample(diffuseMap@shIterator, layerUV); #if !@shPropertyBool(use_specular_@shIterator) - diffuseTex.a = 0; + diffuseTex.a = 0.0; #endif #if @shIterator == 0 @@ -371,7 +371,7 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon #endif #if !IS_FIRST_PASS - previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); + previousAlpha *= 1.0-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @@ -404,7 +404,7 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon lightResult.xyz += lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normal.xyz, lightDir), 0); + * max(dot(normal.xyz, lightDir), 0.0); #if @shIterator == 0 float3 directionalResult = lightResult.xyz; #endif @@ -444,10 +444,10 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon // Specular float3 light0Dir = normalize(lightPos0.xyz); - float NdotL = max(dot(normal, light0Dir), 0); + float NdotL = max(dot(normal, light0Dir), 0.0); float3 halfVec = normalize (light0Dir + eyeDir); - float3 specular = pow(max(dot(normal, halfVec), 0), 32) * lightSpec0; + float3 specular = pow(max(dot(normal, halfVec), 0.0), 32.0) * lightSpec0; shOutputColour(0).xyz += specular * (albedo.a) * shadow; #endif @@ -465,9 +465,9 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if IS_FIRST_PASS - shOutputColour(0).a = 1; + shOutputColour(0).a = 1.0; #else - shOutputColour(0).a = 1.f-previousAlpha; + shOutputColour(0).a = 1.0-previousAlpha; #endif } From 8c81e22f3e3c92905e5ec21c30d439ef225822c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 18:01:53 +0200 Subject: [PATCH 09/17] Determine target for On Touch effects for non-player actors --- apps/openmw/mwworld/worldimp.cpp | 86 ++++++++++++++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 2 +- libs/openengine/bullet/physic.cpp | 4 +- libs/openengine/bullet/physic.hpp | 4 +- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9b747582f4..10c2e4974f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -148,7 +148,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (activationDistanceOverride), mFallback(fallbackMap), mTeleportEnabled(true), mLevitationEnabled(true), - mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles), + mGodMode(false), mContentFiles (contentFiles), mGoToJail(false), mStartCell (startCell), mStartupScript(startupScript) { @@ -290,7 +290,6 @@ namespace MWWorld mGodMode = false; mSky = true; mTeleportEnabled = true; - mFacedDistance = FLT_MAX; mGlobalVariables.fill (mStore); } @@ -1462,30 +1461,22 @@ namespace MWWorld updateFacedHandle (); } - void World::updateFacedHandle () + void World::getFacedHandle(std::string& facedHandle, float maxDistance) { - float telekinesisRangeBonus = - mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() - .get(ESM::MagicEffect::Telekinesis).mMagnitude; - telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); + maxDistance += mRendering->getCameraDistance(); - float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; - activationDistance += mRendering->getCameraDistance(); - - // send new query - // figure out which object we want to test against std::vector < std::pair < float, std::string > > results; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, activationDistance); + results = mPhysics->getFacedHandles(x, y, maxDistance); if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { - results = mPhysics->getFacedHandles(activationDistance); + results = mPhysics->getFacedHandles(maxDistance); } // ignore the player and other things we're not interested in @@ -1508,15 +1499,21 @@ namespace MWWorld if (results.empty() || results.front().second.find("HeightField") != std::string::npos) // Blocked by terrain - { - mFacedHandle = ""; - mFacedDistance = FLT_MAX; - } + facedHandle = ""; else - { - mFacedHandle = results.front().second; - mFacedDistance = results.front().first; - } + facedHandle = results.front().second; + } + + void World::updateFacedHandle () + { + float telekinesisRangeBonus = + mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() + .get(ESM::MagicEffect::Telekinesis).mMagnitude; + telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); + + float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + + getFacedHandle(mFacedHandle, activationDistance); } bool World::isCellExterior() const @@ -2344,8 +2341,49 @@ namespace MWWorld { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - // TODO: this only works for the player - MWWorld::Ptr target = getFacedObject(); + // Get the target to use for "on touch" effects + MWWorld::Ptr target; + float distance = 192.f; // ?? + + if (actor == getPlayerPtr()) + { + // For the player, use camera to aim + std::string facedHandle; + getFacedHandle(facedHandle, distance); + if (!facedHandle.empty()) + target = getPtrViaHandle(facedHandle); + } + else + { + // For NPCs use facing direction from Head node + Ogre::Vector3 origin(actor.getRefData().getPosition().pos); + MWRender::Animation *anim = mRendering->getAnimation(actor); + if(anim != NULL) + { + Ogre::Node *node = anim->getNode("Head"); + if (node == NULL) + node = anim->getNode("Bip01 Head"); + if(node != NULL) + origin += node->_getDerivedPosition(); + } + Ogre::Quaternion orient; + orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + Ogre::Vector3 direction = orient.yAxis(); + Ogre::Vector3 dest = origin + direction * distance; + + + std::vector > collisions = mPhysEngine->rayTest2(btVector3(origin.x, origin.y, origin.z), btVector3(dest.x, dest.y, dest.z)); + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end(); ++cIt) + { + MWWorld::Ptr collided = getPtrViaHandle(cIt->second); + if (collided != actor) + { + target = collided; + break; + } + } + } std::string selectedSpell = stats.getSpells().getSelectedSpell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index dda4429630..5c44388ca9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -92,7 +92,6 @@ namespace MWWorld int mActivationDistanceOverride; std::string mFacedHandle; - float mFacedDistance; std::string mStartupScript; @@ -114,6 +113,7 @@ namespace MWWorld void updateWindowManager (); void performUpdateSceneQueries (); void updateFacedHandle (); + void getFacedHandle(std::string& facedHandle, float maxDistance); float getMaxActivationDistance (); float getNpcActivationDistance (); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 6bc544af4a..954d7c2831 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -737,7 +737,7 @@ namespace Physic { } - std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap, Ogre::Vector3* normal) + std::pair PhysicEngine::rayTest(const btVector3 &from, const btVector3 &to, bool raycastingObjectOnly, bool ignoreHeightMap, Ogre::Vector3* normal) { std::string name = ""; float d = -1; @@ -801,7 +801,7 @@ namespace Physic return std::make_pair(false, 1); } - std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) + std::vector< std::pair > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to) { MyRayResultCallback resultCallback1; resultCallback1.m_collisionFilterGroup = 0xff; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index e37caee386..09bff4b049 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -302,13 +302,13 @@ namespace Physic * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * If \a normal is non-NULL, the hit normal will be written there (if there is a hit) */ - std::pair rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true, + std::pair rayTest(const btVector3& from,const btVector3& to,bool raycastingObjectOnly = true, bool ignoreHeightMap = false, Ogre::Vector3* normal = NULL); /** * Return all objects hit by a ray. */ - std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + std::vector< std::pair > rayTest2(const btVector3 &from, const btVector3 &to); std::pair sphereCast (float radius, btVector3& from, btVector3& to); ///< @return (hit, relative distance) From d7acb7fc7dfc0f462232732f9d3fcf4d0f7793ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 22:24:05 +0200 Subject: [PATCH 10/17] Ignore invalid shader cache index (Bug #1664) --- extern/shiny/Main/Factory.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 72af07d58f..eab5c8dfc8 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -51,24 +51,32 @@ namespace sh { assert(mCurrentLanguage != Language_None); - if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) + try { - std::ifstream file; - file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str()); - - std::string line; - while (getline(file, line)) + if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) { - std::string sourceFile = line; + std::ifstream file; + file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str()); - if (!getline(file, line)) - assert(0); + std::string line; + while (getline(file, line)) + { + std::string sourceFile = line; - int modified = boost::lexical_cast(line); + if (!getline(file, line)) + assert(0); - mShadersLastModified[sourceFile] = modified; + int modified = boost::lexical_cast(line); + + mShadersLastModified[sourceFile] = modified; + } } } + catch (std::exception& e) + { + std::cerr << "Failed to load shader modification index: " << e.what() << std::endl; + mShadersLastModified.clear(); + } // load configurations { From 47e42d4fda6bdadb67b6b1eadcd893da172cf3e0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 22:29:04 +0200 Subject: [PATCH 11/17] Destroy Engine after exception is logged In cases where OpenMW throws an exception, then crashes in the Engine destructor (ideally should not happen, but keeps happening), we will at least see what the exception was about. --- apps/openmw/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index adde408b9e..f91d7de7a1 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -309,6 +309,8 @@ int main(int argc, char**argv) boost::filesystem::ofstream logfile; + std::auto_ptr engine; + int ret = 0; try { @@ -350,11 +352,11 @@ int main(int argc, char**argv) boost::filesystem::current_path(bundlePath); #endif - OMW::Engine engine(cfgMgr); + engine.reset(new OMW::Engine(cfgMgr)); - if (parseOptions(argc, argv, engine, cfgMgr)) + if (parseOptions(argc, argv, *engine, cfgMgr)) { - engine.go(); + engine->go(); } } catch (std::exception &e) From 1a04501951c6dce2f7b762e619bc3daa24cfcabe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 18:53:54 +0200 Subject: [PATCH 12/17] Handle faction save/load properly when player has faction reputation in a faction he is not a member of (Fixes #1573) --- components/esm/npcstats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 3fa954182c..26d8f9596b 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -4,7 +4,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (0), mReputation (0) {} +ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (-1), mReputation (0) {} void ESM::NpcStats::load (ESMReader &esm) { @@ -98,7 +98,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const esm.writeHNT ("FAEX", expelled); } - if (iter->second.mRank) + if (iter->second.mRank >= 0) esm.writeHNT ("FARA", iter->second.mRank); if (iter->second.mReputation) From d81e9cfefd179c8498325baf3bb231ec9fd10a07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 20:30:52 +0200 Subject: [PATCH 13/17] Implement actors fighting for the actor they are following (Fixes #1141) --- apps/openmw/mwclass/creature.cpp | 13 ++- apps/openmw/mwmechanics/actors.cpp | 86 +++++++++---------- apps/openmw/mwmechanics/aicombat.cpp | 2 + apps/openmw/mwmechanics/aifollow.cpp | 5 ++ apps/openmw/mwmechanics/aifollow.hpp | 2 + apps/openmw/mwmechanics/aisequence.cpp | 10 +++ apps/openmw/mwmechanics/aisequence.hpp | 4 + .../mwmechanics/mechanicsmanagerimp.cpp | 2 + apps/openmw/mwworld/class.hpp | 6 -- 9 files changed, 78 insertions(+), 52 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a74ee6fd67..c175ce3ebd 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -366,10 +366,17 @@ namespace MWClass getCreatureStats(ptr).setAttacked(true); // Self defense - if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker) - && (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures - // (they have no movement or attacks anyway) + if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)) + || attacker == MWBase::Environment::get().getWorld()->getPlayerPtr()) + && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker) + && (canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures + // (they have no movement or attacks anyway) + ) + { + // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. + // Note: accidental or collateral damage attacks are ignored. MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + } if(!successful) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8f12098277..36397ac09b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -192,6 +192,15 @@ namespace MWMechanics CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player + if (actor2.getClass().getCreatureStats(actor2).isDead() + || actor1.getClass().getCreatureStats(actor1).isDead()) + return; + + const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); + const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); + float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos)); + if (sqrDist > 7168*7168) + return; // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile @@ -208,9 +217,9 @@ namespace MWMechanics else { aggressive = false; - // if one of actors is creature then we should make a decision to start combat or not - // NOTE: function doesn't take into account combat between 2 creatures - if (!actor1.getClass().isNpc()) + + // Make guards fight aggressive creatures + if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { // if creature is hostile then it is necessarily to start combat if (creatureStats.isHostile()) aggressive = true; @@ -218,24 +227,34 @@ namespace MWMechanics } } + // start combat if we are in combat with any followers of this actor + const std::list& followers = getActorsFollowing(actor2); + for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) + { + if (creatureStats.getAiSequence().isInCombat(*it)) + aggressive = true; + } + // start combat if we are in combat with someone this actor is following + const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); + for (std::list::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && + creatureStats.getAiSequence().isInCombat(dynamic_cast(*it)->getTarget())) + aggressive = true; + } + if(aggressive) { - const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); - const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); - float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); - if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d)) + bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); + + if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + + if (LOS) { - bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - - if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); - - if (LOS) + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + if (!againstPlayer) // start combat between each other { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - if (!againstPlayer) // start combat between each other - { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); - } + MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); } } } @@ -964,19 +983,10 @@ namespace MWMechanics } } - static Ogre::Vector3 sBasePoint; - bool comparePtrDist (const MWWorld::Ptr& ptr1, const MWWorld::Ptr& ptr2) - { - return (sBasePoint.squaredDistance(Ogre::Vector3(ptr1.getRefData().getPosition().pos)) - < sBasePoint.squaredDistance(Ogre::Vector3(ptr2.getRefData().getPosition().pos))); - } - void Actors::update (float duration, bool paused) { if(!paused) - { - std::list listGuards; // at the moment only guards certainly will fight with creatures - + { static float timerUpdateAITargets = 0; // target lists get updated once every 1.0 sec @@ -989,16 +999,10 @@ namespace MWMechanics // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation // (below) iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); - - // add guards to list to later make them fight with creatures - if (timerUpdateAITargets == 0 && iter->first.getClass().isClass(iter->first, "Guard")) - listGuards.push_back(iter->first); } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - listGuards.push_back(player); - // AI and magic effects update for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1008,20 +1012,16 @@ namespace MWMechanics if (MWBase::Environment::get().getMechanicsManager()->isAIActive()) { - // make guards and creatures fight each other - if (timerUpdateAITargets == 0 && iter->first.getTypeName() == typeid(ESM::Creature).name() && !listGuards.empty()) - { - sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos); - listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest guard - - for (std::list::iterator it = listGuards.begin(); it != listGuards.end(); ++it) + if (timerUpdateAITargets == 0) + { + for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { - engageCombat(iter->first, *it, *it == player); + if (it->first == iter->first || iter->first == player) // player is not AI-controlled + continue; + engageCombat(iter->first, it->first, it->first == player); } } - if (iter->first != player) engageCombat(iter->first, player, true); - if (iter->first.getClass().isNpc() && iter->first != player) updateCrimePersuit(iter->first, duration); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c0a97a1b1f..4919544952 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -174,6 +174,8 @@ namespace MWMechanics return true; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + if (target.isEmpty()) + return false; if(target.getClass().getCreatureStats(target).isDead()) return true; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 5ab7e17305..c2bb0f2f4e 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -121,3 +121,8 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) { } + +MWWorld::Ptr MWMechanics::AiFollow::getTarget() const +{ + return MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index e9587b36eb..744a9c5054 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -31,6 +31,8 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); + MWWorld::Ptr getTarget() const; + virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 02f00dfc67..1d00656791 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -72,6 +72,16 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const return true; } +std::list::const_iterator AiSequence::begin() const +{ + return mPackages.begin(); +} + +std::list::const_iterator AiSequence::end() const +{ + return mPackages.end(); +} + bool AiSequence::isInCombat() 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 bc20dc61b0..af14cce593 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -50,6 +50,10 @@ namespace MWMechanics virtual ~AiSequence(); + /// Iterator may be invalidated by any function calls other than begin() or end(). + std::list::const_iterator begin() const; + std::list::const_iterator end() const; + /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ int getTypeId() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 861d5e1100..fd4269930a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1171,6 +1171,8 @@ namespace MWMechanics void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { + if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target)) + return; ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) { diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index c3f94d7f10..e880030f95 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -72,12 +72,6 @@ namespace MWWorld public: - /// NPC-stances. - enum Stance - { - Run, Sneak - }; - virtual ~Class(); const std::string& getTypeName() const { From 6262d6c9644b5459078126b389dc218db8eb680a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 23:10:58 +0200 Subject: [PATCH 14/17] Don't leave stale player CharacterController in Actors when loading game (Fixes #1713) --- apps/openmw/mwmechanics/actors.cpp | 14 ++++++++------ apps/openmw/mwworld/worldimp.cpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 36397ac09b..a215a71a32 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -924,12 +924,7 @@ namespace MWMechanics Actors::~Actors() { - PtrControllerMap::iterator it(mActors.begin()); - for (; it != mActors.end(); ++it) - { - delete it->second; - it->second = NULL; - } + clear(); } void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately) @@ -1359,6 +1354,13 @@ namespace MWMechanics void Actors::clear() { + PtrControllerMap::iterator it(mActors.begin()); + for (; it != mActors.end(); ++it) + { + delete it->second; + it->second = NULL; + } + mActors.clear(); mDeathCount.clear(); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 10c2e4974f..14746526ba 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2111,8 +2111,8 @@ namespace MWWorld void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) { OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); - - physicActor->enableCollisionBody(enable); + if (physicActor) + physicActor->enableCollisionBody(enable); } bool World::findInteriorPosition(const std::string &name, ESM::Position &pos) From 45206bc3f6c973d899f70e367921206bf1a53715 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 00:32:59 +0200 Subject: [PATCH 15/17] Savegame: write and read dynamic Store before Cells --- apps/openmw/mwworld/worldimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 14746526ba..ebbc16c232 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -319,8 +319,9 @@ namespace MWWorld MWMechanics::CreatureStats::writeActorIdCounter(writer); progress.increaseProgress(); + mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that + // references to custom made records will be recognized mCells.write (writer, progress); - mStore.write (writer, progress); mGlobalVariables.write (writer, progress); mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); From 315b022d2d01a1887cd5a76f108b14a2d160b103 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 00:55:57 +0200 Subject: [PATCH 16/17] Add transfer gold from all services to NPC trade gold pool --- apps/openmw/mwgui/merchantrepair.cpp | 6 ++++++ apps/openmw/mwgui/spellbuyingwindow.cpp | 5 +++++ apps/openmw/mwgui/spellcreationdialog.cpp | 8 +++++++- apps/openmw/mwgui/trainingwindow.cpp | 3 +++ apps/openmw/mwgui/travelwindow.cpp | 6 ++++++ apps/openmw/mwmechanics/enchanting.cpp | 4 ++++ 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 013a400172..b6f9936a8e 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -10,6 +10,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" @@ -134,6 +136,10 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor); + actorStats.setGoldPool(actorStats.getGoldPool() + price); + startRepair(mActor); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index d6c716453a..cd378aadf8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -135,6 +135,11 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index f1ca3c802a..63cbdb567d 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -352,7 +352,13 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, boost::lexical_cast(mPriceLabel->getCaption()), player); + int price = boost::lexical_cast(mPriceLabel->getCaption()); + + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 6463db3d79..babfe099f7 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -159,6 +159,9 @@ namespace MWGui // remove gold player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + npcStats.setGoldPool(npcStats.getGoldPool() + price); + // go back to game mode MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index d874e8c0ce..ae191e7642 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -12,6 +12,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" @@ -150,6 +152,10 @@ namespace MWGui player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 18732514bd..9663a2d519 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -292,5 +292,9 @@ namespace MWMechanics MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player); + + // add gold to NPC trading gold pool + CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter); + enchanterStats.setGoldPool(enchanterStats.getGoldPool() + getEnchantPrice()); } } From c6d3b0b70b27d7bf6c590dbaa08ae1e60e1d38fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 02:27:48 +0200 Subject: [PATCH 17/17] Moved merchant restock from trade start to dialogue start, since other services also interact with it. --- apps/openmw/mwgui/dialogue.cpp | 25 +++++++++++++++++++++++++ apps/openmw/mwgui/dialogue.hpp | 1 + apps/openmw/mwgui/tradewindow.cpp | 25 ------------------------- apps/openmw/mwgui/tradewindow.hpp | 2 -- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 2283e0cbe7..23098bf429 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -396,6 +396,31 @@ namespace MWGui mLinks.clear(); updateOptions(); + + restock(); + } + + void DialogueWindow::restock() + { + MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); + + if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) + { + sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); + + mPtr.getClass().restock(mPtr); + + // Also restock any containers owned by this merchant, which are also available to buy in the trade window + std::vector itemSources; + MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); + for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) + { + it->getClass().restock(*it); + } + + sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); + } } void DialogueWindow::setKeywords(std::list keyWords) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 516c04942c..71935dfaf9 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -152,6 +152,7 @@ namespace MWGui private: void updateOptions(); + void restock(); int mServices; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c56c2ee94a..f94e39b2f5 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -99,8 +99,6 @@ namespace MWGui mCurrentBalance = 0; mCurrentMerchantOffer = 0; - restock(); - std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); @@ -509,29 +507,6 @@ namespace MWGui return merchantGold; } - void TradeWindow::restock() - { - MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); - float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); - - if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) - { - sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); - - mPtr.getClass().restock(mPtr); - - // Also restock any containers owned by this merchant, which are also available to buy in the trade window - std::vector itemSources; - MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); - for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) - { - it->getClass().restock(*it); - } - - sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); - } - } - void TradeWindow::resetReference() { ReferenceInterface::resetReference(); diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b487a8870e..7baf0ce8ec 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -104,8 +104,6 @@ namespace MWGui virtual void onReferenceUnavailable(); int getMerchantGold(); - - void restock(); }; }