quake2-action/acesrc/acebot_spawn.c

1214 lines
31 KiB
C

///////////////////////////////////////////////////////////////////////
//
// ACE - Quake II Bot Base Code
//
// Version 1.0
//
// Original file is Copyright(c), Steve Yeager 1998, All Rights Reserved
//
//
// All other files are Copyright(c) Id Software, Inc.
////////////////////////////////////////////////////////////////////////
/*
* $Header: /LicenseToKill/src/acesrc/acebot_spawn.c 29 16/10/99 18:59 Riever $
*
* $Log: /LicenseToKill/src/acesrc/acebot_spawn.c $
*
* 29 16/10/99 18:59 Riever
* Fixed bug in WeaponChoice code.
*
* 28 16/10/99 14:03 Riever
* Added favourite weapon and equipment to config file.
*
* 27 16/10/99 10:37 Riever
* Fixed a mis-type in a skin name.
*
* 26 16/10/99 9:45 Riever
* New config files now working...
*
* 25 16/10/99 9:15 Riever
* Changed to use new bot config data.
* Removed bot saving code.
* Enabled configs per level.
*
* 24 16/10/99 8:59 Riever
* New bot config file code added.
* ltk_chat CVAR enabled to turn off botchat.
*
* 23 10/10/99 14:46 Riever
* Changed removebot call to use ClientDisconnect.
*
* 22 6/10/99 20:19 Riever
* Fixed bugs in Teamplay spawning so teams always balance out.
*
* 21 6/10/99 18:33 Riever
* Random weapon choice for Teamplay now working.
*
* 20 6/10/99 17:40 Riever
* Added TeamPlay state STATE_POSITION to enable bots to seperate and
* avoid centipede formations.
*
* 19 5/10/99 20:09 Riever
* Changes to let bots load the route file and load the bots in a
* dedicated server after the level has changed.
*
* 18 3/10/99 21:43 Riever
* Re-enabled bot naming...
*
* 17 29/09/99 17:42 Riever
* Random switch chooses between standard or Sindarin names.
*
* 16 29/09/99 16:52 Riever
* Set the name switch to show that a particular name is used.
* Added extra name pairs from Sindarin.
*
* 15 29/09/99 8:45 Riever
* lastkilledby is now used to insult the person who killed the bot.
*
* 14 29/09/99 8:08 Riever
* Modified killed message code to find last enemy
*
* 13 29/09/99 7:53 Riever
* Changed respawn insults to use last attacker.
*
* 12 29/09/99 7:47 Riever
* Comment added
*
* 11 29/09/99 7:37 Riever
* Increased max available names to 100
*
* 10 29/09/99 7:21 Riever
* Added first version of LTKsetBotName to get random bot names that look
* different to eachother.
*
* 9 28/09/99 7:38 Riever
* Reduced frequency of respawn chats.
*
* 8 28/09/99 7:37 Riever
* LTK_Chat method added and utilised by bots on spawn and respawn.
* Need to get insults in for killing opponents and then work on graphical
* taunts.
*
* 7 27/09/99 13:22 Riever
* Initialise pathList on spawn
*
* 6 21/09/99 21:15 Riever
* Random angle facing added for TeamPlay mode starts.
*
* 5 21/09/99 17:20 Riever
* Temporary centipede fix achieved by having teamPauseTime set forward
* and not allowing goal following until this time is reached.
*
* 4 21/09/99 7:56 Riever
* Fixed Team spawning code so that it automatically selects the best team
* for a spawned bot to join. This works with the bots.tmp file too!
*
* 3 16/09/99 7:40 Riever
* Changed a bprint I missed to a safeprint
*
* 2 13/09/99 19:52 Riever
* Added headers
*
*/
///////////////////////////////////////////////////////////////////////
//
// acebot_spawn.c - This file contains all of the
// spawing support routines for the ACE bot.
//
///////////////////////////////////////////////////////////////////////
#include "../g_local.h"
#include "../m_player.h"
#include "acebot.h"
#include "botchat.h"
#include "botscan.h"
//AQ2 ADD
#define CONFIG_FILE_VERSION 1
void AllItems( edict_t *ent );
void AllWeapons( edict_t *ent );
void EquipClient( edict_t *ent );
char *TeamName(int team);
void LTKsetBotName( char *bot_name );
void ACEAI_Cmd_Choose( edict_t *ent, char *s);
//==========================================
// Joining a team (hacked from AQ2 code)
//==========================================
/* defined in a_team.h
#define NOTEAM 0
#define TEAM1 1
#define TEAM2 2
*/
//==============================
// Get the number of the next team a bot should join
//==============================
int GetNextTeamNumber()
{
int i, onteam1 = 0, onteam2 = 0;
edict_t *e;
// only use this function during [2]team games...
if (!teamplay->value )
return 0;
// gi.bprintf(PRINT_HIGH, "Checking team balance..\n");
for (i = 1; i < maxclients->value+1; i++)
{
e = g_edicts + i;
if (e->inuse)
{
if (e->client->resp.team == TEAM1)
onteam1++;
else if (e->client->resp.team == TEAM2)
onteam2++;
}
}
// Return the team number that needs the next bot
if (onteam1 > onteam2)
return (2);
else if (onteam2 >= onteam1)
return (1);
//default
return (1);
}
//==========================
// Join a Team
//==========================
void ACESP_JoinTeam(edict_t *ent, int desired_team)
{
char *s, *a;
if (ent->client->resp.team == desired_team)
return;
a = (ent->client->resp.team == NOTEAM) ? "joined" : "changed to";
ent->client->resp.team = desired_team;
s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
AssignSkin(ent, s);
if (ent->solid != SOLID_NOT) // alive, in game
{
ent->health = 0;
player_die (ent, ent, ent, 100000, vec3_origin);
ent->deadflag = DEAD_DEAD;
}
safe_bprintf(PRINT_HIGH, "%s %s %s.\n",
ent->client->pers.netname, a, TeamName(desired_team));
ent->client->resp.joined_team = level.framenum;
CheckForUnevenTeams();
}
//======================================
// ACESP_LoadBotConfig()
//======================================
// Using RiEvEr's new config file
//
void ACESP_LoadBotConfig()
{
FILE *pIn;
cvar_t *game_dir;
int i;
char filename[60];
// Scanner stuff
int fileVersion = 0;
char inString[81];
char tokenString[81];
char *sp, *tp;
int ttype;
game_dir = gi.cvar ("game", "", 0);
// Try to load the file for THIS level
#ifdef _WIN32
i = sprintf(filename, ".\\");
i += sprintf(filename + i, game_dir->string);
i += sprintf(filename + i, "\\bots\\");
i += sprintf(filename + i, level.mapname);
i += sprintf(filename + i, ".cfg");
#else
strcpy(filename, "./");
strcat(filename, game_dir->string);
strcat(filename, "/bots/");
strcat(filename, level.mapname);
strcat(filename,".cfg");
#endif
// If there's no specific file for this level then get the normal one
if((pIn = fopen(filename, "rb" )) == NULL)
{
#ifdef _WIN32
i = sprintf(filename, ".\\");
i += sprintf(filename + i, game_dir->string);
i += sprintf(filename + i, "\\bots\\botdata.cfg");
#else
strcpy(filename, "./");
strcat(filename, game_dir->string);
strcat(filename,"/bots/botdata.cfg");
#endif
// No bot file available, get out of here!
if((pIn = fopen(filename, "rb" )) == NULL)
return; // bail
}
// Now scan each line for information
// First line should be the file version number
fgets( inString, 80, pIn );
sp = inString;
tp = tokenString;
ttype = UNDEF;
// Scan it for the version number
scanner( &sp, tp, &ttype );
if(ttype == BANG)
{
scanner( &sp, tp, &ttype );
if(ttype == INTLIT)
{
fileVersion = atoi( tokenString );
}
if( fileVersion != CONFIG_FILE_VERSION )
{
// ERROR!
safe_bprintf(PRINT_HIGH, "Bot Config file is out of date!\n");
fclose(pIn);
return;
}
}
// Now process each line of the config file
while( fgets(inString, 80, pIn) )
{
ACESP_SpawnBotFromConfig( inString );
}
/* fread(&count,sizeof (int),1,pIn);
for(i=0;i<count;i++)
{
fread(userinfo,sizeof(char) * MAX_INFO_STRING,1,pIn);
ACESP_SpawnBot (NULL, NULL, NULL, userinfo);
}*/
fclose(pIn);
}
//===========================
// ACESP_SpawnBotFromConfig
//===========================
// Input string is from the config file and should have
// all the data we need to spawn a bot
//
void ACESP_SpawnBotFromConfig( char *inString )
{
edict_t *bot;
char userinfo[MAX_INFO_STRING];
int count=1;
char name[32];
char modelskin[80];
int team=0, weaponchoice=0, equipchoice=0;
// Scanner stuff
char tokenString[81];
char *sp, *tp;
int ttype;
sp = inString;
ttype = UNDEF;
while( ttype != EOL )
{
tp = tokenString;
scanner(&sp, tp, &ttype);
// Check for comments
if( ttype == HASH || ttype == LEXERR)
return;
// Check for semicolon (end of input marker)
if( ttype == SEMIC || ttype == EOL)
continue;
// Keep track of which parameter we are reading
if( ttype == COMMA )
{
count++;
continue;
}
// NAME (parameter 1)
if(count == 1 && ttype == STRLIT)
{
// strncpy( name, tokenString, 32 );
strcpy( name, tokenString);
continue;
}
// MODELSKIN (parameter 2)
if(count == 2 && ttype == STRLIT)
{
// strncpy( modelskin, tokenString, 32 );
strcpy( modelskin, tokenString);
continue;
}
if(count == 3 && ttype == INTLIT )
{
team = atoi(tokenString);
continue;
}
if(count == 4 && ttype == INTLIT )
{
weaponchoice = atoi(tokenString);
continue;
}
if(count == 5 && ttype == INTLIT )
{
equipchoice = atoi(tokenString);
continue;
}
}// End while
bot = ACESP_FindFreeClient ();
if (!bot)
{
safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
return;
}
bot->yaw_speed = 100; // yaw speed
bot->inuse = true;
bot->is_bot = true;
// To allow bots to respawn
// initialise userinfo
memset (userinfo, 0, sizeof(userinfo));
// add bot's name/skin/hand to userinfo
Info_SetValueForKey (userinfo, "name", name);
Info_SetValueForKey (userinfo, "skin", modelskin);
Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now!
Info_SetValueForKey (userinfo, "spectator", "0"); // NOT a spectator
ClientConnect (bot, userinfo);
G_InitEdict (bot);
// locate ent at a spawn point
if(teamplay->value)
{
// Make sure we have a team
if(!team)
team = GetNextTeamNumber();
}
// Set up the preferred weapon & equipment
bot->weaponchoice = weaponchoice;
bot->equipchoice = equipchoice;
InitClientResp (bot->client);
if(teamplay->value)
{
ACESP_PutClientInServer (bot,true, team);
}
else
ACESP_PutClientInServer (bot,true,0);
// make sure all view stuff is valid
ClientEndServerFrame (bot);
ACEIT_PlayerAdded (bot); // let the world know we added another
ACEAI_PickLongRangeGoal(bot); // pick a new goal
// LTK chat stuff
if( random() < 0.33)
{
// Store current enemies available
int i, counter = 0;
edict_t *myplayer[MAX_BOTS];
for(i=0;i<=num_players;i++)
{
// Find all available enemies to insult
if(players[i] == NULL || players[i] == bot ||
players[i]->solid == SOLID_NOT)
continue;
if(teamplay->value && OnSameTeam( bot, players[i]) )
continue;
myplayer[counter++] = players[i];
}
if(counter > 0)
{
// Say something insulting to them!
if(ltk_chat->value) // Some people don't want this *sigh*
LTK_Chat( bot, myplayer[rand()%counter], DBC_WELCOME);
}
}
}
//AQ2 END
///////////////////////////////////////////////////////////////////////
// Called by PutClient in Server to actually release the bot into the game
// Keep from killin' each other when all spawned at once
///////////////////////////////////////////////////////////////////////
void ACESP_HoldSpawn(edict_t *self)
{
if (!KillBox (self))
{ // could't spawn in?
}
gi.linkentity (self);
self->think = ACEAI_Think;
self->nextthink = level.time + FRAMETIME;
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (self-g_edicts);
gi.WriteByte (MZ_LOGIN);
gi.multicast (self->s.origin, MULTICAST_PVS);
/* if(ctf->value)
safe_bprintf(PRINT_MEDIUM, "%s joined the %s team.\n",
self->client->pers.netname, CTFTeamName(self->client->resp.ctf_team));
else*/
safe_bprintf (PRINT_MEDIUM, "%s entered the game\n", self->client->pers.netname);
}
///////////////////////////////////////////////////////////////////////
// Modified version of id's code
///////////////////////////////////////////////////////////////////////
void ACESP_PutClientInServer (edict_t *bot, qboolean respawn, int team)
{
vec3_t mins = {-16, -16, -24};
vec3_t maxs = {16, 16, 32};
int index;
vec3_t spawn_origin, spawn_angles;
gclient_t *client;
int i, counter;
client_persistant_t saved;
client_respawn_t resp;
char *s;
int going_observer;//AQ2
// find a spawn point
// do it before setting health back up, so farthest
// ranging doesn't count this client
SelectSpawnPoint (bot, spawn_origin, spawn_angles);
index = bot-g_edicts-1;
client = bot->client;
// deathmatch wipes most client data every spawn
if (deathmatch->value)
{
char userinfo[MAX_INFO_STRING];
resp = bot->client->resp;
memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
InitClientPersistant (client);
ClientUserinfoChanged (bot, userinfo);
}
else
memset (&resp, 0, sizeof(resp));
// clear everything but the persistant data
saved = client->pers;
memset (client, 0, sizeof(*client));
client->pers = saved;
client->resp = resp;
// copy some data from the client to the entity
FetchClientEntData (bot);
// clear entity values
bot->groundentity = NULL;
bot->client = &game.clients[index];
bot->takedamage = DAMAGE_AIM;
bot->movetype = MOVETYPE_WALK;
bot->viewheight = 24;
bot->classname = "bot";
bot->mass = 200;
bot->solid = SOLID_BBOX;
bot->deadflag = DEAD_NO;
bot->air_finished = level.time + 12;
bot->clipmask = MASK_PLAYERSOLID;
bot->model = "players/male/tris.md2";
bot->pain = player_pain;
bot->die = player_die;
bot->waterlevel = 0;
bot->watertype = 0;
bot->flags &= ~FL_NO_KNOCKBACK;
bot->svflags &= ~SVF_DEADMONSTER;
bot->is_jumping = false;
//AQ2 ADD
if (!teamplay->value || bot->client->resp.team != NOTEAM)
{
bot->flags &= ~FL_GODMODE;
bot->svflags &= ~SVF_NOCLIENT;
}
//AQ2 END
VectorCopy (mins, bot->mins);
VectorCopy (maxs, bot->maxs);
VectorClear (bot->velocity);
// clear playerstate values
memset (&bot->client->ps, 0, sizeof(client->ps));
client->ps.pmove.origin[0] = spawn_origin[0]*8;
client->ps.pmove.origin[1] = spawn_origin[1]*8;
client->ps.pmove.origin[2] = spawn_origin[2]*8;
//ZOID
//AQ2 client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
//ZOID
if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
{
client->ps.fov = 90;
}
else
{
client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
if (client->ps.fov < 1)
client->ps.fov = 90;
else if (client->ps.fov > 160)
client->ps.fov = 160;
}
client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
// clear entity state values
bot->s.effects = 0;
bot->s.skinnum = bot - g_edicts - 1;
bot->s.modelindex = 255; // will use the skin specified model
//AQ2 bot->s.modelindex2 = 255; // custom gun model
//AQ2 ADD
ShowGun(bot);
//AQ2 END
bot->s.frame = 0;
VectorCopy (spawn_origin, bot->s.origin);
bot->s.origin[2] += 1; // make sure off ground
VectorCopy (bot->s.origin, bot->s.old_origin);//AQ2
// set the delta angle
for (i=0 ; i<3 ; i++)
client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
bot->s.angles[PITCH] = 0;
bot->s.angles[YAW] = spawn_angles[YAW];
bot->s.angles[ROLL] = 0;
VectorCopy (bot->s.angles, client->ps.viewangles);
VectorCopy (bot->s.angles, client->v_angle);
// force the current weapon up
client->newweapon = client->pers.weapon;
ChangeWeapon (bot);
bot->enemy = NULL;
bot->movetarget = NULL;
if( !teamplay->value)
bot->state = STATE_MOVE;
else
bot->state = STATE_POSITION;
// Set the current node
bot->current_node = ACEND_FindClosestReachableNode(bot,NODE_DENSITY, NODE_ALL);
bot->goal_node = bot->current_node;
bot->next_node = bot->current_node;
bot->next_move_time = level.time;
bot->suicide_timeout = level.time + 15.0;
// If we are not respawning hold off for up to three seconds before releasing into game
if(!respawn)
{
bot->think = ACESP_HoldSpawn;
bot->nextthink = level.time + 0.1;
bot->nextthink = level.time + random()*3.0; // up to three seconds
}
else
{
//AQ2 ADD
//@@ check this out!
// Sets to an observer unless joined a team! (ent->client->resp.team != NOTEAM)
if (teamplay->value)
{
going_observer = StartClient(bot);
}
else
{
going_observer = bot->client->pers.spectator;
if (going_observer)
{
bot->movetype = MOVETYPE_NOCLIP;
bot->solid = SOLID_NOT;
bot->svflags |= SVF_NOCLIENT;
bot->client->resp.team = NOTEAM;
bot->client->ps.gunindex = 0;
}
}
//FIREBLADE
if (!going_observer && !teamplay->value)
{ // this handles telefrags...
KillBox(bot);
}
//FIREBLADE
gi.linkentity (bot);
bot->think = ACEAI_Think;
bot->nextthink = level.time + FRAMETIME;
//AQ2 ADD
client->mk23_max = 12;
client->mp5_max = 30;
client->m4_max = 24;
client->shot_max = 7;
client->sniper_max = 6;
client->cannon_max = 2;
client->dual_max = 24;
client->mk23_rds = client->mk23_max;
client->dual_rds = client->mk23_max;
client->knife_max = 10;
client->grenade_max = 2;
bot->lasersight = NULL;
//other
client->bandaging = 0;
client->leg_damage = 0;
client->leg_noise = 0;
client->leg_dam_count = 0;
client->desired_fov = 90;
client->ps.fov = 90;
client->idle_weapon = 0;
client->drop_knife = 0;
client->no_sniper_display = 0;
client->knife_sound = 0;
client->doortoggle = 0;
client->have_laser = 0;
// Choose Teamplay weapon
if( bot->weaponchoice == 0)
counter = rand() % 5;
else
counter = bot->weaponchoice -1; // Range is 1..5
switch(counter)
{
case 0:
ACEAI_Cmd_Choose( bot, MP5_NAME);
break;
case 1:
ACEAI_Cmd_Choose( bot, M4_NAME);
break;
case 2:
ACEAI_Cmd_Choose( bot, M3_NAME);
break;
case 3:
ACEAI_Cmd_Choose( bot, HC_NAME);
break;
case 4:
ACEAI_Cmd_Choose( bot, SNIPER_NAME);
break;
default:
ACEAI_Cmd_Choose( bot, M3_NAME);
break;
}
// Choose Teamplay equipment
if(bot->equipchoice == 0)
counter = rand() % 5;
else
counter = bot->equipchoice - 1; // Range is 1..5
switch(counter)
{
case 0:
ACEAI_Cmd_Choose( bot, SIL_NAME);
break;
case 1:
ACEAI_Cmd_Choose( bot, SLIP_NAME);
break;
case 2:
ACEAI_Cmd_Choose( bot, BAND_NAME);
break;
case 3:
ACEAI_Cmd_Choose( bot, KEV_NAME);
break;
case 4:
ACEAI_Cmd_Choose( bot, LASER_NAME);
break;
default:
ACEAI_Cmd_Choose( bot, KEV_NAME);
break;
}
//FIREBLADE
if (!going_observer)
{
// items up here so that the bandolier will change equipclient below
if ( allitem->value )
{
AllItems( bot );
}
if (teamplay->value)
EquipClient(bot);
if (bot->client->menu)
{
PMenu_Close(bot);
return;
}
//FIREBLADE
if ( allweapon->value )
{
AllWeapons( bot );
}
// force the current weapon up
client->newweapon = client->pers.weapon;
ChangeWeapon (bot);
//FIREBLADE
if (teamplay->value)
{
bot->solid = SOLID_TRIGGER;
gi.linkentity(bot);
}
//FIREBLADE
}
if(teamplay->value)
{
int randomnode;
bot->client->resp.team = team;
s = Info_ValueForKey (bot->client->pers.userinfo, "skin");
AssignSkin(bot, s);
// Anti centipede timer
bot->teamPauseTime = level.time + 3.0 + (rand() % 7);
// Change facing angle for each bot
randomnode = (int)(num_players * random() );
VectorSubtract (nodes[randomnode].origin, bot->s.origin, bot->move_vector);
bot->move_vector[2] = 0;
}
else
bot->teamPauseTime = level.time;
//AQ2 END
//RiEvEr - new node pathing system
memset(&bot->pathList, 0, sizeof(bot->pathList) );
bot->pathList.head = bot->pathList.tail = NULL;
//R
/*
//AQ2 REMOVED
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (bot-g_edicts);
gi.WriteByte (MZ_LOGIN);
gi.multicast (bot->s.origin, MULTICAST_PVS);
//AQ2 END
*/
}
}
///////////////////////////////////////////////////////////////////////
// Respawn the bot
///////////////////////////////////////////////////////////////////////
void ACESP_Respawn (edict_t *self)
{
// AQ2 CHANGED
if (self->solid != SOLID_NOT || self->deadflag == DEAD_DEAD)
{
CopyToBodyQue (self);
}
if(teamplay->value)
ACESP_PutClientInServer (self,true, self->client->resp.team);
else
ACESP_PutClientInServer (self,true,0);
self->svflags &= ~SVF_NOCLIENT;
//AQ2 END
// add a teleportation effect
//AQ2 self->s.event = EV_PLAYER_TELEPORT;
// hold in place briefly
//AQ2 self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
//AQ2 self->client->ps.pmove.pm_time = 14;
self->client->respawn_time = level.time;
if( random() < 0.15)
{
// Store current enemies available
int i, counter = 0;
edict_t *myplayer[MAX_BOTS];
if( self->lastkilledby)
{
// Have a comeback line!
if(ltk_chat->value) // Some people don't want this *sigh*
LTK_Chat( self, self->lastkilledby, DBC_KILLED);
self->lastkilledby = NULL;
}
else
{
// Pick someone at random to insult
for(i=0;i<=num_players;i++)
{
// Find all available enemies to insult
if(players[i] == NULL || players[i] == self ||
players[i]->solid == SOLID_NOT)
continue;
if(teamplay->value && OnSameTeam( self, players[i]) )
continue;
myplayer[counter++] = players[i];
}
if(counter > 0)
{
// Say something insulting to them!
if(ltk_chat->value) // Some people don't want this *sigh*
LTK_Chat( self, myplayer[rand()%counter], DBC_INSULT);
}
}
}
}
///////////////////////////////////////////////////////////////////////
// Find a free client spot
///////////////////////////////////////////////////////////////////////
edict_t *ACESP_FindFreeClient (void)
{
edict_t *bot;
int i;
int max_count=0;
// This is for the naming of the bots
for (i = maxclients->value; i > 0; i--)
{
bot = g_edicts + i + 1;
if(bot->count > max_count)
max_count = bot->count;
}
// Check for free spot
for (i = maxclients->value; i > 0; i--)
{
bot = g_edicts + i + 1;
if (!bot->inuse)
break;
}
bot->count = max_count + 1; // Will become bot name...
if (bot->inuse)
bot = NULL;
return bot;
}
///////////////////////////////////////////////////////////////////////
// Set the name of the bot and update the userinfo
///////////////////////////////////////////////////////////////////////
void ACESP_SetName(edict_t *bot, char *name, char *skin, char *team)
{
float rnd;
char userinfo[MAX_INFO_STRING];
char bot_skin[MAX_INFO_STRING];
char bot_name[MAX_INFO_STRING];
// Set the name for the bot.
// name
if(strlen(name) == 0)
{
// RiEvEr - new code to get random bot names
LTKsetBotName(bot_name);
}
else
strcpy(bot_name,name);
// skin
if(strlen(skin) == 0)
{
// randomly choose skin
rnd = random();
if(rnd < 0.05)
sprintf(bot_skin,"male/bluebeard");
else if(rnd < 0.1)
sprintf(bot_skin,"female/brianna");
else if(rnd < 0.15)
sprintf(bot_skin,"male/blues");
else if(rnd < 0.2)
sprintf(bot_skin,"female/ensign");
else if(rnd < 0.25)
sprintf(bot_skin,"female/jezebel");
else if(rnd < 0.3)
sprintf(bot_skin,"female/jungle");
else if(rnd < 0.35)
sprintf(bot_skin,"sas/sasurban");
else if(rnd < 0.4)
sprintf(bot_skin,"terror/urbanterr");
else if(rnd < 0.45)
sprintf(bot_skin,"female/venus");
else if(rnd < 0.5)
sprintf(bot_skin,"sydney/sydney");
else if(rnd < 0.55)
sprintf(bot_skin,"male/cajin");
else if(rnd < 0.6)
sprintf(bot_skin,"male/commando");
else if(rnd < 0.65)
sprintf(bot_skin,"male/grunt");
else if(rnd < 0.7)
sprintf(bot_skin,"male/mclaine");
else if(rnd < 0.75)
sprintf(bot_skin,"male/robber");
else if(rnd < 0.8)
sprintf(bot_skin,"male/snowcamo");
else if(rnd < 0.85)
sprintf(bot_skin,"terror/swat");
else if(rnd < 0.9)
sprintf(bot_skin,"terror/jungleterr");
else if(rnd < 0.95)
sprintf(bot_skin,"sas/saspolice");
else
sprintf(bot_skin,"sas/sasuc");
}
else
strcpy(bot_skin,skin);
// initialise userinfo
memset (userinfo, 0, sizeof(userinfo));
// add bot's name/skin/hand to userinfo
Info_SetValueForKey (userinfo, "name", bot_name);
Info_SetValueForKey (userinfo, "skin", bot_skin);
Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now!
//AQ2 ADD
Info_SetValueForKey (userinfo, "spectator", "0"); // NOT a spectator
//AQ2 END
ClientConnect (bot, userinfo);
// ACESP_SaveBots(); // make sure to save the bots
}
//RiEvEr - new global to enable bot self-loading of routes
extern char current_map[55];
//
char *LocalTeamNames[3] = {"spectator", "1", "2" };
///////////////////////////////////////////////////////////////////////
// Spawn the bot
///////////////////////////////////////////////////////////////////////
void ACESP_SpawnBot (char *team, char *name, char *skin, char *userinfo)
{
edict_t *bot;
bot = ACESP_FindFreeClient ();
if (!bot)
{
safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
return;
}
bot->yaw_speed = 100; // yaw speed
bot->inuse = true;
bot->is_bot = true;
// To allow bots to respawn
if(userinfo == NULL)
ACESP_SetName(bot, name, skin, team);
else
ClientConnect (bot, userinfo);
G_InitEdict (bot);
// Balance the teams!
if(teamplay->value)
{
if( (team == NULL) || (strlen(team) < 1) )
{
if( GetNextTeamNumber() == 1 )
{
gi.bprintf(PRINT_HIGH, "Assigned to team 1\n");
team = LocalTeamNames[1];
}
else
{
team = LocalTeamNames[2];
gi.bprintf(PRINT_HIGH, "Assigned to team 2\n");
}
}
}
InitClientResp (bot->client);
//AQ2 CHANGE
// Set up the preferred weapon & equipment
bot->weaponchoice = 0;
bot->equipchoice = 0;
// ACESP_PutClientInServer (bot,false,0);
// locate ent at a spawn point
if(teamplay->value)
{
if ((team != NULL) && (strcmp(team,"1")==0) )
ACESP_PutClientInServer (bot,true, TEAM1);
else
ACESP_PutClientInServer (bot,true, TEAM2);
}
else
ACESP_PutClientInServer (bot,true,0);
//AQ2 END
// make sure all view stuff is valid
ClientEndServerFrame (bot);
ACEIT_PlayerAdded (bot); // let the world know we added another
ACEAI_PickLongRangeGoal(bot); // pick a new goal
// LTK chat stuff
if( random() < 0.33)
{
// Store current enemies available
int i, counter = 0;
edict_t *myplayer[MAX_BOTS];
for(i=0;i<=num_players;i++)
{
// Find all available enemies to insult
if(players[i] == NULL || players[i] == bot ||
players[i]->solid == SOLID_NOT)
continue;
if(teamplay->value && OnSameTeam( bot, players[i]) )
continue;
myplayer[counter++] = players[i];
}
if(counter > 0)
{
// Say something insulting to them!
if(ltk_chat->value) // Some people don't want this *sigh*
LTK_Chat( bot, myplayer[rand()%counter], DBC_WELCOME);
}
}
}
void ClientDisconnect( edict_t *ent );
///////////////////////////////////////////////////////////////////////
// Remove a bot by name or all bots
///////////////////////////////////////////////////////////////////////
void ACESP_RemoveBot(char *name)
{
int i;
qboolean freed=false;
edict_t *bot;
for(i=0;i<maxclients->value;i++)
{
bot = g_edicts + i + 1;
if(bot->inuse)
{
if(bot->is_bot && (strcmp(bot->client->pers.netname,name)==0 || strcmp(name,"all")==0))
{
bot->health = 0;
player_die (bot, bot, bot, 100000, vec3_origin);
// don't even bother waiting for death frames
// bot->deadflag = DEAD_DEAD;
// bot->inuse = false;
freed = true;
ClientDisconnect( bot );
// ACEIT_PlayerRemoved (bot);
// safe_bprintf (PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
}
}
}
if(!freed)
safe_bprintf (PRINT_MEDIUM, "%s not found\n", name);
// ACESP_SaveBots(); // Save them again
}
//====================================
// Stuff to generate pseudo-random names
//====================================
#define NUMNAMES 10
char *names1[NUMNAMES] = {
"Bad", "d3th", "L33t", "Fasst", "mAx", "l3thal", "kw1k", "Hard", "Angel", "Red"};
char *names2[NUMNAMES] = {
"Moon", "eevil", "wakko", "d00d", "killa", "dog", "sodja", "joos", "frags", "akimbo" };
char *names3[NUMNAMES] = {
"An", "Bal", "Calen", "Cor", "Fan", "Gil", "Hal", "Lin", "Mal", "Per"};
char *names4[NUMNAMES] = {
"adan", "rog", "born", "dor", "fing", "galad", "iel", "loss", "orch", "riel" };
qboolean nameused[NUMNAMES][NUMNAMES];
//====================================
// New random bot naming routine
//====================================
void LTKsetBotName( char *bot_name )
{
int part1,part2;
part1 = part2 = 0;
do
{
part1 = rand()% NUMNAMES;
part2 = rand()% NUMNAMES;
}while( nameused[part1][part2]);
// Mark that name as used
nameused[part1][part2] = true;
// Now put the name together
if( random() < 0.5 )
{
strcpy( bot_name, names1[part1]);
strcat( bot_name, names2[part2]);
}
else
{
strcpy( bot_name, names3[part1]);
strcat( bot_name, names4[part2]);
}
}