Im trying to figure out some way to implement a formula to calculate dodge and evasion in my game in some reliable way. The factor involved are dodge/evasion skill levels, of course, the target's dexterity and the attacker's dexterity and whatever offensive skill rules the specific attack. The idea is no matter how high the level difference, the attacker always have at least a 15-20% chance to hit and viceversa. Although the game doesnt limit levels, Im considering that average player wont be reaching more than 20-30 points in each skill or character stats, with some crazy people with no life or extreme builds reaching maybe 50 points. The second formula deals with lockpick,and Im trying to do something like Fallout 2: no matter how hard the lock was, if you try several times you had a very remote chance to open it (and also break it). Lockpick depends on Suberfuge skill and dexterity, with same estimated values (1-3x points). What can you suggest me for this?

  • \$\begingroup\$ Are you saying that you want the attacker to always have at least a 15-20% or are you saying that's what you currently have & you want something else? \$\endgroup\$
    – Pikalek
    Commented Mar 9, 2023 at 20:05
  • \$\begingroup\$ Thas what I want \$\endgroup\$ Commented Mar 10, 2023 at 23:03

1 Answer 1


Here's a Google Sheets document where you can play with these formulas - see the "Diminishing Returns" tab:

Screenshot of Google Sheets workbook showing graph

You can adjust the min and max output values, as well as provide a sample input-output pair that the curve should pass through. The sheet then calculates three possible curves meeting those criteria, and plots them so you can compare their behaviour.

Expanding on the answer here: Sliding scale for secondary stats, we can use any of a family of functions that give diminishing returns, and just rescale them into our desired range.

Let's say we have some function DiminishingReturn(float x) on the interval [0, infinity) that has these properties:

  • DiminishingReturn(0) == 0

  • As x approaches infinity, DiminishingReturn(x) approaches 1

  • DiminishingReturn(x) is an increasing function (x2 > x1 ==> DiminishingReturn(x2) > DiminishingReturn(x1)

Here are a few ways we could implement that function, corresponding to the three curves shown in the Google Sheets example above - pick whichever of the return statements looks best to you:

float DiminishingReturn(float x) {
    // hyperbolic
    return 1 - 1/(x + 1);

    // arc tangent
    return atan(x) * 2/PI;

    // exponential
    return 1 - exp(-x);

Then we can turn that into a stat output with a desired minimum and maximum like so:

float MinMaxStat(float min, float max, float input) {
    // Don't let the input drop below 0.
    input = max(0, input);

    return min + (max - min) * DiminishingReturn(input);

Now we can implement the chance to hit / pick lock like so:

float netAttackStat = attacker.level + attacker.dex + getAttackBonus(attacker);
netAttackStat -= defender.level + defender.dex;
float hitChance = MinMaxStat(0.15f, 1.0f, netAttackStat * 1.0f);

float netPickingStat = lockPicker.level + lockPicker.dex + lockPicker.subterfuge;
netPickingStat -= lock.difficulty;
float success = MinMaxStat(0.01f, 1.0f, netPickingStat * 1.0f);

So here higher levels stats by the attacking/picking player drive you rightward up the curve, while higher stats of the defender/lock drive you leftward down the curve, and the output success is always clamped between the given min and max. You can change the * 1.0 multipliers to change the slope: how large a success chance increase you get from a small change in the input stat. The "Rate Coefficient" line in the Google Sheets doc shows the formula to calculate the appropriate multiplier to pass through a given input-output pair.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .