How to create adjustable formula for RPG level up requirements?

Truncheon
  • How to create adjustable formula for RPG level up requirements? Truncheon

    I'm trying to create a formula that can be modified simply by changing two values: number_of_levels, and last_level_experience. This is to enable people modding the game to change the levelling requirements.

    I've got it so that I can specify the number of XP needed for the last level up, but I want to be able to control the XP needed for the first level up, which in this case can differ wildly. For example, if I have 40 levels, and 1,000,000 XP for the last level, the first level up requirement is then 625. But if I change the levels to 80, the first level up becomes 156. In both cases, the last level needs 1,000,000.

    There must be some way to get the computer to work out a suitable curve given just these two basic values.

    #include <iostream>
    
    int main()
    {
        int levels = 40;
        if (levels < 2) levels = 2;
    
        int experience_for_last_level = 1e6;
        float fraction = 1.0 / levels;
    
        {
            int i = 0;
            float fraction_counter = fraction;
            int counter = levels;
            int total = 0;
    
            for (i = 1; i <= levels; ++i, fraction_counter += fraction, --counter)
            {
                int a = static_cast<int>(fraction_counter * experience_for_last_level / counter);
    
                std::cout <<"Level "<<i<<":  "<<a<<" ("<<counter<<")"<<"\n";
    
                total += a;
            }
    
            std::cout << "\nTotal Exp: " << total;
        }
    }
    

    Output:

    Level 1:  625   (40)      Level 15: 14423  (26)      Level 29: 60416  (12)
    Level 2:  1282  (39)      Level 16: 16000  (25)      Level 30: 68181  (11)
    Level 3:  1973  (38)      Level 17: 17708  (24)      Level 31: 77499  (10)
    Level 4:  2702  (37)      Level 18: 19565  (23)      Level 32: 88888  (9)
    Level 5:  3472  (36)      Level 19: 21590  (22)      Level 33: 103124 (8)
    Level 6:  4285  (35)      Level 20: 23809  (21)      Level 34: 121428 (7)
    Level 7:  5147  (34)      Level 21: 26250  (20)      Level 35: 145833 (6)
    Level 8:  6060  (33)      Level 22: 28947  (19)      Level 36: 179999 (5)
    Level 9:  7031  (32)      Level 23: 31944  (18)      Level 37: 231249 (4)
    Level 10: 8064  (31)      Level 24: 35294  (17)      Level 38: 316666 (3)
    Level 11: 9166  (30)      Level 25: 39062  (16)      Level 39: 487499 (2)
    Level 12: 10344 (29)      Level 26: 43333  (15)      Level 40: 999999 (1)
    Level 13: 11607 (28)      Level 27: 48214  (14)
    Level 14: 12962 (27)      Level 28: 53846  (13)
    

  • Though there are infinitely many ways to choose them, it is common for leveling curves to follow a power rule such as the following one:

    f(level) == A * exp(B * level)
    

    The major advantage of this formula can be easily explained: for a given rule, there is a fixed value N such that each level costs N percent more than the previous one.

    Your initial variables add the following restrictions:

    f(1) - f(0) == experience_for_first_level
    f(levels) - f(levels - 1) == experience_for_last_level
    

    Two equations, two unknowns. This looks good. Simple maths give A and B:

    B = log(experience_for_last_level / experience_for_first_level) / (levels - 1);
    A = experience_for_first_level / (exp(B) - 1);
    

    Resulting in the following code:

    #include <cmath>
    #include <iostream>
    
    int main(void)
    {
        int levels = 40;
        int xp_for_first_level = 1000;
        int xp_for_last_level = 1000000;
    
        double B = log((double)xp_for_last_level / xp_for_first_level) / (levels - 1);
        double A = (double)xp_for_first_level / (exp(B) - 1.0);
    
        for (int i = 1; i <= levels; i++)
        {
            int old_xp = round(A * exp(B * (i - 1)));
            int new_xp = round(A * exp(B * i));
            std::cout << i << " " << (new_xp - old_xp) << std::endl;
        }
    }
    

    And the following output:

    1 1000          9 4125          17 17012        25 70170        33 289427
    2 1193          10 4924         18 20309        26 83768        34 345511
    3 1425          11 5878         19 24245        27 100000       35 412462
    4 1702          12 7017         20 28943        28 119378       36 492389
    5 2031          13 8377         21 34551        29 142510       37 587801
    6 2424          14 10000        22 41246        30 170125       38 701704
    7 2894          15 11938        23 49239        31 203092       39 837678
    8 3455          16 14251        24 58780        32 242446       40 1000000
    

Tags
c++ rpg mathematics progression
Related questions and answers
  • theScreen("ascreen"); string splashScreen[24] = { // HERE"S THE SPLASH ! // 1 2 3 4 5 6 7 8... */ "|______________________________________________________________________________|",}; string _lines_[56] = { // 1 2 3 4 5 6 7 8 // 123456789 123456789 123456789...}; // for the old/new screen command // Some nCurses setup int r = 0, c = 0; // current row and column (upper-left is (0,0)) const int nrows = 56, // number of rows in window ncols = 79

  • in the shader code) for( int n = 0; n &lt; vShaderArgs.size(); n ++) glBindAttribLocation( m_nProgramId, n, vShaderArgs[n].sFieldName.c_str() ); // Create and bind to a vertex array object, which...: float m_Position[3]; // x, y, z // offset 0, size = 3*sizeof(float) float m_TexCoords[2]; // u, v // offset 3*sizeof(float), size = 2*sizeof(float) float m_Normal[3]; // nx, ny, nz; float colour[4]; // r, g, b, a float padding[20]; // padded for performance }; I've already written a working VertexBufferObject class that creates a vertex buffer

  • ) if (In) // Check To See If The File Opened ( NEW ) { for (i = 0; i &lt; 32; i++) // Loop Though The 32 Greyscale Values ( NEW..., In); // Get The Current Line ( NEW ) shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = float(atof (Line)); // Copy Over The Value ( NEW ) } fclose...); // Set The Color Of The Model ( NEW ) // ORIGINAL DRAWING CODE //Draw the model as an interpolation between the two frames glBegin(GL_TRIANGLES); for(int i = 0; i

  • (); // create the index buffer out of DWORDs DWORD OurIndices[] = { 0, 1, 2, // side 1 2, 1, 3, 4, 0, 6, // side 2 6, 0, 2, 7, 5, 6, // side 3 6, 5, 4, 3, 1, 7, // side 4 7, 1, 5, 4, 5, 0, // side 5 0, 5, 1, 3, 7, 2, // side 6 2, 7, 6, }; // create the index buffer // D3D10_BUFFER_DESC bd; // redefinition bd.Usage = D3D10_USAGE_DYNAMIC... 2, 1, 3, 4, 0, 6, // side 2 6, 0, 2, 7, 5, 6, // side 3 6, 5, 4, 3, 1, 7, // side 4 7, 1, 5, 4, 5, 0, // side 5 0, 5, 1, 3, 7, 2, // side 6 2

  • I've got 3 points in space that define a triangle. I've also got a vertex buffer made up of three vertices, that also represent a triangle that I will refer to as a "model". How can I can I find the matrix M that will transform vertex in my buffer to those 3 points in space ? For example, let's say my three points A, B, C are at locations: A.x = 10, A.y = 16, A.z = 8 B.x = 12, B.y = 11, B.z = 1 C.x = 19, C.y = 12, C.z = 3 given these coordinates how can I build a matrix that will translate and rotate my model such that both triangles have the exact same world space ? That is, I want

  • char index_normal; }; typedef short md2_textcoord_t[2]; struct md2_frame_t { float scale[3]; vec3_t translate; char name[16]; md2_vertex_t vertices[1]; // First vertex of this frame. }; struct md2_triangle_t { short index_xyz[3]; short index_st[3]; }; struct md2_anim_t { int first_frame, last_frame, fps; }; struct md2_animstate_t { md2_anim_t* animation; float current_time, old_time, interpol; int type, current_frame, next_frame; }; struct md2_glcmd_t { float s; float t; int index; }; #define MD2_IDENT (('2'&lt;&lt;24) + ('P'&lt;&lt;16

  • = nodeChildren[i]->getCharData(); stringstream stm(data); stm >> matWorld.m[3][0]; stm >> matWorld.m[3][1]; stm >> matWorld.m[3][2]; matWorld.m...D3DXMATRIX ColladaFileLoader::processMatrix(daeElement* node) { D3DXMATRIX matWorld; daeTArray<daeElementRef> nodeChildren = node->getChildren(); for (int i = 0; i &lt...); stm >> matWorld.m[0][0]; stm >> matWorld.m[0][1]; stm >> matWorld.m[0][2]; stm >> matWorld.m[0][3]; } if (type == "rotationY

  • the ship? int main( int argc, char* args[] ) { Player p; Timer fps; bool quit = false; if( init() == false ) { return 1; } //Load the files if( load_files() == false ) { return 1; } clip[ 0 ].x = 0; clip[ 0 ].y = 0; clip[ 0 ].w = 30; clip[ 0 ].h = 36; clip[ 1 ].x = 31; clip[ 1 ].y = 0; clip[ 1 ].w = 39; clip[ 1 ].h = 36; clip[ 2 ].x = 71; clip[ 2 ].y = 0; clip[ 2 ].w = 29; clip[ 2 ].h = 36; lazers [ 0 ].x = 0; lazers [ 0 ].y = 0; lazers [ 0 ].w = 3; lazers [ 0 ].h = 9; lazers [ 1 ].x = 5; lazers [ 1 ].y = 0; lazers [ 1 ].w = 3; lazers [ 1 ].h = 7; while( quit == false

  • in advance of first use. Here is a bit a sketch of the classes I am using: typedef unsigned int ResourceId; // Resource is an abstract data type. class Resource { Resource(); virtual ~Resource(); virtual bool load() = 0; virtual bool unload() = 0; virtual size_t getSize() = 0; // Used in determining how much memory is // being used. bool... a soft target for the total memory used by all the game's resources combined. Other classes will create resource objects, which will be in an unloaded state, and pass them to the ResourceManager

Data information