Exp formula

Started by Locke Cole, May 08, 2006, 11:34 AM

Previous topic - Next topic

Locke Cole

Parameter usage: The level parameter is zero based, so for level 1 you pass 0, for level 50 you pass 49. The chart parameter assumes a value of 100. So for a Human Warrior (100% chart) you'd pass 0. For a Nekojin Mystic (300% chart) you'd pass 200.

So I spent a lil while tonight reverse engineering the MajorMUD exp formula. Now here's the thing to realize, there's actually two formulas. There's the old classic WCC formula (which was in use when WCC sold the game to Metro), and there's the bastardized crap Metro put together to get around the 4.2 billion exp cap (better known to people with programming background as the 32-bit integer limit). I've reverse engineered the classic WCC formula, not the newer bastardized formula (which is itself broken for cases where the difference between levels exceeds 4.2 billion). Anyways. Here's the code (this is in C#)

static uint[] expModTable = {
               1,  1, 40, 20, 44, 24, 44, 24, 48, 28, 48, 28, 52,
              32, 52, 32, 56, 36, 56, 36, 60, 40, 60, 40, 65, 45,
              65, 45, 70, 50, 70, 50, 75, 55, 50, 40, 50, 40, 50,
              40, 50, 40, 50, 40, 50, 40, 50, 40, 50, 40, 23, 20,
              23, 20, 23, 20, 23, 20, 23, 20, 23, 20, 23, 20 };

static uint calcExpNeededOld(uint level, uint chart)
{
    uint res = 0,
        i = 0,
        scalemul = 0,
        scalediv = 0;

    res = ((chart * 1000) + 100000) / 100;

    while (i < level)
    {
        if (i < 26)
        {
            scalemul = expModTable[i * 2];
            scalediv = expModTable[(i * 2) + 1];
        }
        else if (i > 60)
        {
            scalemul = 105;
            scalediv = 100;
        }
        else if (i > 52)
        {
            scalemul = 110;
            scalediv = 100;
        }
        else
        {
            scalemul = 115;
            scalediv = 100;
        }

        if ((res <= (res * scalemul)) && (((res * scalemul) / scalemul) == res))
        {
            res = (res * scalemul) / scalediv;
        }
        else
        {
            res = res / 100;
            if ((res <= (res * scalemul)) && (((res * scalemul) / scalemul) == res))
                res = (res * scalemul) / scalediv;
            else
                res = (((res / 100) * scalemul) / scalediv) * 100;
            res = res * 100;
        }
        i++;
    }

    return res;
}

I've tested this and the code works fine. You can extend this code to handle 64-bit values (for higher levels) by changing the return type and the res variable to ulong. Note that when you extend it to 64-bit values you get slightly different results for levels above 37 (only a few hundred exp, if that).

For the sake of people not wanting to throw this at Visual C# and try it, here's a table showing the values for a 300% exp chart (Gaunt Ranger, Nekojin Mystic, etc).


LevelExp needed
13,000
23,000
36,000
411,000
520,166
634,570
759,262
896,300
9156,487
10243,424
11378,659
12567,988
13851,982
141,230,640
151,777,591
162,488,627
173,484,077
184,751,014
195,938,767
207,423,458
219,279,322
2211,599,152
2314,498,940
2418,123,675
2522,654,593
2628,318,241
2732,565,977
2837,450,873
2943,068,503
3049,528,778
3156,958,094
3265,501,808
3375,327,079
3486,626,140
3599,620,061
36114,563,070
37131,747,530
38151,509,659
39174,236,107
40200,371,523
41230,427,251
42264,991,338
43304,740,038
44350,451,043
45403,018,699
46463,471,503
47532,992,228
48612,941,062
49704,882,221
50810,614,554
51932,206,737
521,072,037,747
531,232,843,409
541,417,769,920
551,559,546,912
561,715,501,603
571,887,051,763
582,075,756,939
592,283,332,632
602,511,665,895
612,762,832,484
623,039,115,732
633,191,071,518
643,350,625,093
653,518,156,347
663,694,064,164
673,878,767,372
684,072,705,740
694,276,341,027
704,490,158,078
714,714,665,981
724,950,399,280
735,197,919,244
745,457,815,206
755,730,705,966
766,017,241,264
776,318,103,327
786,634,008,493
796,965,708,917
807,313,994,362

Comments welcome. =)

DeathCow

You mean I shouldn't need 1.3 zillion exp <and no i'm not joking I need 1.3 zillion exp> to level?

Locke Cole

Here's the same table as in my previous message, but for a 100% exp chart (Human Warrior)

LevelExp needed
11,000
21,000
32,000
43,666
56,721
611,521
719,750
832,093
952,151
1081,123
11126,191
12189,286
13283,929
14410,119
15592,394
16829,351
171,161,091
181,583,305
191,979,131
202,473,913
213,092,391
223,865,488
234,831,860
246,039,825
257,549,781
269,437,226
2710,852,809
2812,480,730
2914,352,839
3016,505,764
3118,981,628
3221,828,872
3325,103,202
3428,868,682
3533,198,984
3638,178,831
3743,905,655
3850,491,503
3958,065,228
4066,775,012
4176,791,263
4288,309,952
43101,556,444
44116,789,910
45134,308,396
46154,454,655
47177,622,853
48204,266,280
49234,906,222
50270,142,155
51310,663,478
52357,262,999
53410,852,448
54472,480,315
55519,728,346
56571,701,180
57628,871,298
58691,758,427
59760,934,269
60837,027,695
61920,730,464
621,012,803,510
631,063,443,685
641,116,615,869
651,172,446,662
661,231,068,995
671,292,622,444
681,357,253,566
691,425,116,244
701,496,372,056
711,571,190,658
721,649,750,190
731,732,237,699
741,818,849,583
751,909,792,062
762,005,281,665
772,105,545,748
782,210,823,035
792,321,364,186
802,437,432,395

Locke Cole

By popular demand (heh), here's the code for Metro's bastardized exp formula. Note that it uses the same array as in the previous code.

static uint[] expModTable = {     
               1,  1, 40, 20, 44, 24, 44, 24, 48, 28, 48, 28, 52,
              32, 52, 32, 56, 36, 56, 36, 60, 40, 60, 40, 65, 45,
              65, 45, 70, 50, 70, 50, 75, 55, 50, 40, 50, 40, 50,
              40, 50, 40, 50, 40, 50, 40, 50, 40, 50, 40, 23, 20,
              23, 20, 23, 20, 23, 20, 23, 20, 23, 20, 23, 20 };

static ulong calcExpNeeded(uint level, uint chart)
{
    uint res = 0,       // 32-bit intermediate result
        b = 0,          // billions count
        i = 0,
        j = 0,
        scalemul = 0,
        scalediv = 0;

    res = ((chart * 1000) + 100000) / 100;

    while (i < level)
    {
        if (i < 26)
        {
            scalemul = expModTable[i * 2];
            scalediv = expModTable[(i * 2) + 1];
        }
        else if (i < 54)
        {
            scalemul = 115;
            scalediv = 100;
        }
        else if (i < 57)
        {
            scalemul = 109;
            scalediv = 100;
        }
        else if (i < 59)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 63)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 65)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 67)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 69)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 70)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else
        {
            scalemul = 108;
            scalediv = 100;
        }

        j = (res * scalemul);

        if ((res <= j) && ((j / scalemul) == res))
        {
            res = (j / scalediv);
        }
        else
        {
            int x = 0;

            while ((res > j) || ((j / scalemul) != res))
            {
                x++;
                res = (res / 100);
                j = (res * scalemul);
            }

            if (x <= 1)
            {
                res = (j / scalediv);
            }
            else if (x <= 2)
            {
                res = (((res * scalemul) * 100) / scalediv);
            }
            else
            {
                res = (((res * scalemul) * 100) / scalediv);
            }
            while (x > 0)
            {
                res = (res * 100);
                x--;
            }
        }

        j = (b * scalemul) * 1000000;

        while (j >= 1000000000)
        {
            j = (j - 1000000000);
            b++;
        }

        res = (res + j);

        while (res >= 1000000000)
        {
            res = (res - 1000000000);
            b++;
        }

        i++;
    }

    return (((ulong)b * 1000000000) + res);
}

Locke Cole

Okay, as some of you may or may not be aware, Metro's bastardized exp formula suffers from problems at higher levels. Namely, once the difference between levels exceeds 4.2 billion, it rolls over (so you'll go back to only needing 300+ million between levels, until it creeps back up and rolls over again, etc, etc.). That bug is present in the code I pasted above (for the sake of authenticity and perfectly duplicating MMUD down to it's bugs).

The code below, implements Metro's formula with a fix for the 4.2 billion exp between level rollover issue (otherwise known as the "haven't we been here before" bug). It's a minor change on the above code, and really pretty simple, but I figured I'd provide it for completeness sake. If for some reason Metro's formula is used, I suggest using this fixed version (otherwise you'll have issues with characters above level 88).

static uint[] expModTable = {     
               1,  1, 40, 20, 44, 24, 44, 24, 48, 28, 48, 28, 52,
              32, 52, 32, 56, 36, 56, 36, 60, 40, 60, 40, 65, 45,
              65, 45, 70, 50, 70, 50, 75, 55, 50, 40, 50, 40, 50,
              40, 50, 40, 50, 40, 50, 40, 50, 40, 50, 40, 23, 20,
              23, 20, 23, 20, 23, 20, 23, 20, 23, 20, 23, 20 };

static ulong calcExpNeededFixed(uint level, uint chart)
{
    ulong k = 0;        // 64-bit intermediate result
    uint res = 0,       // 32-bit intermediate result
        b = 0,          // billions count
        i = 0,
        j = 0,
        scalemul = 0,
        scalediv = 0;

    res = ((chart * 1000) + 100000) / 100;

    while (i < level)
    {
        if (i < 26)
        {
            scalemul = expModTable[i * 2];
            scalediv = expModTable[(i * 2) + 1];
        }
        else if (i < 54)
        {
            scalemul = 115;
            scalediv = 100;
        }
        else if (i < 57)
        {
            scalemul = 109;
            scalediv = 100;
        }
        else if (i < 59)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 63)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 65)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 67)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 69)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else if (i < 70)
        {
            scalemul = 108;
            scalediv = 100;
        }
        else
        {
            scalemul = 108;
            scalediv = 100;
        }

        j = (res * scalemul);

        if ((res <= j) && ((j / scalemul) == res))
        {
            res = (j / scalediv);
        }
        else
        {
            int x = 0;

            while ((res > j) || ((j / scalemul) != res))
            {
                x++;
                res = (res / 100);
                j = (res * scalemul);
            }

            if (x <= 1)
            {
                res = (j / scalediv);
            }
            else if (x <= 2)
            {
                res = (((res * scalemul) * 100) / scalediv);
            }
            else
            {
                res = (((res * scalemul) * 100) / scalediv);
            }
            while (x > 0)
            {
                res = (res * 100);
                x--;
            }
        }

        k = ((ulong)b * scalemul) * 1000000;

        while (k >= 1000000000)
        {
            k = (k - 1000000000);
            b++;
        }

        res = (res + (uint)k);

        while (res >= 1000000000)
        {
            res = (res - 1000000000);
            b++;
        }

        i++;
    }

    return (((ulong)b * 1000000000) + res);
}