Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Bots

Development::Bots Forum for bots.

Reply
 
Thread Tools Display Modes
  #1  
Old 02-16-2014, 06:39 PM
DrakePhoenix
Fire Beetle
 
Join Date: Jan 2014
Posts: 22
Default [HOW-TO] Enable expanded race options for bots.

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...
Reply With Quote
  #2  
Old 02-16-2014, 06:40 PM
DrakePhoenix
Fire Beetle
 
Join Date: Jan 2014
Posts: 22
Default

OK, first let's talk about setting things up so that the #bot spawn command can accept optional texture and helmtexture variables. Once we have that done, then we'll talk about how to make the system actually use those variables when provided, and choose appropriate defaults for them when the are not provided.

Navigate through the bot.cpp file to the "Bot::ProcessBotCommands(Client *c, const Seperator *sep)" function again. This time we're going to the section for bot spawning, instead of bot creation, so search for the line that starts "if(!strcasecmp(sep->arg[1], "spawn") )". This is where the #bot spawn command is parsed and processed.

Right at the top of this code segment you'll find a line that reads "uint32 botId = GetBotIDByBotName(std::string(sep->arg[2]));". Immediately below that line, add in the following...
Code:
		uint8 textureparam = 0;
		uint8 helmtextureparam = 0;
Now, continue on through this section, past all the potential spawn failure checks, to where you find the line "Bot* TempBot = LoadBot(botId, &TempErrorMessage)". This is the point where the bot object is loaded into the system, but it is not yet spawned and made visible in the zone. The system then checks if the LoadBot function failed in some way, but if it didn't and we have successfully loaded a bot object into the system, then the command processing continues on to do a few other quick changes to the bot object, and then to spawn the bot into the zone.

Now, *INSIDE* the if(TempBot) if statement code section, and *above* the "TempBot->Spawn(c, &TempErrorMessage);" function call, enter the following (comments are optional)...
Code:
			// DrakePhoenix:  need to establlish textureparam and helmtextureparam default values based on race...
			// default values are set to 0 above, but provided values can also be 0
			// if nonstandard race, we want default of 0 if not provided, but if provided, we want to use what was provided (including 0, if that is the case)
			// if standard race, then we want default of 255 if not provided, else use provided value (including 0 or 255, if that is the case)
			uint16 race = TempBot->GetRace(); // get the bot's race so that we can use it to check if it is a standard race or a non-standard one
			if(sep->arg[3][0] != '\0') // check for texture parameter, and if it exists do the following...
			{
				textureparam = uint8(atoi(sep->arg[3])); // get the texture parameter, convert from string to int, convert to uint8, and set textureparam variable to equal the resulting value
			}
			else if((race > 12) && (race != 128) && (race != 130) && (race != 330) && (race != 522)) // if no texture parameter provided, and if non-standard race, set value to default 0
			{
				textureparam = 0;
			}
			else // if no texture parameter provided and standard race, set value to default 255.
			{
				textureparam = 0xFF;
			}

			if(sep->arg[4][0] != '\0') // check for helmtexture parameter, and if it exists do the following...
			{
				helmtextureparam = uint8(atoi(sep->arg[4])); // get the helmtexture parameter, convert from string to int, convert to uint8, and set textureparam variable to equal the resulting value
			}
			else if((race > 12) && (race != 128) && (race != 130) && (race != 330) && (race != 522)) // if no helmtexture parameter provided, and if non-standard race, set value to default 0
			{
				helmtextureparam = 0;
			}
			else // if no helmtexture parameter provided and standard race, set value to default 255.
			{
				helmtextureparam = 0xFF;
			}
So now we have told the system how to determine what values to use for the texture and helmtexture paramaters that we will be working with, including default values if the parameters were not included in the #bot spawn command that the player used.


Now we need to change the "Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage)" command to actually accept the additional parameters of texture and helmtexture. And we will also need to change how the function works so that it actually uses those parameters once they are in place.

So first, open up the bot.h header file, because we need to change the function declaration for the Spawn function. In the bot.h file, navigate to the Spawn function declaration. Should read as "void Spawn(Client* botCharacterOwner, std::string* errorMessage);". Change it to read as "void Spawn(Client* botCharacterOwner, std::string* errorMessage, uint8 textureparam, uint8 helmtextureparam);" instead. This tells the system that this function now requires 4 parameters instead of just 2.

Now go back to bot.cpp and the #bot spawn command processing section. Find the line for "TempBot->Spawn(c, &TempErrorMessage);" and change it to "TempBot->Spawn(c, &TempErrorMessage, textureparam, helmtextureparam);" instead. Remember that we already changed an earlier part of this code section to create the variables of textureparam and helmtextureparam that are being used here.

We also need to change every other instance that this function is called to make it use 4 paramaters, instead of the original 2. If you're working visual studio you can simply right-click on "Spawn" in the function call and then click on Find All References to search through every location in the code. But I'm going to walk you through them anyway, because it isn't as simple as just changing the function calls, you have also create the parameter variables that will be used.

So the next place to do this is in the "Bot::LoadAndSpawnAllZonedBots(Client* botOwner)" function. Go to that function, then find the line that reads "activeBot->Spawn(botOwner, &errorMessage);". We need to change this function call, and we need to make the variables for textureparam and helmtextureparam. Since this function is for active bots that already exist, we want to get the parameters from the existing bot object. Change the code to the following...
Code:
							uint8 textureparam = activeBot->GetTexture();
							uint8 helmtextureparam = activeBot->GetHelmTexture();
							activeBot->Spawn(botOwner, &errorMessage, textureparam, helmtextureparam);
Then the next two places we need to change this are deep inside the bot command processing function. First, look for the line that reads "botGroupLeader->Spawn(c, &TempErrorMessage);". We need to add the variables here, as well as the modifying the function call, so enter the following...
Code:
		uint8 textureparam = botGroupLeader->GetTexture();
		uint8 helmtextureparam = botGroupLeader->GetHelmTexture();
		botGroupLeader->Spawn(c, &TempErrorMessage,  textureparam, helmtextureparam);
Then, about 35-40 code lines later you should find another line that reads "botGroupMember->Spawn(c, &TempErrorMessage);". We need the variables again, as well as the change to the function call. So, for the botGroupMember line, enter the following in its place...
Code:
			uint8 textureparam = botGroupMember->GetTexture();
			uint8 helmtextureparam = botGroupMember->GetHelmTexture();
			botGroupMember->Spawn(c, &TempErrorMessage, textureparam, helmtextureparam);
Now it's time to edit the "Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage)" function itself. This is where the rest of the work takes place...
Reply With Quote
  #3  
Old 02-16-2014, 06:40 PM
DrakePhoenix
Fire Beetle
 
Join Date: Jan 2014
Posts: 22
Default

First, since we've changed the format of the function in the function declaration in bot.h, we need to change the function definition itself to also match those changes.

So in the bot.cpp, navigate to the "Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage)" function and change the top line to the following...
Code:
void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage, uint8 textureparam, uint8 helmtextureparam) {
This adds the textureparam and helmtextureparam variable parameters to the function definition itself.

Now we need to modify the function code itself to work with these new parameters and set up the bot the way we want it for additional races. To start with, find the lines that read "this->helmtexture = 0xFF;" and "this->texture = 0xFF;", and comment out both of them. Since we're getting the texture and helmtexture values passed to the function when it's called, we don't need to set them here, and we definitely don't want them to always default to 255 like that.

Next, we need to set the bot object's texture and helmtexture according to the values passed in as parameters from the function call. We also need to make sure that non-standard races have their face entry value set to 0 and their gender value set to 2. So immediately below the two lines you just commented out, and before the "if(this->Save())" line, enter the following (comments are optional)...
Code:
		uint16 race = this->GetRace();
		if((race > 12) && (race != 128) && (race != 130) && (race != 330) && (race != 522))
		{
			this->texture = textureparam; // set texture... defaults to 0 if not provided, determined in command process before Spawn was called, else retrieved from active bot that is being respawned
			this->helmtexture = textureparam; // for non-standard races, force helmtexture to match texture
			this->gender = 2; // for non-standard races, force gender to 2 (neutral/non)
			this->luclinface = 0; // for non-standard races, force face to 0
		}
		else // DrakePhoenix: for standard races, use provided or default textureparam and helmtextureparam, no change to gender or luclinface
		{
			this->helmtexture = helmtextureparam; // if value was not provided in #bot spawn command, then default is 255, determined in command process before Spawn was called, else retrieved from active bot that is being respawned
			this->texture = textureparam; // if value was not provided in #bot spawn command, then default is 255, determined in command process before Spawn was called, else retrieved from active bot that is being respawned
		}
Now down a little further you should find a line that reads "entity_list.AddBot(this, true, true);". This is the point at which the bot is actually spawned into the zone and becomes visible to the clients. But for some reason (that I haven't managed to figure out) it refuses to display the correct texture/helmtexture that was used when the Spawn function was called. So we need to force the spawned MOB to update with the correct visual settings. To do this, we make a call to the SendIllusionPacket function. So, after the entity_list update line, and before the LoadPet function call, add in the following (whitespace is optional, but makes it easier to read for later)...
Code:
		this->SendIllusionPacket(this->race, this->gender, this->texture, this->helmtexture, this->haircolor, this->beardcolor,
												this->eyecolor1, this->eyecolor2, this->hairstyle, this->luclinface, this->beard, 0xFF,
												this->drakkin_heritage, this->drakkin_tattoo, this->drakkin_details, this->size);
And that's it! So now save all of your changes (assuming you haven't already, though I hope you've been saving along the way). Then compile the code (right-click on ALL_BUILD, click on Build). Copy the newly changed zone.* files from your build directory to your server directory, and load up your server. If everything went properly, it should be working, and your added races for bots should now be available.


I personally also made changes to the "#bot help" command, and added in a new "#bot help spawn" command with more information about the optional texture and helmtexture parameters, but that isn't necessary, so I'm not going to post how to do that. I also personally made changes to the "#bot hair|face|haircolor|etc..." command to add in options for texture, helmtexture, and size, but that also isn't required, so I will not be posting how to do that.


If you run into any problems, feel free to post a reply asking for help. But please be aware that I am not always active on these forums, and you may not get a very quick response from me. Others might be able to help you as well though, so you still might get some fairly quick help.


Enjoy,
Drake Phoenix
Reply With Quote
  #4  
Old 02-16-2014, 07:44 PM
Kingly_Krab
Administrator
 
Join Date: May 2013
Location: United States
Posts: 1,589
Default

Could you post this in DIFF format so people have a better understanding of what to do rather than just [code] blocks?
Reply With Quote
  #5  
Old 02-17-2014, 02:14 AM
DrakePhoenix
Fire Beetle
 
Join Date: Jan 2014
Posts: 22
Default

If this was something I wanted to get merged into the release code, then I might do a diff format, but as it is, I believe the current race limitation in the code is intentional.

With that being the case, and since the code can change considerably between releases, I feel that a diff setup would only be useful when the source file that a server-op wants to modify is from the same revision as the one used when the diff was generated. I have no interest or intention of maintaining a diff over multiple revisions. As such, I believe that it is better, in the long run, to leave my instructions as they are. I also don't believe that my instructions are particularly difficult to understand, at least not for someone with a good grasp of the English language and a basic grasp of C++ programming. It may be less convenient for some, especially if they don't want to read much, but if followed from start to finish without trying to skim or skip anything, it should be perfectly understandable and easy to follow.

But, if you really want to have it presented in diff format, feel free to do it yourself. I have no problem with that at all.

Later,
Drake Phoenix
Reply With Quote
  #6  
Old 02-20-2014, 12:15 PM
DrakePhoenix
Fire Beetle
 
Join Date: Jan 2014
Posts: 22
Default Enabling equipment for the non-standard race bots...

OK, after some additional testing I found that I had completely forgotten to enable equipment for the newly added non-standard race bots. Since bots have only the most basic of stats without equipment, being able to equip them is fairly critical. So here are some additional instructions on how to make the system allow you to equip bots that are of a non-standard race...


Basically, you have to tell the system the bot is allowed to equip and use items even though its race doesn't match the races allowed to use the item (all items are limited to standard races only, or a subset of the standard races).

So first, let's set the system up to allow you to equip the items. Open up the bot.cpp file again. Now find the "Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client)" function. Navigate through that function until you find the line that reads "if(!failedLoreCheck && mWeaponItem && inst->IsEquipable(GetBaseRace(), GetClass()) && (GetLevel() >= mWeaponItem->ReqLevel)) {". This section of the code is called when you use the #bot giveitem command, and it checks to see if the bot is allowed to equip the item based on the item's race/class limitations compared to the bot's race and class. Since all items are limited to standard races only (or a subset), this check will always fail for bots of non-standard race, and your bot will tell you they cannot use the item you've given them. You need to change the check so that if the bot is of a non-standard race, it doesn't check against the bot's actual race. To accomplish this, replace that entire line with the following...
Code:
				uint16 checkRace = GetBaseRace(); // get the current base race for the current MOB
				if((checkRace > 12) && (checkRace != 128) && (checkRace != 130) && (checkRace != 330) && (checkRace != 522)) // if the race is a non-standard race, then...
				{
					checkRace = 1; // set race used for check to 1 (Human)
				}
				if(!failedLoreCheck && mWeaponItem && inst->IsEquipable(checkRace, GetClass()) && (GetLevel() >= mWeaponItem->ReqLevel)) {
This tells the system that when the bot's actual race is non-standard, to assume the bot is Human for the item check.

Now this does still leave some limitations. Specifically, if the bot is of a class that Humans are not allowed to be, then there is the possibility that some items may still be unusable. For example, any items that are Shaman-only are also possibly only usable by races that can be Shamans, and since Humans cannot be Shamans, this would prevent Shaman bots of non-standard races from being able to use such items. The number of such items should be quite limited, so this may be an acceptable problem. But if you want to make the system work for more or less all items, then you could also check for the bot's class, and if their class is one that Humans cannot be, then change the checkRace to a raceID value other than 1. For example, you could check for class, and if class is Shaman, then set race to be Barbarian instead of Human.


But that will only get you part of the way. You should now be able to equip items on your bots that are of non-standard race, and you should gain all of the stats from those items. However, using weapons adds in another check, and you need to adjust that one as well, or your bots of non-standard race will always be unarmed (which will not only limit their damage output, but will also prevent them from being able to strike a target that is immune to non-magic melee, at least for non-monks anyway).

So this time, open up the attack.cpp file. Then find the "Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate)" function. Please note that the Mob::GetWeaponDamage function is an overloaded function and you need this specific version of it. Now navigate through the function to find the line that reads "if(!weapon_item->IsEquipable(GetBaseRace(), GetClass())){". This is the check that needs to be changed so your bots of non-standard race can use weapons. Replace the line with the following...
Code:
			uint16 checkRace = GetBaseRace();
			if(((checkRace > 12) && (checkRace != 128) && (checkRace != 130) && (checkRace != 330) && (checkRace != 522)) && this->IsBot())
			{
				checkRace = 1; // DrakePhoenix: if MOB is a bot, and mob race is non-standard, treat as race 1 (Human) for equippable weapon check...
			}
			if(!weapon_item->IsEquipable(checkRace, GetClass())){
Again, I've set this up to assume a race of Human, so this could result in limitations as noted above.


Now recompile, and that's it. If you make those changes, then bots of non-standard race should be able to equip any item usable by a Human of the same class as the bot, and should be able to *use* them as well.

Have Fun,
Drake Phoenix
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 02:13 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3