mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-30 03:32:36 +00:00
5c4e1252e9
Fixes #5483 This only applies to "base game" spells. When adding an AutoCalc spell with TES:CS its cost is computed and stored inside game files. This stored cost was being used by OpenMW and the actual cost was never recomputed at runtime whereas Morrowind.exe discards the stored cost. While this worked fine in vanilla, mods can update AutoCalc spell effects without ever updating the stored cost. The formula was mostly there already but there was a few differences, namely a 1 second offset in duration.
762 lines
27 KiB
C++
762 lines
27 KiB
C++
#include "spellcreationdialog.hpp"
|
|
|
|
#include <MyGUI_ImageBox.h>
|
|
#include <MyGUI_Gui.h>
|
|
|
|
#include <components/esm/records.hpp>
|
|
#include <components/widgets/list.hpp>
|
|
|
|
#include "../mwbase/windowmanager.hpp"
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/world.hpp"
|
|
|
|
#include "../mwworld/containerstore.hpp"
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
|
|
#include "../mwmechanics/spells.hpp"
|
|
#include "../mwmechanics/creaturestats.hpp"
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
#include "../mwmechanics/spellutil.hpp"
|
|
|
|
#include "tooltips.hpp"
|
|
#include "class.hpp"
|
|
#include "widgets.hpp"
|
|
|
|
namespace
|
|
{
|
|
|
|
bool sortMagicEffects (short id1, short id2)
|
|
{
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
return gmst.find(ESM::MagicEffect::effectIdToString (id1))->mValue.getString()
|
|
< gmst.find(ESM::MagicEffect::effectIdToString (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;
|
|
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?
|
|
|
|
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().getWorld()->getStore().get<ESM::MagicEffect>().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(MWBase::Environment::get().getWindowManager()->correctIconPath(effect->mIcon));
|
|
|
|
mEffectName->setCaptionWithReplacing("#{"+ESM::MagicEffect::effectIdToString (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;
|
|
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 (int skill)
|
|
{
|
|
mEffect.mSkill = skill;
|
|
eventEffectModified(mEffect);
|
|
}
|
|
|
|
void EditEffectDialog::setAttribute (int attribute)
|
|
{
|
|
mEffect.mAttribute = 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)
|
|
{
|
|
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 () == "")
|
|
{
|
|
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, player);
|
|
|
|
// add gold to NPC trading gold pool
|
|
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
|
|
npcStats.setGoldPool(npcStats.getGoldPool() + price);
|
|
|
|
MWBase::Environment::get().getWindowManager()->playSound ("Mysticism Hit");
|
|
|
|
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(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().getWorld()->getStore();
|
|
|
|
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;
|
|
}
|
|
|
|
ESM::EffectList effectList;
|
|
effectList.mList = mEffects;
|
|
mSpell.mEffects = effectList;
|
|
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<ESM::GameSetting>().find("fSpellMakingValueMult")->mValue.getFloat();
|
|
|
|
int price = std::max(1, static_cast<int>(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()
|
|
, mSelectAttributeDialog(nullptr)
|
|
, mSelectSkillDialog(nullptr)
|
|
, 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<short> knownEffects;
|
|
|
|
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
|
{
|
|
const ESM::Spell* spell = it->first;
|
|
|
|
// only normal spells count
|
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
|
continue;
|
|
|
|
for (const ESM::ENAMstruct& effectInfo : spell->mEffects.mList)
|
|
{
|
|
const ESM::MagicEffect * effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectInfo.mEffectID);
|
|
|
|
// 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(), effectInfo.mEffectID) == knownEffects.end())
|
|
knownEffects.push_back(effectInfo.mEffectID);
|
|
}
|
|
}
|
|
|
|
std::sort(knownEffects.begin(), knownEffects.end(), sortMagicEffects);
|
|
|
|
mAvailableEffectsList->clear ();
|
|
|
|
int i=0;
|
|
for (const short effectId : knownEffects)
|
|
{
|
|
mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(
|
|
ESM::MagicEffect::effectIdToString(effectId))->mValue.getString());
|
|
mButtonMapping[i] = effectId;
|
|
++i;
|
|
}
|
|
mAvailableEffectsList->adjustSize ();
|
|
mAvailableEffectsList->scrollToTop();
|
|
|
|
for (const short effectId : knownEffects)
|
|
{
|
|
std::string name = MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(
|
|
ESM::MagicEffect::effectIdToString(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().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
|
|
|
mAddEffectDialog.newEffect(effect);
|
|
mAddEffectDialog.setAttribute (mSelectAttributeDialog->getAttributeId());
|
|
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);
|
|
mSelectAttributeDialog = nullptr;
|
|
}
|
|
|
|
void EffectEditorBase::onSelectSkill ()
|
|
{
|
|
const ESM::MagicEffect* effect =
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
|
|
|
mAddEffectDialog.newEffect(effect);
|
|
mAddEffectDialog.setSkill (mSelectSkillDialog->getSkillId());
|
|
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);
|
|
mSelectSkillDialog = nullptr;
|
|
}
|
|
|
|
void EffectEditorBase::onAttributeOrSkillCancel ()
|
|
{
|
|
if (mSelectSkillDialog)
|
|
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectSkillDialog);
|
|
if (mSelectAttributeDialog)
|
|
MWBase::Environment::get().getWindowManager ()->removeDialog (mSelectAttributeDialog);
|
|
|
|
mSelectSkillDialog = nullptr;
|
|
mSelectAttributeDialog = nullptr;
|
|
}
|
|
|
|
void EffectEditorBase::onAvailableEffectClicked (MyGUI::Widget* sender)
|
|
{
|
|
if (mEffects.size() >= 8)
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}");
|
|
return;
|
|
}
|
|
|
|
int buttonId = *sender->getUserData<int>();
|
|
mSelectedKnownEffectId = mButtonMapping[buttonId];
|
|
|
|
const ESM::MagicEffect* effect =
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
|
|
|
|
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
|
{
|
|
delete mSelectSkillDialog;
|
|
mSelectSkillDialog = new SelectSkillDialog();
|
|
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)
|
|
{
|
|
delete mSelectAttributeDialog;
|
|
mSelectAttributeDialog = new SelectAttributeDialog();
|
|
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 = effectInfo.mSkill;
|
|
params.mAttribute = 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::Button>("", 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<Widgets::MWSpellEffect>("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<int>();
|
|
|
|
mSelectedEffect = id;
|
|
|
|
mAddEffectDialog.editEffect (mEffects[id]);
|
|
mAddEffectDialog.setVisible (true);
|
|
}
|
|
|
|
void EffectEditorBase::setConstantEffect(bool constant)
|
|
{
|
|
mAddEffectDialog.setConstantEffect(constant);
|
|
mConstantEffect = constant;
|
|
|
|
if (!constant)
|
|
return;
|
|
|
|
for (auto it = mEffects.begin(); it != mEffects.end();)
|
|
{
|
|
if (it->mRange != ESM::RT_Self)
|
|
{
|
|
auto& store = MWBase::Environment::get().getWorld()->getStore();
|
|
auto magicEffect = store.get<ESM::MagicEffect>().find(it->mEffectID);
|
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::CastSelf) == 0)
|
|
{
|
|
it = mEffects.erase(it);
|
|
continue;
|
|
}
|
|
it->mRange = ESM::RT_Self;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
}
|