From 0ac0c62091a2e7762b22bac4b0975cb3e6e5d6c3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Apr 2025 10:19:07 +0000 Subject: [PATCH 01/19] Replace openmw_spellcreation_dialog.layout --- .../mygui/openmw_spellcreation_dialog.layout | 191 +++++++++--------- 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index c284927a37..fafe4635e7 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -1,93 +1,98 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 15a525676c5ec503edf2650c0bd019f7f4197be3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Apr 2025 10:38:53 +0000 Subject: [PATCH 02/19] Replace spellcreationdialog.cpp to add gold counter for player --- apps/openmw/mwgui/spellcreationdialog.cpp | 1521 +++++++++++---------- 1 file changed, 763 insertions(+), 758 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index d8302df87c..86c46c61e3 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -1,758 +1,763 @@ -#include "spellcreationdialog.hpp" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/windowmanager.hpp" - -#include "../mwworld/class.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/esmstore.hpp" -#include "../mwworld/store.hpp" - -#include "../mwmechanics/actorutil.hpp" -#include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/spellutil.hpp" - -#include "class.hpp" -#include "tooltips.hpp" -#include "widgets.hpp" - -namespace -{ - - bool sortMagicEffects(short id1, short id2) - { - const MWWorld::Store& gmst - = MWBase::Environment::get().getESMStore()->get(); - - return gmst.find(ESM::MagicEffect::indexToGmstString(id1))->mValue.getString() - < gmst.find(ESM::MagicEffect::indexToGmstString(id2))->mValue.getString(); - } - - void init(ESM::ENAMstruct& effect) - { - effect.mArea = 0; - effect.mDuration = 0; - effect.mEffectID = -1; - effect.mMagnMax = 0; - effect.mMagnMin = 0; - effect.mRange = 0; - effect.mSkill = -1; - effect.mAttribute = -1; - } -} - -namespace MWGui -{ - - EditEffectDialog::EditEffectDialog() - : WindowModal("openmw_edit_effect.layout") - , mEditing(false) - , mMagicEffect(nullptr) - , mConstantEffect(false) - { - init(mEffect); - init(mOldEffect); - - getWidget(mCancelButton, "CancelButton"); - getWidget(mOkButton, "OkButton"); - getWidget(mDeleteButton, "DeleteButton"); - getWidget(mRangeButton, "RangeButton"); - getWidget(mMagnitudeMinValue, "MagnitudeMinValue"); - getWidget(mMagnitudeMaxValue, "MagnitudeMaxValue"); - getWidget(mDurationValue, "DurationValue"); - getWidget(mAreaValue, "AreaValue"); - getWidget(mMagnitudeMinSlider, "MagnitudeMinSlider"); - getWidget(mMagnitudeMaxSlider, "MagnitudeMaxSlider"); - getWidget(mDurationSlider, "DurationSlider"); - getWidget(mAreaSlider, "AreaSlider"); - getWidget(mEffectImage, "EffectImage"); - getWidget(mEffectName, "EffectName"); - getWidget(mAreaText, "AreaText"); - getWidget(mDurationBox, "DurationBox"); - getWidget(mAreaBox, "AreaBox"); - getWidget(mMagnitudeBox, "MagnitudeBox"); - - mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); - mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); - mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked); - - mMagnitudeMinSlider->eventScrollChangePosition - += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged); - mMagnitudeMaxSlider->eventScrollChangePosition - += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); - mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); - mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); - } - - void EditEffectDialog::setConstantEffect(bool constant) - { - mConstantEffect = constant; - } - - void EditEffectDialog::onOpen() - { - WindowModal::onOpen(); - center(); - } - - bool EditEffectDialog::exit() - { - if (mEditing) - eventEffectModified(mOldEffect); - else - eventEffectRemoved(mEffect); - return true; - } - - void EditEffectDialog::newEffect(const ESM::MagicEffect* effect) - { - bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - - setMagicEffect(effect); - mEditing = false; - - mDeleteButton->setVisible(false); - - mEffect.mRange = ESM::RT_Self; - if (!allowSelf) - mEffect.mRange = ESM::RT_Touch; - if (!allowTouch) - mEffect.mRange = ESM::RT_Target; - mEffect.mMagnMin = 1; - mEffect.mMagnMax = 1; - mEffect.mDuration = 1; - mEffect.mArea = 0; - mEffect.mSkill = -1; - mEffect.mAttribute = -1; - eventEffectAdded(mEffect); - - onRangeButtonClicked(mRangeButton); - - mMagnitudeMinSlider->setScrollPosition(0); - mMagnitudeMaxSlider->setScrollPosition(0); - mAreaSlider->setScrollPosition(0); - mDurationSlider->setScrollPosition(0); - - mDurationValue->setCaption("1"); - mMagnitudeMinValue->setCaption("1"); - const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; - - mMagnitudeMaxValue->setCaption(to + " 1"); - mAreaValue->setCaption("0"); - - setVisible(true); - } - - void EditEffectDialog::editEffect(ESM::ENAMstruct effect) - { - const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().find(effect.mEffectID); - - setMagicEffect(magicEffect); - mOldEffect = effect; - mEffect = effect; - mEditing = true; - - mDeleteButton->setVisible(true); - - mMagnitudeMinSlider->setScrollPosition(effect.mMagnMin - 1); - mMagnitudeMaxSlider->setScrollPosition(effect.mMagnMax - 1); - mAreaSlider->setScrollPosition(effect.mArea); - mDurationSlider->setScrollPosition(effect.mDuration - 1); - - if (mEffect.mRange == ESM::RT_Self) - mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); - else if (mEffect.mRange == ESM::RT_Target) - mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); - else if (mEffect.mRange == ESM::RT_Touch) - mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); - - onMagnitudeMinChanged(mMagnitudeMinSlider, effect.mMagnMin - 1); - onMagnitudeMaxChanged(mMagnitudeMinSlider, effect.mMagnMax - 1); - onAreaChanged(mAreaSlider, effect.mArea); - onDurationChanged(mDurationSlider, effect.mDuration - 1); - eventEffectModified(mEffect); - - updateBoxes(); - } - - void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) - { - mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( - effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); - - mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); - - mEffect.mEffectID = effect->mIndex; - - mMagicEffect = effect; - - updateBoxes(); - } - - void EditEffectDialog::updateBoxes() - { - static int startY = mMagnitudeBox->getPosition().top; - int curY = startY; - - mMagnitudeBox->setVisible(false); - mDurationBox->setVisible(false); - mAreaBox->setVisible(false); - - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY); - mMagnitudeBox->setVisible(true); - curY += mMagnitudeBox->getSize().height; - } - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && mConstantEffect == false) - { - mDurationBox->setPosition(mDurationBox->getPosition().left, curY); - mDurationBox->setVisible(true); - curY += mDurationBox->getSize().height; - } - if (mEffect.mRange != ESM::RT_Self) - { - mAreaBox->setPosition(mAreaBox->getPosition().left, curY); - mAreaBox->setVisible(true); - // curY += mAreaBox->getSize().height; - } - } - - void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) - { - mEffect.mRange = (mEffect.mRange + 1) % 3; - - // cycle through range types until we find something that's allowed - // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect - // dialog) - bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; - if (mEffect.mRange == ESM::RT_Self && !allowSelf) - mEffect.mRange = (mEffect.mRange + 1) % 3; - if (mEffect.mRange == ESM::RT_Touch && !allowTouch) - mEffect.mRange = (mEffect.mRange + 1) % 3; - if (mEffect.mRange == ESM::RT_Target && !allowTarget) - mEffect.mRange = (mEffect.mRange + 1) % 3; - - if (mEffect.mRange == ESM::RT_Self) - { - mAreaSlider->setScrollPosition(0); - onAreaChanged(mAreaSlider, 0); - } - - if (mEffect.mRange == ESM::RT_Self) - mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); - else if (mEffect.mRange == ESM::RT_Target) - mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); - else if (mEffect.mRange == ESM::RT_Touch) - mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); - - updateBoxes(); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onDeleteButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - - eventEffectRemoved(mEffect); - } - - void EditEffectDialog::onOkButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - } - - void EditEffectDialog::onCancelButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - exit(); - } - - void EditEffectDialog::setSkill(ESM::RefId skill) - { - mEffect.mSkill = ESM::Skill::refIdToIndex(skill); - eventEffectModified(mEffect); - } - - void EditEffectDialog::setAttribute(ESM::RefId attribute) - { - mEffect.mAttribute = ESM::Attribute::refIdToIndex(attribute); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos + 1)); - mEffect.mMagnMin = pos + 1; - - // trigger the check again (see below) - onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition()); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos) - { - // make sure the max value is actually larger or equal than the min value - size_t magnMin - = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning - if (pos + 1 < magnMin) - { - pos = mEffect.mMagnMin - 1; - sender->setScrollPosition(pos); - } - - mEffect.mMagnMax = pos + 1; - const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; - - mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos + 1)); - - eventEffectModified(mEffect); - } - - void EditEffectDialog::onDurationChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mDurationValue->setCaption(MyGUI::utility::toString(pos + 1)); - mEffect.mDuration = pos + 1; - eventEffectModified(mEffect); - } - - void EditEffectDialog::onAreaChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mAreaValue->setCaption(MyGUI::utility::toString(pos)); - mEffect.mArea = pos; - eventEffectModified(mEffect); - } - - // ------------------------------------------------------------------------------------------------ - - SpellCreationDialog::SpellCreationDialog() - : WindowBase("openmw_spellcreation_dialog.layout") - , EffectEditorBase(EffectEditorBase::Spellmaking) - { - getWidget(mNameEdit, "NameEdit"); - getWidget(mMagickaCost, "MagickaCost"); - getWidget(mSuccessChance, "SuccessChance"); - getWidget(mAvailableEffectsList, "AvailableEffects"); - getWidget(mUsedEffectsView, "UsedEffects"); - getWidget(mPriceLabel, "PriceLabel"); - getWidget(mBuyButton, "BuyButton"); - getWidget(mCancelButton, "CancelButton"); - - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); - mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); - mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); - - setWidgets(mAvailableEffectsList, mUsedEffectsView); - } - - void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) - { - if (actor.isEmpty() || !actor.getClass().isActor()) - throw std::runtime_error("Invalid argument in SpellCreationDialog::setPtr"); - - mPtr = actor; - mNameEdit->setCaption({}); - - startEditing(); - } - - void SpellCreationDialog::onCancelButtonClicked(MyGUI::Widget* sender) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_SpellCreation); - } - - void SpellCreationDialog::onBuyButtonClicked(MyGUI::Widget* sender) - { - if (mEffects.size() <= 0) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage30}"); - return; - } - - if (mNameEdit->getCaption().empty()) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage10}"); - return; - } - - if (mMagickaCost->getCaption() == "0") - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sEnchantmentMenu8}"); - return; - } - - MWWorld::Ptr player = MWMechanics::getPlayer(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - - int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); - if (price > playerGold) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage18}"); - return; - } - - mSpell.mName = mNameEdit->getCaption(); - - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price); - - // add gold to NPC trading gold pool - MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); - npcStats.setGoldPool(npcStats.getGoldPool() + price); - - MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Mysticism Hit")); - - const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->insert(mSpell); - - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - spells.add(spell->mId); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); - } - - void SpellCreationDialog::onAccept(MyGUI::EditBox* sender) - { - onBuyButtonClicked(sender); - - // To do not spam onAccept() again and again - MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); - } - - void SpellCreationDialog::onOpen() - { - center(); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); - } - - void SpellCreationDialog::onReferenceUnavailable() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); - } - - void SpellCreationDialog::notifyEffectsChanged() - { - if (mEffects.empty()) - { - mMagickaCost->setCaption("0"); - mPriceLabel->setCaption("0"); - mSuccessChance->setCaption("0"); - return; - } - - float y = 0; - - const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - - for (const ESM::ENAMstruct& effect : mEffects) - { - y += std::max( - 1.f, MWMechanics::calcEffectCost(effect, nullptr, MWMechanics::EffectCostMethod::PlayerSpell)); - - if (effect.mRange == ESM::RT_Target) - y *= 1.5; - } - - mSpell.mEffects.populate(mEffects); - mSpell.mData.mCost = int(y); - mSpell.mData.mType = ESM::Spell::ST_Spell; - mSpell.mData.mFlags = 0; - - mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); - - float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->mValue.getFloat(); - - int price = std::max(1, static_cast(y * fSpellMakingValueMult)); - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); - - mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - - float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); - - int intChance = std::min(100, int(chance)); - mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); - } - - // ------------------------------------------------------------------------------------------------ - - EffectEditorBase::EffectEditorBase(Type type) - : mAvailableEffectsList(nullptr) - , mUsedEffectsView(nullptr) - , mAddEffectDialog() - , mSelectedEffect(0) - , mSelectedKnownEffectId(0) - , mConstantEffect(false) - , mType(type) - { - mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); - mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); - mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved); - - mAddEffectDialog.setVisible(false); - } - - EffectEditorBase::~EffectEditorBase() {} - - void EffectEditorBase::startEditing() - { - // get the list of magic effects that are known to the player - - MWWorld::Ptr player = MWMechanics::getPlayer(); - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - - std::vector knownEffects; - - for (const ESM::Spell* spell : spells) - { - // only normal spells count - if (spell->mData.mType != ESM::Spell::ST_Spell) - continue; - - for (const ESM::IndexedENAMstruct& effectInfo : spell->mEffects.mList) - { - int16_t effectId = effectInfo.mData.mEffectID; - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(effectId); - - // skip effects that do not allow spellmaking/enchanting - int requiredFlags - = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting; - if (!(effect->mData.mFlags & requiredFlags)) - continue; - - if (std::find(knownEffects.begin(), knownEffects.end(), effectId) == knownEffects.end()) - knownEffects.push_back(effectId); - } - } - - std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects); - - mAvailableEffectsList->clear(); - - int i = 0; - for (const short effectId : knownEffects) - { - mAvailableEffectsList->addItem(MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString()); - mButtonMapping[i] = effectId; - ++i; - } - mAvailableEffectsList->adjustSize(); - mAvailableEffectsList->scrollToTop(); - - for (const short effectId : knownEffects) - { - const std::string& name = MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString(); - MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); - - ToolTips::createMagicEffectToolTip(w, effectId); - } - - mEffects.clear(); - updateEffectsView(); - } - - void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) - { - mAvailableEffectsList = availableEffectsList; - mUsedEffectsView = usedEffectsView; - - mAvailableEffectsList->eventWidgetSelected - += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked); - } - - void EffectEditorBase::onSelectAttribute() - { - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - mAddEffectDialog.newEffect(effect); - mAddEffectDialog.setAttribute(mSelectAttributeDialog->getAttributeId()); - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); - } - - void EffectEditorBase::onSelectSkill() - { - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - mAddEffectDialog.newEffect(effect); - mAddEffectDialog.setSkill(mSelectSkillDialog->getSkillId()); - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); - } - - void EffectEditorBase::onAttributeOrSkillCancel() - { - if (mSelectSkillDialog != nullptr) - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); - if (mSelectAttributeDialog != nullptr) - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); - } - - void EffectEditorBase::onAvailableEffectClicked(MyGUI::Widget* sender) - { - if (mEffects.size() >= 8) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); - return; - } - - int buttonId = *sender->getUserData(); - mSelectedKnownEffectId = mButtonMapping[buttonId]; - - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; - - if (!allowSelf && !allowTouch && !allowTarget) - return; // TODO: Show an error message popup? - - if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - mSelectSkillDialog = std::make_unique(); - mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); - mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); - mSelectSkillDialog->setVisible(true); - } - else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - mSelectAttributeDialog = std::make_unique(); - mSelectAttributeDialog->eventCancel - += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); - mSelectAttributeDialog->eventItemSelected - += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); - mSelectAttributeDialog->setVisible(true); - } - else - { - for (const ESM::ENAMstruct& effectInfo : mEffects) - { - if (effectInfo.mEffectID == mSelectedKnownEffectId) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sOnetypeEffectMessage}"); - return; - } - } - - mAddEffectDialog.newEffect(effect); - } - } - - void EffectEditorBase::onEffectModified(ESM::ENAMstruct effect) - { - mEffects[mSelectedEffect] = effect; - - updateEffectsView(); - } - - void EffectEditorBase::onEffectRemoved(ESM::ENAMstruct effect) - { - mEffects.erase(mEffects.begin() + mSelectedEffect); - updateEffectsView(); - } - - void EffectEditorBase::updateEffectsView() - { - MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator(); - MyGUI::Gui::getInstance().destroyWidgets(oldWidgets); - - MyGUI::IntSize size(0, 0); - - int i = 0; - for (const ESM::ENAMstruct& effectInfo : mEffects) - { - Widgets::SpellEffectParams params; - params.mEffectID = effectInfo.mEffectID; - params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill); - params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute); - params.mDuration = effectInfo.mDuration; - params.mMagnMin = effectInfo.mMagnMin; - params.mMagnMax = effectInfo.mMagnMax; - params.mRange = effectInfo.mRange; - params.mArea = effectInfo.mArea; - params.mIsConstant = mConstantEffect; - - MyGUI::Button* button = mUsedEffectsView->createWidget( - {}, MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); - button->setUserData(i); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect); - button->setNeedMouseFocus(true); - - Widgets::MWSpellEffectPtr effect = button->createWidget( - "MW_EffectImage", MyGUI::IntCoord(0, 0, 0, 24), MyGUI::Align::Default); - - effect->setNeedMouseFocus(false); - effect->setSpellEffect(params); - - effect->setSize(effect->getRequestedWidth(), 24); - button->setSize(effect->getRequestedWidth(), 24); - - size.width = std::max(size.width, effect->getRequestedWidth()); - size.height += 24; - ++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(); - } - - void EffectEditorBase::onEffectAdded(ESM::ENAMstruct effect) - { - mEffects.push_back(effect); - mSelectedEffect = mEffects.size() - 1; - - updateEffectsView(); - } - - void EffectEditorBase::onEditEffect(MyGUI::Widget* sender) - { - int id = *sender->getUserData(); - - mSelectedEffect = id; - - mAddEffectDialog.editEffect(mEffects[id]); - mAddEffectDialog.setVisible(true); - } - - void EffectEditorBase::setConstantEffect(bool constant) - { - mAddEffectDialog.setConstantEffect(constant); - if (!mConstantEffect && constant) - for (ESM::ENAMstruct& effect : mEffects) - effect.mRange = ESM::RT_Self; - mConstantEffect = constant; - } -} +#include "spellcreationdialog.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/store.hpp" + +#include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellutil.hpp" + +#include "class.hpp" +#include "tooltips.hpp" +#include "widgets.hpp" + +namespace +{ + + bool sortMagicEffects(short id1, short id2) + { + const MWWorld::Store& gmst + = MWBase::Environment::get().getESMStore()->get(); + + return gmst.find(ESM::MagicEffect::indexToGmstString(id1))->mValue.getString() + < gmst.find(ESM::MagicEffect::indexToGmstString(id2))->mValue.getString(); + } + + void init(ESM::ENAMstruct& effect) + { + effect.mArea = 0; + effect.mDuration = 0; + effect.mEffectID = -1; + effect.mMagnMax = 0; + effect.mMagnMin = 0; + effect.mRange = 0; + effect.mSkill = -1; + effect.mAttribute = -1; + } +} + +namespace MWGui +{ + + EditEffectDialog::EditEffectDialog() + : WindowModal("openmw_edit_effect.layout") + , mEditing(false) + , mMagicEffect(nullptr) + , mConstantEffect(false) + { + init(mEffect); + init(mOldEffect); + + getWidget(mCancelButton, "CancelButton"); + getWidget(mOkButton, "OkButton"); + getWidget(mDeleteButton, "DeleteButton"); + getWidget(mRangeButton, "RangeButton"); + getWidget(mMagnitudeMinValue, "MagnitudeMinValue"); + getWidget(mMagnitudeMaxValue, "MagnitudeMaxValue"); + getWidget(mDurationValue, "DurationValue"); + getWidget(mAreaValue, "AreaValue"); + getWidget(mMagnitudeMinSlider, "MagnitudeMinSlider"); + getWidget(mMagnitudeMaxSlider, "MagnitudeMaxSlider"); + getWidget(mDurationSlider, "DurationSlider"); + getWidget(mAreaSlider, "AreaSlider"); + getWidget(mEffectImage, "EffectImage"); + getWidget(mEffectName, "EffectName"); + getWidget(mAreaText, "AreaText"); + getWidget(mDurationBox, "DurationBox"); + getWidget(mAreaBox, "AreaBox"); + getWidget(mMagnitudeBox, "MagnitudeBox"); + + mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); + mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked); + + mMagnitudeMinSlider->eventScrollChangePosition + += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged); + mMagnitudeMaxSlider->eventScrollChangePosition + += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); + mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); + mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + } + + void EditEffectDialog::setConstantEffect(bool constant) + { + mConstantEffect = constant; + } + + void EditEffectDialog::onOpen() + { + WindowModal::onOpen(); + center(); + } + + bool EditEffectDialog::exit() + { + if (mEditing) + eventEffectModified(mOldEffect); + else + eventEffectRemoved(mEffect); + return true; + } + + void EditEffectDialog::newEffect(const ESM::MagicEffect* effect) + { + bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + + setMagicEffect(effect); + mEditing = false; + + mDeleteButton->setVisible(false); + + mEffect.mRange = ESM::RT_Self; + if (!allowSelf) + mEffect.mRange = ESM::RT_Touch; + if (!allowTouch) + mEffect.mRange = ESM::RT_Target; + mEffect.mMagnMin = 1; + mEffect.mMagnMax = 1; + mEffect.mDuration = 1; + mEffect.mArea = 0; + mEffect.mSkill = -1; + mEffect.mAttribute = -1; + eventEffectAdded(mEffect); + + onRangeButtonClicked(mRangeButton); + + mMagnitudeMinSlider->setScrollPosition(0); + mMagnitudeMaxSlider->setScrollPosition(0); + mAreaSlider->setScrollPosition(0); + mDurationSlider->setScrollPosition(0); + + mDurationValue->setCaption("1"); + mMagnitudeMinValue->setCaption("1"); + const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; + + mMagnitudeMaxValue->setCaption(to + " 1"); + mAreaValue->setCaption("0"); + + setVisible(true); + } + + void EditEffectDialog::editEffect(ESM::ENAMstruct effect) + { + const ESM::MagicEffect* magicEffect + = MWBase::Environment::get().getESMStore()->get().find(effect.mEffectID); + + setMagicEffect(magicEffect); + mOldEffect = effect; + mEffect = effect; + mEditing = true; + + mDeleteButton->setVisible(true); + + mMagnitudeMinSlider->setScrollPosition(effect.mMagnMin - 1); + mMagnitudeMaxSlider->setScrollPosition(effect.mMagnMax - 1); + mAreaSlider->setScrollPosition(effect.mArea); + mDurationSlider->setScrollPosition(effect.mDuration - 1); + + if (mEffect.mRange == ESM::RT_Self) + mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); + else if (mEffect.mRange == ESM::RT_Target) + mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); + else if (mEffect.mRange == ESM::RT_Touch) + mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); + + onMagnitudeMinChanged(mMagnitudeMinSlider, effect.mMagnMin - 1); + onMagnitudeMaxChanged(mMagnitudeMinSlider, effect.mMagnMax - 1); + onAreaChanged(mAreaSlider, effect.mArea); + onDurationChanged(mDurationSlider, effect.mDuration - 1); + eventEffectModified(mEffect); + + updateBoxes(); + } + + void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) + { + mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( + effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); + + mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); + + mEffect.mEffectID = effect->mIndex; + + mMagicEffect = effect; + + updateBoxes(); + } + + void EditEffectDialog::updateBoxes() + { + static int startY = mMagnitudeBox->getPosition().top; + int curY = startY; + + mMagnitudeBox->setVisible(false); + mDurationBox->setVisible(false); + mAreaBox->setVisible(false); + + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY); + mMagnitudeBox->setVisible(true); + curY += mMagnitudeBox->getSize().height; + } + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && mConstantEffect == false) + { + mDurationBox->setPosition(mDurationBox->getPosition().left, curY); + mDurationBox->setVisible(true); + curY += mDurationBox->getSize().height; + } + if (mEffect.mRange != ESM::RT_Self) + { + mAreaBox->setPosition(mAreaBox->getPosition().left, curY); + mAreaBox->setVisible(true); + // curY += mAreaBox->getSize().height; + } + } + + void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) + { + mEffect.mRange = (mEffect.mRange + 1) % 3; + + // cycle through range types until we find something that's allowed + // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect + // dialog) + bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; + if (mEffect.mRange == ESM::RT_Self && !allowSelf) + mEffect.mRange = (mEffect.mRange + 1) % 3; + if (mEffect.mRange == ESM::RT_Touch && !allowTouch) + mEffect.mRange = (mEffect.mRange + 1) % 3; + if (mEffect.mRange == ESM::RT_Target && !allowTarget) + mEffect.mRange = (mEffect.mRange + 1) % 3; + + if (mEffect.mRange == ESM::RT_Self) + { + mAreaSlider->setScrollPosition(0); + onAreaChanged(mAreaSlider, 0); + } + + if (mEffect.mRange == ESM::RT_Self) + mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); + else if (mEffect.mRange == ESM::RT_Target) + mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); + else if (mEffect.mRange == ESM::RT_Touch) + mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); + + updateBoxes(); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onDeleteButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + + eventEffectRemoved(mEffect); + } + + void EditEffectDialog::onOkButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + } + + void EditEffectDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + exit(); + } + + void EditEffectDialog::setSkill(ESM::RefId skill) + { + mEffect.mSkill = ESM::Skill::refIdToIndex(skill); + eventEffectModified(mEffect); + } + + void EditEffectDialog::setAttribute(ESM::RefId attribute) + { + mEffect.mAttribute = ESM::Attribute::refIdToIndex(attribute); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos + 1)); + mEffect.mMagnMin = pos + 1; + + // trigger the check again (see below) + onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition()); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos) + { + // make sure the max value is actually larger or equal than the min value + size_t magnMin + = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning + if (pos + 1 < magnMin) + { + pos = mEffect.mMagnMin - 1; + sender->setScrollPosition(pos); + } + + mEffect.mMagnMax = pos + 1; + const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; + + mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos + 1)); + + eventEffectModified(mEffect); + } + + void EditEffectDialog::onDurationChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mDurationValue->setCaption(MyGUI::utility::toString(pos + 1)); + mEffect.mDuration = pos + 1; + eventEffectModified(mEffect); + } + + void EditEffectDialog::onAreaChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mAreaValue->setCaption(MyGUI::utility::toString(pos)); + mEffect.mArea = pos; + eventEffectModified(mEffect); + } + + // ------------------------------------------------------------------------------------------------ + + SpellCreationDialog::SpellCreationDialog() + : WindowBase("openmw_spellcreation_dialog.layout") + , EffectEditorBase(EffectEditorBase::Spellmaking) + { + getWidget(mNameEdit, "NameEdit"); + getWidget(mMagickaCost, "MagickaCost"); + getWidget(mSuccessChance, "SuccessChance"); + getWidget(mAvailableEffectsList, "AvailableEffects"); + getWidget(mUsedEffectsView, "UsedEffects"); + getWidget(mPriceLabel, "PriceLabel"); + getWidget(mPlayerGold, "PlayerGold"); + getWidget(mBuyButton, "BuyButton"); + getWidget(mCancelButton, "CancelButton"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); + mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); + mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); + + setWidgets(mAvailableEffectsList, mUsedEffectsView); + } + + void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) + { + if (actor.isEmpty() || !actor.getClass().isActor()) + throw std::runtime_error("Invalid argument in SpellCreationDialog::setPtr"); + + mPtr = actor; + mNameEdit->setCaption({}); + + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + + startEditing(); + } + + void SpellCreationDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_SpellCreation); + } + + void SpellCreationDialog::onBuyButtonClicked(MyGUI::Widget* sender) + { + if (mEffects.size() <= 0) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage30}"); + return; + } + + if (mNameEdit->getCaption().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage10}"); + return; + } + + if (mMagickaCost->getCaption() == "0") + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sEnchantmentMenu8}"); + return; + } + + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); + if (price > playerGold) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage18}"); + return; + } + + mSpell.mName = mNameEdit->getCaption(); + + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Mysticism Hit")); + + const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->insert(mSpell); + + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + spells.add(spell->mId); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); + } + + void SpellCreationDialog::onAccept(MyGUI::EditBox* sender) + { + onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); + } + + void SpellCreationDialog::onOpen() + { + center(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + } + + void SpellCreationDialog::onReferenceUnavailable() + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); + } + + void SpellCreationDialog::notifyEffectsChanged() + { + if (mEffects.empty()) + { + mMagickaCost->setCaption("0"); + mPriceLabel->setCaption("0"); + mSuccessChance->setCaption("0"); + return; + } + + float y = 0; + + const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); + + for (const ESM::ENAMstruct& effect : mEffects) + { + y += std::max( + 1.f, MWMechanics::calcEffectCost(effect, nullptr, MWMechanics::EffectCostMethod::PlayerSpell)); + + if (effect.mRange == ESM::RT_Target) + y *= 1.5; + } + + mSpell.mEffects.populate(mEffects); + mSpell.mData.mCost = int(y); + mSpell.mData.mType = ESM::Spell::ST_Spell; + mSpell.mData.mFlags = 0; + + mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); + + float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->mValue.getFloat(); + + int price = std::max(1, static_cast(y * fSpellMakingValueMult)); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + + mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); + + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); + + int intChance = std::min(100, int(chance)); + mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); + } + + // ------------------------------------------------------------------------------------------------ + + EffectEditorBase::EffectEditorBase(Type type) + : mAvailableEffectsList(nullptr) + , mUsedEffectsView(nullptr) + , mAddEffectDialog() + , mSelectedEffect(0) + , mSelectedKnownEffectId(0) + , mConstantEffect(false) + , mType(type) + { + mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); + mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); + mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved); + + mAddEffectDialog.setVisible(false); + } + + EffectEditorBase::~EffectEditorBase() {} + + void EffectEditorBase::startEditing() + { + // get the list of magic effects that are known to the player + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + + std::vector knownEffects; + + for (const ESM::Spell* spell : spells) + { + // only normal spells count + if (spell->mData.mType != ESM::Spell::ST_Spell) + continue; + + for (const ESM::IndexedENAMstruct& effectInfo : spell->mEffects.mList) + { + int16_t effectId = effectInfo.mData.mEffectID; + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(effectId); + + // skip effects that do not allow spellmaking/enchanting + int requiredFlags + = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting; + if (!(effect->mData.mFlags & requiredFlags)) + continue; + + if (std::find(knownEffects.begin(), knownEffects.end(), effectId) == knownEffects.end()) + knownEffects.push_back(effectId); + } + } + + std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects); + + mAvailableEffectsList->clear(); + + int i = 0; + for (const short effectId : knownEffects) + { + mAvailableEffectsList->addItem(MWBase::Environment::get() + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString()); + mButtonMapping[i] = effectId; + ++i; + } + mAvailableEffectsList->adjustSize(); + mAvailableEffectsList->scrollToTop(); + + for (const short effectId : knownEffects) + { + const std::string& name = MWBase::Environment::get() + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString(); + MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); + + ToolTips::createMagicEffectToolTip(w, effectId); + } + + mEffects.clear(); + updateEffectsView(); + } + + void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) + { + mAvailableEffectsList = availableEffectsList; + mUsedEffectsView = usedEffectsView; + + mAvailableEffectsList->eventWidgetSelected + += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked); + } + + void EffectEditorBase::onSelectAttribute() + { + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + mAddEffectDialog.newEffect(effect); + mAddEffectDialog.setAttribute(mSelectAttributeDialog->getAttributeId()); + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); + } + + void EffectEditorBase::onSelectSkill() + { + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + mAddEffectDialog.newEffect(effect); + mAddEffectDialog.setSkill(mSelectSkillDialog->getSkillId()); + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); + } + + void EffectEditorBase::onAttributeOrSkillCancel() + { + if (mSelectSkillDialog != nullptr) + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); + if (mSelectAttributeDialog != nullptr) + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); + } + + void EffectEditorBase::onAvailableEffectClicked(MyGUI::Widget* sender) + { + if (mEffects.size() >= 8) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); + return; + } + + int buttonId = *sender->getUserData(); + mSelectedKnownEffectId = mButtonMapping[buttonId]; + + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; + + if (!allowSelf && !allowTouch && !allowTarget) + return; // TODO: Show an error message popup? + + if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + mSelectSkillDialog = std::make_unique(); + mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); + mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); + mSelectSkillDialog->setVisible(true); + } + else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + { + mSelectAttributeDialog = std::make_unique(); + mSelectAttributeDialog->eventCancel + += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); + mSelectAttributeDialog->eventItemSelected + += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); + mSelectAttributeDialog->setVisible(true); + } + else + { + for (const ESM::ENAMstruct& effectInfo : mEffects) + { + if (effectInfo.mEffectID == mSelectedKnownEffectId) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sOnetypeEffectMessage}"); + return; + } + } + + mAddEffectDialog.newEffect(effect); + } + } + + void EffectEditorBase::onEffectModified(ESM::ENAMstruct effect) + { + mEffects[mSelectedEffect] = effect; + + updateEffectsView(); + } + + void EffectEditorBase::onEffectRemoved(ESM::ENAMstruct effect) + { + mEffects.erase(mEffects.begin() + mSelectedEffect); + updateEffectsView(); + } + + void EffectEditorBase::updateEffectsView() + { + MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator(); + MyGUI::Gui::getInstance().destroyWidgets(oldWidgets); + + MyGUI::IntSize size(0, 0); + + int i = 0; + for (const ESM::ENAMstruct& effectInfo : mEffects) + { + Widgets::SpellEffectParams params; + params.mEffectID = effectInfo.mEffectID; + params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill); + params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute); + params.mDuration = effectInfo.mDuration; + params.mMagnMin = effectInfo.mMagnMin; + params.mMagnMax = effectInfo.mMagnMax; + params.mRange = effectInfo.mRange; + params.mArea = effectInfo.mArea; + params.mIsConstant = mConstantEffect; + + MyGUI::Button* button = mUsedEffectsView->createWidget( + {}, MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); + button->setUserData(i); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect); + button->setNeedMouseFocus(true); + + Widgets::MWSpellEffectPtr effect = button->createWidget( + "MW_EffectImage", MyGUI::IntCoord(0, 0, 0, 24), MyGUI::Align::Default); + + effect->setNeedMouseFocus(false); + effect->setSpellEffect(params); + + effect->setSize(effect->getRequestedWidth(), 24); + button->setSize(effect->getRequestedWidth(), 24); + + size.width = std::max(size.width, effect->getRequestedWidth()); + size.height += 24; + ++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(); + } + + void EffectEditorBase::onEffectAdded(ESM::ENAMstruct effect) + { + mEffects.push_back(effect); + mSelectedEffect = mEffects.size() - 1; + + updateEffectsView(); + } + + void EffectEditorBase::onEditEffect(MyGUI::Widget* sender) + { + int id = *sender->getUserData(); + + mSelectedEffect = id; + + mAddEffectDialog.editEffect(mEffects[id]); + mAddEffectDialog.setVisible(true); + } + + void EffectEditorBase::setConstantEffect(bool constant) + { + mAddEffectDialog.setConstantEffect(constant); + if (!mConstantEffect && constant) + for (ESM::ENAMstruct& effect : mEffects) + effect.mRange = ESM::RT_Self; + mConstantEffect = constant; + } +} From f8c8b9b433c66ffb7e545df4367eab6fab160a90 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Apr 2025 10:50:05 +0000 Subject: [PATCH 03/19] Replace spellcreationdialog.hpp to add player gold to spell creation window. --- apps/openmw/mwgui/spellcreationdialog.hpp | 375 +++++++++++----------- 1 file changed, 191 insertions(+), 184 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 6dfe61fc57..0ffa875620 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,184 +1,191 @@ -#ifndef MWGUI_SPELLCREATION_H -#define MWGUI_SPELLCREATION_H - -#include - -#include -#include - -#include "referenceinterface.hpp" -#include "windowbase.hpp" - -namespace Gui -{ - class MWList; -} - -namespace MWGui -{ - - class SelectSkillDialog; - class SelectAttributeDialog; - - class EditEffectDialog : public WindowModal - { - public: - EditEffectDialog(); - - void onOpen() override; - bool exit() override; - - void setConstantEffect(bool constant); - - void setSkill(ESM::RefId skill); - void setAttribute(ESM::RefId attribute); - - void newEffect(const ESM::MagicEffect* effect); - void editEffect(ESM::ENAMstruct effect); - typedef MyGUI::delegates::MultiDelegate EventHandle_Effect; - - EventHandle_Effect eventEffectAdded; - EventHandle_Effect eventEffectModified; - EventHandle_Effect eventEffectRemoved; - - protected: - MyGUI::Button* mCancelButton; - MyGUI::Button* mOkButton; - MyGUI::Button* mDeleteButton; - - MyGUI::Button* mRangeButton; - - MyGUI::Widget* mDurationBox; - MyGUI::Widget* mMagnitudeBox; - MyGUI::Widget* mAreaBox; - - MyGUI::TextBox* mMagnitudeMinValue; - MyGUI::TextBox* mMagnitudeMaxValue; - MyGUI::TextBox* mDurationValue; - MyGUI::TextBox* mAreaValue; - - MyGUI::ScrollBar* mMagnitudeMinSlider; - MyGUI::ScrollBar* mMagnitudeMaxSlider; - MyGUI::ScrollBar* mDurationSlider; - MyGUI::ScrollBar* mAreaSlider; - - MyGUI::TextBox* mAreaText; - - MyGUI::ImageBox* mEffectImage; - MyGUI::TextBox* mEffectName; - - bool mEditing; - - protected: - void onRangeButtonClicked(MyGUI::Widget* sender); - void onDeleteButtonClicked(MyGUI::Widget* sender); - void onOkButtonClicked(MyGUI::Widget* sender); - void onCancelButtonClicked(MyGUI::Widget* sender); - - void onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos); - void onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos); - void onDurationChanged(MyGUI::ScrollBar* sender, size_t pos); - void onAreaChanged(MyGUI::ScrollBar* sender, size_t pos); - void setMagicEffect(const ESM::MagicEffect* effect); - - void updateBoxes(); - - protected: - ESM::ENAMstruct mEffect; - ESM::ENAMstruct mOldEffect; - - const ESM::MagicEffect* mMagicEffect; - - bool mConstantEffect; - }; - - class EffectEditorBase - { - public: - enum Type - { - Spellmaking, - Enchanting - }; - - EffectEditorBase(Type type); - virtual ~EffectEditorBase(); - - void setConstantEffect(bool constant); - - protected: - std::map mButtonMapping; // maps button ID to effect ID - - Gui::MWList* mAvailableEffectsList; - MyGUI::ScrollView* mUsedEffectsView; - - EditEffectDialog mAddEffectDialog; - std::unique_ptr mSelectAttributeDialog; - std::unique_ptr mSelectSkillDialog; - - int mSelectedEffect; - short mSelectedKnownEffectId; - - bool mConstantEffect; - - std::vector mEffects; - - void onEffectAdded(ESM::ENAMstruct effect); - void onEffectModified(ESM::ENAMstruct effect); - void onEffectRemoved(ESM::ENAMstruct effect); - - void onAvailableEffectClicked(MyGUI::Widget* sender); - - void onAttributeOrSkillCancel(); - void onSelectAttribute(); - void onSelectSkill(); - - void onEditEffect(MyGUI::Widget* sender); - - void updateEffectsView(); - - void startEditing(); - void setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView); - - virtual void notifyEffectsChanged() {} - - private: - Type mType; - }; - - class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase - { - public: - SpellCreationDialog(); - - void onOpen() override; - void clear() override { resetReference(); } - - void onFrame(float dt) override { checkReferenceAvailable(); } - - void setPtr(const MWWorld::Ptr& actor) override; - - std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; } - - protected: - void onReferenceUnavailable() override; - - void onCancelButtonClicked(MyGUI::Widget* sender); - void onBuyButtonClicked(MyGUI::Widget* sender); - void onAccept(MyGUI::EditBox* sender); - - void notifyEffectsChanged() override; - - MyGUI::EditBox* mNameEdit; - MyGUI::TextBox* mMagickaCost; - MyGUI::TextBox* mSuccessChance; - MyGUI::Button* mBuyButton; - MyGUI::Button* mCancelButton; - MyGUI::TextBox* mPriceLabel; - - ESM::Spell mSpell; - }; - -} - -#endif +#ifndef MWGUI_SPELLCREATION_H +#define MWGUI_SPELLCREATION_H + +#include + +#include +#include + +#include "referenceinterface.hpp" +#include "windowbase.hpp" + +namespace Gui +{ + class MWList; +} + +namespace MWGui +{ + + class SelectSkillDialog; + class SelectAttributeDialog; + + class EditEffectDialog : public WindowModal + { + public: + EditEffectDialog(); + + void onOpen() override; + bool exit() override; + + void setConstantEffect(bool constant); + + void setSkill(ESM::RefId skill); + void setAttribute(ESM::RefId attribute); + + void newEffect(const ESM::MagicEffect* effect); + void editEffect(ESM::ENAMstruct effect); + typedef MyGUI::delegates::MultiDelegate EventHandle_Effect; + + EventHandle_Effect eventEffectAdded; + EventHandle_Effect eventEffectModified; + EventHandle_Effect eventEffectRemoved; + + protected: + MyGUI::Button* mCancelButton; + MyGUI::Button* mOkButton; + MyGUI::Button* mDeleteButton; + + + MyGUI::Button* mRangeButton; + + MyGUI::Widget* mDurationBox; + MyGUI::Widget* mMagnitudeBox; + MyGUI::Widget* mAreaBox; + + MyGUI::TextBox* mMagnitudeMinValue; + MyGUI::TextBox* mMagnitudeMaxValue; + MyGUI::TextBox* mDurationValue; + MyGUI::TextBox* mAreaValue; + + MyGUI::ScrollBar* mMagnitudeMinSlider; + MyGUI::ScrollBar* mMagnitudeMaxSlider; + MyGUI::ScrollBar* mDurationSlider; + MyGUI::ScrollBar* mAreaSlider; + + MyGUI::TextBox* mAreaText; + + MyGUI::ImageBox* mEffectImage; + MyGUI::TextBox* mEffectName; + + bool mEditing; + + protected: + void onRangeButtonClicked(MyGUI::Widget* sender); + void onDeleteButtonClicked(MyGUI::Widget* sender); + void onOkButtonClicked(MyGUI::Widget* sender); + void onCancelButtonClicked(MyGUI::Widget* sender); + + void onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos); + void onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos); + void onDurationChanged(MyGUI::ScrollBar* sender, size_t pos); + void onAreaChanged(MyGUI::ScrollBar* sender, size_t pos); + void setMagicEffect(const ESM::MagicEffect* effect); + + void updateBoxes(); + + protected: + ESM::ENAMstruct mEffect; + ESM::ENAMstruct mOldEffect; + + const ESM::MagicEffect* mMagicEffect; + + bool mConstantEffect; + }; + + class EffectEditorBase + { + public: + enum Type + { + Spellmaking, + Enchanting + }; + + EffectEditorBase(Type type); + virtual ~EffectEditorBase(); + + void setConstantEffect(bool constant); + + + + protected: + std::map mButtonMapping; // maps button ID to effect ID + + Gui::MWList* mAvailableEffectsList; + MyGUI::ScrollView* mUsedEffectsView; + + + EditEffectDialog mAddEffectDialog; + std::unique_ptr mSelectAttributeDialog; + std::unique_ptr mSelectSkillDialog; + + int mSelectedEffect; + short mSelectedKnownEffectId; + + bool mConstantEffect; + + std::vector mEffects; + + void onEffectAdded(ESM::ENAMstruct effect); + void onEffectModified(ESM::ENAMstruct effect); + void onEffectRemoved(ESM::ENAMstruct effect); + + void onAvailableEffectClicked(MyGUI::Widget* sender); + + void onAttributeOrSkillCancel(); + void onSelectAttribute(); + void onSelectSkill(); + + void onEditEffect(MyGUI::Widget* sender); + + void updateEffectsView(); + + void startEditing(); + void setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView); + + virtual void notifyEffectsChanged() {} + + private: + Type mType; + }; + + class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase + { + public: + SpellCreationDialog(); + + void onOpen() override; + void clear() override { resetReference(); } + + void onFrame(float dt) override { checkReferenceAvailable(); } + + void setPtr(const MWWorld::Ptr& actor) override; + + void updateLabels(); + + std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; } + + protected: + void onReferenceUnavailable() override; + + void onCancelButtonClicked(MyGUI::Widget* sender); + void onBuyButtonClicked(MyGUI::Widget* sender); + void onAccept(MyGUI::EditBox* sender); + + void notifyEffectsChanged() override; + + MyGUI::EditBox* mNameEdit; + MyGUI::TextBox* mMagickaCost; + MyGUI::TextBox* mSuccessChance; + MyGUI::Button* mBuyButton; + MyGUI::Button* mCancelButton; + MyGUI::TextBox* mPriceLabel; + MyGUI::TextBox* mPlayerGold; + + ESM::Spell mSpell; + }; + +} + +#endif From bcb27fff6a5c966cc2a004dc09362137adea5d2b Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 3 Apr 2025 01:45:28 +0000 Subject: [PATCH 04/19] Update 2 files - /apps/openmw/mwgui/spellcreationdialog.cpp - /apps/openmw/mwgui/spellcreationdialog.hpp --- apps/openmw/mwgui/spellcreationdialog.cpp | 16 ++++++++-------- apps/openmw/mwgui/spellcreationdialog.hpp | 8 +------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 86c46c61e3..884f2136cb 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -83,7 +83,7 @@ namespace MWGui getWidget(mDurationBox, "DurationBox"); getWidget(mAreaBox, "AreaBox"); getWidget(mMagnitudeBox, "MagnitudeBox"); - + mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); @@ -370,13 +370,13 @@ namespace MWGui mPtr = actor; mNameEdit->setCaption({}); - + MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); - + startEditing(); - } + } void SpellCreationDialog::onCancelButtonClicked(MyGUI::Widget* sender) { @@ -556,10 +556,10 @@ namespace MWGui for (const short effectId : knownEffects) { mAvailableEffectsList->addItem(MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString()); + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString()); mButtonMapping[i] = effectId; ++i; } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 0ffa875620..b3c6ac51be 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -45,7 +45,6 @@ namespace MWGui MyGUI::Button* mCancelButton; MyGUI::Button* mOkButton; MyGUI::Button* mDeleteButton; - MyGUI::Button* mRangeButton; @@ -107,14 +106,11 @@ namespace MWGui void setConstantEffect(bool constant); - - protected: std::map mButtonMapping; // maps button ID to effect ID Gui::MWList* mAvailableEffectsList; MyGUI::ScrollView* mUsedEffectsView; - EditEffectDialog mAddEffectDialog; std::unique_ptr mSelectAttributeDialog; @@ -162,8 +158,6 @@ namespace MWGui void setPtr(const MWWorld::Ptr& actor) override; - void updateLabels(); - std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; } protected: @@ -182,7 +176,7 @@ namespace MWGui MyGUI::Button* mCancelButton; MyGUI::TextBox* mPriceLabel; MyGUI::TextBox* mPlayerGold; - + ESM::Spell mSpell; }; From 2d2df30f8016ce8e22947facf283e8a10e7ecc19 Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 3 Apr 2025 05:12:15 +0000 Subject: [PATCH 05/19] Update file spellcreationdialog.cpp --- apps/openmw/mwgui/spellcreationdialog.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 884f2136cb..bdd1a96d87 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -30,7 +30,6 @@ namespace { - bool sortMagicEffects(short id1, short id2) { const MWWorld::Store& gmst @@ -556,10 +555,10 @@ namespace MWGui for (const short effectId : knownEffects) { mAvailableEffectsList->addItem(MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString()); + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString()); mButtonMapping[i] = effectId; ++i; } From 190a1266a13f71a2893d2652abd926ec90facf7d Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Apr 2025 05:09:18 +0000 Subject: [PATCH 06/19] Update 2 files - /apps/openmw/mwgui/spellcreationdialog.cpp - /apps/openmw/mwgui/spellcreationdialog.hpp --- apps/openmw/mwgui/spellcreationdialog.cpp | 1524 ++++++++++----------- apps/openmw/mwgui/spellcreationdialog.hpp | 370 ++--- 2 files changed, 947 insertions(+), 947 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index bdd1a96d87..651d3014c1 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -1,762 +1,762 @@ -#include "spellcreationdialog.hpp" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/windowmanager.hpp" - -#include "../mwworld/class.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/esmstore.hpp" -#include "../mwworld/store.hpp" - -#include "../mwmechanics/actorutil.hpp" -#include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/spellutil.hpp" - -#include "class.hpp" -#include "tooltips.hpp" -#include "widgets.hpp" - -namespace -{ - bool sortMagicEffects(short id1, short id2) - { - const MWWorld::Store& gmst - = MWBase::Environment::get().getESMStore()->get(); - - return gmst.find(ESM::MagicEffect::indexToGmstString(id1))->mValue.getString() - < gmst.find(ESM::MagicEffect::indexToGmstString(id2))->mValue.getString(); - } - - void init(ESM::ENAMstruct& effect) - { - effect.mArea = 0; - effect.mDuration = 0; - effect.mEffectID = -1; - effect.mMagnMax = 0; - effect.mMagnMin = 0; - effect.mRange = 0; - effect.mSkill = -1; - effect.mAttribute = -1; - } -} - -namespace MWGui -{ - - EditEffectDialog::EditEffectDialog() - : WindowModal("openmw_edit_effect.layout") - , mEditing(false) - , mMagicEffect(nullptr) - , mConstantEffect(false) - { - init(mEffect); - init(mOldEffect); - - getWidget(mCancelButton, "CancelButton"); - getWidget(mOkButton, "OkButton"); - getWidget(mDeleteButton, "DeleteButton"); - getWidget(mRangeButton, "RangeButton"); - getWidget(mMagnitudeMinValue, "MagnitudeMinValue"); - getWidget(mMagnitudeMaxValue, "MagnitudeMaxValue"); - getWidget(mDurationValue, "DurationValue"); - getWidget(mAreaValue, "AreaValue"); - getWidget(mMagnitudeMinSlider, "MagnitudeMinSlider"); - getWidget(mMagnitudeMaxSlider, "MagnitudeMaxSlider"); - getWidget(mDurationSlider, "DurationSlider"); - getWidget(mAreaSlider, "AreaSlider"); - getWidget(mEffectImage, "EffectImage"); - getWidget(mEffectName, "EffectName"); - getWidget(mAreaText, "AreaText"); - getWidget(mDurationBox, "DurationBox"); - getWidget(mAreaBox, "AreaBox"); - getWidget(mMagnitudeBox, "MagnitudeBox"); - - mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); - mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); - mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked); - - mMagnitudeMinSlider->eventScrollChangePosition - += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged); - mMagnitudeMaxSlider->eventScrollChangePosition - += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); - mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); - mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); - } - - void EditEffectDialog::setConstantEffect(bool constant) - { - mConstantEffect = constant; - } - - void EditEffectDialog::onOpen() - { - WindowModal::onOpen(); - center(); - } - - bool EditEffectDialog::exit() - { - if (mEditing) - eventEffectModified(mOldEffect); - else - eventEffectRemoved(mEffect); - return true; - } - - void EditEffectDialog::newEffect(const ESM::MagicEffect* effect) - { - bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - - setMagicEffect(effect); - mEditing = false; - - mDeleteButton->setVisible(false); - - mEffect.mRange = ESM::RT_Self; - if (!allowSelf) - mEffect.mRange = ESM::RT_Touch; - if (!allowTouch) - mEffect.mRange = ESM::RT_Target; - mEffect.mMagnMin = 1; - mEffect.mMagnMax = 1; - mEffect.mDuration = 1; - mEffect.mArea = 0; - mEffect.mSkill = -1; - mEffect.mAttribute = -1; - eventEffectAdded(mEffect); - - onRangeButtonClicked(mRangeButton); - - mMagnitudeMinSlider->setScrollPosition(0); - mMagnitudeMaxSlider->setScrollPosition(0); - mAreaSlider->setScrollPosition(0); - mDurationSlider->setScrollPosition(0); - - mDurationValue->setCaption("1"); - mMagnitudeMinValue->setCaption("1"); - const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; - - mMagnitudeMaxValue->setCaption(to + " 1"); - mAreaValue->setCaption("0"); - - setVisible(true); - } - - void EditEffectDialog::editEffect(ESM::ENAMstruct effect) - { - const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().find(effect.mEffectID); - - setMagicEffect(magicEffect); - mOldEffect = effect; - mEffect = effect; - mEditing = true; - - mDeleteButton->setVisible(true); - - mMagnitudeMinSlider->setScrollPosition(effect.mMagnMin - 1); - mMagnitudeMaxSlider->setScrollPosition(effect.mMagnMax - 1); - mAreaSlider->setScrollPosition(effect.mArea); - mDurationSlider->setScrollPosition(effect.mDuration - 1); - - if (mEffect.mRange == ESM::RT_Self) - mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); - else if (mEffect.mRange == ESM::RT_Target) - mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); - else if (mEffect.mRange == ESM::RT_Touch) - mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); - - onMagnitudeMinChanged(mMagnitudeMinSlider, effect.mMagnMin - 1); - onMagnitudeMaxChanged(mMagnitudeMinSlider, effect.mMagnMax - 1); - onAreaChanged(mAreaSlider, effect.mArea); - onDurationChanged(mDurationSlider, effect.mDuration - 1); - eventEffectModified(mEffect); - - updateBoxes(); - } - - void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) - { - mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( - effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); - - mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); - - mEffect.mEffectID = effect->mIndex; - - mMagicEffect = effect; - - updateBoxes(); - } - - void EditEffectDialog::updateBoxes() - { - static int startY = mMagnitudeBox->getPosition().top; - int curY = startY; - - mMagnitudeBox->setVisible(false); - mDurationBox->setVisible(false); - mAreaBox->setVisible(false); - - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY); - mMagnitudeBox->setVisible(true); - curY += mMagnitudeBox->getSize().height; - } - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && mConstantEffect == false) - { - mDurationBox->setPosition(mDurationBox->getPosition().left, curY); - mDurationBox->setVisible(true); - curY += mDurationBox->getSize().height; - } - if (mEffect.mRange != ESM::RT_Self) - { - mAreaBox->setPosition(mAreaBox->getPosition().left, curY); - mAreaBox->setVisible(true); - // curY += mAreaBox->getSize().height; - } - } - - void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) - { - mEffect.mRange = (mEffect.mRange + 1) % 3; - - // cycle through range types until we find something that's allowed - // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect - // dialog) - bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; - if (mEffect.mRange == ESM::RT_Self && !allowSelf) - mEffect.mRange = (mEffect.mRange + 1) % 3; - if (mEffect.mRange == ESM::RT_Touch && !allowTouch) - mEffect.mRange = (mEffect.mRange + 1) % 3; - if (mEffect.mRange == ESM::RT_Target && !allowTarget) - mEffect.mRange = (mEffect.mRange + 1) % 3; - - if (mEffect.mRange == ESM::RT_Self) - { - mAreaSlider->setScrollPosition(0); - onAreaChanged(mAreaSlider, 0); - } - - if (mEffect.mRange == ESM::RT_Self) - mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); - else if (mEffect.mRange == ESM::RT_Target) - mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); - else if (mEffect.mRange == ESM::RT_Touch) - mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); - - updateBoxes(); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onDeleteButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - - eventEffectRemoved(mEffect); - } - - void EditEffectDialog::onOkButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - } - - void EditEffectDialog::onCancelButtonClicked(MyGUI::Widget* sender) - { - setVisible(false); - exit(); - } - - void EditEffectDialog::setSkill(ESM::RefId skill) - { - mEffect.mSkill = ESM::Skill::refIdToIndex(skill); - eventEffectModified(mEffect); - } - - void EditEffectDialog::setAttribute(ESM::RefId attribute) - { - mEffect.mAttribute = ESM::Attribute::refIdToIndex(attribute); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos + 1)); - mEffect.mMagnMin = pos + 1; - - // trigger the check again (see below) - onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition()); - eventEffectModified(mEffect); - } - - void EditEffectDialog::onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos) - { - // make sure the max value is actually larger or equal than the min value - size_t magnMin - = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning - if (pos + 1 < magnMin) - { - pos = mEffect.mMagnMin - 1; - sender->setScrollPosition(pos); - } - - mEffect.mMagnMax = pos + 1; - const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; - - mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos + 1)); - - eventEffectModified(mEffect); - } - - void EditEffectDialog::onDurationChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mDurationValue->setCaption(MyGUI::utility::toString(pos + 1)); - mEffect.mDuration = pos + 1; - eventEffectModified(mEffect); - } - - void EditEffectDialog::onAreaChanged(MyGUI::ScrollBar* sender, size_t pos) - { - mAreaValue->setCaption(MyGUI::utility::toString(pos)); - mEffect.mArea = pos; - eventEffectModified(mEffect); - } - - // ------------------------------------------------------------------------------------------------ - - SpellCreationDialog::SpellCreationDialog() - : WindowBase("openmw_spellcreation_dialog.layout") - , EffectEditorBase(EffectEditorBase::Spellmaking) - { - getWidget(mNameEdit, "NameEdit"); - getWidget(mMagickaCost, "MagickaCost"); - getWidget(mSuccessChance, "SuccessChance"); - getWidget(mAvailableEffectsList, "AvailableEffects"); - getWidget(mUsedEffectsView, "UsedEffects"); - getWidget(mPriceLabel, "PriceLabel"); - getWidget(mPlayerGold, "PlayerGold"); - getWidget(mBuyButton, "BuyButton"); - getWidget(mCancelButton, "CancelButton"); - - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); - mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); - mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); - - setWidgets(mAvailableEffectsList, mUsedEffectsView); - } - - void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) - { - if (actor.isEmpty() || !actor.getClass().isActor()) - throw std::runtime_error("Invalid argument in SpellCreationDialog::setPtr"); - - mPtr = actor; - mNameEdit->setCaption({}); - - MWWorld::Ptr player = MWMechanics::getPlayer(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); - - startEditing(); - } - - void SpellCreationDialog::onCancelButtonClicked(MyGUI::Widget* sender) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_SpellCreation); - } - - void SpellCreationDialog::onBuyButtonClicked(MyGUI::Widget* sender) - { - if (mEffects.size() <= 0) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage30}"); - return; - } - - if (mNameEdit->getCaption().empty()) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage10}"); - return; - } - - if (mMagickaCost->getCaption() == "0") - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sEnchantmentMenu8}"); - return; - } - - MWWorld::Ptr player = MWMechanics::getPlayer(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - - int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); - if (price > playerGold) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage18}"); - return; - } - - mSpell.mName = mNameEdit->getCaption(); - - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price); - - // add gold to NPC trading gold pool - MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); - npcStats.setGoldPool(npcStats.getGoldPool() + price); - - MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Mysticism Hit")); - - const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->insert(mSpell); - - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - spells.add(spell->mId); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); - } - - void SpellCreationDialog::onAccept(MyGUI::EditBox* sender) - { - onBuyButtonClicked(sender); - - // To do not spam onAccept() again and again - MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); - } - - void SpellCreationDialog::onOpen() - { - center(); - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); - } - - void SpellCreationDialog::onReferenceUnavailable() - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); - } - - void SpellCreationDialog::notifyEffectsChanged() - { - if (mEffects.empty()) - { - mMagickaCost->setCaption("0"); - mPriceLabel->setCaption("0"); - mSuccessChance->setCaption("0"); - return; - } - - float y = 0; - - const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - - for (const ESM::ENAMstruct& effect : mEffects) - { - y += std::max( - 1.f, MWMechanics::calcEffectCost(effect, nullptr, MWMechanics::EffectCostMethod::PlayerSpell)); - - if (effect.mRange == ESM::RT_Target) - y *= 1.5; - } - - mSpell.mEffects.populate(mEffects); - mSpell.mData.mCost = int(y); - mSpell.mData.mType = ESM::Spell::ST_Spell; - mSpell.mData.mFlags = 0; - - mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); - - float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->mValue.getFloat(); - - int price = std::max(1, static_cast(y * fSpellMakingValueMult)); - price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); - - mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - - float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); - - int intChance = std::min(100, int(chance)); - mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); - } - - // ------------------------------------------------------------------------------------------------ - - EffectEditorBase::EffectEditorBase(Type type) - : mAvailableEffectsList(nullptr) - , mUsedEffectsView(nullptr) - , mAddEffectDialog() - , mSelectedEffect(0) - , mSelectedKnownEffectId(0) - , mConstantEffect(false) - , mType(type) - { - mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); - mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); - mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved); - - mAddEffectDialog.setVisible(false); - } - - EffectEditorBase::~EffectEditorBase() {} - - void EffectEditorBase::startEditing() - { - // get the list of magic effects that are known to the player - - MWWorld::Ptr player = MWMechanics::getPlayer(); - MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - - std::vector knownEffects; - - for (const ESM::Spell* spell : spells) - { - // only normal spells count - if (spell->mData.mType != ESM::Spell::ST_Spell) - continue; - - for (const ESM::IndexedENAMstruct& effectInfo : spell->mEffects.mList) - { - int16_t effectId = effectInfo.mData.mEffectID; - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(effectId); - - // skip effects that do not allow spellmaking/enchanting - int requiredFlags - = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting; - if (!(effect->mData.mFlags & requiredFlags)) - continue; - - if (std::find(knownEffects.begin(), knownEffects.end(), effectId) == knownEffects.end()) - knownEffects.push_back(effectId); - } - } - - std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects); - - mAvailableEffectsList->clear(); - - int i = 0; - for (const short effectId : knownEffects) - { - mAvailableEffectsList->addItem(MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString()); - mButtonMapping[i] = effectId; - ++i; - } - mAvailableEffectsList->adjustSize(); - mAvailableEffectsList->scrollToTop(); - - for (const short effectId : knownEffects) - { - const std::string& name = MWBase::Environment::get() - .getESMStore() - ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) - ->mValue.getString(); - MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); - - ToolTips::createMagicEffectToolTip(w, effectId); - } - - mEffects.clear(); - updateEffectsView(); - } - - void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) - { - mAvailableEffectsList = availableEffectsList; - mUsedEffectsView = usedEffectsView; - - mAvailableEffectsList->eventWidgetSelected - += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked); - } - - void EffectEditorBase::onSelectAttribute() - { - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - mAddEffectDialog.newEffect(effect); - mAddEffectDialog.setAttribute(mSelectAttributeDialog->getAttributeId()); - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); - } - - void EffectEditorBase::onSelectSkill() - { - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - mAddEffectDialog.newEffect(effect); - mAddEffectDialog.setSkill(mSelectSkillDialog->getSkillId()); - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); - } - - void EffectEditorBase::onAttributeOrSkillCancel() - { - if (mSelectSkillDialog != nullptr) - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); - if (mSelectAttributeDialog != nullptr) - MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); - } - - void EffectEditorBase::onAvailableEffectClicked(MyGUI::Widget* sender) - { - if (mEffects.size() >= 8) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); - return; - } - - int buttonId = *sender->getUserData(); - mSelectedKnownEffectId = mButtonMapping[buttonId]; - - const ESM::MagicEffect* effect - = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); - - bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; - bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; - bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; - - if (!allowSelf && !allowTouch && !allowTarget) - return; // TODO: Show an error message popup? - - if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - mSelectSkillDialog = std::make_unique(); - mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); - mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); - mSelectSkillDialog->setVisible(true); - } - else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - mSelectAttributeDialog = std::make_unique(); - mSelectAttributeDialog->eventCancel - += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); - mSelectAttributeDialog->eventItemSelected - += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); - mSelectAttributeDialog->setVisible(true); - } - else - { - for (const ESM::ENAMstruct& effectInfo : mEffects) - { - if (effectInfo.mEffectID == mSelectedKnownEffectId) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sOnetypeEffectMessage}"); - return; - } - } - - mAddEffectDialog.newEffect(effect); - } - } - - void EffectEditorBase::onEffectModified(ESM::ENAMstruct effect) - { - mEffects[mSelectedEffect] = effect; - - updateEffectsView(); - } - - void EffectEditorBase::onEffectRemoved(ESM::ENAMstruct effect) - { - mEffects.erase(mEffects.begin() + mSelectedEffect); - updateEffectsView(); - } - - void EffectEditorBase::updateEffectsView() - { - MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator(); - MyGUI::Gui::getInstance().destroyWidgets(oldWidgets); - - MyGUI::IntSize size(0, 0); - - int i = 0; - for (const ESM::ENAMstruct& effectInfo : mEffects) - { - Widgets::SpellEffectParams params; - params.mEffectID = effectInfo.mEffectID; - params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill); - params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute); - params.mDuration = effectInfo.mDuration; - params.mMagnMin = effectInfo.mMagnMin; - params.mMagnMax = effectInfo.mMagnMax; - params.mRange = effectInfo.mRange; - params.mArea = effectInfo.mArea; - params.mIsConstant = mConstantEffect; - - MyGUI::Button* button = mUsedEffectsView->createWidget( - {}, MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); - button->setUserData(i); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect); - button->setNeedMouseFocus(true); - - Widgets::MWSpellEffectPtr effect = button->createWidget( - "MW_EffectImage", MyGUI::IntCoord(0, 0, 0, 24), MyGUI::Align::Default); - - effect->setNeedMouseFocus(false); - effect->setSpellEffect(params); - - effect->setSize(effect->getRequestedWidth(), 24); - button->setSize(effect->getRequestedWidth(), 24); - - size.width = std::max(size.width, effect->getRequestedWidth()); - size.height += 24; - ++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(); - } - - void EffectEditorBase::onEffectAdded(ESM::ENAMstruct effect) - { - mEffects.push_back(effect); - mSelectedEffect = mEffects.size() - 1; - - updateEffectsView(); - } - - void EffectEditorBase::onEditEffect(MyGUI::Widget* sender) - { - int id = *sender->getUserData(); - - mSelectedEffect = id; - - mAddEffectDialog.editEffect(mEffects[id]); - mAddEffectDialog.setVisible(true); - } - - void EffectEditorBase::setConstantEffect(bool constant) - { - mAddEffectDialog.setConstantEffect(constant); - if (!mConstantEffect && constant) - for (ESM::ENAMstruct& effect : mEffects) - effect.mRange = ESM::RT_Self; - mConstantEffect = constant; - } -} +#include "spellcreationdialog.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/store.hpp" + +#include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellutil.hpp" + +#include "class.hpp" +#include "tooltips.hpp" +#include "widgets.hpp" + +namespace +{ + bool sortMagicEffects(short id1, short id2) + { + const MWWorld::Store& gmst + = MWBase::Environment::get().getESMStore()->get(); + + return gmst.find(ESM::MagicEffect::indexToGmstString(id1))->mValue.getString() + < gmst.find(ESM::MagicEffect::indexToGmstString(id2))->mValue.getString(); + } + + void init(ESM::ENAMstruct& effect) + { + effect.mArea = 0; + effect.mDuration = 0; + effect.mEffectID = -1; + effect.mMagnMax = 0; + effect.mMagnMin = 0; + effect.mRange = 0; + effect.mSkill = -1; + effect.mAttribute = -1; + } +} + +namespace MWGui +{ + + EditEffectDialog::EditEffectDialog() + : WindowModal("openmw_edit_effect.layout") + , mEditing(false) + , mMagicEffect(nullptr) + , mConstantEffect(false) + { + init(mEffect); + init(mOldEffect); + + getWidget(mCancelButton, "CancelButton"); + getWidget(mOkButton, "OkButton"); + getWidget(mDeleteButton, "DeleteButton"); + getWidget(mRangeButton, "RangeButton"); + getWidget(mMagnitudeMinValue, "MagnitudeMinValue"); + getWidget(mMagnitudeMaxValue, "MagnitudeMaxValue"); + getWidget(mDurationValue, "DurationValue"); + getWidget(mAreaValue, "AreaValue"); + getWidget(mMagnitudeMinSlider, "MagnitudeMinSlider"); + getWidget(mMagnitudeMaxSlider, "MagnitudeMaxSlider"); + getWidget(mDurationSlider, "DurationSlider"); + getWidget(mAreaSlider, "AreaSlider"); + getWidget(mEffectImage, "EffectImage"); + getWidget(mEffectName, "EffectName"); + getWidget(mAreaText, "AreaText"); + getWidget(mDurationBox, "DurationBox"); + getWidget(mAreaBox, "AreaBox"); + getWidget(mMagnitudeBox, "MagnitudeBox"); + + mRangeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onRangeButtonClicked); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onOkButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onCancelButtonClicked); + mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EditEffectDialog::onDeleteButtonClicked); + + mMagnitudeMinSlider->eventScrollChangePosition + += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMinChanged); + mMagnitudeMaxSlider->eventScrollChangePosition + += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); + mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); + mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + } + + void EditEffectDialog::setConstantEffect(bool constant) + { + mConstantEffect = constant; + } + + void EditEffectDialog::onOpen() + { + WindowModal::onOpen(); + center(); + } + + bool EditEffectDialog::exit() + { + if (mEditing) + eventEffectModified(mOldEffect); + else + eventEffectRemoved(mEffect); + return true; + } + + void EditEffectDialog::newEffect(const ESM::MagicEffect* effect) + { + bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + + setMagicEffect(effect); + mEditing = false; + + mDeleteButton->setVisible(false); + + mEffect.mRange = ESM::RT_Self; + if (!allowSelf) + mEffect.mRange = ESM::RT_Touch; + if (!allowTouch) + mEffect.mRange = ESM::RT_Target; + mEffect.mMagnMin = 1; + mEffect.mMagnMax = 1; + mEffect.mDuration = 1; + mEffect.mArea = 0; + mEffect.mSkill = -1; + mEffect.mAttribute = -1; + eventEffectAdded(mEffect); + + onRangeButtonClicked(mRangeButton); + + mMagnitudeMinSlider->setScrollPosition(0); + mMagnitudeMaxSlider->setScrollPosition(0); + mAreaSlider->setScrollPosition(0); + mDurationSlider->setScrollPosition(0); + + mDurationValue->setCaption("1"); + mMagnitudeMinValue->setCaption("1"); + const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; + + mMagnitudeMaxValue->setCaption(to + " 1"); + mAreaValue->setCaption("0"); + + setVisible(true); + } + + void EditEffectDialog::editEffect(ESM::ENAMstruct effect) + { + const ESM::MagicEffect* magicEffect + = MWBase::Environment::get().getESMStore()->get().find(effect.mEffectID); + + setMagicEffect(magicEffect); + mOldEffect = effect; + mEffect = effect; + mEditing = true; + + mDeleteButton->setVisible(true); + + mMagnitudeMinSlider->setScrollPosition(effect.mMagnMin - 1); + mMagnitudeMaxSlider->setScrollPosition(effect.mMagnMax - 1); + mAreaSlider->setScrollPosition(effect.mArea); + mDurationSlider->setScrollPosition(effect.mDuration - 1); + + if (mEffect.mRange == ESM::RT_Self) + mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); + else if (mEffect.mRange == ESM::RT_Target) + mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); + else if (mEffect.mRange == ESM::RT_Touch) + mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); + + onMagnitudeMinChanged(mMagnitudeMinSlider, effect.mMagnMin - 1); + onMagnitudeMaxChanged(mMagnitudeMinSlider, effect.mMagnMax - 1); + onAreaChanged(mAreaSlider, effect.mArea); + onDurationChanged(mDurationSlider, effect.mDuration - 1); + eventEffectModified(mEffect); + + updateBoxes(); + } + + void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) + { + mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( + effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); + + mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); + + mEffect.mEffectID = effect->mIndex; + + mMagicEffect = effect; + + updateBoxes(); + } + + void EditEffectDialog::updateBoxes() + { + static int startY = mMagnitudeBox->getPosition().top; + int curY = startY; + + mMagnitudeBox->setVisible(false); + mDurationBox->setVisible(false); + mAreaBox->setVisible(false); + + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + mMagnitudeBox->setPosition(mMagnitudeBox->getPosition().left, curY); + mMagnitudeBox->setVisible(true); + curY += mMagnitudeBox->getSize().height; + } + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) && mConstantEffect == false) + { + mDurationBox->setPosition(mDurationBox->getPosition().left, curY); + mDurationBox->setVisible(true); + curY += mDurationBox->getSize().height; + } + if (mEffect.mRange != ESM::RT_Self) + { + mAreaBox->setPosition(mAreaBox->getPosition().left, curY); + mAreaBox->setVisible(true); + // curY += mAreaBox->getSize().height; + } + } + + void EditEffectDialog::onRangeButtonClicked(MyGUI::Widget* sender) + { + mEffect.mRange = (mEffect.mRange + 1) % 3; + + // cycle through range types until we find something that's allowed + // does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect + // dialog) + bool allowSelf = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; + if (mEffect.mRange == ESM::RT_Self && !allowSelf) + mEffect.mRange = (mEffect.mRange + 1) % 3; + if (mEffect.mRange == ESM::RT_Touch && !allowTouch) + mEffect.mRange = (mEffect.mRange + 1) % 3; + if (mEffect.mRange == ESM::RT_Target && !allowTarget) + mEffect.mRange = (mEffect.mRange + 1) % 3; + + if (mEffect.mRange == ESM::RT_Self) + { + mAreaSlider->setScrollPosition(0); + onAreaChanged(mAreaSlider, 0); + } + + if (mEffect.mRange == ESM::RT_Self) + mRangeButton->setCaptionWithReplacing("#{sRangeSelf}"); + else if (mEffect.mRange == ESM::RT_Target) + mRangeButton->setCaptionWithReplacing("#{sRangeTarget}"); + else if (mEffect.mRange == ESM::RT_Touch) + mRangeButton->setCaptionWithReplacing("#{sRangeTouch}"); + + updateBoxes(); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onDeleteButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + + eventEffectRemoved(mEffect); + } + + void EditEffectDialog::onOkButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + } + + void EditEffectDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + setVisible(false); + exit(); + } + + void EditEffectDialog::setSkill(ESM::RefId skill) + { + mEffect.mSkill = ESM::Skill::refIdToIndex(skill); + eventEffectModified(mEffect); + } + + void EditEffectDialog::setAttribute(ESM::RefId attribute) + { + mEffect.mAttribute = ESM::Attribute::refIdToIndex(attribute); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mMagnitudeMinValue->setCaption(MyGUI::utility::toString(pos + 1)); + mEffect.mMagnMin = pos + 1; + + // trigger the check again (see below) + onMagnitudeMaxChanged(mMagnitudeMaxSlider, mMagnitudeMaxSlider->getScrollPosition()); + eventEffectModified(mEffect); + } + + void EditEffectDialog::onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos) + { + // make sure the max value is actually larger or equal than the min value + size_t magnMin + = std::abs(mEffect.mMagnMin); // should never be < 0, this is just here to avoid the compiler warning + if (pos + 1 < magnMin) + { + pos = mEffect.mMagnMin - 1; + sender->setScrollPosition(pos); + } + + mEffect.mMagnMax = pos + 1; + const std::string to{ MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-") }; + + mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos + 1)); + + eventEffectModified(mEffect); + } + + void EditEffectDialog::onDurationChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mDurationValue->setCaption(MyGUI::utility::toString(pos + 1)); + mEffect.mDuration = pos + 1; + eventEffectModified(mEffect); + } + + void EditEffectDialog::onAreaChanged(MyGUI::ScrollBar* sender, size_t pos) + { + mAreaValue->setCaption(MyGUI::utility::toString(pos)); + mEffect.mArea = pos; + eventEffectModified(mEffect); + } + + // ------------------------------------------------------------------------------------------------ + + SpellCreationDialog::SpellCreationDialog() + : WindowBase("openmw_spellcreation_dialog.layout") + , EffectEditorBase(EffectEditorBase::Spellmaking) + { + getWidget(mNameEdit, "NameEdit"); + getWidget(mMagickaCost, "MagickaCost"); + getWidget(mSuccessChance, "SuccessChance"); + getWidget(mAvailableEffectsList, "AvailableEffects"); + getWidget(mUsedEffectsView, "UsedEffects"); + getWidget(mPriceLabel, "PriceLabel"); + getWidget(mPlayerGold, "PlayerGold"); + getWidget(mBuyButton, "BuyButton"); + getWidget(mCancelButton, "CancelButton"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onCancelButtonClicked); + mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onBuyButtonClicked); + mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SpellCreationDialog::onAccept); + + setWidgets(mAvailableEffectsList, mUsedEffectsView); + } + + void SpellCreationDialog::setPtr(const MWWorld::Ptr& actor) + { + if (actor.isEmpty() || !actor.getClass().isActor()) + throw std::runtime_error("Invalid argument in SpellCreationDialog::setPtr"); + + mPtr = actor; + mNameEdit->setCaption({}); + + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + + startEditing(); + } + + void SpellCreationDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_SpellCreation); + } + + void SpellCreationDialog::onBuyButtonClicked(MyGUI::Widget* sender) + { + if (mEffects.size() <= 0) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage30}"); + return; + } + + if (mNameEdit->getCaption().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage10}"); + return; + } + + if (mMagickaCost->getCaption() == "0") + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sEnchantmentMenu8}"); + return; + } + + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); + if (price > playerGold) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage18}"); + return; + } + + mSpell.mName = mNameEdit->getCaption(); + + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + + MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Mysticism Hit")); + + const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->insert(mSpell); + + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + spells.add(spell->mId); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); + } + + void SpellCreationDialog::onAccept(MyGUI::EditBox* sender) + { + onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); + } + + void SpellCreationDialog::onOpen() + { + center(); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); + } + + void SpellCreationDialog::onReferenceUnavailable() + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellCreation); + } + + void SpellCreationDialog::notifyEffectsChanged() + { + if (mEffects.empty()) + { + mMagickaCost->setCaption("0"); + mPriceLabel->setCaption("0"); + mSuccessChance->setCaption("0"); + return; + } + + float y = 0; + + const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); + + for (const ESM::ENAMstruct& effect : mEffects) + { + y += std::max( + 1.f, MWMechanics::calcEffectCost(effect, nullptr, MWMechanics::EffectCostMethod::PlayerSpell)); + + if (effect.mRange == ESM::RT_Target) + y *= 1.5; + } + + mSpell.mEffects.populate(mEffects); + mSpell.mData.mCost = int(y); + mSpell.mData.mType = ESM::Spell::ST_Spell; + mSpell.mData.mFlags = 0; + + mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); + + float fSpellMakingValueMult = store.get().find("fSpellMakingValueMult")->mValue.getFloat(); + + int price = std::max(1, static_cast(y * fSpellMakingValueMult)); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); + + mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); + + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); + + int intChance = std::min(100, int(chance)); + mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); + } + + // ------------------------------------------------------------------------------------------------ + + EffectEditorBase::EffectEditorBase(Type type) + : mAvailableEffectsList(nullptr) + , mUsedEffectsView(nullptr) + , mAddEffectDialog() + , mSelectedEffect(0) + , mSelectedKnownEffectId(0) + , mConstantEffect(false) + , mType(type) + { + mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded); + mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified); + mAddEffectDialog.eventEffectRemoved += MyGUI::newDelegate(this, &EffectEditorBase::onEffectRemoved); + + mAddEffectDialog.setVisible(false); + } + + EffectEditorBase::~EffectEditorBase() {} + + void EffectEditorBase::startEditing() + { + // get the list of magic effects that are known to the player + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + + std::vector knownEffects; + + for (const ESM::Spell* spell : spells) + { + // only normal spells count + if (spell->mData.mType != ESM::Spell::ST_Spell) + continue; + + for (const ESM::IndexedENAMstruct& effectInfo : spell->mEffects.mList) + { + int16_t effectId = effectInfo.mData.mEffectID; + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(effectId); + + // skip effects that do not allow spellmaking/enchanting + int requiredFlags + = (mType == Spellmaking) ? ESM::MagicEffect::AllowSpellmaking : ESM::MagicEffect::AllowEnchanting; + if (!(effect->mData.mFlags & requiredFlags)) + continue; + + if (std::find(knownEffects.begin(), knownEffects.end(), effectId) == knownEffects.end()) + knownEffects.push_back(effectId); + } + } + + std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects); + + mAvailableEffectsList->clear(); + + int i = 0; + for (const short effectId : knownEffects) + { + mAvailableEffectsList->addItem(MWBase::Environment::get() + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString()); + mButtonMapping[i] = effectId; + ++i; + } + mAvailableEffectsList->adjustSize(); + mAvailableEffectsList->scrollToTop(); + + for (const short effectId : knownEffects) + { + const std::string& name = MWBase::Environment::get() + .getESMStore() + ->get() + .find(ESM::MagicEffect::indexToGmstString(effectId)) + ->mValue.getString(); + MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); + + ToolTips::createMagicEffectToolTip(w, effectId); + } + + mEffects.clear(); + updateEffectsView(); + } + + void EffectEditorBase::setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView) + { + mAvailableEffectsList = availableEffectsList; + mUsedEffectsView = usedEffectsView; + + mAvailableEffectsList->eventWidgetSelected + += MyGUI::newDelegate(this, &EffectEditorBase::onAvailableEffectClicked); + } + + void EffectEditorBase::onSelectAttribute() + { + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + mAddEffectDialog.newEffect(effect); + mAddEffectDialog.setAttribute(mSelectAttributeDialog->getAttributeId()); + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); + } + + void EffectEditorBase::onSelectSkill() + { + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + mAddEffectDialog.newEffect(effect); + mAddEffectDialog.setSkill(mSelectSkillDialog->getSkillId()); + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); + } + + void EffectEditorBase::onAttributeOrSkillCancel() + { + if (mSelectSkillDialog != nullptr) + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectSkillDialog)); + if (mSelectAttributeDialog != nullptr) + MWBase::Environment::get().getWindowManager()->removeDialog(std::move(mSelectAttributeDialog)); + } + + void EffectEditorBase::onAvailableEffectClicked(MyGUI::Widget* sender) + { + if (mEffects.size() >= 8) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); + return; + } + + int buttonId = *sender->getUserData(); + mSelectedKnownEffectId = mButtonMapping[buttonId]; + + const ESM::MagicEffect* effect + = MWBase::Environment::get().getESMStore()->get().find(mSelectedKnownEffectId); + + bool allowSelf = (effect->mData.mFlags & ESM::MagicEffect::CastSelf) != 0 || mConstantEffect; + bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect; + bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect; + + if (!allowSelf && !allowTouch && !allowTarget) + return; // TODO: Show an error message popup? + + if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + mSelectSkillDialog = std::make_unique(); + mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); + mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); + mSelectSkillDialog->setVisible(true); + } + else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + { + mSelectAttributeDialog = std::make_unique(); + mSelectAttributeDialog->eventCancel + += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); + mSelectAttributeDialog->eventItemSelected + += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); + mSelectAttributeDialog->setVisible(true); + } + else + { + for (const ESM::ENAMstruct& effectInfo : mEffects) + { + if (effectInfo.mEffectID == mSelectedKnownEffectId) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sOnetypeEffectMessage}"); + return; + } + } + + mAddEffectDialog.newEffect(effect); + } + } + + void EffectEditorBase::onEffectModified(ESM::ENAMstruct effect) + { + mEffects[mSelectedEffect] = effect; + + updateEffectsView(); + } + + void EffectEditorBase::onEffectRemoved(ESM::ENAMstruct effect) + { + mEffects.erase(mEffects.begin() + mSelectedEffect); + updateEffectsView(); + } + + void EffectEditorBase::updateEffectsView() + { + MyGUI::EnumeratorWidgetPtr oldWidgets = mUsedEffectsView->getEnumerator(); + MyGUI::Gui::getInstance().destroyWidgets(oldWidgets); + + MyGUI::IntSize size(0, 0); + + int i = 0; + for (const ESM::ENAMstruct& effectInfo : mEffects) + { + Widgets::SpellEffectParams params; + params.mEffectID = effectInfo.mEffectID; + params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill); + params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute); + params.mDuration = effectInfo.mDuration; + params.mMagnMin = effectInfo.mMagnMin; + params.mMagnMax = effectInfo.mMagnMax; + params.mRange = effectInfo.mRange; + params.mArea = effectInfo.mArea; + params.mIsConstant = mConstantEffect; + + MyGUI::Button* button = mUsedEffectsView->createWidget( + {}, MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default); + button->setUserData(i); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellCreationDialog::onEditEffect); + button->setNeedMouseFocus(true); + + Widgets::MWSpellEffectPtr effect = button->createWidget( + "MW_EffectImage", MyGUI::IntCoord(0, 0, 0, 24), MyGUI::Align::Default); + + effect->setNeedMouseFocus(false); + effect->setSpellEffect(params); + + effect->setSize(effect->getRequestedWidth(), 24); + button->setSize(effect->getRequestedWidth(), 24); + + size.width = std::max(size.width, effect->getRequestedWidth()); + size.height += 24; + ++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(); + } + + void EffectEditorBase::onEffectAdded(ESM::ENAMstruct effect) + { + mEffects.push_back(effect); + mSelectedEffect = mEffects.size() - 1; + + updateEffectsView(); + } + + void EffectEditorBase::onEditEffect(MyGUI::Widget* sender) + { + int id = *sender->getUserData(); + + mSelectedEffect = id; + + mAddEffectDialog.editEffect(mEffects[id]); + mAddEffectDialog.setVisible(true); + } + + void EffectEditorBase::setConstantEffect(bool constant) + { + mAddEffectDialog.setConstantEffect(constant); + if (!mConstantEffect && constant) + for (ESM::ENAMstruct& effect : mEffects) + effect.mRange = ESM::RT_Self; + mConstantEffect = constant; + } +} diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index b3c6ac51be..0887dd8c94 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,185 +1,185 @@ -#ifndef MWGUI_SPELLCREATION_H -#define MWGUI_SPELLCREATION_H - -#include - -#include -#include - -#include "referenceinterface.hpp" -#include "windowbase.hpp" - -namespace Gui -{ - class MWList; -} - -namespace MWGui -{ - - class SelectSkillDialog; - class SelectAttributeDialog; - - class EditEffectDialog : public WindowModal - { - public: - EditEffectDialog(); - - void onOpen() override; - bool exit() override; - - void setConstantEffect(bool constant); - - void setSkill(ESM::RefId skill); - void setAttribute(ESM::RefId attribute); - - void newEffect(const ESM::MagicEffect* effect); - void editEffect(ESM::ENAMstruct effect); - typedef MyGUI::delegates::MultiDelegate EventHandle_Effect; - - EventHandle_Effect eventEffectAdded; - EventHandle_Effect eventEffectModified; - EventHandle_Effect eventEffectRemoved; - - protected: - MyGUI::Button* mCancelButton; - MyGUI::Button* mOkButton; - MyGUI::Button* mDeleteButton; - - MyGUI::Button* mRangeButton; - - MyGUI::Widget* mDurationBox; - MyGUI::Widget* mMagnitudeBox; - MyGUI::Widget* mAreaBox; - - MyGUI::TextBox* mMagnitudeMinValue; - MyGUI::TextBox* mMagnitudeMaxValue; - MyGUI::TextBox* mDurationValue; - MyGUI::TextBox* mAreaValue; - - MyGUI::ScrollBar* mMagnitudeMinSlider; - MyGUI::ScrollBar* mMagnitudeMaxSlider; - MyGUI::ScrollBar* mDurationSlider; - MyGUI::ScrollBar* mAreaSlider; - - MyGUI::TextBox* mAreaText; - - MyGUI::ImageBox* mEffectImage; - MyGUI::TextBox* mEffectName; - - bool mEditing; - - protected: - void onRangeButtonClicked(MyGUI::Widget* sender); - void onDeleteButtonClicked(MyGUI::Widget* sender); - void onOkButtonClicked(MyGUI::Widget* sender); - void onCancelButtonClicked(MyGUI::Widget* sender); - - void onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos); - void onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos); - void onDurationChanged(MyGUI::ScrollBar* sender, size_t pos); - void onAreaChanged(MyGUI::ScrollBar* sender, size_t pos); - void setMagicEffect(const ESM::MagicEffect* effect); - - void updateBoxes(); - - protected: - ESM::ENAMstruct mEffect; - ESM::ENAMstruct mOldEffect; - - const ESM::MagicEffect* mMagicEffect; - - bool mConstantEffect; - }; - - class EffectEditorBase - { - public: - enum Type - { - Spellmaking, - Enchanting - }; - - EffectEditorBase(Type type); - virtual ~EffectEditorBase(); - - void setConstantEffect(bool constant); - - protected: - std::map mButtonMapping; // maps button ID to effect ID - - Gui::MWList* mAvailableEffectsList; - MyGUI::ScrollView* mUsedEffectsView; - - EditEffectDialog mAddEffectDialog; - std::unique_ptr mSelectAttributeDialog; - std::unique_ptr mSelectSkillDialog; - - int mSelectedEffect; - short mSelectedKnownEffectId; - - bool mConstantEffect; - - std::vector mEffects; - - void onEffectAdded(ESM::ENAMstruct effect); - void onEffectModified(ESM::ENAMstruct effect); - void onEffectRemoved(ESM::ENAMstruct effect); - - void onAvailableEffectClicked(MyGUI::Widget* sender); - - void onAttributeOrSkillCancel(); - void onSelectAttribute(); - void onSelectSkill(); - - void onEditEffect(MyGUI::Widget* sender); - - void updateEffectsView(); - - void startEditing(); - void setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView); - - virtual void notifyEffectsChanged() {} - - private: - Type mType; - }; - - class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase - { - public: - SpellCreationDialog(); - - void onOpen() override; - void clear() override { resetReference(); } - - void onFrame(float dt) override { checkReferenceAvailable(); } - - void setPtr(const MWWorld::Ptr& actor) override; - - std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; } - - protected: - void onReferenceUnavailable() override; - - void onCancelButtonClicked(MyGUI::Widget* sender); - void onBuyButtonClicked(MyGUI::Widget* sender); - void onAccept(MyGUI::EditBox* sender); - - void notifyEffectsChanged() override; - - MyGUI::EditBox* mNameEdit; - MyGUI::TextBox* mMagickaCost; - MyGUI::TextBox* mSuccessChance; - MyGUI::Button* mBuyButton; - MyGUI::Button* mCancelButton; - MyGUI::TextBox* mPriceLabel; - MyGUI::TextBox* mPlayerGold; - - ESM::Spell mSpell; - }; - -} - -#endif +#ifndef MWGUI_SPELLCREATION_H +#define MWGUI_SPELLCREATION_H + +#include + +#include +#include + +#include "referenceinterface.hpp" +#include "windowbase.hpp" + +namespace Gui +{ + class MWList; +} + +namespace MWGui +{ + + class SelectSkillDialog; + class SelectAttributeDialog; + + class EditEffectDialog : public WindowModal + { + public: + EditEffectDialog(); + + void onOpen() override; + bool exit() override; + + void setConstantEffect(bool constant); + + void setSkill(ESM::RefId skill); + void setAttribute(ESM::RefId attribute); + + void newEffect(const ESM::MagicEffect* effect); + void editEffect(ESM::ENAMstruct effect); + typedef MyGUI::delegates::MultiDelegate EventHandle_Effect; + + EventHandle_Effect eventEffectAdded; + EventHandle_Effect eventEffectModified; + EventHandle_Effect eventEffectRemoved; + + protected: + MyGUI::Button* mCancelButton; + MyGUI::Button* mOkButton; + MyGUI::Button* mDeleteButton; + + MyGUI::Button* mRangeButton; + + MyGUI::Widget* mDurationBox; + MyGUI::Widget* mMagnitudeBox; + MyGUI::Widget* mAreaBox; + + MyGUI::TextBox* mMagnitudeMinValue; + MyGUI::TextBox* mMagnitudeMaxValue; + MyGUI::TextBox* mDurationValue; + MyGUI::TextBox* mAreaValue; + + MyGUI::ScrollBar* mMagnitudeMinSlider; + MyGUI::ScrollBar* mMagnitudeMaxSlider; + MyGUI::ScrollBar* mDurationSlider; + MyGUI::ScrollBar* mAreaSlider; + + MyGUI::TextBox* mAreaText; + + MyGUI::ImageBox* mEffectImage; + MyGUI::TextBox* mEffectName; + + bool mEditing; + + protected: + void onRangeButtonClicked(MyGUI::Widget* sender); + void onDeleteButtonClicked(MyGUI::Widget* sender); + void onOkButtonClicked(MyGUI::Widget* sender); + void onCancelButtonClicked(MyGUI::Widget* sender); + + void onMagnitudeMinChanged(MyGUI::ScrollBar* sender, size_t pos); + void onMagnitudeMaxChanged(MyGUI::ScrollBar* sender, size_t pos); + void onDurationChanged(MyGUI::ScrollBar* sender, size_t pos); + void onAreaChanged(MyGUI::ScrollBar* sender, size_t pos); + void setMagicEffect(const ESM::MagicEffect* effect); + + void updateBoxes(); + + protected: + ESM::ENAMstruct mEffect; + ESM::ENAMstruct mOldEffect; + + const ESM::MagicEffect* mMagicEffect; + + bool mConstantEffect; + }; + + class EffectEditorBase + { + public: + enum Type + { + Spellmaking, + Enchanting + }; + + EffectEditorBase(Type type); + virtual ~EffectEditorBase(); + + void setConstantEffect(bool constant); + + protected: + std::map mButtonMapping; // maps button ID to effect ID + + Gui::MWList* mAvailableEffectsList; + MyGUI::ScrollView* mUsedEffectsView; + + EditEffectDialog mAddEffectDialog; + std::unique_ptr mSelectAttributeDialog; + std::unique_ptr mSelectSkillDialog; + + int mSelectedEffect; + short mSelectedKnownEffectId; + + bool mConstantEffect; + + std::vector mEffects; + + void onEffectAdded(ESM::ENAMstruct effect); + void onEffectModified(ESM::ENAMstruct effect); + void onEffectRemoved(ESM::ENAMstruct effect); + + void onAvailableEffectClicked(MyGUI::Widget* sender); + + void onAttributeOrSkillCancel(); + void onSelectAttribute(); + void onSelectSkill(); + + void onEditEffect(MyGUI::Widget* sender); + + void updateEffectsView(); + + void startEditing(); + void setWidgets(Gui::MWList* availableEffectsList, MyGUI::ScrollView* usedEffectsView); + + virtual void notifyEffectsChanged() {} + + private: + Type mType; + }; + + class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase + { + public: + SpellCreationDialog(); + + void onOpen() override; + void clear() override { resetReference(); } + + void onFrame(float dt) override { checkReferenceAvailable(); } + + void setPtr(const MWWorld::Ptr& actor) override; + + std::string_view getWindowIdForLua() const override { return "SpellCreationDialog"; } + + protected: + void onReferenceUnavailable() override; + + void onCancelButtonClicked(MyGUI::Widget* sender); + void onBuyButtonClicked(MyGUI::Widget* sender); + void onAccept(MyGUI::EditBox* sender); + + void notifyEffectsChanged() override; + + MyGUI::EditBox* mNameEdit; + MyGUI::TextBox* mMagickaCost; + MyGUI::TextBox* mSuccessChance; + MyGUI::Button* mBuyButton; + MyGUI::Button* mCancelButton; + MyGUI::TextBox* mPriceLabel; + MyGUI::TextBox* mPlayerGold; + + ESM::Spell mSpell; + }; + +} + +#endif From 937c020e58cda2c23f759b76c22003d5b7b75c1b Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Apr 2025 05:10:26 +0000 Subject: [PATCH 07/19] Update file openmw_spellcreation_dialog.layout --- .../mygui/openmw_spellcreation_dialog.layout | 196 +++++++++--------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index fafe4635e7..ddaff5f14d 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -1,98 +1,98 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 284be88b95c687f989c4a88fbb1880a634bc3a8e Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Apr 2025 19:40:55 +0000 Subject: [PATCH 08/19] Update file openmw_spellcreation_dialog.layout --- .../data/mygui/openmw_spellcreation_dialog.layout | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index ddaff5f14d..9ab895833a 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -42,11 +42,6 @@ - - - - - @@ -73,6 +68,15 @@ + + + + + + + + + @@ -84,7 +88,6 @@ - From 57bb6f2e2f336a53f4339b7c886474b049a2efbc Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Apr 2025 19:43:31 +0000 Subject: [PATCH 09/19] Update file spellcreationdialog.cpp --- apps/openmw/mwgui/spellcreationdialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 651d3014c1..cfa6cb01e7 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -30,6 +30,7 @@ namespace { + bool sortMagicEffects(short id1, short id2) { const MWWorld::Store& gmst @@ -372,7 +373,7 @@ namespace MWGui MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + mPlayerGold->setCaptionWithReplacing(MyGUI::utility::toString(playerGold)); startEditing(); } From 30dae411d02a00278f1cd5fc3bb627f280258f54 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Apr 2025 21:13:50 +0000 Subject: [PATCH 10/19] Update file spellcreationdialog.cpp --- apps/openmw/mwgui/spellcreationdialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index cfa6cb01e7..c9556a0e2c 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -30,7 +30,6 @@ namespace { - bool sortMagicEffects(short id1, short id2) { const MWWorld::Store& gmst From 57f8355bacb6ef8554b048b1099e00d5803489fa Mon Sep 17 00:00:00 2001 From: Garrett Date: Sat, 5 Apr 2025 02:36:51 +0000 Subject: [PATCH 11/19] Update file enchantingdialog.hpp --- apps/openmw/mwgui/enchantingdialog.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index 4c720a11fc..616d8cb89c 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -70,6 +70,7 @@ namespace MWGui MyGUI::TextBox* mSuccessChance; MyGUI::TextBox* mPrice; MyGUI::TextBox* mPriceText; + MyGUI::TextBox* mPlayerGold; MWMechanics::Enchanting mEnchanting; ESM::EffectList mEffectList; From 7f11579d394a335c568e217c0860c771db013c63 Mon Sep 17 00:00:00 2001 From: Garrett Date: Sat, 5 Apr 2025 02:37:31 +0000 Subject: [PATCH 12/19] Update file enchantingdialog.cpp --- apps/openmw/mwgui/enchantingdialog.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index af4a3e8ce3..acd9764e3b 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -50,7 +50,7 @@ namespace MWGui getWidget(mBuyButton, "BuyButton"); getWidget(mPrice, "PriceLabel"); getWidget(mPriceText, "PriceTextLabel"); - + getWidget(mPlayerGold, "PlayerGold"); setWidgets(mAvailableEffectsList, mUsedEffectsView); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked); @@ -65,6 +65,10 @@ namespace MWGui { center(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName); + + MWWorld::Ptr player = MWMechanics::getPlayer(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + mPlayerGold->setCaptionWithReplacing(MyGUI::utility::toString(playerGold)); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr& gem) From fce73395e5e0c26a75be47fe87022819582010d8 Mon Sep 17 00:00:00 2001 From: Garrett Date: Sat, 5 Apr 2025 02:39:19 +0000 Subject: [PATCH 13/19] Update file openmw_enchanting_dialog.layout --- files/data/mygui/openmw_enchanting_dialog.layout | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/files/data/mygui/openmw_enchanting_dialog.layout b/files/data/mygui/openmw_enchanting_dialog.layout index c0e3ba0d21..839de8d399 100644 --- a/files/data/mygui/openmw_enchanting_dialog.layout +++ b/files/data/mygui/openmw_enchanting_dialog.layout @@ -138,6 +138,14 @@ + + + + + + + + From 30e5e17e4c6df58dd24aab97c2b48383f7f470c9 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 4 Jul 2025 13:46:08 -0500 Subject: [PATCH 14/19] Remove enchanting dialog, missing empty lines --- apps/openmw/mwgui/enchantingdialog.cpp | 6 +----- apps/openmw/mwgui/enchantingdialog.hpp | 1 - apps/openmw/mwgui/spellcreationdialog.cpp | 1 + 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index acd9764e3b..af4a3e8ce3 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -50,7 +50,7 @@ namespace MWGui getWidget(mBuyButton, "BuyButton"); getWidget(mPrice, "PriceLabel"); getWidget(mPriceText, "PriceTextLabel"); - getWidget(mPlayerGold, "PlayerGold"); + setWidgets(mAvailableEffectsList, mUsedEffectsView); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked); @@ -65,10 +65,6 @@ namespace MWGui { center(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mName); - - MWWorld::Ptr player = MWMechanics::getPlayer(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - mPlayerGold->setCaptionWithReplacing(MyGUI::utility::toString(playerGold)); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr& gem) diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index 616d8cb89c..4c720a11fc 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -70,7 +70,6 @@ namespace MWGui MyGUI::TextBox* mSuccessChance; MyGUI::TextBox* mPrice; MyGUI::TextBox* mPriceText; - MyGUI::TextBox* mPlayerGold; MWMechanics::Enchanting mEnchanting; ESM::EffectList mEffectList; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index c9556a0e2c..cfa6cb01e7 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -30,6 +30,7 @@ namespace { + bool sortMagicEffects(short id1, short id2) { const MWWorld::Store& gmst From b3105e93826c79ba9746b71c94cdbf407bda3a3a Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 4 Jul 2025 13:49:32 -0500 Subject: [PATCH 15/19] Fix other changes --- files/data/mygui/openmw_enchanting_dialog.layout | 8 -------- files/data/mygui/openmw_spellcreation_dialog.layout | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/files/data/mygui/openmw_enchanting_dialog.layout b/files/data/mygui/openmw_enchanting_dialog.layout index 839de8d399..c0e3ba0d21 100644 --- a/files/data/mygui/openmw_enchanting_dialog.layout +++ b/files/data/mygui/openmw_enchanting_dialog.layout @@ -138,14 +138,6 @@ - - - - - - - - diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index 9ab895833a..2da1eb621d 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -72,9 +72,8 @@ - - + @@ -88,6 +87,7 @@ + From ed8b9742ae7e06cfd5bad3292888263e6c579c56 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 4 Jul 2025 13:52:22 -0500 Subject: [PATCH 16/19] Add name --- files/data/mygui/openmw_spellcreation_dialog.layout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index 2da1eb621d..d241c63350 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -68,7 +68,7 @@ - + From 424a62187abcd74f96164ec7a305c3b98e7ea536 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 4 Jul 2025 14:26:16 -0500 Subject: [PATCH 17/19] whitespace? --- apps/openmw/mwgui/spellcreationdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index cfa6cb01e7..6bd9ef3ac8 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -30,7 +30,7 @@ namespace { - + bool sortMagicEffects(short id1, short id2) { const MWWorld::Store& gmst From 3504e85051a6bf7ced2db4abc232e8afdbe78126 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 4 Jul 2025 14:33:15 -0500 Subject: [PATCH 18/19] More whitespace --- files/data/mygui/openmw_spellcreation_dialog.layout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index d241c63350..4dc895d26a 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -41,7 +41,7 @@ - + From 7c4ed3c11d0063634dd61c86bf43949f09716db9 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 15:19:49 -0700 Subject: [PATCH 19/19] Fixing XML? --- files/data/mygui/openmw_spellcreation_dialog.layout | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/files/data/mygui/openmw_spellcreation_dialog.layout b/files/data/mygui/openmw_spellcreation_dialog.layout index 4dc895d26a..d616e00dcb 100644 --- a/files/data/mygui/openmw_spellcreation_dialog.layout +++ b/files/data/mygui/openmw_spellcreation_dialog.layout @@ -73,9 +73,7 @@ - - - +