First off, make backups of your files before you start messing with this!
Second, I am assuming that you already have Bots eneabled and working for your server. I'm not going to talk about getting that up and running, as it has been covered elsewhere.
Third, because different people use different versions of the EQEmu source code, and because line numbers can change between versions, I'm going to talk about function names, and when necessary specific sections of code within a given function. I will not specify line numbers. If you don't know how to do a basic text search in your preferred code editor, then you'd really better learn, as you'll need it for this process and it is a useful thing to know at any time. And just for reference, I'm using the EQEmu revision retrieved through git on 01/23/2014.
Fourth, I'm going to assume you know at least a little bit about C++ code writing/editing. I may spell a few things out, but I'm not going to explain the inner workings of why something must be done in a certain way. If you don't know your way around C++, I suggest starting with something smaller than EQEmu and look through online C++ tutorials (there are plenty around).
Also, just a quick disclaimer... My C++ coding skills are not spectacular, so some of this code may not be as clean and optimized as it could be. If anyone sees a way that it could be better, please feel free to post.
Now then, in order to enable additional races for use in game as bots, there are a number of different pieces of code that you need to modify. Almost all of the code sections that need to be changed are located in the bot.cpp source file for the zone.exe. If anything needs to be modified in another file, I'll specify which file you need.
You can use whatever code editor you prefer, but since you need Visual Studio for compiling and debugging, I personally just use Visual Studio for editing as well.
The first section of code you need to change in the bot.cpp file is the "Bot::GenerateBaseStats()" function, down where it starts to adjust stats based on racial modifiers. Just look for the section that begins with "switch(this->GetRace()) {". In this section, you need to add the racial modifiers for the race(s) you want to add in. What modifiers you use are up to you. Just enter a new section for "case <raceID>:", add in the modifiers you want to use, and finish up with a "break;" statement. I personally think it is easiest to simply add in a "default" statement that was missing from the original source code so that any race not specifically given modifiers will simply default to use the human standard of no modifiers at all...
Code:
default: // any other race used for bot, treat as human, no bonuses
break;
Once you have the default statement in place you can then add in modifiers above it for any specific race you want to give modifiers to, but can leave all other races using the basic defaults.
So let's say that you wanted to add 4 races to be available as bots, but you only wanted to specify modifiers for one of them. Let's say you're using race 347 as the one you want to give modifiers to (I don't know what race that is, I chose the raceID at random). And let's say that you want to give +5 Str and +10 Dex to that race, but -5 Wis and -10 Cha. Since you only want modifiers for the one race, you can add the default statement as given above. Then, after the break statement for the Drakkin race you can then add in the following...
Code:
case 347: // race name/description
Strength += 5;
Dexterity += 10;
Wisdom -= 5;
Charisma -= 10;
break;
If you notice the other entries for the other races, you can modify just about any stat for the race, inlcuding the default size you want to use.
The second code location you need to change is the "Bot::IsValidRaceClassCombo()" function. In this function you need to add in code to specify what classes are allowed for given bot race. You can use this section to modify the classes that are allowed for any given bot race, including the original races (this will *not* change what classes are available for primary player characters). As with the racial modifiers, I personally find it easiest to simply add a default statement to the function that will apply to all races that are not specifically addressed, and then add specific class limitations only to those races that I want to be limited...
Code:
default: // any/all other races
if((GetClass() >= 1) && (GetClass() <= 16)) // if class is 1-16
{
Result = true;
}
break;
This section basically stats that as long as a classID of 1-16 is specified, then the race/class combo is acceptable. This allows standard classes to be used, but does not allow GM classes, merchant, etc.
Then, if you want to add limitations for a specific race you would add in a section for each individual race that you want to limit. For example, let's take race 347 again, and let's say we want to limit them to only be able to use SK or Necro. In such a case you would add in the follow section (again, must be somewhere before the default statement)...
Code:
case 347: // race name/description
switch(GetClass()) {
case 5: // Shadowknight
case 11: // Necromancer
Result = true;
break;
}
break;
The third code section that needs to be changed is the "Bot::RaceIdToString(uint16 raceId)" function. This function is called from only one place, the #bot list command processing. It's used to send a message to the client that states the race of a given bot in the available bots list. Ideally you'll have an entry for every single race that you enable for bots, but if you have a large number of races that you are enabling, then that can become more than a little tedious. But since this is only used for the #bot list command, it isn't totally unreasonable to set a default statement that simply states it is of an unknown race...
Code:
default: // If we have any other race, simply set the string to "Unknown Race: " + raceId. Will need to fist convert raceId from int to string data type
std::string racestring = itoa(raceId);
Result = std::string("Unknown Race: " + racestring);
break;
But if you are allowing just a few additional races, then you can specify each one individually by duplicating one of the existing segments and simply changing the raceID and race string values. So again, let's take the raceID of 347, and let's say that this race is a Fire Miphit (again, I haven't actually looked it up). In such a case you would add in the following...
Code:
case 347:
Result = std::string("Fire Miphit");
break;
Fourth, you need to modify the code for the #bot create command processing. You'll find this in the "Bot::ProcessBotCommands(Client *c, const Seperator *sep)" function, specifically the segment that begins with "if(!strcasecmp(sep->arg[1], "create"))". The function itself is used to parse and process all #bot commands, but this specific section is used to process the #bot create command in particular.
In this section, look for the second "else if" statement. This section parses arg[4] for race specified by the #bot create command. You should notice that by default the line already checks for and allows raceID of 1-12, 128, 130, 330, and 522 for all of the standard races. To make the system allow *any* race whatsoever, all you need to do is comment out this entire else if segment. But if you only want to allow a few extra races, then you should add them each in specifically and individually. To do this, you need to add code into the line itself, such as...
Code:
&& strcasecmp(sep->arg[4],"347")
You need to add this in after the closing parenthesis immediately following another raceID entry. Be sure to enter in the correct raceID for the race(s) you are trying to allow.
That's it. You and your players should now be allowed to create bots of whatever race(s) you've added in. *HOWEVER*, most, if not all, will appear as generic humans when spawned! You see, it isn't enough to simply make the code allow for the creation of such a bot, you also have to make changes to the code that will make the client display them correctly when they are spawned. In order to do this, you need to do a much larger amount of code changes.
Also, if the race you are using is not already a global race, then you will have to make it global by adding it to the GlobalLoad.txt file, and you'll have to make a copy of the modified file available for all players (because it's a client-side file). I'm not going to talk about how to modify the GlobalLoad.txt file to make a race global, as that has been covered in a number of other posts in these forums.
Basically there are a couple of problems that need to be addressed for making non-standard races appear correctly in game. First, they have to have the correct texture and helmtexture, which is not saved in the bots table of the database, nor anywhere else, so it needs to be hardcoded or it needs to be specified during the #bot spawn command. Second, they have to use a face entry value of 0, so you need to have the code either check for that, or force it. And third, they need to have a gender of 2, so again, you need to either check for it, or force it. And for the gender, if you decide to check for the gender value isntead of forcing it, then you need to change the #bot create command processing to allow a third gender value, something like "monster" or "neutral" for example, that would be used to set gender value to 2.
My personal method for how to handle this is to allow the players the option of specifying a texture and helmtexture during the #bot spawn command, and forcing the value to 255 for standard races and to 0 for non-standard races if the values are not specified in the command. Then, I force the face entry value to 0 for all non-standard races. And lastly, I force the gender value to 2 for all non-standard races. Following is how to actually achieve that...