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