Add an option to use casting animations for magic items (feature #4962)

pull/541/head
Andrei Kortunov 6 years ago
parent 7808cbbfe8
commit 35facc5cde

@ -68,6 +68,7 @@
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable Feature #4890: Make Distant Terrain configurable
Feature #4962: Add casting animations for magic items
Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption Task #4695: Optimize Distant Terrain memory consumption

@ -1195,7 +1195,7 @@ bool CharacterController::updateCreatureState()
if (!spellid.empty() && canCast) if (!spellid.empty() && canCast)
{ {
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid, false);
if (!mAnimation->hasAnimation("spellcast")) if (!mAnimation->hasAnimation("spellcast"))
{ {
@ -1515,23 +1515,53 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
stats.getSpells().setSelectedSpell(selectedSpell); stats.getSpells().setSelectedSpell(selectedSpell);
} }
std::string spellid = stats.getSpells().getSelectedSpell(); std::string spellid = stats.getSpells().getSelectedSpell();
bool isMagicItem = false;
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr); bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
if(!spellid.empty() && canCast) if (spellid.empty())
{
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
const MWWorld::Ptr& enchantItem = *inv.getSelectedEnchantItem();
spellid = enchantItem.getClass().getEnchantment(enchantItem);
isMagicItem = true;
}
}
}
static const bool useCastingAnimations = Settings::Manager::getBool("use magic item animations", "Game");
if (isMagicItem && !useCastingAnimations)
{
// Enchanted items by default do not use casting animations
MWBase::Environment::get().getWorld()->castSpell(mPtr);
resetIdle = false;
}
else if(!spellid.empty() && canCast)
{ {
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid, isMagicItem);
std::vector<ESM::ENAMstruct> effects;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); if (isMagicItem)
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back(); {
const ESM::MagicEffect *effect; const ESM::Enchantment *enchantment = store.get<ESM::Enchantment>().find(spellid);
effects = enchantment->mEffects.mList;
}
else
{
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
effects = spell->mEffects.mList;
}
effect = store.get<ESM::MagicEffect>().find(lastEffect.mEffectID); // use last effect of list for color of VFX_Hands const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(effects.back().mEffectID); // use last effect of list for color of VFX_Hands
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Hands"); const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Hands");
for (size_t iter = 0; iter < spell->mEffects.mList.size(); ++iter) // play hands vfx for each effect for (size_t iter = 0; iter < effects.size(); ++iter) // play hands vfx for each effect
{ {
if (mAnimation->getNode("Bip01 L Hand")) if (mAnimation->getNode("Bip01 L Hand"))
mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle);
@ -1540,7 +1570,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 R Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 R Hand", effect->mParticle);
} }
const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation const ESM::ENAMstruct &firstEffect = effects.at(0); // first effect used for casting animation
std::string startKey; std::string startKey;
std::string stopKey; std::string stopKey;
@ -1574,17 +1604,6 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
resetIdle = false; resetIdle = false;
} }
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
// Enchanted items cast immediately (no animation)
MWBase::Environment::get().getWorld()->castSpell(mPtr);
}
}
} }
else if(mWeaponType == WeapType_PickProbe) else if(mWeaponType == WeapType_PickProbe)
{ {

@ -972,7 +972,7 @@ namespace MWMechanics
// A non-actor doesn't play its spell cast effects from a character controller, so play them here // A non-actor doesn't play its spell cast effects from a character controller, so play them here
if (!mCaster.getClass().isActor()) if (!mCaster.getClass().isActor())
playSpellCastingEffects(mId); playSpellCastingEffects(mId, false);
inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self); inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self);
@ -1047,15 +1047,27 @@ namespace MWMechanics
return true; return true;
} }
void CastSpell::playSpellCastingEffects(const std::string &spellid) void CastSpell::playSpellCastingEffects(const std::string &spellid, bool enchantment)
{ {
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); if (enchantment)
{
const ESM::Enchantment *spell = store.get<ESM::Enchantment>().find(spellid);
playSpellCastingEffects(spell->mEffects.mList);
std::vector<std::string> addedEffects; }
else
{
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
playSpellCastingEffects(spell->mEffects.mList);
}
}
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin(); void CastSpell::playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects)
iter != spell->mEffects.mList.end(); ++iter) {
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
std::vector<std::string> addedEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator iter = effects.begin(); iter != effects.end(); ++iter)
{ {
const ESM::MagicEffect *effect; const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(iter->mEffectID); effect = store.get<ESM::MagicEffect>().find(iter->mEffectID);

@ -83,6 +83,9 @@ namespace MWMechanics
private: private:
MWWorld::Ptr mCaster; // May be empty MWWorld::Ptr mCaster; // May be empty
MWWorld::Ptr mTarget; // May be empty MWWorld::Ptr mTarget; // May be empty
void playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects);
public: public:
bool mStack; bool mStack;
std::string mId; // ID of spell, potion, item etc std::string mId; // ID of spell, potion, item etc
@ -109,7 +112,7 @@ namespace MWMechanics
/// @note Auto detects if spell, ingredient or potion /// @note Auto detects if spell, ingredient or potion
bool cast (const std::string& id); bool cast (const std::string& id);
void playSpellCastingEffects(const std::string &spellid); void playSpellCastingEffects(const std::string &spellid, bool enchantment);
bool spellIncreasesSkill(); bool spellIncreasesSkill();

@ -1098,7 +1098,7 @@ namespace MWScript
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
MWMechanics::CastSpell cast(ptr, target, false, true); MWMechanics::CastSpell cast(ptr, target, false, true);
cast.playSpellCastingEffects(spell->mId); cast.playSpellCastingEffects(spell->mId, false);
cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mHitPosition = target.getRefData().getPosition().asVec3();
cast.mAlwaysSucceed = true; cast.mAlwaysSucceed = true;
cast.cast(spell); cast.cast(spell);

@ -126,6 +126,18 @@ This is how Morrowind behaves.
This setting can be toggled in Advanced tab of the launcher. This setting can be toggled in Advanced tab of the launcher.
use magic item animations
-------------------------
:Type: boolean
:Range: True/False
:Default: False
If this setting is true, the engine will use casting animations for magic items, including scrolls.
Otherwise, there will be no casting animations, just as in original engine
This setting can only be configured by editing the settings configuration file.
show effect duration show effect duration
-------------------- --------------------

@ -261,6 +261,9 @@ weapon sheathing = false
# Allow non-standard ammunition solely to bypass normal weapon resistance or weakness # Allow non-standard ammunition solely to bypass normal weapon resistance or weakness
only appropriate ammunition bypasses resistance = false only appropriate ammunition bypasses resistance = false
# Use casting animations for magic items, just as for spells
use magic item animations = false
[General] [General]
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).

Loading…
Cancel
Save