|
|
|
@ -46,11 +46,6 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
|
|
|
|
|
{
|
|
|
|
|
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
|
|
|
|
|
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
float y = std::numeric_limits<float>::max();
|
|
|
|
|
float lowestSkill = 0;
|
|
|
|
|
|
|
|
|
@ -80,6 +75,13 @@ namespace MWMechanics
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
|
|
|
|
|
|
|
|
|
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
|
|
|
|
|
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude()&& !godmode)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (spell->mData.mType == ESM::Spell::ST_Power)
|
|
|
|
|
return stats.getSpells().canUsePower(spell) ? 100 : 0;
|
|
|
|
|
|
|
|
|
@ -89,6 +91,11 @@ namespace MWMechanics
|
|
|
|
|
if (spell->mData.mFlags & ESM::Spell::F_Always)
|
|
|
|
|
return 100;
|
|
|
|
|
|
|
|
|
|
if (godmode)
|
|
|
|
|
{
|
|
|
|
|
return 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
|
|
|
|
|
|
|
|
|
|
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
|
|
|
@ -709,14 +716,19 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
mStack = false;
|
|
|
|
|
|
|
|
|
|
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
|
|
|
|
|
|
|
|
|
// Check if there's enough charge left
|
|
|
|
|
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
|
|
|
{
|
|
|
|
|
const int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), mCaster);
|
|
|
|
|
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), mCaster);
|
|
|
|
|
|
|
|
|
|
if (item.getCellRef().getEnchantmentCharge() == -1)
|
|
|
|
|
item.getCellRef().setEnchantmentCharge(static_cast<float>(enchantment->mData.mCharge));
|
|
|
|
|
|
|
|
|
|
if (godmode)
|
|
|
|
|
castCost = 0;
|
|
|
|
|
|
|
|
|
|
if (item.getCellRef().getEnchantmentCharge() < castCost)
|
|
|
|
|
{
|
|
|
|
|
if (mCaster == getPlayer())
|
|
|
|
@ -746,8 +758,10 @@ namespace MWMechanics
|
|
|
|
|
if (mCaster == getPlayer())
|
|
|
|
|
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1);
|
|
|
|
|
}
|
|
|
|
|
if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
|
|
|
|
|
if (enchantment->mData.mType == ESM::Enchantment::CastOnce && !godmode)
|
|
|
|
|
{
|
|
|
|
|
item.getContainerStore()->remove(item, 1, mCaster);
|
|
|
|
|
}
|
|
|
|
|
else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes)
|
|
|
|
|
{
|
|
|
|
|
if (mCaster == getPlayer())
|
|
|
|
@ -797,44 +811,50 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
int school = 0;
|
|
|
|
|
|
|
|
|
|
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
|
|
|
|
|
|
|
|
|
if (mCaster.getClass().isActor() && !mAlwaysSucceed)
|
|
|
|
|
{
|
|
|
|
|
school = getSpellSchool(spell, mCaster);
|
|
|
|
|
|
|
|
|
|
CreatureStats& stats = mCaster.getClass().getCreatureStats(mCaster);
|
|
|
|
|
|
|
|
|
|
// Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss)
|
|
|
|
|
static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat();
|
|
|
|
|
static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat();
|
|
|
|
|
DynamicStat<float> fatigue = stats.getFatigue();
|
|
|
|
|
const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
|
|
|
|
|
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
|
|
|
|
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
// Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss)
|
|
|
|
|
static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat();
|
|
|
|
|
static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat();
|
|
|
|
|
DynamicStat<float> fatigue = stats.getFatigue();
|
|
|
|
|
const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
|
|
|
|
|
|
|
|
|
|
bool fail = false;
|
|
|
|
|
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
|
|
|
|
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);
|
|
|
|
|
|
|
|
|
|
// Check success
|
|
|
|
|
if (!(mCaster == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()))
|
|
|
|
|
{
|
|
|
|
|
float successChance = getSpellSuccessChance(spell, mCaster);
|
|
|
|
|
if (Misc::Rng::roll0to99() >= successChance)
|
|
|
|
|
bool fail = false;
|
|
|
|
|
|
|
|
|
|
// Check success
|
|
|
|
|
if (!(mCaster == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()))
|
|
|
|
|
{
|
|
|
|
|
if (mCaster == getPlayer())
|
|
|
|
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
|
|
|
|
|
fail = true;
|
|
|
|
|
float successChance = getSpellSuccessChance(spell, mCaster);
|
|
|
|
|
if (Misc::Rng::roll0to99() >= successChance)
|
|
|
|
|
{
|
|
|
|
|
if (mCaster == getPlayer())
|
|
|
|
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
|
|
|
|
|
fail = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fail)
|
|
|
|
|
{
|
|
|
|
|
// Failure sound
|
|
|
|
|
static const std::string schools[] = {
|
|
|
|
|
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
|
|
|
|
};
|
|
|
|
|
if (fail)
|
|
|
|
|
{
|
|
|
|
|
// Failure sound
|
|
|
|
|
static const std::string schools[] = {
|
|
|
|
|
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
|
|
|
|
sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
|
|
|
|
|
return false;
|
|
|
|
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
|
|
|
|
sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A power can be used once per 24h
|
|
|
|
@ -1042,6 +1062,8 @@ namespace MWMechanics
|
|
|
|
|
|
|
|
|
|
bool receivedMagicDamage = false;
|
|
|
|
|
|
|
|
|
|
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
|
|
|
|
|
|
|
|
|
switch (effectKey.mId)
|
|
|
|
|
{
|
|
|
|
|
case ESM::MagicEffect::DamageAttribute:
|
|
|
|
@ -1064,21 +1086,34 @@ namespace MWMechanics
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude);
|
|
|
|
|
break;
|
|
|
|
|
case ESM::MagicEffect::DamageHealth:
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
|
|
|
|
|
break;
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
|
|
|
|
|
}
|
|
|
|
|
case ESM::MagicEffect::DamageMagicka:
|
|
|
|
|
case ESM::MagicEffect::DamageFatigue:
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESM::MagicEffect::AbsorbHealth:
|
|
|
|
|
if (magnitude > 0.f)
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
|
|
|
|
|
break;
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
if (magnitude > 0.f)
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
|
|
|
|
|
}
|
|
|
|
|
case ESM::MagicEffect::AbsorbMagicka:
|
|
|
|
|
case ESM::MagicEffect::AbsorbFatigue:
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESM::MagicEffect::DisintegrateArmor:
|
|
|
|
@ -1123,9 +1158,13 @@ namespace MWMechanics
|
|
|
|
|
if (weather > 1)
|
|
|
|
|
damageScale *= fMagicSunBlockedMult;
|
|
|
|
|
|
|
|
|
|
adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);
|
|
|
|
|
if (magnitude * damageScale > 0.f)
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);
|
|
|
|
|
if (magnitude * damageScale > 0.f)
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1134,8 +1173,12 @@ namespace MWMechanics
|
|
|
|
|
case ESM::MagicEffect::FrostDamage:
|
|
|
|
|
case ESM::MagicEffect::Poison:
|
|
|
|
|
{
|
|
|
|
|
adjustDynamicStat(creatureStats, 0, -magnitude);
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
if (!godmode)
|
|
|
|
|
{
|
|
|
|
|
adjustDynamicStat(creatureStats, 0, -magnitude);
|
|
|
|
|
receivedMagicDamage = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|