From 0ac0c62091a2e7762b22bac4b0975cb3e6e5d6c3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Apr 2025 10:19:07 +0000 Subject: [PATCH 01/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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 0801e0512d8a0d93fe278998c3e5ed1365b2249e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 27 Jul 2025 18:15:19 +0200 Subject: [PATCH 19/31] Do thread safe local static based initialization --- apps/openmw/mwgui/mapwindow.cpp | 8 +++----- apps/openmw/mwrender/npcanimation.cpp | 8 +++----- apps/openmw/mwrender/ripples.cpp | 3 +-- apps/openmw/mwsound/ffmpegdecoder.cpp | 8 +++----- apps/openmw/mwworld/scene.cpp | 3 +-- components/sceneutil/depth.hpp | 9 +++------ 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 59d21886dc..51a765442a 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -774,12 +774,10 @@ namespace MWGui , mGlobalMapRender(std::make_unique(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { - static bool registered = false; - if (!registered) - { + [[maybe_unused]] static const bool registered = [] { MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - registered = true; - } + return true; + }(); mEditNoteDialog.setVisible(false); mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c14eee27e4..4aface83d6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -401,14 +401,12 @@ namespace MWRender { if (mViewMode == VM_FirstPerson) { - static bool prototypeAdded = false; - if (!prototypeAdded) - { + [[maybe_unused]] static const bool prototypeAdded = [&] { osg::ref_ptr depthClearBin(new osgUtil::RenderBin); depthClearBin->setDrawCallback(new DepthClearCallback()); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); - prototypeAdded = true; - } + return true; + }(); mObjectRoot->getOrCreateStateSet()->setRenderBinDetails( RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); } diff --git a/apps/openmw/mwrender/ripples.cpp b/apps/openmw/mwrender/ripples.cpp index ab982d0c55..b19863d695 100644 --- a/apps/openmw/mwrender/ripples.cpp +++ b/apps/openmw/mwrender/ripples.cpp @@ -88,14 +88,13 @@ namespace MWRender if (mProgramBlobber != nullptr) { - static bool pipelineLogged = [&] { + [[maybe_unused]] static const bool pipelineLogged = [&] { if (mUseCompute) Log(Debug::Info) << "Initialized compute shader pipeline for water ripples"; else Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples"; return true; }(); - (void)pipelineLogged; } setCullCallback(new osg::NodeCallback); diff --git a/apps/openmw/mwsound/ffmpegdecoder.cpp b/apps/openmw/mwsound/ffmpegdecoder.cpp index 5a0f336a93..f8b40ddabb 100644 --- a/apps/openmw/mwsound/ffmpegdecoder.cpp +++ b/apps/openmw/mwsound/ffmpegdecoder.cpp @@ -522,16 +522,14 @@ namespace MWSound /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ - static bool done_init = false; - if (!done_init) - { + [[maybe_unused]] static const bool doneInit = [] { // This is not needed anymore above FFMpeg version 4.0 #if LIBAVCODEC_VERSION_INT < 3805796 av_register_all(); #endif av_log_set_level(AV_LOG_ERROR); - done_init = true; - } + return true; + }(); } FFmpegDecoder::~FFmpegDecoder() diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0c9a13bc47..1a68142c15 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1273,13 +1273,12 @@ namespace MWWorld const std::size_t leftCapacity = mPreloader->getMaxCacheSize() - mPreloader->getCacheSize(); if (cells.size() > leftCapacity) { - static bool logged = [&] { + [[maybe_unused]] static const bool logged = [&] { Log(Debug::Warning) << "Not enough cell preloader cache capacity to preload exterior cells, consider " "increasing \"preload cell cache max\" up to " << (mPreloader->getCacheSize() + cells.size()); return true; }(); - (void)logged; cells.resize(leftCapacity); } diff --git a/components/sceneutil/depth.hpp b/components/sceneutil/depth.hpp index a9f99b145f..b9004c457f 100644 --- a/components/sceneutil/depth.hpp +++ b/components/sceneutil/depth.hpp @@ -95,13 +95,10 @@ namespace SceneUtil static void setReversed(bool reverseZ) { - static bool init = false; - - if (!init) - { + [[maybe_unused]] static const bool init = [&] { AutoDepth::sReversed = reverseZ; - init = true; - } + return true; + }(); } static bool isReversed() From 929a65126aad0dcca243040112feaba177e1ffe0 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 08:25:02 -0700 Subject: [PATCH 20/31] Fix docs --- files/lua_api/openmw/ambient.lua | 4 ++-- files/lua_api/openmw/animation.lua | 6 +++--- files/lua_api/openmw/camera.lua | 5 ++--- files/lua_api/openmw/core.lua | 6 +++--- files/lua_api/openmw/types.lua | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/files/lua_api/openmw/ambient.lua b/files/lua_api/openmw/ambient.lua index 436f10de1a..a9e8c08d4a 100644 --- a/files/lua_api/openmw/ambient.lua +++ b/files/lua_api/openmw/ambient.lua @@ -18,7 +18,7 @@ -- * `scale` - a boolean, to set if the sound's pitch should be scaled by simulation time scaling (default: true); -- * `loop` - a boolean, to set if the sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- scale=false, -- pitch=1.0, @@ -38,7 +38,7 @@ -- * `scale` - a boolean, to set if the sound's pitch should be scaled by simulation time scaling (default: true); -- * `loop` - a boolean, to set if the sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- scale=false, -- pitch=1.0, diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index 929a784ba9..b182a1e0af 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -188,7 +188,7 @@ -- -- * `loops` - a number >= 0, the number of times the animation should loop after the first play (default: 0). -- * `priority` - Either a single #Priority value that will be assigned to all bone groups. Or a table mapping bone groups to its priority (default: PRIORITY.Default). --- * `blendMask` - A mask of which bone groups to include in the animation (Default: BLEND_MASK.All. +-- * `blendMask` - A mask of which bone groups to include in the animation (Default: BLEND_MASK.All). -- * `autoDisable` - If true, the animation will be immediately removed upon finishing, which means information will not be possible to query once completed. (Default: true) -- * `speed` - a floating point number >= 0, the speed at which the animation should play (default: 1) -- * `startKey` - the animation key at which the animation should start (default: "start") @@ -242,7 +242,7 @@ -- model = types.Static.record(mgef.hitStatic).model, -- options = { -- vfxId = mgef.id, --- particuleTextureOverride = mgef.particle, +-- particleTextureOverride = mgef.particle, -- loop = false, -- } -- }) @@ -254,7 +254,7 @@ -- Can only be used on self. -- @function [parent=#animation] removeVfx -- @param openmw.core#GameObject actor --- @param #number vfxId an integer ID that uniquely identifies the VFX to remove +-- @param #string vfxId an integer ID that uniquely identifies the VFX to remove --- -- Removes all vfx from the actor. diff --git a/files/lua_api/openmw/camera.lua b/files/lua_api/openmw/camera.lua index b6b605fc10..67b3ec2a8e 100644 --- a/files/lua_api/openmw/camera.lua +++ b/files/lua_api/openmw/camera.lua @@ -109,7 +109,6 @@ --- -- Additional summand for the yaw angle; useful for camera shaking effects. --- Setting extra pitch doesn't block player input. -- Full yaw is `getYaw()+getExtraYaw()`. -- @function [parent=#camera] setExtraYaw -- @param #number value @@ -122,7 +121,7 @@ --- -- Additional summand for the roll angle; useful for camera shaking effects. --- Full yaw is `getRoll()+getExtraRoll()`. +-- Full roll is `getRoll()+getExtraRoll()`. -- @function [parent=#camera] setExtraRoll -- @param #number value @@ -150,7 +149,7 @@ --- -- Set preferred offset between tracked position (see `getTrackedPosition`) and the camera focal point (the center of the screen) in third person mode. -- The offset is a 2d vector (X, Y) where X is horizontal (to the right from the character) and Y component is vertical (upward). --- The real offset can differ from the preferred one during smooth transition of if blocked by an obstacle. +-- The real offset can differ from the preferred one during smooth transition or if blocked by an obstacle. -- Smooth transition happens by default every time when the preferred offset was changed. Use `instantTransition()` to skip the current transition. -- @function [parent=#camera] setFocalPreferredOffset -- @param openmw.util#Vector2 offset diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 054e96674b..43bb22058b 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -167,7 +167,7 @@ -- @field openmw.util#Transform startingRotation The object original rotation -- @field #ObjectOwner owner Ownership information -- @field #Cell cell The cell where the object currently is. During loading a game and for objects in an inventory or a container `cell` is nil. --- @field #GameObject parentContainer Container or actor that contains (or has in inventory) this object. It is nil if the object is in a cell. +-- @field #GameObject parentContainer Container or actor that contains this object (or has in inventory) . It is nil if the object is in a cell. -- @field #any type Type of the object (one of the tables from the package @{openmw.types#types}). -- @field #number count Count (>1 means a stack of objects). -- @field #string recordId Returns record ID of the object in lowercase. @@ -801,7 +801,7 @@ -- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1); -- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- loop=false, -- pitch=1.0 @@ -822,7 +822,7 @@ -- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1); -- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false); -- @usage local params = { --- timeOffset=0.1 +-- timeOffset=0.1, -- volume=0.3, -- loop=false, -- pitch=1.0 diff --git a/files/lua_api/openmw/types.lua b/files/lua_api/openmw/types.lua index b2711c895b..6d51987ac7 100644 --- a/files/lua_api/openmw/types.lua +++ b/files/lua_api/openmw/types.lua @@ -1004,7 +1004,7 @@ -- @param openmw.core#GameObject actor NPC object -- @param #string faction Faction ID -- @usage local NPC = require('openmw.types').NPC; --- NPC.clearExpell(player, "mages guild"); +-- NPC.clearExpelled(player, "mages guild"); --- -- Check if NPC is expelled from given faction. From 5e64015aa9964f2f21f0dfb65e9ec9243cf304c0 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 15:57:31 -0700 Subject: [PATCH 21/31] Fix typo, remove change --- files/lua_api/openmw/animation.lua | 2 +- files/lua_api/openmw/core.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index b182a1e0af..8237d3ca94 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -254,7 +254,7 @@ -- Can only be used on self. -- @function [parent=#animation] removeVfx -- @param openmw.core#GameObject actor --- @param #string vfxId an integer ID that uniquely identifies the VFX to remove +-- @param #string vfxId an string ID that uniquely identifies the VFX to remove --- -- Removes all vfx from the actor. diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 43bb22058b..c1b888bd43 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -167,7 +167,7 @@ -- @field openmw.util#Transform startingRotation The object original rotation -- @field #ObjectOwner owner Ownership information -- @field #Cell cell The cell where the object currently is. During loading a game and for objects in an inventory or a container `cell` is nil. --- @field #GameObject parentContainer Container or actor that contains this object (or has in inventory) . It is nil if the object is in a cell. +-- @field #GameObject parentContainer Container or actor that contains (or has in inventory) this object. It is nil if the object is in a cell. -- @field #any type Type of the object (one of the tables from the package @{openmw.types#types}). -- @field #number count Count (>1 means a stack of objects). -- @field #string recordId Returns record ID of the object in lowercase. From 6464e2a11b51241f6b1f555923a0dc6a0bd56ad7 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Fri, 1 Aug 2025 16:11:32 -0700 Subject: [PATCH 22/31] remove N --- files/lua_api/openmw/animation.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/lua_api/openmw/animation.lua b/files/lua_api/openmw/animation.lua index 8237d3ca94..9fcbe8ea21 100644 --- a/files/lua_api/openmw/animation.lua +++ b/files/lua_api/openmw/animation.lua @@ -254,7 +254,7 @@ -- Can only be used on self. -- @function [parent=#animation] removeVfx -- @param openmw.core#GameObject actor --- @param #string vfxId an string ID that uniquely identifies the VFX to remove +-- @param #string vfxId a string ID that uniquely identifies the VFX to remove --- -- Removes all vfx from the actor. From 4c4d6078d89a8bb8f003479b6ba4d0db474debf8 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 6 Jul 2025 21:57:47 +0300 Subject: [PATCH 23/31] Training menu fixes (#8584) Rework the layout Use "OK" instead of "Cancel" for the OK button Add gp to the price Use entry spacing consistent with other service menus --- apps/openmw/mwgui/trainingwindow.cpp | 6 +++--- files/data/mygui/openmw_trainingwindow.layout | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 4bde77a552..3fc8412d4c 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -29,7 +29,7 @@ namespace MWGui : WindowBase("openmw_trainingwindow.layout") { getWidget(mTrainingOptions, "TrainingOptions"); - getWidget(mCancelButton, "CancelButton"); + getWidget(mCancelButton, "OkButton"); getWidget(mPlayerGold, "PlayerGold"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked); @@ -115,14 +115,14 @@ namespace MWGui MyGUI::Button* button = mTrainingOptions->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip - MyGUI::IntCoord(5, 5 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight), + MyGUI::IntCoord(4, 3 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight), MyGUI::Align::Default); button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); button->setCaptionWithReplacing( - MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price)); + MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price) + "#{sgp}"); button->setSize(button->getTextSize().width + 12, button->getSize().height); diff --git a/files/data/mygui/openmw_trainingwindow.layout b/files/data/mygui/openmw_trainingwindow.layout index 80526068ce..f3cadcaf46 100644 --- a/files/data/mygui/openmw_trainingwindow.layout +++ b/files/data/mygui/openmw_trainingwindow.layout @@ -1,28 +1,28 @@ - + - + - + - + - + - + - + From 978d8668f4f0ed3711858dc8c613de3c58301730 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 2 Aug 2025 08:39:13 +0000 Subject: [PATCH 24/31] Bring jail time skill changes over to lua --- CMakeLists.txt | 2 +- apps/openmw/mwbase/luamanager.hpp | 1 + apps/openmw/mwgui/jailscreen.cpp | 43 +----------- apps/openmw/mwlua/engineevents.cpp | 9 +++ apps/openmw/mwlua/engineevents.hpp | 7 +- apps/openmw/mwlua/localscripts.cpp | 2 +- apps/openmw/mwlua/localscripts.hpp | 2 + apps/openmw/mwlua/luamanagerimp.cpp | 5 ++ apps/openmw/mwlua/luamanagerimp.hpp | 1 + apps/openmw/mwlua/uibindings.cpp | 4 ++ .../omw/mechanics/playercontroller.lua | 68 +++++++++++++++---- files/data/scripts/omw/skillhandlers.lua | 35 ++++++---- files/data/scripts/omw/ui.lua | 9 ++- 13 files changed, 119 insertions(+), 69 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ef872eb6..57ebeefcfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 86) +set(OPENMW_LUA_API_REVISION 87) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index 61574de3ac..5772c555a3 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -75,6 +75,7 @@ namespace MWBase const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult, std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback) = 0; + virtual void jailTimeServed(const MWWorld::Ptr& actor, int days) = 0; virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0; virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0; virtual void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 2fbaa8d8ac..9244e9dd2f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/luamanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" @@ -86,46 +87,6 @@ namespace MWGui // We should not worsen corprus when in prison player.getClass().getCreatureStats(player).getActiveSpells().skipWorsenings(mDays * 24); - - const auto& skillStore = MWBase::Environment::get().getESMStore()->get(); - std::set skills; - for (int day = 0; day < mDays; ++day) - { - auto& prng = MWBase::Environment::get().getWorld()->getPrng(); - const ESM::Skill* skill = skillStore.searchRandom({}, prng); - skills.insert(skill); - - MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId); - if (skill->mId == ESM::Skill::Security || skill->mId == ESM::Skill::Sneak) - value.setBase(std::min(100.f, value.getBase() + 1)); - else - value.setBase(std::max(0.f, value.getBase() - 1)); - } - - const MWWorld::Store& gmst - = MWBase::Environment::get().getESMStore()->get(); - - std::string message; - if (mDays == 1) - message = gmst.find("sNotifyMessage42")->mValue.getString(); - else - message = gmst.find("sNotifyMessage43")->mValue.getString(); - - message = Misc::StringUtils::format(message, mDays); - - for (const ESM::Skill* skill : skills) - { - int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mId).getBase(); - std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString(); - if (skill->mId == ESM::Skill::Sneak || skill->mId == ESM::Skill::Security) - skillMsg = gmst.find("sNotifyMessage39")->mValue.getString(); - - skillMsg = Misc::StringUtils::format(skillMsg, skill->mName, skillValue); - message += "\n" + skillMsg; - } - - std::vector buttons; - buttons.emplace_back("#{Interface:OK}"); - MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); + MWBase::Environment::get().getLuaManager()->jailTimeServed(player, mDays); } } diff --git a/apps/openmw/mwlua/engineevents.cpp b/apps/openmw/mwlua/engineevents.cpp index 6c652bccba..be7d249bfc 100644 --- a/apps/openmw/mwlua/engineevents.cpp +++ b/apps/openmw/mwlua/engineevents.cpp @@ -113,6 +113,15 @@ namespace MWLua scripts->onSkillLevelUp(event.mSkill, event.mSource); } + void operator()(const OnJailTimeServed& event) const + { + MWWorld::Ptr actor = getPtr(event.mActor); + if (actor.isEmpty()) + return; + if (auto* scripts = getLocalScripts(actor)) + scripts->onJailTimeServed(event.mDays); + } + private: MWWorld::Ptr getPtr(ESM::RefNum id) const { diff --git a/apps/openmw/mwlua/engineevents.hpp b/apps/openmw/mwlua/engineevents.hpp index fb9183eb7c..407739d60e 100644 --- a/apps/openmw/mwlua/engineevents.hpp +++ b/apps/openmw/mwlua/engineevents.hpp @@ -70,8 +70,13 @@ namespace MWLua std::string mSkill; std::string mSource; }; + struct OnJailTimeServed + { + ESM::RefNum mActor; + int mDays; + }; using Event = std::variant; + OnAnimationTextKey, OnSkillUse, OnSkillLevelUp, OnJailTimeServed>; void clear() { mQueue.clear(); } void addToQueue(Event e) { mQueue.push_back(std::move(e)); } diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index d784328035..ad2913dd49 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -232,7 +232,7 @@ namespace MWLua [&](LuaUtil::LuaView& view) { addPackage("openmw.self", sol::make_object(view.sol(), &mData)); }); registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers, &mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse, - &mOnSkillLevelUp }); + &mOnSkillLevelUp, &mOnJailTimeServed }); } void LocalScripts::setActive(bool active, bool callHandlers) diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index b3ec647d0e..bc34576509 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -89,6 +89,7 @@ namespace MWLua { callEngineHandlers(mOnSkillLevelUp, skillId, source); } + void onJailTimeServed(int days) { callEngineHandlers(mOnJailTimeServed, days); } void applyStatsCache(); @@ -118,6 +119,7 @@ namespace MWLua EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" }; EngineHandlerList mOnSkillUse{ "_onSkillUse" }; EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" }; + EngineHandlerList mOnJailTimeServed{ "_onJailTimeServed" }; }; } diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index df9c9cd50f..850dd87eed 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -490,6 +490,11 @@ namespace MWLua EngineEvents::OnSkillLevelUp{ getId(actor), skillId.serializeText(), std::string(source) }); } + void LuaManager::jailTimeServed(const MWWorld::Ptr& actor, int days) + { + mEngineEvents.addToQueue(EngineEvents::OnJailTimeServed{ getId(actor), days }); + } + void LuaManager::onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth, const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType) diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index f47b6f96cf..bd4ab0d30e 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -92,6 +92,7 @@ namespace MWLua bool loopfallback) override; void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) override; void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) override; + void jailTimeServed(const MWWorld::Ptr& actor, int days) override; void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth, const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType) override; diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 826338ca7d..8d9892005c 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -108,6 +108,10 @@ namespace MWLua } luaManager->addUIMessage(message, mode); }; + + api["_showInteractiveMessage"] = [windowManager](std::string_view message, sol::optional) { + windowManager->interactiveMessageBox(message, { "#{Interface:OK}" }); + }; api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs(lua, { { "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) }, diff --git a/files/data/scripts/omw/mechanics/playercontroller.lua b/files/data/scripts/omw/mechanics/playercontroller.lua index 6de31afdea..a0d1b11362 100644 --- a/files/data/scripts/omw/mechanics/playercontroller.lua +++ b/files/data/scripts/omw/mechanics/playercontroller.lua @@ -39,7 +39,8 @@ end local function skillLevelUpHandler(skillid, source, params) local skillStat = NPC.stats.skills[skillid](self) - if skillStat.base >= 100 then + if (skillStat.base >= 100 and params.skillIncreaseValue > 0) or + (skillStat.base <= 0 and params.skillIncreaseValue < 0) then return false end @@ -62,25 +63,67 @@ local function skillLevelUpHandler(skillid, source, params) = levelStat.skillIncreasesForSpecialization[params.levelUpSpecialization] + params.levelUpSpecializationIncreaseValue; end - local skillRecord = Skill.record(skillid) - local npcRecord = NPC.record(self) - local class = NPC.classes.record(npcRecord.class) + if source ~= 'jail' then + local skillRecord = Skill.record(skillid) + local npcRecord = NPC.record(self) + local class = NPC.classes.record(npcRecord.class) - ambient.playSound("skillraise") + ambient.playSound("skillraise") - local message = string.format(core.getGMST('sNotifyMessage39'),skillRecord.name,skillStat.base) + local message = string.format(core.getGMST('sNotifyMessage39'),skillRecord.name,skillStat.base) - if source == I.SkillProgression.SKILL_INCREASE_SOURCES.Book then - message = '#{sBookSkillMessage}\n'..message + if source == I.SkillProgression.SKILL_INCREASE_SOURCES.Book then + message = '#{sBookSkillMessage}\n'..message + end + + ui.showMessage(message, { showInDialogue = false }) + + if levelStat.progress >= core.getGMST('iLevelUpTotal') then + ui.showMessage('#{sLevelUpMsg}', { showInDialogue = false }) + end + + if not source or source == I.SkillProgression.SKILL_INCREASE_SOURCES.Usage then skillStat.progress = 0 end end +end - ui.showMessage(message, { showInDialogue = false }) +local function jailTimeServed(days) + if not days or days <= 0 then + return + end - if levelStat.progress >= core.getGMST('iLevelUpTotal') then - ui.showMessage('#{sLevelUpMsg}', { showInDialogue = false }) + local oldSkillLevels = {} + local skillByNumber = {} + for skillid, skillStat in pairs(NPC.stats.skills) do + oldSkillLevels[skillid] = skillStat(self).base + skillByNumber[#skillByNumber+1] = skillid + end + + math.randomseed(core.getSimulationTime()) + for day=1,days do + local skillid = skillByNumber[math.random(#skillByNumber)] + -- skillLevelUp() handles skill-based increase/decrease + I.SkillProgression.skillLevelUp(skillid, I.SkillProgression.SKILL_INCREASE_SOURCES.Jail) end - if not source or source == I.SkillProgression.SKILL_INCREASE_SOURCES.Usage then skillStat.progress = 0 end + local message = '' + if days == 1 then + message = string.format(core.getGMST('sNotifyMessage42'), days) + else + message = string.format(core.getGMST('sNotifyMessage43'), days) + end + for skillid, skillStat in pairs(NPC.stats.skills) do + local diff = skillStat(self).base - oldSkillLevels[skillid] + if diff ~= 0 then + local skillMsg = core.getGMST('sNotifyMessage39') + if diff < 0 then + skillMsg = core.getGMST('sNotifyMessage44') + end + local skillRecord = Skill.record(skillid) + message = message..'\n'..string.format(skillMsg, skillRecord.name, skillStat(self).base) + end + end + + I.UI.showInteractiveMessage(message) end local function skillUsedHandler(skillid, params) @@ -114,6 +157,7 @@ I.SkillProgression.addSkillLevelUpHandler(skillLevelUpHandler) return { engineHandlers = { onUpdate = onUpdate, + _onJailTimeServed = jailTimeServed, }, eventHandlers = { diff --git a/files/data/scripts/omw/skillhandlers.lua b/files/data/scripts/omw/skillhandlers.lua index d3a224dc46..9b58d81174 100644 --- a/files/data/scripts/omw/skillhandlers.lua +++ b/files/data/scripts/omw/skillhandlers.lua @@ -38,6 +38,7 @@ local Skill = core.stats.Skill -- Table of all existing sources for skill increases. Any sources not listed below will be treated as equal to Trainer. -- @type SkillLevelUpSource -- @field #string Book book +-- @field #string Jail jail -- @field #string Trainer trainer -- @field #string Usage usage @@ -131,15 +132,17 @@ local function skillLevelUp(skillid, source) levelUpAttributeIncreaseValue = core.getGMST('iLevelUpMajorMultAttribute') end - local options = - { - skillIncreaseValue = 1, - levelUpProgress = levelUpProgress, - levelUpAttribute = skillRecord.attribute, - levelUpAttributeIncreaseValue = levelUpAttributeIncreaseValue, - levelUpSpecialization = skillRecord.specialization, - levelUpSpecializationIncreaseValue = core.getGMST('iLevelupSpecialization'), - } + local options = {} + if source == 'jail' and not (skillid == 'security' or skillid == 'sneak') then + options.skillIncreaseValue = -1 + else + options.skillIncreaseValue = 1 + options.levelUpProgress = levelUpProgress + options.levelUpAttribute = skillRecord.attribute + options.levelUpAttributeIncreaseValue = levelUpAttributeIncreaseValue + options.levelUpSpecialization = skillRecord.specialization + options.levelUpSpecializationIncreaseValue = core.getGMST('iLevelupSpecialization') + end for i = #skillLevelUpHandlers, 1, -1 do if skillLevelUpHandlers[i](skillid, source, options) == false then @@ -156,8 +159,15 @@ return { -- @context local -- @usage local I = require('openmw.interfaces') -- + -- -- Make jail time hurt sneak skill instead of benefitting it + -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, source, options) + -- if skillid == 'sneak' and source == 'jail' and options.skillIncreaseValue > 0 then + -- options.skillIncreaseValue = -options.skillIncreaseValue + -- end + -- end) + -- -- -- Forbid increasing destruction skill past 50 - -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, options) + -- I.SkillProgression.addSkillLevelUpHandler(function(skillid, source, options) -- if skillid == 'destruction' and types.NPC.stats.skills.destruction(self).base >= 50 then -- return false -- end @@ -187,7 +197,7 @@ return { -- a modifiable table of skill level up values, and can be modified to change the behavior of later handlers. -- These values are calculated based on vanilla mechanics. Setting any value to nil will cause that mechanic to be skipped. By default contains these values: -- - -- * `skillIncreaseValue` - The numeric amount of skill levels gained. + -- * `skillIncreaseValue` - The numeric amount of skill levels gained. By default this is 1, except when the source is jail in which case it will instead be -1 for all skills except sneak and security. -- * `levelUpProgress` - The numeric amount of level up progress gained. -- * `levelUpAttribute` - The string identifying the attribute that should receive points from this skill level up. -- * `levelUpAttributeIncreaseValue` - The numeric amount of attribute increase points received. This contributes to the amount of each attribute the character receives during a vanilla level up. @@ -263,7 +273,7 @@ return { --- Trigger a skill level up, activating relevant handlers -- @function [parent=#SkillProgression] skillLevelUp -- @param #string skillid The id of the skill to level up. - -- @param #SkillLevelUpSource source The source of the skill increase. + -- @param #SkillLevelUpSource source The source of the skill increase. Note that passing a value of @{#SkillLevelUpSource.Jail} will cause a skill decrease for all skills except sneak and security. skillLevelUp = skillLevelUp, --- @{#SkillLevelUpSource} @@ -272,6 +282,7 @@ return { Book = 'book', Usage = 'usage', Trainer = 'trainer', + Jail = 'jail', }, --- Compute the total skill gain required to level up a skill based on its current level, and other modifying factors such as major skills and specialization. diff --git a/files/data/scripts/omw/ui.lua b/files/data/scripts/omw/ui.lua index 31502fd6ee..c63c085fab 100644 --- a/files/data/scripts/omw/ui.lua +++ b/files/data/scripts/omw/ui.lua @@ -171,7 +171,7 @@ return { interface = { --- Interface version -- @field [parent=#UI] #number version - version = 2, + version = 3, --- All available UI modes. -- Use `view(I.UI.MODE)` in `luap` console mode to see the list. @@ -254,6 +254,13 @@ return { -- @return #boolean isWindowVisible = isWindowVisible, + --- + -- Shows a message as an interactive message box pausing the game, with a single button with the localized text OK. + -- @function [parent=#UI] showInteractiveMessage + -- @param #string message Message to display + -- @param #table options Options (none yet) + showInteractiveMessage = ui._showInteractiveMessage + -- TODO -- registerHudElement = function(name, showFn, hideFn) end, -- showHudElement = function(name, bool) end, From a2bc1569e0027c177fe347fdbb08f153bf21d1bd Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 20 Apr 2025 17:53:08 +0200 Subject: [PATCH 25/31] Remove unused includes --- apps/openmw/engine.cpp | 4 ++-- apps/openmw/mwlua/animationbindings.cpp | 9 --------- apps/openmw/mwlua/birthsignbindings.cpp | 2 +- apps/openmw/mwlua/cellbindings.cpp | 4 ---- apps/openmw/mwlua/classbindings.cpp | 2 +- apps/openmw/mwlua/corebindings.cpp | 1 - apps/openmw/mwlua/localscripts.cpp | 4 ---- apps/openmw/mwlua/localscripts.hpp | 4 +--- apps/openmw/mwlua/luamanagerimp.cpp | 5 ++++- apps/openmw/mwlua/luamanagerimp.hpp | 3 ++- apps/openmw/mwlua/nearbybindings.cpp | 2 ++ apps/openmw/mwlua/object.hpp | 2 -- apps/openmw/mwlua/objectlists.cpp | 4 ---- apps/openmw/mwlua/racebindings.cpp | 2 +- apps/openmw/mwlua/recordstore.hpp | 12 +++++++++--- apps/openmw/mwlua/stats.cpp | 4 +++- apps/openmw/mwlua/userdataserializer.hpp | 3 ++- apps/openmw/mwlua/worldbindings.cpp | 1 - components/detournavigator/dbrefgeometryobject.hpp | 1 - components/detournavigator/generatenavmeshtile.cpp | 4 +--- components/detournavigator/makenavmesh.cpp | 4 ---- components/detournavigator/navmeshtileview.cpp | 1 - components/detournavigator/offmeshconnection.hpp | 1 - components/detournavigator/preparednavmeshdata.cpp | 2 -- components/detournavigator/raycast.hpp | 3 ++- components/detournavigator/recastmesh.hpp | 2 -- components/files/configurationmanager.cpp | 1 + components/files/configurationmanager.hpp | 1 - components/sceneutil/mwshadowtechnique.cpp | 1 - 29 files changed, 32 insertions(+), 57 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0ea8451774..2a66878a04 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +#include #include #include @@ -28,7 +29,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwlua/animationbindings.cpp b/apps/openmw/mwlua/animationbindings.cpp index 1af7acea36..5ecd3bd1fa 100644 --- a/apps/openmw/mwlua/animationbindings.cpp +++ b/apps/openmw/mwlua/animationbindings.cpp @@ -1,13 +1,7 @@ #include "animationbindings.hpp" -#include -#include -#include #include -#include #include -#include -#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -15,11 +9,8 @@ #include "../mwmechanics/character.hpp" -#include "../mwworld/esmstore.hpp" - #include "context.hpp" #include "luamanagerimp.hpp" -#include "objectvariant.hpp" namespace MWLua { diff --git a/apps/openmw/mwlua/birthsignbindings.cpp b/apps/openmw/mwlua/birthsignbindings.cpp index 218d05b804..130144ab9b 100644 --- a/apps/openmw/mwlua/birthsignbindings.cpp +++ b/apps/openmw/mwlua/birthsignbindings.cpp @@ -8,7 +8,7 @@ #include "../mwbase/environment.hpp" #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace sol { diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index ec64a3cddd..4e4542d89f 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -1,7 +1,5 @@ #include "cellbindings.hpp" -#include - #include #include #include @@ -27,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -38,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/openmw/mwlua/classbindings.cpp b/apps/openmw/mwlua/classbindings.cpp index c94631fb3c..338698bfee 100644 --- a/apps/openmw/mwlua/classbindings.cpp +++ b/apps/openmw/mwlua/classbindings.cpp @@ -4,7 +4,7 @@ #include #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace sol { diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index bdf71710af..653eef2a42 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index ad2913dd49..99c994304b 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -1,9 +1,5 @@ #include "localscripts.hpp" -#include -#include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/aicombat.hpp" diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index bc34576509..c51d2d8c19 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -1,9 +1,7 @@ #ifndef MWLUA_LOCALSCRIPTS_H #define MWLUA_LOCALSCRIPTS_H -#include -#include -#include +#include #include #include diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 850dd87eed..66ecfb1c55 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -5,6 +5,10 @@ #include #include +#include +#include +#include + #include #include @@ -15,7 +19,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index bd4ab0d30e..42b18d236f 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -3,9 +3,10 @@ #include #include -#include #include +#include + #include #include #include diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 6c244a0fd4..230fab45db 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -16,6 +16,8 @@ #include "luamanagerimp.hpp" #include "objectlists.hpp" +#include + namespace { template diff --git a/apps/openmw/mwlua/object.hpp b/apps/openmw/mwlua/object.hpp index d032515314..4a50dee8b6 100644 --- a/apps/openmw/mwlua/object.hpp +++ b/apps/openmw/mwlua/object.hpp @@ -1,9 +1,7 @@ #ifndef MWLUA_OBJECT_H #define MWLUA_OBJECT_H -#include #include -#include #include diff --git a/apps/openmw/mwlua/objectlists.cpp b/apps/openmw/mwlua/objectlists.cpp index d0bda5a644..b0169dd651 100644 --- a/apps/openmw/mwlua/objectlists.cpp +++ b/apps/openmw/mwlua/objectlists.cpp @@ -1,9 +1,5 @@ #include "objectlists.hpp" -#include -#include -#include - #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwlua/racebindings.cpp b/apps/openmw/mwlua/racebindings.cpp index b5c4d6093f..590d16b4c9 100644 --- a/apps/openmw/mwlua/racebindings.cpp +++ b/apps/openmw/mwlua/racebindings.cpp @@ -8,7 +8,7 @@ #include "../mwworld/esmstore.hpp" #include "idcollectionbindings.hpp" -#include "types/types.hpp" +#include "recordstore.hpp" namespace { diff --git a/apps/openmw/mwlua/recordstore.hpp b/apps/openmw/mwlua/recordstore.hpp index aed84a1271..f2fb4ed453 100644 --- a/apps/openmw/mwlua/recordstore.hpp +++ b/apps/openmw/mwlua/recordstore.hpp @@ -1,10 +1,16 @@ #ifndef MWLUA_RECORDSTORE_H #define MWLUA_RECORDSTORE_H -#include +#include + +#include +#include +#include +#include +#include +#include +#include -#include -#include #include #include "apps/openmw/mwbase/environment.hpp" diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 637ae0b002..8b103d5943 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -1,9 +1,11 @@ #include "stats.hpp" #include -#include #include +#include #include +#include +#include #include #include diff --git a/apps/openmw/mwlua/userdataserializer.hpp b/apps/openmw/mwlua/userdataserializer.hpp index f52bb22723..35e4c4729b 100644 --- a/apps/openmw/mwlua/userdataserializer.hpp +++ b/apps/openmw/mwlua/userdataserializer.hpp @@ -1,7 +1,8 @@ #ifndef MWLUA_USERDATASERIALIZER_H #define MWLUA_USERDATASERIALIZER_H -#include "object.hpp" +#include +#include namespace LuaUtil { diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index c02bad3bd3..66c17699f2 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/components/detournavigator/dbrefgeometryobject.hpp b/components/detournavigator/dbrefgeometryobject.hpp index dbbafe7e18..71e035d9e6 100644 --- a/components/detournavigator/dbrefgeometryobject.hpp +++ b/components/detournavigator/dbrefgeometryobject.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/components/detournavigator/generatenavmeshtile.cpp b/components/detournavigator/generatenavmeshtile.cpp index 118114181e..115ca75d40 100644 --- a/components/detournavigator/generatenavmeshtile.cpp +++ b/components/detournavigator/generatenavmeshtile.cpp @@ -10,10 +10,8 @@ #include -#include #include #include -#include #include namespace DetourNavigator @@ -79,7 +77,7 @@ namespace DetourNavigator return; } - const auto data + const std::unique_ptr data = prepareNavMeshTileData(*recastMesh, mWorldspace, mTilePosition, mAgentBounds, mSettings.mRecast); if (data == nullptr) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index fe63d27a1e..06bb693aad 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -3,8 +3,6 @@ #include "exceptions.hpp" #include "flags.hpp" #include "navmeshdata.hpp" -#include "navmeshdb.hpp" -#include "navmeshtilescache.hpp" #include "offmeshconnection.hpp" #include "preparednavmeshdata.hpp" #include "recastcontext.hpp" @@ -22,8 +20,6 @@ #include #include -#include -#include namespace DetourNavigator { diff --git a/components/detournavigator/navmeshtileview.cpp b/components/detournavigator/navmeshtileview.cpp index d22efcbfff..e77962bb3c 100644 --- a/components/detournavigator/navmeshtileview.cpp +++ b/components/detournavigator/navmeshtileview.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include diff --git a/components/detournavigator/offmeshconnection.hpp b/components/detournavigator/offmeshconnection.hpp index fb8b166498..01bae02732 100644 --- a/components/detournavigator/offmeshconnection.hpp +++ b/components/detournavigator/offmeshconnection.hpp @@ -6,7 +6,6 @@ #include #include -#include namespace DetourNavigator { diff --git a/components/detournavigator/preparednavmeshdata.cpp b/components/detournavigator/preparednavmeshdata.cpp index a737ae19a5..32e559b8d0 100644 --- a/components/detournavigator/preparednavmeshdata.cpp +++ b/components/detournavigator/preparednavmeshdata.cpp @@ -4,8 +4,6 @@ #include -#include - namespace { void initPolyMeshDetail(rcPolyMeshDetail& value) noexcept diff --git a/components/detournavigator/raycast.hpp b/components/detournavigator/raycast.hpp index 855c5562fc..374b09320c 100644 --- a/components/detournavigator/raycast.hpp +++ b/components/detournavigator/raycast.hpp @@ -3,9 +3,10 @@ #include "flags.hpp" -#include #include +#include + class dtNavMeshQuery; namespace DetourNavigator diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 6d06db0799..1a67bb5495 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -11,9 +11,7 @@ #include #include -#include #include -#include #include #include diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 49fdd996a7..bef98b73f0 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -1,6 +1,7 @@ #include "configurationmanager.hpp" #include +#include #include #include diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 184c6ebb82..da4397146b 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,6 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP -#include #include #include #include diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 9fa01385d5..d456795781 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include "glextensions.hpp" From eeca0b13b006fef6e0cdcdffd2b4fd1949046dab Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 2 Aug 2025 12:18:28 +0200 Subject: [PATCH 26/31] Add missing files to components/detournavigator list --- components/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8a3325ea71..6b7fe32d40 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -407,6 +407,7 @@ add_component_dir(detournavigator areatype asyncnavmeshupdater bounds + cellgridbounds changetype collisionshapetype commulativeaabb From e87b95f642661f118321999d37a9716cae772080 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 20 Apr 2025 17:53:29 +0200 Subject: [PATCH 27/31] Use forward declaration for context and sol --- apps/openmw/mwlua/birthsignbindings.hpp | 4 ++-- apps/openmw/mwlua/cellbindings.hpp | 8 ++++---- apps/openmw/mwlua/classbindings.hpp | 4 ++-- apps/openmw/mwlua/corebindings.cpp | 1 + apps/openmw/mwlua/corebindings.hpp | 6 +++--- apps/openmw/mwlua/debugbindings.hpp | 2 +- apps/openmw/mwlua/factionbindings.hpp | 4 ++-- apps/openmw/mwlua/inputbindings.cpp | 1 + apps/openmw/mwlua/inputbindings.hpp | 6 +++--- apps/openmw/mwlua/landbindings.cpp | 10 ++++++++++ apps/openmw/mwlua/landbindings.hpp | 4 +++- apps/openmw/mwlua/luabindings.hpp | 17 +++++++++-------- apps/openmw/mwlua/magicbindings.hpp | 4 ++-- apps/openmw/mwlua/markupbindings.hpp | 6 +++--- apps/openmw/mwlua/menuscripts.cpp | 2 ++ apps/openmw/mwlua/menuscripts.hpp | 2 +- apps/openmw/mwlua/mwscriptbindings.cpp | 1 + apps/openmw/mwlua/mwscriptbindings.hpp | 6 ++---- apps/openmw/mwlua/nearbybindings.cpp | 1 + apps/openmw/mwlua/nearbybindings.hpp | 6 +++--- apps/openmw/mwlua/objectbindings.hpp | 8 ++++---- apps/openmw/mwlua/postprocessingbindings.cpp | 1 + apps/openmw/mwlua/postprocessingbindings.hpp | 6 +++--- apps/openmw/mwlua/racebindings.hpp | 4 ++-- apps/openmw/mwlua/soundbindings.hpp | 6 +++--- apps/openmw/mwlua/uibindings.hpp | 6 +++--- apps/openmw/mwlua/vfsbindings.hpp | 6 +++--- apps/openmw/mwlua/worldbindings.cpp | 1 + apps/openmw/mwlua/worldbindings.hpp | 6 +++--- 29 files changed, 79 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwlua/birthsignbindings.hpp b/apps/openmw/mwlua/birthsignbindings.hpp index bf41707d47..e57d56c971 100644 --- a/apps/openmw/mwlua/birthsignbindings.hpp +++ b/apps/openmw/mwlua/birthsignbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initBirthSignRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/cellbindings.hpp b/apps/openmw/mwlua/cellbindings.hpp index 0d8e90e989..70255d8d7f 100644 --- a/apps/openmw/mwlua/cellbindings.hpp +++ b/apps/openmw/mwlua/cellbindings.hpp @@ -1,12 +1,12 @@ #ifndef MWLUA_CELLBINDINGS_H #define MWLUA_CELLBINDINGS_H -#include "context.hpp" - namespace MWLua { - void initCellBindingsForLocalScripts(const Context&); - void initCellBindingsForGlobalScripts(const Context&); + struct Context; + + void initCellBindingsForLocalScripts(const Context& context); + void initCellBindingsForGlobalScripts(const Context& context); } #endif // MWLUA_CELLBINDINGS_H diff --git a/apps/openmw/mwlua/classbindings.hpp b/apps/openmw/mwlua/classbindings.hpp index 1acb0a9ad3..cc67f2df02 100644 --- a/apps/openmw/mwlua/classbindings.hpp +++ b/apps/openmw/mwlua/classbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initClassRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index 653eef2a42..282a9f970b 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -19,6 +19,7 @@ #include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" +#include "context.hpp" #include "coremwscriptbindings.hpp" #include "dialoguebindings.hpp" #include "factionbindings.hpp" diff --git a/apps/openmw/mwlua/corebindings.hpp b/apps/openmw/mwlua/corebindings.hpp index ef385ca993..6d27dd0412 100644 --- a/apps/openmw/mwlua/corebindings.hpp +++ b/apps/openmw/mwlua/corebindings.hpp @@ -3,13 +3,13 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + void addCoreTimeBindings(sol::table& api, const Context& context); - sol::table initCorePackage(const Context&); + sol::table initCorePackage(const Context& context); } #endif // MWLUA_COREBINDINGS_H diff --git a/apps/openmw/mwlua/debugbindings.hpp b/apps/openmw/mwlua/debugbindings.hpp index c508b54496..d7902cb618 100644 --- a/apps/openmw/mwlua/debugbindings.hpp +++ b/apps/openmw/mwlua/debugbindings.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_MWLUA_DEBUGBINDINGS_H #define OPENMW_MWLUA_DEBUGBINDINGS_H -#include +#include namespace MWLua { diff --git a/apps/openmw/mwlua/factionbindings.hpp b/apps/openmw/mwlua/factionbindings.hpp index 0dc06ceaf2..280c7d4791 100644 --- a/apps/openmw/mwlua/factionbindings.hpp +++ b/apps/openmw/mwlua/factionbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initCoreFactionBindings(const Context& context); } diff --git a/apps/openmw/mwlua/inputbindings.cpp b/apps/openmw/mwlua/inputbindings.cpp index c3b47c5061..9a781106a6 100644 --- a/apps/openmw/mwlua/inputbindings.cpp +++ b/apps/openmw/mwlua/inputbindings.cpp @@ -13,6 +13,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwinput/actions.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" namespace sol diff --git a/apps/openmw/mwlua/inputbindings.hpp b/apps/openmw/mwlua/inputbindings.hpp index 195f69f5f9..e9a19ed34d 100644 --- a/apps/openmw/mwlua/inputbindings.hpp +++ b/apps/openmw/mwlua/inputbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initInputPackage(const Context&); + struct Context; + + sol::table initInputPackage(const Context& context); } #endif // MWLUA_INPUTBINDINGS_H diff --git a/apps/openmw/mwlua/landbindings.cpp b/apps/openmw/mwlua/landbindings.cpp index 3f85e2b066..2b053802db 100644 --- a/apps/openmw/mwlua/landbindings.cpp +++ b/apps/openmw/mwlua/landbindings.cpp @@ -1,14 +1,24 @@ #include "landbindings.hpp" +#include +#include + +#include +#include +#include + #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/worldmodel.hpp" + +#include "context.hpp" #include "object.hpp" namespace diff --git a/apps/openmw/mwlua/landbindings.hpp b/apps/openmw/mwlua/landbindings.hpp index 8cdf30046b..f38d260759 100644 --- a/apps/openmw/mwlua/landbindings.hpp +++ b/apps/openmw/mwlua/landbindings.hpp @@ -1,10 +1,12 @@ #ifndef MWLUA_LANDBINDINGS_H #define MWLUA_LANDBINDINGS_H -#include "context.hpp" +#include namespace MWLua { + struct Context; + sol::table initCoreLandBindings(const Context& context); } diff --git a/apps/openmw/mwlua/luabindings.hpp b/apps/openmw/mwlua/luabindings.hpp index 749987e5b2..e7b73dbca1 100644 --- a/apps/openmw/mwlua/luabindings.hpp +++ b/apps/openmw/mwlua/luabindings.hpp @@ -1,29 +1,30 @@ #ifndef MWLUA_LUABINDINGS_H #define MWLUA_LUABINDINGS_H -#include #include -#include -#include "context.hpp" +#include +#include namespace MWLua { + struct Context; + // Initialize Lua packages that are available for all scripts. - std::map initCommonPackages(const Context&); + std::map initCommonPackages(const Context& context); // Initialize Lua packages that are available for global scripts (additionally to common packages). - std::map initGlobalPackages(const Context&); + std::map initGlobalPackages(const Context& context); // Initialize Lua packages that are available for local scripts (additionally to common packages). - std::map initLocalPackages(const Context&); + std::map initLocalPackages(const Context& context); // Initialize Lua packages that are available only for local scripts on the player (additionally to common and local // packages). - std::map initPlayerPackages(const Context&); + std::map initPlayerPackages(const Context& context); // Initialize Lua packages that are available only for menu scripts (additionally to common packages). - std::map initMenuPackages(const Context&); + std::map initMenuPackages(const Context& context); } #endif // MWLUA_LUABINDINGS_H diff --git a/apps/openmw/mwlua/magicbindings.hpp b/apps/openmw/mwlua/magicbindings.hpp index 047bd2e3d9..d9c5182d76 100644 --- a/apps/openmw/mwlua/magicbindings.hpp +++ b/apps/openmw/mwlua/magicbindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initCoreMagicBindings(const Context& context); void addActorMagicBindings(sol::table& actor, const Context& context); } diff --git a/apps/openmw/mwlua/markupbindings.hpp b/apps/openmw/mwlua/markupbindings.hpp index 9105ab5edf..ec4b6dbbb2 100644 --- a/apps/openmw/mwlua/markupbindings.hpp +++ b/apps/openmw/mwlua/markupbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initMarkupPackage(const Context&); + struct Context; + + sol::table initMarkupPackage(const Context& context); } #endif // MWLUA_MARKUPBINDINGS_H diff --git a/apps/openmw/mwlua/menuscripts.cpp b/apps/openmw/mwlua/menuscripts.cpp index 32520c0822..a7ff80e63e 100644 --- a/apps/openmw/mwlua/menuscripts.cpp +++ b/apps/openmw/mwlua/menuscripts.cpp @@ -7,6 +7,8 @@ #include "../mwbase/statemanager.hpp" #include "../mwstate/character.hpp" +#include "context.hpp" + namespace MWLua { static const MWState::Character* findCharacter(std::string_view characterDir) diff --git a/apps/openmw/mwlua/menuscripts.hpp b/apps/openmw/mwlua/menuscripts.hpp index 8721224413..11c6cc1912 100644 --- a/apps/openmw/mwlua/menuscripts.hpp +++ b/apps/openmw/mwlua/menuscripts.hpp @@ -9,11 +9,11 @@ #include "../mwbase/luamanager.hpp" -#include "context.hpp" #include "inputprocessor.hpp" namespace MWLua { + struct Context; sol::table initMenuPackage(const Context& context); diff --git a/apps/openmw/mwlua/mwscriptbindings.cpp b/apps/openmw/mwlua/mwscriptbindings.cpp index 90d24b39fe..179b35aa5c 100644 --- a/apps/openmw/mwlua/mwscriptbindings.cpp +++ b/apps/openmw/mwlua/mwscriptbindings.cpp @@ -12,6 +12,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/worldimp.hpp" +#include "context.hpp" #include "object.hpp" #include diff --git a/apps/openmw/mwlua/mwscriptbindings.hpp b/apps/openmw/mwlua/mwscriptbindings.hpp index 9598f051ae..c0c88ec28c 100644 --- a/apps/openmw/mwlua/mwscriptbindings.hpp +++ b/apps/openmw/mwlua/mwscriptbindings.hpp @@ -3,13 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; - sol::table initMWScriptBindings(const Context&); - + sol::table initMWScriptBindings(const Context& context); } #endif // MWLUA_MWSCRIPTBINDINGS_H diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 230fab45db..b31a934a54 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -13,6 +13,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/scene.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" #include "objectlists.hpp" diff --git a/apps/openmw/mwlua/nearbybindings.hpp b/apps/openmw/mwlua/nearbybindings.hpp index ee0022898e..2d8140195e 100644 --- a/apps/openmw/mwlua/nearbybindings.hpp +++ b/apps/openmw/mwlua/nearbybindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initNearbyPackage(const Context&); + struct Context; + + sol::table initNearbyPackage(const Context& context); } #endif // MWLUA_NEARBYBINDINGS_H diff --git a/apps/openmw/mwlua/objectbindings.hpp b/apps/openmw/mwlua/objectbindings.hpp index 5f69c54da3..3861211357 100644 --- a/apps/openmw/mwlua/objectbindings.hpp +++ b/apps/openmw/mwlua/objectbindings.hpp @@ -1,12 +1,12 @@ #ifndef MWLUA_OBJECTBINDINGS_H #define MWLUA_OBJECTBINDINGS_H -#include "context.hpp" - namespace MWLua { - void initObjectBindingsForLocalScripts(const Context&); - void initObjectBindingsForGlobalScripts(const Context&); + struct Context; + + void initObjectBindingsForLocalScripts(const Context& context); + void initObjectBindingsForGlobalScripts(const Context& context); } #endif // MWLUA_OBJECTBINDINGS_H diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index f12bda8650..6d0c33848a 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwrender/postprocessor.hpp" +#include "context.hpp" #include "luamanagerimp.hpp" namespace diff --git a/apps/openmw/mwlua/postprocessingbindings.hpp b/apps/openmw/mwlua/postprocessingbindings.hpp index 50cd84fa24..55b46d2524 100644 --- a/apps/openmw/mwlua/postprocessingbindings.hpp +++ b/apps/openmw/mwlua/postprocessingbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initPostprocessingPackage(const Context&); + struct Context; + + sol::table initPostprocessingPackage(const Context& context); } #endif // MWLUA_POSTPROCESSINGBINDINGS_H diff --git a/apps/openmw/mwlua/racebindings.hpp b/apps/openmw/mwlua/racebindings.hpp index 43ba9237c5..405a158899 100644 --- a/apps/openmw/mwlua/racebindings.hpp +++ b/apps/openmw/mwlua/racebindings.hpp @@ -3,10 +3,10 @@ #include -#include "context.hpp" - namespace MWLua { + struct Context; + sol::table initRaceRecordBindings(const Context& context); } diff --git a/apps/openmw/mwlua/soundbindings.hpp b/apps/openmw/mwlua/soundbindings.hpp index 333ed898c4..d7af17cb69 100644 --- a/apps/openmw/mwlua/soundbindings.hpp +++ b/apps/openmw/mwlua/soundbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initCoreSoundBindings(const Context&); + struct Context; + + sol::table initCoreSoundBindings(const Context& context); sol::table initAmbientPackage(const Context& context); } diff --git a/apps/openmw/mwlua/uibindings.hpp b/apps/openmw/mwlua/uibindings.hpp index 930ba7f3d8..32fb54e590 100644 --- a/apps/openmw/mwlua/uibindings.hpp +++ b/apps/openmw/mwlua/uibindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initUserInterfacePackage(const Context&); + struct Context; + + sol::table initUserInterfacePackage(const Context& context); } #endif // MWLUA_UIBINDINGS_H diff --git a/apps/openmw/mwlua/vfsbindings.hpp b/apps/openmw/mwlua/vfsbindings.hpp index b251db6fd4..ab3ad3662c 100644 --- a/apps/openmw/mwlua/vfsbindings.hpp +++ b/apps/openmw/mwlua/vfsbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initVFSPackage(const Context&); + struct Context; + + sol::table initVFSPackage(const Context& context); } #endif // MWLUA_VFSBINDINGS_H diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index 66c17699f2..d98c769e65 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -26,6 +26,7 @@ #include "luamanagerimp.hpp" #include "animationbindings.hpp" +#include "context.hpp" #include "corebindings.hpp" #include "mwscriptbindings.hpp" diff --git a/apps/openmw/mwlua/worldbindings.hpp b/apps/openmw/mwlua/worldbindings.hpp index 4bd2318b68..368344429b 100644 --- a/apps/openmw/mwlua/worldbindings.hpp +++ b/apps/openmw/mwlua/worldbindings.hpp @@ -3,11 +3,11 @@ #include -#include "context.hpp" - namespace MWLua { - sol::table initWorldPackage(const Context&); + struct Context; + + sol::table initWorldPackage(const Context& context); } #endif // MWLUA_WORLDBINDINGS_H From 7c4ed3c11d0063634dd61c86bf43949f09716db9 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 15:19:49 -0700 Subject: [PATCH 28/31] 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 @@ - - - + From 2abed374f9d600cdfc3584707468053c6054fd93 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 19:00:12 -0700 Subject: [PATCH 29/31] Fix missing link --- docs/source/reference/lua-scripting/index_interfaces.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/reference/lua-scripting/index_interfaces.rst b/docs/source/reference/lua-scripting/index_interfaces.rst index 37a2df63c4..0b113c7744 100644 --- a/docs/source/reference/lua-scripting/index_interfaces.rst +++ b/docs/source/reference/lua-scripting/index_interfaces.rst @@ -11,6 +11,7 @@ Interfaces AI AnimationController Camera + Combat Controls Crimes GamepadControls From 286c4d9101bf360fd75561ff58d452d1e8679755 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 21:40:09 -0700 Subject: [PATCH 30/31] a word --- docs/source/reference/lua-scripting/events.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index 590ff14d88..02eff68f8c 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -108,7 +108,7 @@ Example: attacker = self, weapon = Actor.getEquipment(self, Actor.EQUIPMENT_SLOT.CarriedRight), sourceType = I.Combat.ATTACK_SOURCE_TYPE.Melee, - strenght = 1, + strength = 1, type = self.ATTACK_TYPE.Chop, damage = { health = 20, From c276f850dc9ca47f5df49b052f4171a9be4f0470 Mon Sep 17 00:00:00 2001 From: SkyHasACat Date: Sat, 2 Aug 2025 22:49:58 -0700 Subject: [PATCH 31/31] One more fix --- docs/source/reference/lua-scripting/events.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/reference/lua-scripting/events.rst b/docs/source/reference/lua-scripting/events.rst index 02eff68f8c..810739caeb 100644 --- a/docs/source/reference/lua-scripting/events.rst +++ b/docs/source/reference/lua-scripting/events.rst @@ -107,7 +107,7 @@ Example: local attack = { attacker = self, weapon = Actor.getEquipment(self, Actor.EQUIPMENT_SLOT.CarriedRight), - sourceType = I.Combat.ATTACK_SOURCE_TYPE.Melee, + sourceType = I.Combat.ATTACK_SOURCE_TYPES.Melee, strength = 1, type = self.ATTACK_TYPE.Chop, damage = {