Spell success breakpoints
This affects my skill training quite a bit, especially at higher levels since skills are very expensive and the differences between breakpoints become quite large. It's also something that very few players are even aware of.
This happens because of the very imprecise calculation of spell power. All the relevant code is in spl-cast.cc:
- Code:
/**
* Calculate the player's failure rate with the given spell, including all
* modifiers. (Armour, mutations, statuses effects, etc.)
*
* @param spell The spell in question.
* @return A failure rate. This is *not* a percentage - for a human-
* readable version, call _get_true_fail_rate().
*/
int raw_spell_fail(spell_type spell)
{
int chance = 60;
// Don't cap power for failure rate purposes.
chance -= 6 * calc_spell_power(spell, false, true, false);
chance -= (you.intel() * 2);
const int armour_shield_penalty = player_armour_shield_spell_penalty();
dprf("Armour+Shield spell failure penalty: %d", armour_shield_penalty);
chance += armour_shield_penalty;
static const int difficulty_by_level[] =
{
0,
3,
15,
35,
70,
100,
150,
200,
260,
330,
};
const int spell_level = spell_difficulty(spell);
ASSERT_RANGE(spell_level, 0, (int) ARRAYSZ(difficulty_by_level));
chance += difficulty_by_level[spell_level];
int chance2 = chance;
const int chance_breaks[][2] =
{
{45, 45}, {42, 43}, {38, 41}, {35, 40}, {32, 38}, {28, 36},
{22, 34}, {16, 32}, {10, 30}, {2, 28}, {-7, 26}, {-12, 24},
{-18, 22}, {-24, 20}, {-30, 18}, {-38, 16}, {-46, 14},
{-60, 12}, {-80, 10}, {-100, 8}, {-120, 6}, {-140, 4},
{-160, 2}, {-180, 0}
};
for (const int (&cbrk)[2] : chance_breaks)
if (chance < cbrk[0])
chance2 = cbrk[1];
chance2 += get_form()->spellcasting_penalty;
chance2 -= 2 * you.get_mutation_level(MUT_SUBDUED_MAGIC);
chance2 += 4 * you.get_mutation_level(MUT_WILD_MAGIC);
chance2 += 4 * you.get_mutation_level(MUT_ANTI_WIZARDRY);
if (you.props.exists(SAP_MAGIC_KEY))
chance2 += you.props[SAP_MAGIC_KEY].get_int() * 12;
chance2 += you.duration[DUR_VERTIGO] ? 7 : 0;
// Apply the effects of Vehumet and items of wizardry.
chance2 = _apply_spellcasting_success_boosts(spell, chance2);
if (chance2 > 100)
chance2 = 100;
return chance2;
}
int stepdown_spellpower(int power)
{
return stepdown_value(power / 100, 50, 50, 150, 200);
}
int calc_spell_power(spell_type spell, bool apply_intel, bool fail_rate_check,
bool cap_power)
{
int power = 0;
const spschools_type disciplines = get_spell_disciplines(spell);
int skillcount = count_bits(disciplines);
if (skillcount)
{
for (const auto bit : spschools_type::range())
if (disciplines & bit)
power += you.skill(spell_type2skill(bit), 200);
power /= skillcount;
}
power += you.skill(SK_SPELLCASTING, 50);
// Brilliance boosts spell power a bit (equivalent to three
// spell school levels).
if (!fail_rate_check && you.duration[DUR_BRILLIANCE])
power += 600;
if (apply_intel)
power = (power * you.intel()) / 10;
// [dshaligram] Enhancers don't affect fail rates any more, only spell
// power. Note that this does not affect Vehumet's boost in castability.
if (!fail_rate_check)
power = apply_enhancement(power, _spell_enhancement(spell));
// Wild magic boosts spell power but decreases success rate.
if (!fail_rate_check)
{
power *= (10 + 3 * you.get_mutation_level(MUT_WILD_MAGIC));
power /= (10 + 3 * you.get_mutation_level(MUT_SUBDUED_MAGIC));
}
// Augmentation boosts spell power at high HP.
if (!fail_rate_check)
{
power *= 10 + 4 * augmentation_amount();
power /= 10;
}
// Each level of horror reduces spellpower by 10%
if (you.duration[DUR_HORROR] && !fail_rate_check)
{
power *= 10;
power /= 10 + (you.props[HORROR_PENALTY_KEY].get_int() * 3) / 2;
}
power = stepdown_spellpower(power);
const int cap = spell_power_cap(spell);
if (cap > 0 && cap_power)
power = min(power, cap);
return power;
}
chance_breaks then introduces additional breakpoints. There is a very close cubic fit for the points in chance_breaks:
Polynomial fit calculator that you can conveniently paste this right into:
I have tried a local branch with raw_spell_fail using floating point while computing the failure rate (only casting to an int at the end), using a separate power stepdown without the integer division, and with chance_breaks replaced with
(larger constant than the "best" fit to avoid buffing general spell success).
Success rates are always very close to current ones, but with skill breakpoints gone. One great effect of getting rid of chance_breaks is that spells transition from 100% to 99% to 98% etc. etc. earlier, instead of staying at 100% for ages and then quickly falling. A 99% fail spell is still useless but it makes it much easier for unspoiled players to tell how close they are to getting it castable, compared to seeing both level 5 and level 9 spells as 100% fail.
I'm very pleased with the outcome and I think mainline Crawl should do something similar to get rid of these breakpoints. With the chance_breaks replacement I used, level 9 spells are slightly easier to cast than before but that can easily be changed; a nice thing about using an actual formula instead of a nonsense lookup table is that you can easily adjust the coefficients.
- For this message the author duvessa has received thanks: 20
- Implojin, johlstei, Lasty, MainiacJoe, mattlistener, nago, Shard1697, Sprucery, tasonir, ZipZipskins and 10 more users