Spell success breakpoints


Although the central place for design discussion is ##crawl-dev on freenode, some may find it helpful to discuss requests and suggestions here first.

Ziggurat Zagger

Posts: 8786

Joined: Sunday, 5th May 2013, 08:25

Post Tuesday, 11th April 2017, 20:46

Spell success breakpoints

As you may or may not know, spell success only changes at intervals of 0.5 effective spell skill. For example, if your Spellcasting skill is an even number, then having 0.9 Necromancy is exactly the same as having only 0.5 Necromancy for the purposes of Animate Skeleton success. If you have 11 Spellcasting, and 11 Ice Magic, having 12.3 Necromancy is the same as having only 11.5 necromancy for the purposes of Simulacrum success.
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;
}
The main culprits are the terrible resolution of raw_spell_fail and the integer division by 100 in stepdown_spellpower. The latter is what results in skills only applying to spell success in intervals of 0.5 effective level. (For breakpoints in spell power itself, the effect of intelligence etc. makes it more complicated; I just want to talk about spell success for now.)

chance_breaks then introduces additional breakpoints. There is a very close cubic fit for the points in chance_breaks:
Image
Polynomial fit calculator that you can conveniently paste this right into:
  Code:
{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}
if you want to experiment.

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
  Code:
chance2 = 28.0+chance2*0.32+chance2*chance2*0.0016+chance2*chance2*chance2*0.0000038.
(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

Spider Stomper

Posts: 247

Joined: Monday, 10th November 2014, 21:32

Post Tuesday, 11th April 2017, 20:49

Re: Spell success breakpoints

Why are all of Crawl's formulas batfuck crazy nonsense, anyway? Stuff like Airstrike damage is just insane.

For this message the author milski has received thanks: 8
Gigaslurp, Hellmonk, ichbins, Leszczynek, nago, Rast, Speleothing, yesno
User avatar

Abyss Ambulator

Posts: 1194

Joined: Friday, 18th April 2014, 01:41

Post Wednesday, 12th April 2017, 00:05

Re: Spell success breakpoints

milski wrote:Why are all of Crawl's formulas batfuck crazy nonsense, anyway? Stuff like Airstrike damage is just insane.

probably no reason in particular, just adding various hacks like multipliers, floors, additional die etc. to get the desired damage output by whoever was writing the spell
remove food

Ziggurat Zagger

Posts: 5382

Joined: Friday, 25th November 2011, 07:36

Post Wednesday, 12th April 2017, 00:24

Re: Spell success breakpoints

Is this related to/will this fix things like it being impossible to have a spell at 5% fail? I always thought it was odd that it jumps from 6 to 4% fail. Regardless of that part, overall a smoother curve would be wonderful. I didn't realize it only counted .5 skill levels. And I think being able to see spells coming down to 99/98% sooner is helpful. I check my spell success rate quite often as I gain exp if I'm currently training for a new spell.

Ziggurat Zagger

Posts: 8786

Joined: Sunday, 5th May 2013, 08:25

Post Wednesday, 12th April 2017, 00:46

Re: Spell success breakpoints

tasonir wrote:Is this related to/will this fix things like it being impossible to have a spell at 5% fail? I always thought it was odd that it jumps from 6 to 4% fail.
Yes, that's chance_breaks's doing and would be fixed by changing to a smooth formula.
User avatar

Barkeep

Posts: 4435

Joined: Tuesday, 11th January 2011, 12:28

Post Wednesday, 12th April 2017, 04:07

Re: Spell success breakpoints

duvessa wrote:If you have 11 Spellcasting, and 11 Ice Magic, having 12.3 Necromancy is the same as having only 11.5 necromancy for the purposes of Simulacrum success.

Wow. I have been playing Crawl for yonks and I didn't know this was the case; I just knew spell success seemed "kinda jumpy." There's a bunch of XP between 11.5 necro and 12.3 necro.
I am not a very good player. My mouth is a foul pit of LIES. KNOW THIS.

Vaults Vanquisher

Posts: 443

Joined: Thursday, 16th February 2017, 15:23

Post Wednesday, 12th April 2017, 14:39

Re: Spell success breakpoints

njvack wrote:
duvessa wrote:If you have 11 Spellcasting, and 11 Ice Magic, having 12.3 Necromancy is the same as having only 11.5 necromancy for the purposes of Simulacrum success.

Wow. I have been playing Crawl for yonks and I didn't know this was the case; I just knew spell success seemed "kinda jumpy." There's a bunch of XP between 11.5 necro and 12.3 necro.


It's an important deficiency too. The way people get a feel for what they're supposed to do in a game is some variation of gradient descent, so having an apparently continuous parameter depend in a non-smooth, especially "piecewise constant," way on other continuous parameters the player can control hurts discoverability pretty severely.
*Lana Del Rey voice* , video games...

bel

Cocytus Succeeder

Posts: 2184

Joined: Tuesday, 3rd February 2015, 22:05

Post Wednesday, 12th April 2017, 15:01

Re: Spell success breakpoints

The OP is pretty good, but it would have been nice if the axes in the graph had some labels and/or values.

For this message the author bel has received thanks:
duvessa

Halls Hopper

Posts: 72

Joined: Wednesday, 13th April 2016, 01:37

Post Wednesday, 12th April 2017, 15:44

Re: Spell success breakpoints

I also wish there were labelled axes on the graph, but I get the main thrust of what duvessa is saying. It seems absolutely crazy that the system works the way it does.

Ziggurat Zagger

Posts: 5382

Joined: Friday, 25th November 2011, 07:36

Post Wednesday, 26th April 2017, 08:41

Re: Spell success breakpoints

After all my years of crawl, it finally happened.

Image

Thanks duvessa!

For this message the author tasonir has received thanks: 2
nago, Shtopit

Return to Game Design Discussion

Who is online

Users browsing this forum: No registered users and 8 guests

cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.