1 ///////////////////////////////////////////////////////////////////////
2 //
3 //  ACE - Quake II Bot Base Code
4 //
5 //  Version 1.0
6 //
7 //  This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
8 //
9 //
10 //	All other files are Copyright(c) Id Software, Inc.
11 //
12 //	Please see liscense.txt in the source directory for the copyright
13 //	information regarding those files belonging to Id Software, Inc.
14 //
15 //	Should you decide to release a modified version of ACE, you MUST
16 //	include the following text (minus the BEGIN and END lines) in the
17 //	documentation for your modification.
18 //
19 //	--- BEGIN ---
20 //
21 //	The ACE Bot is a product of Steve Yeager, and is available from
22 //	the ACE Bot homepage, at http://www.axionfx.com/ace.
23 //
24 //	This program is a modification of the ACE Bot, and is therefore
25 //	in NO WAY supported by Steve Yeager.
26 
27 //	This program MUST NOT be sold in ANY form. If you have paid for
28 //	this product, you should contact Steve Yeager immediately, via
29 //	the ACE Bot homepage.
30 //
31 //	--- END ---
32 //
33 //	I, Steve Yeager, hold no responsibility for any harm caused by the
34 //	use of this source code, especially to small children and animals.
35 //  It is provided as-is with no implied warranty or support.
36 //
37 //  I also wish to thank and acknowledge the great work of others
38 //  that has helped me to develop this code.
39 //
40 //  John Cricket    - For ideas and swapping code.
41 //  Ryan Feltrin    - For ideas and swapping code.
42 //  SABIN           - For showing how to do true client based movement.
43 //  BotEpidemic     - For keeping us up to date.
44 //  Telefragged.com - For giving ACE a home.
45 //  Microsoft       - For giving us such a wonderful crash free OS.
46 //  id              - Need I say more.
47 //
48 //  And to all the other testers, pathers, and players and people
49 //  who I can't remember who the heck they were, but helped out.
50 //
51 ///////////////////////////////////////////////////////////////////////
52 
53 ///////////////////////////////////////////////////////////////////////
54 //
55 //  acebot_spawn.c - This file contains all of the
56 //                   spawing support routines for the ACE bot.
57 //
58 ///////////////////////////////////////////////////////////////////////
59 
60 #include "../g_local.h"
61 #include "../m_player.h"
62 #include "acebot.h"
63 
64 
65 //====================================
66 // Stuff to generate pseudo-random names AQ2
67 //====================================
68 #define NUMNAMES	10
69 char	*names1[NUMNAMES] = {
70 	"Bad", "d3th", "L33t", "Fasst", "mAx", "l3thal", "kw1k", "Hard", "Angel", "Red"};
71 
72 char	*names2[NUMNAMES] = {
73 	"Moon", "eevil", "wakko", "d00d", "killa", "dog", "sodja", "joos", "frags", "akimbo" };
74 
75 char	*names3[NUMNAMES] = {
76 	"An", "Bal", "Calen", "Cor", "Fan", "Gil", "Hal", "Lin", "Mal", "Per"};
77 
78 char	*names4[NUMNAMES] = {
79 	"adan", "rog", "born", "dor", "fing", "galad", "iel", "loss", "orch", "riel" };
80 
81 qboolean	nameused[NUMNAMES][NUMNAMES];
82 
83 //====================================
84 // New random bot naming routine
85 //====================================
SetBotNames(char * bot_name)86 void	SetBotNames( char	*bot_name )
87 {
88 	int	part1,part2;
89 
90 	part1 = part2 = 0;
91 
92 	do
93 	{
94 		part1 = rand()% NUMNAMES;
95 		part2 = rand()% NUMNAMES;
96 	}while( nameused[part1][part2]);
97 
98 	// Mark that name as used
99 	nameused[part1][part2] = true;
100 	// Now put the name together
101 	if( random() < 0.5 )
102 	{
103 		strcpy( bot_name, names1[part1]);
104 		strcat( bot_name, names2[part2]);
105 	}
106 	else
107 	{
108 		strcpy( bot_name, names3[part1]);
109 		strcat( bot_name, names4[part2]);
110 	}
111 }
112 
113 ///////////////////////////////////////////////////////////////////////
114 // Had to add this function in this version for some reason.
115 // any globals are wiped out between level changes....so
116 // save the bots out to a file.
117 //
118 // NOTE: There is still a bug when the bots are saved for
119 //       a dm game and then reloaded into a CTF game.
120 ///////////////////////////////////////////////////////////////////////
121 
ACESP_SaveBots()122 void ACESP_SaveBots()
123 {
124     edict_t *bot;
125     FILE *pOut;
126 	int i,count = 0;
127 
128 	if((pOut = fopen("baseq2/bots.tmp", "wb" )) == NULL)
129 		return; // bail
130 
131 	// Get number of bots
132 	for (i = maxclients->value; i > 0; i--)
133 	{
134 		bot = g_edicts + i + 1;
135 
136 		if (bot->inuse && bot->is_bot)
137 			count++;
138 	}
139 
140 	fwrite(&count,sizeof (int),1,pOut); // Write number of bots
141 
142 	for (i = maxclients->value; i > 0; i--)
143 	{
144 		bot = g_edicts + i + 1;
145 
146 		if (bot->inuse && bot->is_bot)
147 			fwrite(bot->client->pers.userinfo,sizeof (char) * MAX_INFO_STRING,1,pOut);
148 	}
149 
150     fclose(pOut);
151 }
152 
153 ///////////////////////////////////////////////////////////////////////
154 // Had to add this function in this version for some reason.
155 // any globals are wiped out between level changes....so
156 // load the bots from a file.
157 //
158 // Side effect/benifit are that the bots persist between games.
159 ///////////////////////////////////////////////////////////////////////
160 
ACESP_LoadBots()161 void ACESP_LoadBots()
162 {
163     FILE *pIn;
164 	char userinfo[MAX_INFO_STRING];
165 	int i, count;
166 
167 	if((pIn = fopen("baseq2/bots.tmp", "rb" )) == NULL)
168 		return; // bail
169 
170 	fread(&count,sizeof (int),1,pIn);
171 
172 	for(i=0;i<count;i++)
173 	{
174 		fread(userinfo,sizeof(char) * MAX_INFO_STRING,1,pIn);
175 		ACESP_SpawnBot (NULL, NULL, NULL, userinfo);
176 	}
177 
178     fclose(pIn);
179 }
180 
181 ///////////////////////////////////////////////////////////////////////
182 // Called by PutClient in Server to actually release the bot into the game
183 // Keep from killin' each other when all spawned at once
184 ///////////////////////////////////////////////////////////////////////
ACESP_HoldSpawn(edict_t * self)185 void ACESP_HoldSpawn(edict_t *self)
186 {
187 	if (!KillBox (self))
188 	{	// could't spawn in?
189 	}
190 
191 	gi.linkentity (self);
192 
193 	self->think = ACEAI_Think;
194 	self->nextthink = level.time + FRAMETIME;
195 
196 	// send effect
197 	gi.WriteByte (svc_muzzleflash);
198 	gi.WriteShort (self-g_edicts);
199 	gi.WriteByte (MZ_LOGIN);
200 	gi.multicast (self->s.origin, MULTICAST_PVS);
201 
202 	/*if(ctf->value)
203 	safe_bprintf(PRINT_MEDIUM, "%s joined the %s team.\n",
204 		self->client->pers.netname, CTFTeamName(self->client->resp.ctf_team));
205 	else*/
206 		safe_bprintf (PRINT_MEDIUM, "%s entered the game\n", self->client->pers.netname);
207 
208 }
209 
210 ///////////////////////////////////////////////////////////////////////
211 // Modified version of id's code
212 ///////////////////////////////////////////////////////////////////////
ACESP_PutClientInServer(edict_t * bot,qboolean respawn,int team)213 void ACESP_PutClientInServer (edict_t *bot, qboolean respawn, int team)
214 {
215 	vec3_t	mins = {-16, -16, -24};
216 	vec3_t	maxs = {16, 16, 32};
217 	int		index;
218 	vec3_t	spawn_origin, spawn_angles;
219 	gclient_t	*client;
220 	int		i;
221 	client_persistant_t	saved;
222 	client_respawn_t	resp;
223 //	char *s;
224 
225 	// find a spawn point
226 	// do it before setting health back up, so farthest
227 	// ranging doesn't count this client
228 	SelectSpawnPoint (bot, spawn_origin, spawn_angles);
229 
230 	index = bot-g_edicts-1;
231 	client = bot->client;
232 
233 	// deathmatch wipes most client data every spawn
234 	if (deathmatch->value)
235 	{
236 		char userinfo[MAX_INFO_STRING];
237 
238 		resp = bot->client->resp;
239 		memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
240 		InitClientPersistant (client);
241 		ClientUserinfoChanged (bot, userinfo);
242 	}
243 	else
244 		memset (&resp, 0, sizeof(resp));
245 
246 	// clear everything but the persistant data
247 	saved = client->pers;
248 	memset (client, 0, sizeof(*client));
249 	client->pers = saved;
250 	client->resp = resp;
251 
252 	// copy some data from the client to the entity
253 	FetchClientEntData (bot);
254 
255 	// clear entity values
256 	bot->groundentity = NULL;
257 	bot->client = &game.clients[index];
258 	bot->takedamage = DAMAGE_AIM;
259 	bot->movetype = MOVETYPE_WALK;
260 	bot->viewheight = 24;
261 	bot->classname = "bot";
262 	bot->mass = 200;
263 	bot->solid = SOLID_BBOX;
264 	bot->deadflag = DEAD_NO;
265 	bot->air_finished = level.time + 12;
266 	bot->clipmask = MASK_PLAYERSOLID;
267 	bot->model = "players/male/tris.md2";
268 	bot->pain = player_pain;
269 	bot->die = player_die;
270 	bot->waterlevel = 0;
271 	bot->watertype = 0;
272 	bot->flags &= ~FL_NO_KNOCKBACK;
273 	bot->svflags &= ~SVF_DEADMONSTER;
274 	bot->is_jumping = false;
275 
276 	/*if(ctf->value)
277 	{
278 		client->resp.ctf_team = team;
279 		client->resp.ctf_state = CTF_STATE_START;
280 		s = Info_ValueForKey (client->pers.userinfo, "skin");
281 		CTFAssignSkin(bot, s);
282 	}*/
283 
284 	VectorCopy (mins, bot->mins);
285 	VectorCopy (maxs, bot->maxs);
286 	VectorClear (bot->velocity);
287 
288 	// clear playerstate values
289 	memset (&bot->client->ps, 0, sizeof(client->ps));
290 
291 	client->ps.pmove.origin[0] = spawn_origin[0]*8;
292 	client->ps.pmove.origin[1] = spawn_origin[1]*8;
293 	client->ps.pmove.origin[2] = spawn_origin[2]*8;
294 
295 //ZOID
296 	client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
297 //ZOID
298 
299 	if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
300 	{
301 		client->ps.fov = 90;
302 	}
303 	else
304 	{
305 		client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
306 		if (client->ps.fov < 1)
307 			client->ps.fov = 90;
308 		else if (client->ps.fov > 160)
309 			client->ps.fov = 160;
310 	}
311 
312 	client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
313 
314 	// clear entity state values
315 	bot->s.effects = 0;
316 	bot->s.skinnum = bot - g_edicts - 1;
317 	bot->s.modelindex = 255;		// will use the skin specified model
318 	bot->s.modelindex2 = 255;		// custom gun model
319 	bot->s.frame = 0;
320 	VectorCopy (spawn_origin, bot->s.origin);
321 	bot->s.origin[2] += 1;	// make sure off ground
322 
323 	// set the delta angle
324 	for (i=0 ; i<3 ; i++)
325 		client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
326 
327 	bot->s.angles[PITCH] = 0;
328 	bot->s.angles[YAW] = spawn_angles[YAW];
329 	bot->s.angles[ROLL] = 0;
330 	VectorCopy (bot->s.angles, client->ps.viewangles);
331 	VectorCopy (bot->s.angles, client->v_angle);
332 
333 	// force the current weapon up
334 	client->newweapon = client->pers.weapon;
335 	ChangeWeapon (bot);
336 
337 	bot->enemy = NULL;
338 	bot->movetarget = NULL;
339 	bot->state = STATE_MOVE;
340 
341 	// Set the current node
342 	bot->current_node = ACEND_FindClosestReachableNode(bot,NODE_DENSITY, NODE_ALL);
343 	bot->goal_node = bot->current_node;
344 	bot->next_node = bot->current_node;
345 	bot->next_move_time = level.time;
346 	bot->suicide_timeout = level.time + 15.0;
347 
348 	// If we are not respawning hold off for up to three seconds before releasing into game
349     if(!respawn)
350 	{
351 		bot->think = ACESP_HoldSpawn;
352 		bot->nextthink = level.time + 0.1;
353 		bot->nextthink = level.time + random()*3.0; // up to three seconds
354 	}
355 	else
356 	{
357 		if (!KillBox (bot))
358 		{	// could't spawn in?
359 		}
360 
361 		gi.linkentity (bot);
362 
363 		bot->think = ACEAI_Think;
364 		bot->nextthink = level.time + FRAMETIME;
365 
366 			// send effect
367 		gi.WriteByte (svc_muzzleflash);
368 		gi.WriteShort (bot-g_edicts);
369 		gi.WriteByte (MZ_LOGIN);
370 		gi.multicast (bot->s.origin, MULTICAST_PVS);
371 	}
372 
373 }
374 
375 ///////////////////////////////////////////////////////////////////////
376 // Respawn the bot
377 ///////////////////////////////////////////////////////////////////////
ACESP_Respawn(edict_t * self)378 void ACESP_Respawn (edict_t *self)
379 {
380 	CopyToBodyQue (self);
381 
382 	/*if(ctf->value)
383 		ACESP_PutClientInServer (self,true, self->client->resp.ctf_team);
384 	else*/
385 		ACESP_PutClientInServer (self,true,0);
386 
387 	// add a teleportation effect
388 	self->s.event = EV_PLAYER_TELEPORT;
389 
390 		// hold in place briefly
391 	self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
392 	self->client->ps.pmove.pm_time = 14;
393 
394 	self->client->respawn_time = level.time;
395 
396 }
397 
398 ///////////////////////////////////////////////////////////////////////
399 // Find a free client spot
400 ///////////////////////////////////////////////////////////////////////
ACESP_FindFreeClient(void)401 edict_t *ACESP_FindFreeClient (void)
402 {
403 	edict_t *bot;
404 	int	i;
405 	int max_count=0;
406 
407 	// This is for the naming of the bots
408 	for (i = maxclients->value; i > 0; i--)
409 	{
410 		bot = g_edicts + i + 1;
411 
412 		if(bot->count > max_count)
413 			max_count = bot->count;
414 	}
415 
416 	// Check for free spot
417 	for (i = maxclients->value; i > 0; i--)
418 	{
419 		bot = g_edicts + i + 1;
420 
421 		if (!bot->inuse)
422 			break;
423 	}
424 
425 	bot->count = max_count + 1; // Will become bot name...
426 
427 	if (bot->inuse)
428 		bot = NULL;
429 
430 	return bot;
431 }
432 
433 ///////////////////////////////////////////////////////////////////////
434 // Set the name of the bot and update the userinfo
435 ///////////////////////////////////////////////////////////////////////
ACESP_SetName(edict_t * bot,char * name,char * skin,char * team)436 void ACESP_SetName(edict_t *bot, char *name, char *skin, char *team)
437 {
438 	float rnd;
439 	char userinfo[MAX_INFO_STRING];
440 	char bot_skin[MAX_INFO_STRING];
441 	char bot_name[MAX_INFO_STRING];
442 
443 	// Set the name for the bot.
444 	// name
445 	if(strlen(name) == 0)
446 		SetBotNames(bot_name);
447 	else
448 		strcpy(bot_name,name);
449 
450 	// skin
451 	if(strlen(skin) == 0)
452 	{
453 		// randomly choose skin
454 		rnd = random();
455 		if(rnd  < 0.05)
456 			sprintf(bot_skin,"female/athena");
457 		else if(rnd < 0.1)
458 			sprintf(bot_skin,"female/brianna");
459 		else if(rnd < 0.15)
460 			sprintf(bot_skin,"female/cobalt");
461 		else if(rnd < 0.2)
462 			sprintf(bot_skin,"female/ensign");
463 		else if(rnd < 0.25)
464 			sprintf(bot_skin,"female/jezebel");
465 		else if(rnd < 0.3)
466 			sprintf(bot_skin,"female/jungle");
467 		else if(rnd < 0.35)
468 			sprintf(bot_skin,"female/lotus");
469 		else if(rnd < 0.4)
470 			sprintf(bot_skin,"female/stiletto");
471 		else if(rnd < 0.45)
472 			sprintf(bot_skin,"female/venus");
473 		else if(rnd < 0.5)
474 			sprintf(bot_skin,"female/voodoo");
475 		else if(rnd < 0.55)
476 			sprintf(bot_skin,"male/cipher");
477 		else if(rnd < 0.6)
478 			sprintf(bot_skin,"male/flak");
479 		else if(rnd < 0.65)
480 			sprintf(bot_skin,"male/grunt");
481 		else if(rnd < 0.7)
482 			sprintf(bot_skin,"male/howitzer");
483 		else if(rnd < 0.75)
484 			sprintf(bot_skin,"male/major");
485 		else if(rnd < 0.8)
486 			sprintf(bot_skin,"male/nightops");
487 		else if(rnd < 0.85)
488 			sprintf(bot_skin,"male/pointman");
489 		else if(rnd < 0.9)
490 			sprintf(bot_skin,"male/psycho");
491 		else if(rnd < 0.95)
492 			sprintf(bot_skin,"male/razor");
493 		else
494 			sprintf(bot_skin,"male/sniper");
495 	}
496 	else
497 		strcpy(bot_skin,skin);
498 
499 	// initialise userinfo
500 	memset (userinfo, 0, sizeof(userinfo));
501 
502 	// add bot's name/skin/hand to userinfo
503 	Info_SetValueForKey (userinfo, "name", bot_name);
504 	Info_SetValueForKey (userinfo, "skin", bot_skin);
505 	Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now!
506 
507 	ClientConnect (bot, userinfo);
508 
509 	if (botauto_respawn->value) {
510 		ACESP_SaveBots(); // make sure to save the bots
511 	}
512 }
513 
514 ///////////////////////////////////////////////////////////////////////
515 // Spawn the bot
516 ///////////////////////////////////////////////////////////////////////
ACESP_SpawnBot(char * team,char * name,char * skin,char * userinfo)517 void ACESP_SpawnBot (char *team, char *name, char *skin, char *userinfo)
518 {
519 	edict_t	*bot;
520 
521 	bot = ACESP_FindFreeClient ();
522 
523 	if (!bot)
524 	{
525 		safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
526 		return;
527 	}
528 
529 	bot->yaw_speed = 100; // yaw speed
530 	bot->inuse = true;
531 	bot->is_bot = true;
532 
533 	// To allow bots to respawn
534 	if(userinfo == NULL)
535 		ACESP_SetName(bot, name, skin, team);
536 	else
537 		ClientConnect (bot, userinfo);
538 
539 	G_InitEdict (bot);
540 
541 	InitClientResp (bot->client);
542 
543 	// locate ent at a spawn point
544     /*if(ctf->value)
545 	{
546 		if (team != NULL && strcmp(team,"red")==0)
547 			ACESP_PutClientInServer (bot,false, CTF_TEAM1);
548 		else
549 			ACESP_PutClientInServer (bot,false, CTF_TEAM2);
550 	}
551 	else*/
552  		ACESP_PutClientInServer (bot,false,0);
553 
554 	// make sure all view stuff is valid
555 	ClientEndServerFrame (bot);
556 
557 	ACEIT_PlayerAdded (bot); // let the world know we added another
558 
559 	ACEAI_PickLongRangeGoal(bot); // pick a new goal
560 
561 }
562 
563 ///////////////////////////////////////////////////////////////////////
564 // Remove a bot by name or all bots
565 ///////////////////////////////////////////////////////////////////////
ACESP_RemoveBot(char * name)566 void ACESP_RemoveBot(char *name)
567 {
568 	int i;
569 	qboolean freed=false;
570 	edict_t *bot;
571 
572 	for(i=0;i<maxclients->value;i++)
573 	{
574 		bot = g_edicts + i + 1;
575 		if(bot->inuse)
576 		{
577 			if(bot->is_bot && (strcmp(bot->client->pers.netname,name)==0 || strcmp(name,"all")==0))
578 			{
579 				bot->health = 0;
580 				player_die (bot, bot, bot, 100000, vec3_origin);
581 				// don't even bother waiting for death frames
582 				bot->deadflag = DEAD_DEAD;
583 				bot->inuse = false;
584 				freed = true;
585 				ACEIT_PlayerRemoved (bot);
586 				safe_bprintf (PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
587 			}
588 		}
589 	}
590 
591 	if(!freed)
592 		safe_bprintf (PRINT_MEDIUM, "%s not found\n", name);
593 
594 	if (botauto_respawn->value) {
595 		ACESP_SaveBots();  // Save them again
596 	}
597 
598 }
599 
600 
601