Research:Magic: Difference between revisions
Line 105: | Line 105: | ||
else: effect is applied with resistance x% | else: effect is applied with resistance x% | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=====Comments===== | =====Comments===== | ||
There is a design problem, where spells which the enemy has a low chance of casting (<25%) but above zero, will increase its resistance to near 100%. Inversely, spells which the enemy has >100% casting chance will weaken its resistance to them. This is counter to normal logic, but any changes affect the viability of normal play. Before the castChance is applied, x can be near 100 if the enemy has willpower >60. The current design makes attacking with weak spells possible, instead of near 100% resisted if the castChance scaling was removed. Fixing resists to scale the other way poses several problems; there is no feedback to the player that spells are resisted due to being too weak. | There is a design problem, where spells which the enemy has a low chance of casting (<25%) but above zero, will increase its resistance to near 100%. Inversely, spells which the enemy has >100% casting chance will weaken its resistance to them. This is counter to normal logic, but any changes affect the viability of normal play. Before the ''castChance'' is applied, x can be near 100 if the enemy has willpower >60. The current design makes attacking with weak spells possible, instead of near 100% resisted if the ''castChance'' scaling was removed. Fixing resists to scale the other way poses several problems; there is no feedback to the player that spells are resisted due to being too weak. | ||
Has an interesting situation, where spells with magnitude _do_ take resistance from willpower but at a very low rate (0.01x due to lack of rescaling) than would be expected, making willpower resistance almost useless for that class of spells. | Has an interesting situation, where spells with magnitude _do_ take resistance from willpower but at a very low rate (0.01x due to lack of rescaling) than would be expected, making willpower resistance almost useless for that class of spells. | ||
The way resistance affects the final attribute, is that magnitude is stored untouched, resistance is stored with the active effect, and resistance is applied every time the spell has an application tick. The outward effect is that spell effect tooltips list the original magnitude, without factoring resistance. | The way resistance affects the final attribute, is that magnitude is stored untouched, resistance is stored with the active effect, and resistance is applied every time the spell has an application tick. The outward effect is that spell effect tooltips list the original magnitude, without factoring resistance. | ||
====Spell application types "Disease" or "Blight"==== | ====Spell application types "Disease" or "Blight"==== |
Revision as of 19:46, 30 July 2014
Spell Casting
Chance of successful spell cast
Actions affected | On cast attempt, on AI spell evaluation, on UI update or in spellmaking |
Description | Uses common term fatigueTerm. |
Implementation status | Implemented |
Analysis status | Verified |
if actor is silenced: return 0
if <flag> and current magicka < magicka cost: return 0 # see comments
if spell.castingType not (spell or power): return 100
if castingType is power:
check power timeout for this spell (24 hours from previous use)
if power is available: return 100 else return 0
if castingType is spell:
if spell is flagged always succeeds: return 100
y = FLOAT_MAX
for each effect in spell:
x = effect.duration
if not effect.magicEffect.flags & UNCAPPED_DAMAGE: x = max(1, x)
x *= 0.1 * effect.magicEffect.baseMagickaCost
x *= 0.5 * (effect.magnitudeMin + effect.magnitudeMax)
x += effect.radius * 0.05 * effect.magicEffect.baseMagickaCost
if effect.rangeType & CAST_TARGET: x *= 1.5
x *= fEffectCostMult
s = 2 * actor.skill[effect.magicEffect.school.associatedSkillId]
if (s - x) < y:
y = s - x
effectiveSchool = effect.magicEffect.school
lowestSkill = s
# castBonus is the stat modified by the Sound magic effect;
# it behaves in the same way as other hidden stats, that is positive for a bonus, and negative for a penalty
castChance = (lowestSkill - spellCost + actor.castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * fatigueTerm
if godmode is on: castChance = 100
return castChance, effectiveSchool
Comments
The cast chance calculation is used in multiple places, some where magicka use may not be important, so there is a <flag>, which is normally true for casting/AI use and the inventory spell list, and false for all other UI uses of spell chance. The function returns the weakest school used out of all the effects, which is the skill that will gain progress when the spell is cast. Note that spells that are marked "always succeed" do not seem to contribute to skill progress.
Cast cost
Actions affected | On cast attempt |
Description | Magicka and fatigue costs of casting. |
Implementation status | Implemented, bug corrected |
Analysis status | Verified, but fatigue cost contains a bug |
Casting cost is an independent variable in the spell data. For spellmaker generated spells, refer to Spell merchant.
magicka loss = spell.magickaCost
Failed casts still incur all costs.
Casting incurs a fatigue hit.
fatigue loss = magickaCost * (fFatigueSpellBase * normalizedEncumbrance * fFatigueSpellMult)
which is how Morrowind behaves but is clearly incorrect; a fixed formula is
fatigue loss = magickaCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult)
The GMST fFatigueSpellCostMult is unused.
Magic resistance
Spell application type "Magic"
Actions affected | On magic effect hit, applies seperately for each effect |
Description | Uses resistance effect attributes. |
Implementation status | Implemented |
Analysis status | Surrounding mechanics still need evaluating |
i = look up corresponding resistance effect attribute for effect
if effect is not linked to a resistance effect attribute: effect cannot be resisted
resistance = get actor effect attribute i
castChance = enemy chance of casting spell (uncapped)
x = (willpower + 0.1 * luck) * fatigueTerm
if castChance > 0: x *= 50 / castChance # design bug
roll float [0..100]
if effect.flags & NO_MAGNITUDE: roll -= resistance
if x <= roll:
x = 0
else:
if effect.flags & NO_MAGNITUDE:
x = 100
else:
x = roll / min(x, 100)
x = min(x + resistance, 100)
if x > 100: effect is fully resisted, show message, remove effect
else: effect is applied with resistance x%
Comments
There is a design problem, where spells which the enemy has a low chance of casting (<25%) but above zero, will increase its resistance to near 100%. Inversely, spells which the enemy has >100% casting chance will weaken its resistance to them. This is counter to normal logic, but any changes affect the viability of normal play. Before the castChance is applied, x can be near 100 if the enemy has willpower >60. The current design makes attacking with weak spells possible, instead of near 100% resisted if the castChance scaling was removed. Fixing resists to scale the other way poses several problems; there is no feedback to the player that spells are resisted due to being too weak.
Has an interesting situation, where spells with magnitude _do_ take resistance from willpower but at a very low rate (0.01x due to lack of rescaling) than would be expected, making willpower resistance almost useless for that class of spells.
The way resistance affects the final attribute, is that magnitude is stored untouched, resistance is stored with the active effect, and resistance is applied every time the spell has an application tick. The outward effect is that spell effect tooltips list the original magnitude, without factoring resistance.
Spell application types "Disease" or "Blight"
Actions affected | On spell hit, applies once for entire spell |
Description | |
Implementation status | Implemented |
Analysis status | Surrounding mechanics still need evaluating |
x = actor effect attribute "Resist Disease" or "Resist Blight"
roll 100, if roll <= x then fully resist the disease
Item Charge
Using enchanted items
Actions affected | On enchanted item use |
Description | Charge consumed on use depends on skill. |
Implementation status | Implemented |
Analysis status | Verified; but a poorly scaling mechanic |
x = 0.01 * (110 - pcEnchant)
charge used = int(x * enchant base charge cost)
This makes items last 10 times longer at 100 enchant compared to 10 enchant, making it scale a little too well. MCP uses x = 0.025 * (400 - pcEnchant).
Natural recharge
Actions affected | On game tick |
Description | Enchanted items may gain charge over time. |
Implementation status | Implemented |
Analysis status | Verified |
Every item in the player's inventory charges by tickTime * fMagicItemRechargePerSecond. Items anywhere else do not charge.
Soulgem charging
Refer to Player Craft Skills.
Magic effects
Spells are combinations of one or more effects, which have associated code specific to the effect, and properties which cover cast ranges, resistances and other generalized behaviours that may be shared by a small set of effects.
Effect attributes
All actors have "hidden" attributes that effects apply to, to allow effects to stack, have resistance applied and counteract each other. These attributes are saved with actors, appearing in the actor data in the same order. All effect attributes are applied and stored as integers. Resists and weaknesses are unified under the Resist- attributes, with resistances being positive and weaknesses being negative. Non-magnitude spells have an effective attribute of 1 when active.
Some attributes provide resistance values vs magic effects when calculating magic mitigation. These resistances only apply in certain cases, check magic resistance.
Index | Name | Provides resistance against magic effects |
---|---|---|
0 | Fortify Attack | |
1 | Sanctuary | Disintegrate * |
2 | Resist Magicka | Drain *, Damage *, Absorb *, Weakness to *, Burden, Charm, Silence, Blind, Sound, Calm, Frenzy, Demoralize, Rally, Turn Undead |
3 | Resist Fire | Fire |
4 | Resist Frost | Frost |
5 | Resist Shock | Shock |
6 | Resist Common Disease | Vampirism |
7 | Resist Blight Disease | |
8 | Resist Corprus | Corprus |
9 | Resist Poison | Poison |
10 | Resist Paralysis | Paralysis |
11 | Chameleon | |
12 | Resist Normal Weapons | |
13 | Water Breathing | |
14 | Water Walking | |
15 | Swift Swim | |
16 | Jump | |
17 | Levitate | |
18 | Shield | |
19 | Sound | |
20 | Silence | |
21 | Blind | |
22 | Paralyze | |
23 | Invisibility | |
24 | Fight | |
25 | Flee | |
26 | Hello | |
27 | Alarm |
Effect flags
These are fixed properties of magic effects. Obtained from the code, these are not affected by mods.
Bit | Enum name | Status | Description |
---|---|---|---|
0x1 | TARGET_SKILL | implemented | Affects a specific skill, which is specified elsewhere in the effect structure. |
0x2 | TARGET_ATTR | implemented | Affects a specific attribute, which is specified elsewhere in the effect structure. |
0x4 | NO_DURATION | implemented | Has no duration. Only runs effect once on cast. |
0x8 | NO_MAGNITUDE | implemented | Has no magnitude. |
0x10 | NEGATIVE | implemented | Counts as a negative effect. Interpreted as useful for attack, and is treated as a bad effect in alchemy. |
0x20 | CONTINUOUS_VFX | implemented | The effect's hit particle VFX repeats for the full duration of the spell, rather than occuring once on hit. |
0x40 | CAST_SELF | implemented | Allows range - cast on self. |
0x80 | CAST_TOUCH | implemented | Allows range - cast on touch. |
0x100 | CAST_TARGET | implemented | Allows range - cast on target. |
0x200 - 0x800 | not started | Unverified. Appears unused. | |
0x1000 | UNCAPPED_DAMAGE | not started | Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second. |
0x2000 | not started | Unverified. Appears unused. | |
0x4000 | NON_RECASTABLE | not started | Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target. |
0x8000 | not started | Unverified. Appears to be flagged on summon daedra effects. | |
0x10000 | UNREFLECTABLE | implemented | Cannot be reflected, the effect always lands normally. |
0x20000 | CASTER_LINKED | implemented | Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. |
Flags for all effects:
int effectFlags[] = { 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0, 0x11d0, 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0, 0x1f0, 0x11d2, 0x11f0, 0x11d0, 0x11d0, 0x11d1, 0x1d2, 0x1f0, 0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8, 0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8, 0x11d0, 0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11c4, 0x111b8, 0x1040, 0x104c, 0x104c, 0x104c, 0x104c, 0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc, 0x1cc, 0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2, 0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190, 0x20190, 0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x1c0, 0x11190, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1188, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40, 0x11c8, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048 };