well aside from the hl1 interface i try to write an empty interface with only comments. So you only have to 'insert' code there to feed the information layer so bots actually work.
I try to keep as much as possible out of the bot code. Ie, for joining a team in a game, the bot simply does not know what game it is. All it should do is
(snippet from itm)
Code:
void c_ITM_Bot::BOT_Think()
{
if (bJoinedTeam(this) == false)
return; // do nothing yet
// We joined the game, think!
}
The join function differs from game to game, so what i have done is:
Code:
// PURPOSE: Return TRUE when joined a team
bool bJoinedTeam(c_ITM_Bot *pBot)
{
// we already joined the action
if (pBot->bJoinedAction)
return true;
// In order to keep this part 'engine independant' we run
// a function shared in the entire framework called "IF_JOINTEAM"
// which should return TRUE, when its done joining the game, which is
// game and mod depended.
pBot->bJoinedAction = IF_JOINTEAM(pBot);
return pBot->bJoinedAction; // return this
}
where IF_JOINTEAM() is an InterFace function (IF_...) with a pointer of this bot. The IF_JOINTEAM function is a 'standard shared' function in the interface. Which is EMPTY in the 'empty/example' interface.
The contents look similiar to the join function in Real/HPB/Racc/whatever bot.
Code:
/////////////////////////////////////////////////////////////////
// The only BOT / GAME SPECIFIC "join the game" FUNCTION
/////////////////////////////////////////////////////////////////
bool IF_JOINTEAM(c_ITM_Bot *pBot)
{
// We handle CS joining here, its very basic, as we are lazy
// and let CS decide what team we are ;) We later on check
// what team we are with the UTIL_ function at the 'sense'
// part. Which is, even not needed, because the Update Sequence
// keeps our bot data up-to-date!
edict_t *pEdict = UTIL_GetBotEdict(pBot);
if (pEdict == NULL)
{
IF_PRINT("ERROR: Could not get pEdict from pBot\n");
return false;
}
// handle Counter-Strike stuff here...
if (pBot->iStartAction == ITM_MSG_SELECT_A_TEAM)
{
pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
// Select 'auto'
FakeClientCommand(pEdict, "menuselect", "5", NULL);
return false; // we did not complete joining yet
}
if (pBot->iStartAction == ITM_MSG_SELECT_CLASS_ALPHA) // counter terrorist
{
pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
// Select 'auto'
FakeClientCommand(pEdict, "menuselect", "5", NULL);
// bot has now joined the game (doesn't need to be started)
return true;
}
if (pBot->iStartAction == ITM_MSG_SELECT_CLASS_BETA) // terrorist select
{
pBot->iStartAction = ITM_MSG_NONE; // switch back to idle
// Select 'auto'
FakeClientCommand(pEdict, "menuselect", "5", NULL);
// bot has now joined the game (doesn't need to be started)
return true;
}
return false;
}
The reason for this way is simple: keep everything game specific out of the bot code.
This is the only example code i have so far that directly calls an interface function from the BOT. Apart frmo the runplayermove logic in the HL1 engine, which i call with "MakeItHappen". This is a more general way of saying to do what we thought about (the bot) and get things in motion. Every game has a different way of doing this, so MakeItHappen was a nice name i thought.
For the moment things are very, very basic and very naked coded. Only the barebones are there, but thats the point. It should not be bigger then nescesary.
Just FYI here the contents of the itm_interface_hl1.h file:
Code:
// Shared interface functions
#ifndef ITM_INTERFACE_HL1_H
#define ITM_INTERFACE_HL1_H
#define MAX_HL1_CLIENTS 32 // Max 32 clients in HL1
struct t_timeBot
{
int msecnum;
float msecdel;
int msecval;
};
// The function names should not change
// The params may change though
void IF_Init(); // Init any (global) vars we use in the game specific interface
void IF_Spawn(edict_t *pent); // Spawns something
void IF_UpdateSequence(); // aka Startframe()
int IF_CreateBot (edict_t * pPlayer, const char *arg1, const char *arg2, const char *arg3, const char *arg4);
void IF_PRINT(char *msg); // print a message on the server
void IF_ClientCommand(edict_t * pBot, char *arg1, char *arg2, char *arg3); // command from a bot to the server
int IF_MakeItHappen(c_ITM_Bot *pBot); // Aka RunPlayerMove()
// "Converts" any game vector of this game into the vector of our own format!
c_ITM_Vector IF_ConvertVector(Vector vec);
Vector IF_ConvertVector(c_ITM_Vector vec);
t_timeBot TimeBot[MAX_HL1_CLIENTS];
/////////////////////////
//
// Newly added functions:
//
/////////////////////////
// UTIL FUNCTIONS
c_ITM_Bot * UTIL_GetBotPointer (edict_t * pEdict);
int UTIL_GetBotIndex (edict_t * pEdict);
// DRAW SOMETHING ON THE HUD
void HUD_DrawString (int r, int g, int b, char *msg, edict_t * edict);
//
// CLIENT STUFF
//
//
// COUNTER-STRIKE
void BotClient_CS_VGUI (void *p, int bot_index);
void BotClient_CS_ShowMenu (void *p, int bot_index);
void BotClient_CS_WeaponList (void *p, int bot_index);
void BotClient_CS_CurrentWeapon (void *p, int bot_index);
void BotClient_CS_AmmoX (void *p, int bot_index);
void BotClient_CS_AmmoPickup (void *p, int bot_index);
void BotClient_CS_WeaponPickup (void *p, int bot_index);
void BotClient_CS_ItemPickup (void *p, int bot_index);
void BotClient_CS_Health (void *p, int bot_index);
void BotClient_CS_Battery (void *p, int bot_index);
void BotClient_CS_Damage (void *p, int bot_index);
void BotClient_CS_Money (void *p, int bot_index);
void BotClient_CS_DeathMsg (void *p, int bot_index);
void BotClient_CS_ScreenFade (void *p, int bot_index);
void BotClient_CS_HLTV(void *p, int bot_index);
void BotClient_CS_SayText(void *p, int bot_index);
// StatusIcon
void BotClient_CS_StatusIcon (void *p, int bot_index);
// VALVE DEATHMATCH
void BotClient_Valve_WeaponList (void *p, int bot_index);
void BotClient_Valve_CurrentWeapon (void *p, int bot_index);
void BotClient_Valve_AmmoX (void *p, int bot_index);
void BotClient_Valve_AmmoPickup (void *p, int bot_index);
void BotClient_Valve_WeaponPickup (void *p, int bot_index);
void BotClient_Valve_ItemPickup (void *p, int bot_index);
void BotClient_Valve_Health (void *p, int bot_index);
void BotClient_Valve_Battery (void *p, int bot_index);
void BotClient_Valve_Damage (void *p, int bot_index);
void BotClient_Valve_DeathMsg (void *p, int bot_index);
void BotClient_Valve_ScreenFade (void *p, int bot_index);
#endif // ITM_INTERFACE_HL1_H
as another note, i do use the clients[] var , but in a different way. As for in HPB you see they are not linked to the bots in any way. The clients[] var is handling ALL clients joining the game, and i share the indexes with my bots. So i can simply get a pEdict WITHOUT even using that in my c_ITM_Bot class. All i need is an index...