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).
Level | Exp needed |
1 | 3,000 |
2 | 3,000 |
3 | 6,000 |
4 | 11,000 |
5 | 20,166 |
6 | 34,570 |
7 | 59,262 |
8 | 96,300 |
9 | 156,487 |
10 | 243,424 |
11 | 378,659 |
12 | 567,988 |
13 | 851,982 |
14 | 1,230,640 |
15 | 1,777,591 |
16 | 2,488,627 |
17 | 3,484,077 |
18 | 4,751,014 |
19 | 5,938,767 |
20 | 7,423,458 |
21 | 9,279,322 |
22 | 11,599,152 |
23 | 14,498,940 |
24 | 18,123,675 |
25 | 22,654,593 |
26 | 28,318,241 |
27 | 32,565,977 |
28 | 37,450,873 |
29 | 43,068,503 |
30 | 49,528,778 |
31 | 56,958,094 |
32 | 65,501,808 |
33 | 75,327,079 |
34 | 86,626,140 |
35 | 99,620,061 |
36 | 114,563,070 |
37 | 131,747,530 |
38 | 151,509,659 |
39 | 174,236,107 |
40 | 200,371,523 |
41 | 230,427,251 |
42 | 264,991,338 |
43 | 304,740,038 |
44 | 350,451,043 |
45 | 403,018,699 |
46 | 463,471,503 |
47 | 532,992,228 |
48 | 612,941,062 |
49 | 704,882,221 |
50 | 810,614,554 |
51 | 932,206,737 |
52 | 1,072,037,747 |
53 | 1,232,843,409 |
54 | 1,417,769,920 |
55 | 1,559,546,912 |
56 | 1,715,501,603 |
57 | 1,887,051,763 |
58 | 2,075,756,939 |
59 | 2,283,332,632 |
60 | 2,511,665,895 |
61 | 2,762,832,484 |
62 | 3,039,115,732 |
63 | 3,191,071,518 |
64 | 3,350,625,093 |
65 | 3,518,156,347 |
66 | 3,694,064,164 |
67 | 3,878,767,372 |
68 | 4,072,705,740 |
69 | 4,276,341,027 |
70 | 4,490,158,078 |
71 | 4,714,665,981 |
72 | 4,950,399,280 |
73 | 5,197,919,244 |
74 | 5,457,815,206 |
75 | 5,730,705,966 |
76 | 6,017,241,264 |
77 | 6,318,103,327 |
78 | 6,634,008,493 |
79 | 6,965,708,917 |
80 | 7,313,994,362 |
Comments welcome. =)
You mean I shouldn't need 1.3 zillion exp <and no i'm not joking I need 1.3 zillion exp> to level?
Here's the same table as in my previous message, but for a 100% exp chart (Human Warrior)
Level | Exp needed |
1 | 1,000 |
2 | 1,000 |
3 | 2,000 |
4 | 3,666 |
5 | 6,721 |
6 | 11,521 |
7 | 19,750 |
8 | 32,093 |
9 | 52,151 |
10 | 81,123 |
11 | 126,191 |
12 | 189,286 |
13 | 283,929 |
14 | 410,119 |
15 | 592,394 |
16 | 829,351 |
17 | 1,161,091 |
18 | 1,583,305 |
19 | 1,979,131 |
20 | 2,473,913 |
21 | 3,092,391 |
22 | 3,865,488 |
23 | 4,831,860 |
24 | 6,039,825 |
25 | 7,549,781 |
26 | 9,437,226 |
27 | 10,852,809 |
28 | 12,480,730 |
29 | 14,352,839 |
30 | 16,505,764 |
31 | 18,981,628 |
32 | 21,828,872 |
33 | 25,103,202 |
34 | 28,868,682 |
35 | 33,198,984 |
36 | 38,178,831 |
37 | 43,905,655 |
38 | 50,491,503 |
39 | 58,065,228 |
40 | 66,775,012 |
41 | 76,791,263 |
42 | 88,309,952 |
43 | 101,556,444 |
44 | 116,789,910 |
45 | 134,308,396 |
46 | 154,454,655 |
47 | 177,622,853 |
48 | 204,266,280 |
49 | 234,906,222 |
50 | 270,142,155 |
51 | 310,663,478 |
52 | 357,262,999 |
53 | 410,852,448 |
54 | 472,480,315 |
55 | 519,728,346 |
56 | 571,701,180 |
57 | 628,871,298 |
58 | 691,758,427 |
59 | 760,934,269 |
60 | 837,027,695 |
61 | 920,730,464 |
62 | 1,012,803,510 |
63 | 1,063,443,685 |
64 | 1,116,615,869 |
65 | 1,172,446,662 |
66 | 1,231,068,995 |
67 | 1,292,622,444 |
68 | 1,357,253,566 |
69 | 1,425,116,244 |
70 | 1,496,372,056 |
71 | 1,571,190,658 |
72 | 1,649,750,190 |
73 | 1,732,237,699 |
74 | 1,818,849,583 |
75 | 1,909,792,062 |
76 | 2,005,281,665 |
77 | 2,105,545,748 |
78 | 2,210,823,035 |
79 | 2,321,364,186 |
80 | 2,437,432,395 |
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);
}
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);
}