Bug:
The fizzle rate check is capping skill-based spell casting difficulty at Level 49 values. A Level 90 character casting a Level 90 spell will be fizzling basically only as often if they were only casting a Level 49 spell.
Mitigating Factor:
Classic servers with a Level Cap of 50 won't really be affected by this, but the problem gets bigger and bigger the more levels a character gets above that on more modern/customized servers.
Full Report:
I happened to be looking over the fizzle code to see how fizzles are rolled, and am scratching my head over how the math came out as I mentally walked through it.
I wanted to see what odds are like for fizzles on a worst-case scenario character with very low skill for the level spell they're casting.
It looks like the code is
far too lenient in these cases!
The code is in spells.cpp:
Code:
...
Line 675: bool Client::CheckFizzle(uint16 spell_id)
...
I won't paste the full function here, as I'll reference its relevant lines as I go.
First, to get a few things out of the way, we'll ignore the GM test, specializations, and Mastery of the Past style AA's that are checked, as we're assuming our character doesn't have any of those benefits to fall back on.
This leaves us with the following walkthrough. Let's assume we've got a character trying to cast a Level 90 spell with absolutely 0 skill in the spell's type. Should be eeeeasily maximum fizzle chance, right? Like, crazy in the red. Let's find out.
Code:
par_skill = spells[spell_id].classes[GetClass()-1] * 5 - 10;//IIRC even if you are lagging behind the skill levels you don't fizzle much
if (par_skill > 235)
par_skill = 235;
par_skill += spells[spell_id].classes[GetClass()-1]; // maximum of 270 for level 65 spell
par_skill = (Spell Level 90) * 5 - 10 = 450 - 10 = 440
if (par_skill > 235) par_skill = 235.
^ This. This right here. We'll get back to it. Caps maximum spell level checking at Level 49 (49 * 5 - 10 = 235).
par_skill += (Spell Level 90) = 235 + 90 = 325.
So, absolute maximum par_skill is 325 for a Level 90 spell, thanks to the L49 cap above.
Code:
act_skill = GetSkill(spells[spell_id].skill);
act_skill += GetLevel(); // maximum of whatever the client can cheat
actual skill = character's skill + character's level.
Sure, we'll give them a 1-point leeway for each level they've attained. Works for me.
Code:
float diff = par_skill + static_cast<float>(spells[spell_id].basediff) - act_skill;
Note: Spell base difficulty, aka fizzle adjust, is a field for each spell in spells_new that ranges up to a max of around 120 (24 levels worth of skill points) for some Level 90 spells, aside from a couple outliers (Direwind Cliffs ports) in the 400s for whatever reason.
Difficulty = (325 at Level 90) + (spell.basediff, 120 at Level 90) - actual_skill (minimum 90 at Level 90)
So, our Difficulty = 325 + 120 - 90 = 355
Code:
// if you have high int/wis you fizzle less, you fizzle more if you are stupid
if(GetClass() == BARD)
{
diff -= (GetCHA() - 110) / 20.0;
}
else if (GetCasterClass() == 'W')
{
diff -= (GetWIS() - 125) / 20.0;
}
else if (GetCasterClass() == 'I')
{
diff -= (GetINT() - 125) / 20.0;
}
Minus one difficulty point (0.2% fizzle chance) for every 20 points of WIS/INT/CHA(huh?) over an arbitrary threshold, depending on class.
If you have 325 INT, you're looking at ((325 - 125) / 20) = 10. A 2% improvement in fizzle rates for your stats. Sounds kinda low for that high of a stat, but whatev. Couldn't say how it compares to Live. Heh.
We're assuming our worst-case scenario character is freshly rezzed and naked, so our difficulty is still 355. Would be 345 if he had his gear on and registered 325 INT.
Code:
// base fizzlechance is lets say 5%, we can make it lower for AA skills or whatever
float basefizzle = 10;
float fizzlechance = basefizzle - specialize + diff / 5.0;
// always at least 1% chance to fail or 5% to succeed
fizzlechance = fizzlechance < 1 ? 1 : (fizzlechance > 95 ? 95 : fizzlechance);
// ...
float fizzle_roll = zone->random.Real(0, 100);
fizzlechance = 10 - 0 + (355/5) = 10 + 71 = 81 %
Umm...
So... our absolute worst-case scenario of a fully naked Level 90 character with 0 skill, 0 AAs, and 0 specialization, casting a Level 90 spell with normal +120 base difficulty (aka fizzle adjust) per spells_new... is an 81% fizzle rate.
Every fifth time casting the spell on average, it'll succeed.
That really doesn't sound right to me at all. It should be easily bottoming out the 5% chance to succeed check above.
It seems pretty clear to me that the problem is the Level 49 / 235 Difficulty cap near the top.
If we remove that and walk through the math again...
par_skill = (Spell Level 90) * 5 - 10. 90 * 5 = 450, 450 - 10 = 440.
// Aborting this check -> if (par_skill > 235) par_skill = 235.
par_skill += (Spell Level 90) = 440 + 90 = 530.
actual skill = character's skill + character's level.
Difficulty = (530 at Level 90) + (spell.basediff, 120 at Level 90) - actual_skill (minimum 90 at Level 90 with 0 skill)
So, our new Difficulty = 530 + 120 - 90 = 560
fizzlechance = 10 - 0 + (560/5) = 10 + 112 = 122 %
This gets scaled down to the 95% cap to give the player a 1:20 chance to cast successfully no matter what.
This makes perfect sense to me. 122% (effective 95%) chance to fizzle a spell they have absolutely no chance of casting logically totally works for me.
Why is the 235 check in there? It seems like it should be removed.
Just thought I'd share my head scratch moment and see about getting an oversight corrected for higher-level casting.
inb4 "NOOO, fizzles r nerfed!"