diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b36eb2525..f549eac686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ Bug #7665: Alchemy menu is missing the ability to deselect and choose different qualities of an apparatus Bug #7675: Successful lock spell doesn't produce a sound Bug #7679: Scene luminance value flashes when toggling shaders + Bug #7712: Casting doesn't support spells and enchantments with no effects Feature #3537: Shader-based water ripples Feature #5492: Let rain and snow collide with statics Feature #6149: Dehardcode Lua API_REVISION diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index bd38174183..1c8aad5447 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -423,17 +423,21 @@ namespace MWGui mSpellBox->setUserString("Spell", spellId.serialize()); mSpellBox->setUserData(MyGUI::Any::Null); - // use the icon of the first effect - const ESM::MagicEffect* effect = MWBase::Environment::get().getESMStore()->get().find( - spell->mEffects.mList.front().mEffectID); - - std::string icon = effect->mIcon; - std::replace(icon.begin(), icon.end(), '/', '\\'); - int slashPos = icon.rfind('\\'); - icon.insert(slashPos + 1, "b_"); - icon = Misc::ResourceHelpers::correctIconPath(icon, MWBase::Environment::get().getResourceSystem()->getVFS()); - - mSpellImage->setSpellIcon(icon); + if (!spell->mEffects.mList.empty()) + { + // use the icon of the first effect + const ESM::MagicEffect* effect = MWBase::Environment::get().getESMStore()->get().find( + spell->mEffects.mList.front().mEffectID); + std::string icon = effect->mIcon; + std::replace(icon.begin(), icon.end(), '/', '\\'); + size_t slashPos = icon.rfind('\\'); + icon.insert(slashPos + 1, "b_"); + icon = Misc::ResourceHelpers::correctIconPath( + icon, MWBase::Environment::get().getResourceSystem()->getVFS()); + mSpellImage->setSpellIcon(icon); + } + else + mSpellImage->setSpellIcon({}); } void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 5ee74c6e87..05fff2d40f 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -202,7 +202,7 @@ namespace MWGui setIcon(ptr); } - void SpellWidget::setSpellIcon(const std::string& icon) + void SpellWidget::setSpellIcon(std::string_view icon) { if (mFrame && !mCurrentFrame.empty()) { diff --git a/apps/openmw/mwgui/itemwidget.hpp b/apps/openmw/mwgui/itemwidget.hpp index 29b0063203..63837ae92f 100644 --- a/apps/openmw/mwgui/itemwidget.hpp +++ b/apps/openmw/mwgui/itemwidget.hpp @@ -58,7 +58,7 @@ namespace MWGui { MYGUI_RTTI_DERIVED(SpellWidget) public: - void setSpellIcon(const std::string& icon); + void setSpellIcon(std::string_view icon); }; } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index bdcc4e76d7..a13cfe4d77 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -237,13 +237,15 @@ namespace MWGui params.mNoTarget = false; effects.push_back(params); } - if (MWMechanics::spellIncreasesSkill( - spell)) // display school of spells that contribute to skill progress + // display school of spells that contribute to skill progress + if (MWMechanics::spellIncreasesSkill(spell)) { - MWWorld::Ptr player = MWMechanics::getPlayer(); - const auto& school - = store->get().find(MWMechanics::getSpellSchool(spell, player))->mSchool; - info.text = "#{sSchool}: " + MyGUI::TextIterator::toTagsString(school->mName).asUTF8(); + ESM::RefId id = MWMechanics::getSpellSchool(spell, MWMechanics::getPlayer()); + if (!id.empty()) + { + const auto& school = store->get().find(id)->mSchool; + info.text = "#{sSchool}: " + MyGUI::TextIterator::toTagsString(school->mName).asUTF8(); + } } auto cost = focus->getUserString("SpellCost"); if (!cost.empty() && cost != "0") diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 77bc51423e..713add719b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1605,18 +1605,19 @@ namespace MWMechanics effects = &spell->mEffects.mList; cast.playSpellCastingEffects(spell); } - if (mCanCast) + if (!effects->empty()) { - const ESM::MagicEffect* effect = store.get().find( - effects->back().mEffectID); // use last effect of list for color of VFX_Hands + if (mCanCast) + { + const ESM::MagicEffect* effect = store.get().find( + effects->back().mEffectID); // use last effect of list for color of VFX_Hands - const ESM::Static* castStatic - = world->getStore().get().find(ESM::RefId::stringRefId("VFX_Hands")); + const ESM::Static* castStatic + = world->getStore().get().find(ESM::RefId::stringRefId("VFX_Hands")); - const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + const VFS::Manager* const vfs + = MWBase::Environment::get().getResourceSystem()->getVFS(); - if (!effects->empty()) - { if (mAnimation->getNode("Bip01 L Hand")) mAnimation->addEffect( Misc::ResourceHelpers::correctMeshPath(castStatic->mModel, vfs), -1, false, @@ -1627,44 +1628,44 @@ namespace MWMechanics Misc::ResourceHelpers::correctMeshPath(castStatic->mModel, vfs), -1, false, "Bip01 R Hand", effect->mParticle); } - } - - const ESM::ENAMstruct& firstEffect = effects->at(0); // first effect used for casting animation + // first effect used for casting animation + const ESM::ENAMstruct& firstEffect = effects->front(); - std::string startKey; - std::string stopKey; - if (isRandomAttackAnimation(mCurrentWeapon)) - { - startKey = "start"; - stopKey = "stop"; - if (mCanCast) - world->castSpell( - mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately - mCastingManualSpell = false; - mCanCast = false; - } - else - { - switch (firstEffect.mRange) + std::string startKey; + std::string stopKey; + if (isRandomAttackAnimation(mCurrentWeapon)) { - case 0: - mAttackType = "self"; - break; - case 1: - mAttackType = "touch"; - break; - case 2: - mAttackType = "target"; - break; + startKey = "start"; + stopKey = "stop"; + if (mCanCast) + world->castSpell( + mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately + mCastingManualSpell = false; + mCanCast = false; } + else + { + switch (firstEffect.mRange) + { + case 0: + mAttackType = "self"; + break; + case 1: + mAttackType = "touch"; + break; + case 2: + mAttackType = "target"; + break; + } - startKey = mAttackType + " start"; - stopKey = mAttackType + " stop"; - } + startKey = mAttackType + " start"; + stopKey = mAttackType + " stop"; + } - mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, 1, - startKey, stopKey, 0.0f, 0); - mUpperBodyState = UpperBodyState::Casting; + mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, + 1, startKey, stopKey, 0.0f, 0); + mUpperBodyState = UpperBodyState::Casting; + } } else { diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index de3c2b011d..e6080ce447 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -524,11 +524,9 @@ namespace MWWorld const ESM::Enchantment* enchantment = MWBase::Environment::get().getESMStore()->get().search(enchantmentName); - if (!enchantment) + if (!enchantment || enchantment->mEffects.mList.empty()) return result; - assert(enchantment->mEffects.mList.size()); - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getESMStore()->get().search( enchantment->mEffects.mList.front().mEffectID); if (!magicEffect)