View Single Post
  #17  
Old 01-25-2016, 12:19 PM
Kingly_Krab
Administrator
 
Join Date: May 2013
Location: United States
Posts: 1,604
Default

Titanium's race-class combinations are actually set separately in the source code. world/client.cpp contains this:
Code:
bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
{
    uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up
    int classtemp, racetemp;
    int Charerrors = 0;


// if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES 16

    static const int BaseRace[_TABLE_RACES][7] =
    {            /* STR  STA  AGI  DEX  WIS  INT  CHR */
    { /*Human*/      75,  75,  75,  75,  75,  75,  75},
    { /*Barbarian*/ 103,  95,  82,  70,  70,  60,  55},
    { /*Erudite*/    60,  70,  70,  70,  83, 107,  70},
    { /*Wood Elf*/   65,  65,  95,  80,  80,  75,  75},
    { /*High Elf*/   55,  65,  85,  70,  95,  92,  80},
    { /*Dark Elf*/   60,  65,  90,  75,  83,  99,  60},
    { /*Half Elf*/   70,  70,  90,  85,  60,  75,  75},
    { /*Dwarf*/      90,  90,  70,  90,  83,  60,  45},
    { /*Troll*/     108, 109,  83,  75,  60,  52,  40},
    { /*Ogre*/      130, 122,  70,  70,  67,  60,  37},
    { /*Halfling*/   70,  75,  95,  90,  80,  67,  50},
    { /*Gnome*/      60,  70,  85,  85,  67,  98,  60},
    { /*Iksar*/      70,  70,  90,  85,  80,  75,  55},
    { /*Vah Shir*/   90,  75,  90,  70,  70,  65,  65},
    { /*Froglok*/    70,  80, 100, 100,  75,  75,  50},
    { /*Drakkin*/    70,  80,  85,  75,  80,  85,  75}
    };

    static const int BaseClass[PLAYER_CLASS_COUNT][8] =
    {              /* STR  STA  AGI  DEX  WIS  INT  CHR  ADD*/
    { /*Warrior*/      10,  10,   5,   0,   0,   0,   0,  25},
    { /*Cleric*/        5,   5,   0,   0,  10,   0,   0,  30},
    { /*Paladin*/      10,   5,   0,   0,   5,   0,  10,  20},
    { /*Ranger*/        5,  10,  10,   0,   5,   0,   0,  20},
    { /*ShadowKnight*/ 10,   5,   0,   0,   0,   10,  5,  20},
    { /*Druid*/         0,  10,   0,   0,  10,   0,   0,  30},
    { /*Monk*/          5,   5,  10,  10,   0,   0,   0,  20},
    { /*Bard*/          5,   0,   0,  10,   0,   0,  10,  25},
    { /*Rouge*/         0,   0,  10,  10,   0,   0,   0,  30},
    { /*Shaman*/        0,   5,   0,   0,  10,   0,   5,  30},
    { /*Necromancer*/   0,   0,   0,  10,   0,  10,   0,  30},
    { /*Wizard*/        0,  10,   0,   0,   0,  10,   0,  30},
    { /*Magician*/      0,  10,   0,   0,   0,  10,   0,  30},
    { /*Enchanter*/     0,   0,   0,   0,   0,  10,  10,  30},
    { /*Beastlord*/     0,  10,   5,   0,  10,   0,   5,  20},
    { /*Berserker*/    10,   5,   0,  10,   0,   0,   0,  25}
    };

    static const bool ClassRaceLookupTable[PLAYER_CLASS_COUNT][_TABLE_RACES]=
    {                   /*Human  Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf  Troll  Ogre   Halfling Gnome  Iksar  Vahshir Froglok Drakkin*/
    { /*Warrior*/         true,  true,     false,  true,   false,  true,   true,   true,  true,  true,  true,    true,  true,  true,   true,   true},
    { /*Cleric*/          true,  false,    true,   false,  true,   true,   true,   true,  false, false, true,    true,  false, false,  true,   true},
    { /*Paladin*/         true,  false,    true,   false,  true,   false,  true,   true,  false, false, true,    true,  false, false,  true,   true},
    { /*Ranger*/          true,  false,    false,  true,   false,  false,  true,   false, false, false, true,    false, false, false,  false,  true},
    { /*ShadowKnight*/    true,  false,    true,   false,  false,  true,   false,  false, true,  true,  false,   true,  true,  false,  true,   true},
    { /*Druid*/           true,  false,    false,  true,   false,  false,  true,   false, false, false, true,    false, false, false,  false,  true},
    { /*Monk*/            true,  false,    false,  false,  false,  false,  false,  false, false, false, false,   false, true,  false,  false,  true},
    { /*Bard*/            true,  false,    false,  true,   false,  false,  true,   false, false, false, false,   false, false, true,   false,  true},
    { /*Rogue*/           true,  true,     false,  true,   false,  true,   true,   true,  false, false, true,    true,  false, true,   true,   true},
    { /*Shaman*/          false, true,     false,  false,  false,  false,  false,  false, true,  true,  false,   false, true,  true,   true,   false},
    { /*Necromancer*/     true,  false,    true,   false,  false,  true,   false,  false, false, false, false,   true,  true,  false,  true,   true},
    { /*Wizard*/          true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  true,   true},
    { /*Magician*/        true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  false,  true},
    { /*Enchanter*/       true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  false,  true},
    { /*Beastlord*/       false, true,     false,  false,  false,  false,  false,  false, true,  true,  false,   false, true,  true,   false,  false},
    { /*Berserker*/       false, true,     false,  false,  false,  false,  false,  true,  true,  true,  false,   false, false, true,   false,  false}
    };

    if (!cc)
        return false;

    Log.Out(Logs::Detail, Logs::World_Server,"Validating char creation info...");

    classtemp = cc->class_ - 1;
    racetemp = cc->race - 1;
    // these have non sequential race numbers so they need to be mapped
    if (cc->race == FROGLOK) racetemp = 14;
    if (cc->race == VAHSHIR) racetemp = 13;
    if (cc->race == IKSAR) racetemp = 12;
    if (cc->race == DRAKKIN) racetemp = 15;

    // if out of range looking it up in the table would crash stuff
    // so we return from these
    if (classtemp >= PLAYER_CLASS_COUNT) {
        Log.Out(Logs::Detail, Logs::World_Server,"  class is out of range");
        return false;
    }
    if (racetemp >= _TABLE_RACES) {
        Log.Out(Logs::Detail, Logs::World_Server,"  race is out of range");
        return false;
    }

    if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs?
        Log.Out(Logs::Detail, Logs::World_Server,"  invalid race/class combination");
        // we return from this one, since if it's an invalid combination our table
        // doesn't have meaningful values for the stats
        return false;
    }

    // add up the base values for this class/race
    // this is what they start with, and they have stat_points more
    // that can distributed
    bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
    bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
    bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
    bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
    bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
    bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
    bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
    stat_points = BaseClass[classtemp][7];
    bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
    cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;

    // the first check makes sure the total is exactly what was expected.
    // this will catch all the stat cheating, but there's still the issue
    // of reducing CHA or INT or something, to use for STR, so we check
    // that none are lower than the base or higher than base + stat_points
    // NOTE: these could just be else if, but i want to see all the stats
    // that are messed up not just the first hit

    if (bTOTAL + stat_points != cTOTAL) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat points total doesn't match expected value: expecting %d got %d", bTOTAL + stat_points, cTOTAL);
        Charerrors++;
    }

    if (cc->STR > bSTR + stat_points || cc->STR < bSTR) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat STR is out of range");
        Charerrors++;
    }
    if (cc->STA > bSTA + stat_points || cc->STA < bSTA) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat STA is out of range");
        Charerrors++;
    }
    if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat AGI is out of range");
        Charerrors++;
    }
    if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat DEX is out of range");
        Charerrors++;
    }
    if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat WIS is out of range");
        Charerrors++;
    }
    if (cc->INT > bINT + stat_points || cc->INT < bINT) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat INT is out of range");
        Charerrors++;
    }
    if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) {
        Log.Out(Logs::Detail, Logs::World_Server,"  stat CHA is out of range");
        Charerrors++;
    }

    /*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
    I am NOT writing those tables - kathgar*/

    Log.Out(Logs::Detail, Logs::World_Server,"Found %d errors in character creation request", Charerrors);

    return Charerrors == 0;
}
Reply With Quote