1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-13 20:09:40 +00:00

Implement Resist & Weakness effects

This commit is contained in:
scrawl 2013-11-16 02:34:43 +01:00
parent d49b6f19ff
commit b1a29eb27e
10 changed files with 159 additions and 74 deletions

View file

@ -69,7 +69,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate repair enchanting pathfinding security spellsuccess aiescort aiactivate repair enchanting pathfinding security spellsuccess spellcasting
) )
add_openmw_dir (mwbase add_openmw_dir (mwbase

View file

@ -468,9 +468,9 @@ namespace MWClass
{ {
weapon.getCellRef().mEnchantmentCharge -= castCost; weapon.getCellRef().mEnchantmentCharge -= castCost;
// Touch // Touch
othercls.getCreatureStats(victim).getActiveSpells().addSpell(enchantmentName, victim, ESM::RT_Touch, weapon.getClass().getName(weapon)); othercls.getCreatureStats(victim).getActiveSpells().addSpell(enchantmentName, victim, ptr, ESM::RT_Touch, weapon.getClass().getName(weapon));
// Self // Self
getCreatureStats(ptr).getActiveSpells().addSpell(enchantmentName, ptr, ESM::RT_Self, weapon.getClass().getName(weapon)); getCreatureStats(ptr).getActiveSpells().addSpell(enchantmentName, ptr, ptr, ESM::RT_Self, weapon.getClass().getName(weapon));
// Target // Target
MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon)); MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon));
} }
@ -936,7 +936,7 @@ namespace MWClass
/// \todo consider instant effects /// \todo consider instant effects
return stats.getActiveSpells().addSpell (id, actor); return stats.getActiveSpells().addSpell (id, actor, actor);
} }
void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const

View file

@ -5,7 +5,7 @@
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwgui/inventorywindow.hpp" #include "../mwgui/inventorywindow.hpp"
#include "../mwgui/bookwindow.hpp" #include "../mwgui/bookwindow.hpp"
#include "../mwgui/scrollwindow.hpp" #include "../mwgui/scrollwindow.hpp"

View file

@ -9,7 +9,7 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/actionequip.hpp" #include "../mwworld/actionequip.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "spellicons.hpp" #include "spellicons.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"

View file

@ -16,11 +16,14 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
@ -74,7 +77,7 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin());
effectIter!=effects.first.mList.end(); ++effectIter, ++i) effectIter!=effects.first.mList.end(); ++effectIter, ++i)
{ {
float magnitude = iter->second.mRandom[i]; float random = iter->second.mRandom[i];
if (effectIter->mRange != iter->second.mRange) if (effectIter->mRange != iter->second.mRange)
continue; continue;
@ -83,7 +86,7 @@ namespace MWMechanics
int duration = effectIter->mDuration; int duration = effectIter->mDuration;
if (effects.second.first) if (effects.second.first)
duration *= magnitude; duration *= random;
MWWorld::TimeStamp end = start; MWWorld::TimeStamp end = start;
end += static_cast<double> (duration)* end += static_cast<double> (duration)*
@ -102,19 +105,21 @@ namespace MWMechanics
if (effectIter->mDuration==0) if (effectIter->mDuration==0)
{ {
param.mMagnitude = param.mMagnitude =
static_cast<int> (magnitude / (0.1 * magicEffect->mData.mBaseCost)); static_cast<int> (random / (0.1 * magicEffect->mData.mBaseCost));
} }
else else
{ {
param.mMagnitude = param.mMagnitude =
static_cast<int> (0.05*magnitude / (0.1 * magicEffect->mData.mBaseCost)); static_cast<int> (0.05*random / (0.1 * magicEffect->mData.mBaseCost));
} }
} }
else else
param.mMagnitude = static_cast<int> ( param.mMagnitude = static_cast<int> (
(effectIter->mMagnMax-effectIter->mMagnMin)*magnitude + effectIter->mMagnMin); (effectIter->mMagnMax-effectIter->mMagnMin)*random + effectIter->mMagnMin);
param.mMagnitude *= iter->second.mMultiplier[i];
mEffects.add (*effectIter, param);
if (param.mMagnitude)
mEffects.add (*effectIter, param);
} }
} }
} }
@ -185,7 +190,7 @@ namespace MWMechanics
: mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) : mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
{} {}
bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range, const std::string& name, int effectIndex) bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, ESM::RangeType range, const std::string& name, int effectIndex)
{ {
const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor);
@ -197,6 +202,8 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
iter!=effects.first.mList.end(); ++iter) iter!=effects.first.mList.end(); ++iter)
{ {
if (iter->mRange != range)
continue;
if (iter->mDuration) if (iter->mDuration)
{ {
found = true; found = true;
@ -204,41 +211,37 @@ namespace MWMechanics
} }
} }
// If none of the effects need to apply, no need to add the spell
if (!found) if (!found)
return false; return false;
TContainer::iterator iter = mSpells.find (id); TContainer::iterator iter = mSpells.find (id);
float random = static_cast<float> (std::rand()) / RAND_MAX;
if (effects.second.first)
{
// ingredient -> special treatment required.
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
float x =
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.2 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified())
* creatureStats.getFatigueTerm();
random *= 100;
random = random / std::min (x, 100.0f);
random *= 0.25 * x;
}
ActiveSpellParams params; ActiveSpellParams params;
for (unsigned int i=0; i<effects.first.mList.size(); ++i) for (unsigned int i=0; i<effects.first.mList.size(); ++i)
params.mRandom.push_back(static_cast<float> (std::rand()) / RAND_MAX); {
float random = static_cast<float> (std::rand()) / RAND_MAX;
if (effects.second.first)
{
// ingredient -> special treatment required.
const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor);
float x =
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.2 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified())
* creatureStats.getFatigueTerm();
random *= 100;
random = random / std::min (x, 100.0f);
random *= 0.25 * x;
}
params.mRandom.push_back(random);
}
params.mRange = range; params.mRange = range;
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mName = name; params.mName = name;
params.mMultiplier.resize(effects.first.mList.size(), 1);
if (iter==mSpells.end() || stacks)
mSpells.insert (std::make_pair (id, params));
else
iter->second = params;
/* /*
for (int i=0; i<effects.first.mList.size(); ++i) for (int i=0; i<effects.first.mList.size(); ++i)
@ -258,19 +261,35 @@ namespace MWMechanics
} }
*/ */
// Play sounds & particles
bool first=true; bool first=true;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin()); int i = 0;
iter!=effects.first.mList.end(); ++iter) for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.first.mList.begin());
effectIt!=effects.first.mList.end(); ++effectIt, ++i)
{ {
if (iter->mRange != range) if (effectIt->mRange != range)
continue;
// Try resisting effect in case its harmful
const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id);
params.mMultiplier[i] = MWMechanics::getEffectMultiplier(effectIt->mEffectID, actor, caster, spell);
if (params.mMultiplier[i] == 0)
{
if (actor.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
else
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
}
// If fully resisted, don't play sounds or particles
if (params.mMultiplier[i] == 0)
continue; continue;
// TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World. // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.
const ESM::MagicEffect *magicEffect = const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
iter->mEffectID); effectIt->mEffectID);
// Only the sound of the first effect plays // Only the sound of the first effect plays
if (first) if (first)
@ -296,6 +315,11 @@ namespace MWMechanics
first = false; first = false;
} }
if (iter==mSpells.end() || stacks)
mSpells.insert (std::make_pair (id, params));
else
iter->second = params;
mSpellsChanged = true; mSpellsChanged = true;
return true; return true;
@ -415,7 +439,9 @@ namespace MWMechanics
magnitude = static_cast<int> (0.05*it->second.mRandom[i] / (0.1 * magicEffect->mData.mBaseCost)); magnitude = static_cast<int> (0.05*it->second.mRandom[i] / (0.1 * magicEffect->mData.mBaseCost));
} }
visitor.visit(*effectIt, name, magnitude, remainingTime); magnitude *= it->second.mMultiplier[i];
if (magnitude)
visitor.visit(*effectIt, name, magnitude, remainingTime);
} }
} }
} }

View file

@ -37,7 +37,7 @@ namespace MWMechanics
// Effect magnitude multiplier. Use 0 to completely disable the effect // Effect magnitude multiplier. Use 0 to completely disable the effect
// (if it was resisted, reflected or absorbed). Use (0,1) for partially resisted. // (if it was resisted, reflected or absorbed). Use (0,1) for partially resisted.
std::vector<bool> mMultiplier; std::vector<float> mMultiplier;
// Display name, we need this for enchantments, which don't have a name - so you need to supply the // Display name, we need this for enchantments, which don't have a name - so you need to supply the
// name of the item with the enchantment to addSpell // name of the item with the enchantment to addSpell
@ -85,11 +85,12 @@ namespace MWMechanics
ActiveSpells(); ActiveSpells();
bool addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range = ESM::RT_Self, const std::string& name = "", int effectIndex = -1); bool addSpell (const std::string& id, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, ESM::RangeType range = ESM::RT_Self, const std::string& name = "", int effectIndex = -1);
///< Overwrites an existing spell with the same ID. If the spell does not have any ///< Overwrites an existing spell with the same ID. If the spell does not have any
/// non-instant effects, it is ignored. /// non-instant effects, it is ignored.
/// @param id /// @param id
/// @param actor /// @param actor actor to add the spell to
/// @param caster actor who casted the spell
/// @param range Only effects with range type \a range will be applied /// @param range Only effects with range type \a range will be applied
/// @param name Display name for enchantments, since they don't have a name in their record /// @param name Display name for enchantments, since they don't have a name in their record
/// @param effectIndex Only apply one specific effect - useful for reflecting spells, since each effect is reflected individually /// @param effectIndex Only apply one specific effect - useful for reflecting spells, since each effect is reflected individually

View file

@ -114,6 +114,72 @@ namespace MWMechanics
return school; return school;
} }
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
inline float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectId);
const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);
float resistance = 0;
if (resistanceEffect != -1)
resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
if (weaknessEffect != -1)
resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa
if (spell != NULL)
{
float castChance = getSpellSuccessChance(spell, caster);
if (castChance > 0)
x *= 50 / castChance;
}
float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
roll -= resistance;
if (x <= roll)
x = 0;
else
{
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
x = 100;
else
x = roll / std::min(x, 100.f);
}
x = std::min(x + resistance, 100.f);
resisted = x;
}
return resisted;
}
inline float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
{
float resistance = getEffectResistance(effectId, actor, caster, spell);
if (resistance >= 0)
return 1 - resistance / 100.f;
else
return -(resistance-100) / 100.f;
}
} }
#endif #endif

View file

@ -16,7 +16,7 @@ namespace MWWorld
// TODO: Apply RT_Self effects on the door / container that triggered the trap. Not terribly useful, but you could // TODO: Apply RT_Self effects on the door / container that triggered the trap. Not terribly useful, but you could
// make it lock itself when activated for example. // make it lock itself when activated for example.
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(mSpellId, actor, ESM::RT_Touch); actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(mSpellId, actor, actor, ESM::RT_Touch);
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);

View file

@ -11,6 +11,8 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "esmstore.hpp" #include "esmstore.hpp"
#include "class.hpp" #include "class.hpp"
@ -292,47 +294,37 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect) if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
continue; continue;
// Roll some dice, one for each effect
std::vector<EffectParams> params; std::vector<EffectParams> params;
params.resize(enchantment.mEffects.mList.size());
for (unsigned int i=0; i<params.size();++i)
params[i].mRandom = static_cast<float> (std::rand()) / RAND_MAX;
bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end()); bool existed = (mPermanentMagicEffectMagnitudes.find((**iter).getCellRef().mRefID) != mPermanentMagicEffectMagnitudes.end());
if (!existed) if (!existed)
{ {
// Roll some dice, one for each effect
params.resize(enchantment.mEffects.mList.size());
for (unsigned int i=0; i<params.size();++i)
params[i].mRandom = static_cast<float> (std::rand()) / RAND_MAX;
// Try resisting each effect // Try resisting each effect
int i=0; int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
effectIt!=enchantment.mEffects.mList.end(); ++effectIt) effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
{ {
const ESM::MagicEffect *magicEffect = params[i].mMultiplier = MWMechanics::getEffectMultiplier(effectIt->mEffectID, actor, actor);
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effectIt->mEffectID);
//const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
}
params[i].mMultiplier = (100.f - resisted) / 100.f;
++i; ++i;
} }
// Note that using the RefID as a key here is not entirely correct. // Note that using the RefID as a key here is not entirely correct.
// Consider equipping the same item twice (e.g. a ring) // Consider equipping the same item twice (e.g. a ring)
// However, permanent enchantments with a random magnitude are kind of an exploit anyway, // However, permanent enchantments with a random magnitude are kind of an exploit anyway,
// so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case. // so it doesn't really matter if both items will get the same magnitude. *Extreme* edge case.
mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = params; mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID] = params;
} }
else
params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID];
int i=0; int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
effectIt!=enchantment.mEffects.mList.end(); ++effectIt) effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i)
{ {
const ESM::MagicEffect *magicEffect = const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
@ -355,7 +347,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
magnitude *= params[i].mMultiplier; magnitude *= params[i].mMultiplier;
if (magnitude) if (magnitude)
mMagicEffects.add (*effectIt, magnitude); mMagicEffects.add (*effectIt, magnitude);
++i;
} }
} }
} }
@ -554,8 +545,9 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
effectIt!=enchantment.mEffects.mList.end(); ++effectIt) effectIt!=enchantment.mEffects.mList.end(); ++effectIt)
{ {
float random = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i].mRandom; const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
magnitude *= params.mMultiplier;
visitor.visit(*effectIt, (**iter).getClass().getName(**iter), magnitude); visitor.visit(*effectIt, (**iter).getClass().getName(**iter), magnitude);
++i; ++i;

View file

@ -26,7 +26,7 @@
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellsuccess.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
@ -2158,7 +2158,7 @@ namespace MWWorld
// Now apply the spell! // Now apply the spell!
// Apply Self portion // Apply Self portion
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, ESM::RT_Self, sourceName); actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, actor, ESM::RT_Self, sourceName);
// Apply Touch portion // Apply Touch portion
// TODO: Distance is probably incorrect, and should it be hardcoded? // TODO: Distance is probably incorrect, and should it be hardcoded?
@ -2166,7 +2166,7 @@ namespace MWWorld
if (!contact.first.isEmpty()) if (!contact.first.isEmpty())
{ {
if (contact.first.getClass().isActor()) if (contact.first.getClass().isActor())
contact.first.getClass().getCreatureStats(contact.first).getActiveSpells().addSpell(selectedSpell, contact.first, ESM::RT_Touch, sourceName); contact.first.getClass().getCreatureStats(contact.first).getActiveSpells().addSpell(selectedSpell, contact.first, actor, ESM::RT_Touch, sourceName);
else else
{ {
// We hit a non-actor, e.g. a door. Only instant effects are relevant. // We hit a non-actor, e.g. a door. Only instant effects are relevant.