1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 /*****************************************************************************
31  * name:		ai_main.c
32  *
33  * desc:		Quake3 bot AI
34  *
35  *
36  *****************************************************************************/
37 
38 #include "g_local.h"
39 #include "../qcommon/q_shared.h"
40 #include "../botlib/botlib.h"      //bot lib interface
41 #include "../botlib/be_aas.h"
42 #include "../botlib/be_ea.h"
43 #include "../botlib/be_ai_char.h"
44 #include "../botlib/be_ai_chat.h"
45 #include "../botlib/be_ai_gen.h"
46 #include "../botlib/be_ai_goal.h"
47 #include "../botlib/be_ai_move.h"
48 #include "../botlib/be_ai_weap.h"
49 #include "../botlib/botai.h"          //bot ai interface
50 
51 #include "ai_main.h"
52 #include "ai_dmq3.h"
53 #include "ai_chat.h"
54 #include "ai_cmd.h"
55 #include "ai_dmnet.h"
56 //
57 #include "chars.h"
58 #include "inv.h"
59 #include "syn.h"
60 
61 //bot states
62 bot_state_t *botstates[MAX_CLIENTS];
63 //number of bots
64 int numbots;
65 //time to do a regular update
66 float regularupdate_time;
67 //
68 vmCvar_t bot_thinktime;
69 vmCvar_t memorydump;
70 
71 
72 /*
73 ==================
74 BotAI_Print
75 ==================
76 */
BotAI_Print(int type,char * fmt,...)77 void QDECL BotAI_Print( int type, char *fmt, ... ) {
78 	char str[2048];
79 	va_list ap;
80 
81 	va_start( ap, fmt );
82 	Q_vsnprintf(str, sizeof(str), fmt, ap);
83 	va_end( ap );
84 
85 	switch ( type ) {
86 	case PRT_MESSAGE: {
87 		G_Printf( "%s", str );
88 		break;
89 	}
90 	case PRT_WARNING: {
91 		G_Printf( S_COLOR_YELLOW "Warning: %s", str );
92 		break;
93 	}
94 	case PRT_ERROR: {
95 		G_Printf( S_COLOR_RED "Error: %s", str );
96 		break;
97 	}
98 	case PRT_FATAL: {
99 		G_Printf( S_COLOR_RED "Fatal: %s", str );
100 		break;
101 	}
102 	case PRT_EXIT: {
103 		G_Error( S_COLOR_RED "Exit: %s", str );
104 		break;
105 	}
106 	default: {
107 		G_Printf( "unknown print type\n" );
108 		break;
109 	}
110 	}
111 }
112 
113 /*
114 ==================
115 BotAI_Trace
116 ==================
117 */
BotAI_Trace(bsp_trace_t * bsptrace,vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int passent,int contentmask)118 void BotAI_Trace( bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask ) {
119 	trace_t trace;
120 
121 	trap_Trace( &trace, start, mins, maxs, end, passent, contentmask );
122 	//copy the trace information
123 	bsptrace->allsolid = trace.allsolid;
124 	bsptrace->startsolid = trace.startsolid;
125 	bsptrace->fraction = trace.fraction;
126 	VectorCopy( trace.endpos, bsptrace->endpos );
127 	bsptrace->plane.dist = trace.plane.dist;
128 	VectorCopy( trace.plane.normal, bsptrace->plane.normal );
129 	bsptrace->plane.signbits = trace.plane.signbits;
130 	bsptrace->plane.type = trace.plane.type;
131 	bsptrace->surface.value = 0;
132 	bsptrace->surface.flags = trace.surfaceFlags;
133 	bsptrace->ent = trace.entityNum;
134 	bsptrace->exp_dist = 0;
135 	bsptrace->sidenum = 0;
136 	bsptrace->contents = 0;
137 }
138 
139 /*
140 ==================
141 BotAI_GetClientState
142 ==================
143 */
BotAI_GetClientState(int clientNum,playerState_t * state)144 int BotAI_GetClientState( int clientNum, playerState_t *state ) {
145 	gentity_t   *ent;
146 
147 	ent = &g_entities[clientNum];
148 	if ( !ent->inuse ) {
149 		return qfalse;
150 	}
151 	if ( !ent->client ) {
152 		return qfalse;
153 	}
154 
155 	memcpy( state, &ent->client->ps, sizeof( playerState_t ) );
156 	return qtrue;
157 }
158 
159 /*
160 ==================
161 BotAI_GetEntityState
162 ==================
163 */
BotAI_GetEntityState(int entityNum,entityState_t * state)164 int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
165 	gentity_t   *ent;
166 
167 	ent = &g_entities[entityNum];
168 	memset( state, 0, sizeof( entityState_t ) );
169 	if ( !ent->inuse ) {
170 		return qfalse;
171 	}
172 	if ( !ent->r.linked ) {
173 		return qfalse;
174 	}
175 	if ( ent->r.svFlags & SVF_NOCLIENT ) {
176 		return qfalse;
177 	}
178 	memcpy( state, &ent->s, sizeof( entityState_t ) );
179 	return qtrue;
180 }
181 
182 /*
183 ==================
184 BotAI_GetSnapshotEntity
185 ==================
186 */
BotAI_GetSnapshotEntity(int clientNum,int sequence,entityState_t * state)187 int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) {
188 	int entNum;
189 
190 	entNum = trap_BotGetSnapshotEntity( clientNum, sequence );
191 	if ( entNum == -1 ) {
192 		memset( state, 0, sizeof( entityState_t ) );
193 		return -1;
194 	}
195 
196 	BotAI_GetEntityState( entNum, state );
197 
198 	return sequence + 1;
199 }
200 
201 /*
202 ==================
203 BotAI_BotInitialChat
204 ==================
205 */
BotAI_BotInitialChat(bot_state_t * bs,char * type,...)206 void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) {
207 	int i, mcontext;
208 	va_list ap;
209 	char    *p;
210 	char    *vars[MAX_MATCHVARIABLES];
211 
212 	memset( vars, 0, sizeof( vars ) );
213 	va_start( ap, type );
214 	p = va_arg( ap, char * );
215 	for ( i = 0; i < MAX_MATCHVARIABLES; i++ ) {
216 		if ( !p ) {
217 			break;
218 		}
219 		vars[i] = p;
220 		p = va_arg( ap, char * );
221 	}
222 	va_end( ap );
223 
224 	mcontext = CONTEXT_NORMAL | CONTEXT_NEARBYITEM | CONTEXT_NAMES;
225 	if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) {
226 		mcontext |= CONTEXT_CTFREDTEAM;
227 	} else { mcontext |= CONTEXT_CTFBLUETEAM;}
228 
229 	trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] );
230 }
231 
232 /*
233 ==============
234 BotInterbreeding
235 ==============
236 */
BotInterbreeding(void)237 void BotInterbreeding( void ) {
238 	float ranks[MAX_CLIENTS];
239 	int parent1, parent2, child;
240 	int i;
241 
242 	// get rankings for all the bots
243 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
244 		if ( botstates[i] && botstates[i]->inuse ) {
245 			ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths;
246 		} else {
247 			ranks[i] = -1;
248 		}
249 	}
250 
251 	if ( trap_GeneticParentsAndChildSelection( MAX_CLIENTS, ranks, &parent1, &parent2, &child ) ) {
252 		trap_BotInterbreedGoalFuzzyLogic( botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs );
253 		trap_BotMutateGoalFuzzyLogic( botstates[child]->gs, 1 );
254 	}
255 	// reset the kills and deaths
256 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
257 		if ( botstates[i] && botstates[i]->inuse ) {
258 			botstates[i]->num_kills = 0;
259 			botstates[i]->num_deaths = 0;
260 		}
261 	}
262 }
263 
264 /*
265 ==============
266 BotEntityInfo
267 ==============
268 */
BotEntityInfo(int entnum,aas_entityinfo_t * info)269 void BotEntityInfo( int entnum, aas_entityinfo_t *info ) {
270 	trap_AAS_EntityInfo( entnum, info );
271 }
272 
273 /*
274 ==============
275 NumBots
276 ==============
277 */
NumBots(void)278 int NumBots( void ) {
279 	return numbots;
280 }
281 
282 /*
283 ==============
284 AngleDifference
285 ==============
286 */
AngleDifference(float ang1,float ang2)287 float AngleDifference( float ang1, float ang2 ) {
288 	float diff;
289 
290 	diff = ang1 - ang2;
291 	if ( ang1 > ang2 ) {
292 		if ( diff > 180.0 ) {
293 			diff -= 360.0;
294 		}
295 	} else {
296 		if ( diff < -180.0 ) {
297 			diff += 360.0;
298 		}
299 	}
300 	return diff;
301 }
302 
303 /*
304 ==============
305 BotChangeViewAngle
306 ==============
307 */
BotChangeViewAngle(float angle,float ideal_angle,float speed)308 float BotChangeViewAngle( float angle, float ideal_angle, float speed ) {
309 	float move;
310 
311 	angle = AngleMod( angle );
312 	ideal_angle = AngleMod( ideal_angle );
313 	if ( angle == ideal_angle ) {
314 		return angle;
315 	}
316 	move = ideal_angle - angle;
317 	if ( ideal_angle > angle ) {
318 		if ( move > 180.0 ) {
319 			move -= 360.0;
320 		}
321 	} else {
322 		if ( move < -180.0 ) {
323 			move += 360.0;
324 		}
325 	}
326 	if ( move > 0 ) {
327 		if ( move > speed ) {
328 			move = speed;
329 		}
330 	} else {
331 		if ( move < -speed ) {
332 			move = -speed;
333 		}
334 	}
335 	return AngleMod( angle + move );
336 }
337 
338 /*
339 ==============
340 BotChangeViewAngles
341 ==============
342 */
BotChangeViewAngles(bot_state_t * bs,float thinktime)343 void BotChangeViewAngles( bot_state_t *bs, float thinktime ) {
344 	float diff, factor, maxchange, anglespeed;
345 	int i;
346 
347 	if ( bs->ideal_viewangles[PITCH] > 180 ) {
348 		bs->ideal_viewangles[PITCH] -= 360;
349 	}
350 	//
351 	if ( bs->enemy >= 0 ) {
352 		factor = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01, 1 );
353 		maxchange = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800 );
354 	} else {
355 		factor = 0.25;
356 		maxchange = 300;
357 	}
358 	maxchange *= thinktime;
359 	for ( i = 0; i < 2; i++ ) {
360 		diff = fabs( AngleDifference( bs->viewangles[i], bs->ideal_viewangles[i] ) );
361 		anglespeed = diff * factor;
362 		if ( anglespeed > maxchange ) {
363 			anglespeed = maxchange;
364 		}
365 		bs->viewangles[i] = BotChangeViewAngle( bs->viewangles[i],
366 												bs->ideal_viewangles[i], anglespeed );
367 		//BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);`
368 		//bs->viewangles[i] = bs->ideal_viewangles[i];
369 	}
370 	if ( bs->viewangles[PITCH] > 180 ) {
371 		bs->viewangles[PITCH] -= 360;
372 	}
373 	//elementary action: view
374 	trap_EA_View( bs->client, bs->viewangles );
375 }
376 
377 /*
378 ==============
379 BotInputToUserCommand
380 ==============
381 */
BotInputToUserCommand(bot_input_t * bi,usercmd_t * ucmd,int delta_angles[3],int time)382 void BotInputToUserCommand( bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time ) {
383 	vec3_t angles, forward, right;
384 	short temp;
385 	int j;
386 	float f, r, u, m;
387 
388 	//clear the whole structure
389 	memset( ucmd, 0, sizeof( usercmd_t ) );
390 	//
391 	//the duration for the user command in milli seconds
392 	ucmd->serverTime = time;
393 	//
394 	if ( bi->actionflags & ACTION_DELAYEDJUMP ) {
395 		bi->actionflags |= ACTION_JUMP;
396 		bi->actionflags &= ~ACTION_DELAYEDJUMP;
397 	}
398 	//set the buttons
399 	if ( bi->actionflags & ACTION_RESPAWN ) {
400 		ucmd->buttons = BUTTON_ATTACK;
401 	}
402 	if ( bi->actionflags & ACTION_ATTACK ) {
403 		ucmd->buttons |= BUTTON_ATTACK;
404 	}
405 	if ( bi->actionflags & ACTION_TALK ) {
406 		ucmd->buttons |= BUTTON_TALK;
407 	}
408 	if ( bi->actionflags & ACTION_GESTURE ) {
409 		ucmd->buttons |= BUTTON_GESTURE;
410 	}
411 	if ( bi->actionflags & ACTION_USE ) {
412 		ucmd->buttons |= BUTTON_USE_HOLDABLE;
413 	}
414 	if ( bi->actionflags & ACTION_WALK ) {
415 		ucmd->buttons |= BUTTON_WALKING;
416 	}
417 	ucmd->weapon = bi->weapon;
418 	//set the view angles
419 	//NOTE: the ucmd->angles are the angles WITHOUT the delta angles
420 	ucmd->angles[PITCH] = ANGLE2SHORT( bi->viewangles[PITCH] );
421 	ucmd->angles[YAW] = ANGLE2SHORT( bi->viewangles[YAW] );
422 	ucmd->angles[ROLL] = ANGLE2SHORT( bi->viewangles[ROLL] );
423 	//subtract the delta angles
424 	for ( j = 0; j < 3; j++ ) {
425 		temp = ucmd->angles[j] - delta_angles[j];
426 		/*NOTE: disabled because temp should be mod first
427 		if ( j == PITCH ) {
428 			// don't let the player look up or down more than 90 degrees
429 			if ( temp > 16000 ) temp = 16000;
430 			else if ( temp < -16000 ) temp = -16000;
431 		}
432 		*/
433 		ucmd->angles[j] = temp;
434 	}
435 	//NOTE: movement is relative to the REAL view angles
436 	//get the horizontal forward and right vector
437 	//get the pitch in the range [-180, 180]
438 	if ( bi->dir[2] ) {
439 		angles[PITCH] = bi->viewangles[PITCH];
440 	} else { angles[PITCH] = 0;}
441 	angles[YAW] = bi->viewangles[YAW];
442 	angles[ROLL] = 0;
443 	AngleVectors( angles, forward, right, NULL );
444 	//bot input speed is in the range [0, 400]
445 	bi->speed = bi->speed * 127 / 400;
446 	//set the view independent movement
447 	f = DotProduct(forward, bi->dir);
448 	r = DotProduct(right, bi->dir);
449 	u = fabs(forward[2]) * bi->dir[2];
450 	m = fabs(f);
451 
452 	if (fabs(r) > m) {
453 		m = fabs(r);
454 	}
455 
456 	if (fabs(u) > m) {
457 		m = fabs(u);
458 	}
459 
460 	if (m > 0) {
461 		f *= bi->speed / m;
462 		r *= bi->speed / m;
463 		u *= bi->speed / m;
464 	}
465 
466 	ucmd->forwardmove = f;
467 	ucmd->rightmove = r;
468 	ucmd->upmove = u;
469 
470 	if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove = 127;
471 	if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove = -127;
472 	if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove = -127;
473 	if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove = 127;
474 	//jump/moveup
475 	if (bi->actionflags & ACTION_JUMP) ucmd->upmove = 127;
476 	//crouch/movedown
477 	if (bi->actionflags & ACTION_CROUCH) ucmd->upmove = -127;
478 }
479 
480 /*
481 ==============
482 BotUpdateInput
483 ==============
484 */
BotUpdateInput(bot_state_t * bs,int time)485 void BotUpdateInput( bot_state_t *bs, int time ) {
486 	bot_input_t bi;
487 	int j;
488 
489 	//add the delta angles to the bot's current view angles
490 	for ( j = 0; j < 3; j++ ) {
491 		bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
492 	}
493 	//
494 	BotChangeViewAngles( bs, (float) time / 1000 );
495 	trap_EA_GetInput( bs->client, (float) time / 1000, &bi );
496 	//respawn hack
497 	if ( bi.actionflags & ACTION_RESPAWN ) {
498 		if ( bs->lastucmd.buttons & BUTTON_ATTACK ) {
499 			bi.actionflags &= ~( ACTION_RESPAWN | ACTION_ATTACK );
500 		}
501 	}
502 	//
503 	BotInputToUserCommand( &bi, &bs->lastucmd, bs->cur_ps.delta_angles, time );
504 	bs->lastucmd.serverTime = time;
505 	//subtract the delta angles
506 	for ( j = 0; j < 3; j++ ) {
507 		bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
508 	}
509 }
510 
511 /*
512 ==============
513 BotAIRegularUpdate
514 ==============
515 */
BotAIRegularUpdate(void)516 void BotAIRegularUpdate( void ) {
517 	if ( regularupdate_time < trap_AAS_Time() ) {
518 		trap_BotUpdateEntityItems();
519 		regularupdate_time = trap_AAS_Time() + 1;
520 	}
521 }
522 
523 /*
524 ==============
525 BotAI
526 ==============
527 */
BotAI(int client,float thinktime)528 int BotAI( int client, float thinktime ) {
529 	bot_state_t *bs;
530 	char buf[1024], *args;
531 	int j;
532 
533 	trap_EA_ResetInput( client, NULL );
534 	//
535 	bs = botstates[client];
536 	if ( !bs || !bs->inuse ) {
537 		BotAI_Print( PRT_FATAL, "client %d hasn't been setup\n", client );
538 		return BLERR_AICLIENTNOTSETUP;
539 	}
540 
541 	//retrieve the current client state
542 	if ( !BotAI_GetClientState( client, &bs->cur_ps ) ) {
543 		BotAI_Print( PRT_FATAL, "BotAI: failed to get player state for player %d\n", client );
544 		return qfalse;
545 	}
546 
547 	//retrieve any waiting console messages
548 	while ( trap_BotGetServerCommand( client, buf, sizeof( buf ) ) ) {
549 		//have buf point to the command and args to the command arguments
550 		args = strchr( buf, ' ' );
551 		if ( !args ) {
552 			continue;
553 		}
554 		*args++ = '\0';
555 
556 		//remove color espace sequences from the arguments
557 		Q_CleanStr( args );
558 
559 		//botai_import.Print(PRT_MESSAGE, "ConsoleMessage: \"%s\"\n", buf);
560 		if ( !Q_stricmp( buf, "cp " ) ) { /*CenterPrintf*/
561 		} else if ( !Q_stricmp( buf, "cs" ) )                                                      { /*ConfigStringModified*/
562 		} else if ( !Q_stricmp( buf, "print" ) )                                                                                                                       {
563 			trap_BotQueueConsoleMessage( bs->cs, CMS_NORMAL, args );
564 		} else if ( !Q_stricmp( buf, "chat" ) ) {
565 			trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args );
566 		} else if ( !Q_stricmp( buf, "tchat" ) ) {
567 			trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args );
568 		} else if ( !Q_stricmp( buf, "scores" ) ) { /*FIXME: parse scores?*/
569 		} else if ( !Q_stricmp( buf, "clientLevelShot" ) )                                                                    { /*ignore*/
570 		}
571 	}
572 	//add the delta angles to the bot's current view angles
573 	for ( j = 0; j < 3; j++ ) {
574 		bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
575 	}
576 	//increase the local time of the bot
577 	bs->ltime += thinktime;
578 	//
579 	bs->thinktime = thinktime;
580 	//origin of the bot
581 	VectorCopy( bs->cur_ps.origin, bs->origin );
582 	//eye coordinates of the bot
583 	VectorCopy( bs->cur_ps.origin, bs->eye );
584 	bs->eye[2] += bs->cur_ps.viewheight;
585 	//get the area the bot is in
586 	bs->areanum = BotPointAreaNum( bs->origin );
587 	//the real AI
588 	BotDeathmatchAI( bs, thinktime );
589 	//set the weapon selection every AI frame
590 	trap_EA_SelectWeapon( bs->client, bs->weaponnum );
591 	//subtract the delta angles
592 	for ( j = 0; j < 3; j++ ) {
593 		bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
594 	}
595 	//everything was ok
596 	return BLERR_NOERROR;
597 }
598 
599 /*
600 ==================
601 BotScheduleBotThink
602 ==================
603 */
BotScheduleBotThink(void)604 void BotScheduleBotThink( void ) {
605 	int i, botnum;
606 
607 	botnum = 0;
608 
609 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
610 		if ( !botstates[i] || !botstates[i]->inuse ) {
611 			continue;
612 		}
613 		//initialize the bot think residual time
614 		botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots;
615 		botnum++;
616 	}
617 }
618 
619 /*
620 ==============
621 BotAISetupClient
622 ==============
623 */
BotAISetupClient(int client,struct bot_settings_s * settings)624 int BotAISetupClient( int client, struct bot_settings_s *settings ) {
625 	char filename[144], name[144], gender[144];
626 	bot_state_t *bs;
627 	int errnum;
628 
629 	if ( !botstates[client] ) {
630 		botstates[client] = G_Alloc( sizeof( bot_state_t ) );
631 	}
632 	bs = botstates[client];
633 
634 	if ( !bs ) {
635 		return qfalse;
636 	}
637 
638 	if ( bs && bs->inuse ) {
639 		BotAI_Print( PRT_FATAL, "client %d already setup\n", client );
640 		return qfalse;
641 	}
642 
643 	if ( !trap_AAS_Initialized() ) {
644 		BotAI_Print( PRT_FATAL, "AAS not initialized\n" );
645 		return qfalse;
646 	}
647 
648 	//load the bot character
649 	bs->character = trap_BotLoadCharacter( settings->characterfile, settings->skill );
650 	if ( !bs->character ) {
651 		BotAI_Print( PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile );
652 		return qfalse;
653 	}
654 	//copy the settings
655 	memcpy( &bs->settings, settings, sizeof( bot_settings_t ) );
656 	//allocate a goal state
657 	bs->gs = trap_BotAllocGoalState( client );
658 	//load the item weights
659 	trap_Characteristic_String( bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, sizeof( filename ) );
660 	errnum = trap_BotLoadItemWeights( bs->gs, filename );
661 	if ( errnum != BLERR_NOERROR ) {
662 		trap_BotFreeGoalState( bs->gs );
663 		return qfalse;
664 	}
665 	//allocate a weapon state
666 	bs->ws = trap_BotAllocWeaponState();
667 	//load the weapon weights
668 	trap_Characteristic_String( bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, sizeof( filename ) );
669 	errnum = trap_BotLoadWeaponWeights( bs->ws, filename );
670 	if ( errnum != BLERR_NOERROR ) {
671 		trap_BotFreeGoalState( bs->gs );
672 		trap_BotFreeWeaponState( bs->ws );
673 		return qfalse;
674 	}
675 	//allocate a chat state
676 	bs->cs = trap_BotAllocChatState();
677 	//load the chat file
678 	trap_Characteristic_String( bs->character, CHARACTERISTIC_CHAT_FILE, filename, sizeof( filename ) );
679 	trap_Characteristic_String( bs->character, CHARACTERISTIC_CHAT_NAME, name, sizeof( name ) );
680 	errnum = trap_BotLoadChatFile( bs->cs, filename, name );
681 	if ( errnum != BLERR_NOERROR ) {
682 		trap_BotFreeChatState( bs->cs );
683 		trap_BotFreeGoalState( bs->gs );
684 		trap_BotFreeWeaponState( bs->ws );
685 		return qfalse;
686 	}
687 	//get the gender characteristic
688 	trap_Characteristic_String( bs->character, CHARACTERISTIC_GENDER, gender, sizeof( gender ) );
689 	//set the chat gender
690 	if ( *gender == 'f' || *gender == 'F' ) {
691 		trap_BotSetChatGender( bs->cs, CHAT_GENDERFEMALE );
692 	} else if ( *gender == 'm' || *gender == 'M' )  {
693 		trap_BotSetChatGender( bs->cs, CHAT_GENDERMALE );
694 	} else { trap_BotSetChatGender( bs->cs, CHAT_GENDERLESS );}
695 
696 	bs->inuse = qtrue;
697 	bs->client = client;
698 	bs->entitynum = client;
699 	bs->setupcount = 4;
700 	bs->entergame_time = trap_AAS_Time();
701 	bs->ms = trap_BotAllocMoveState();
702 	bs->walker = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_WALKER, 0, 1 );
703 	numbots++;
704 
705 	if ( trap_Cvar_VariableIntegerValue( "bot_testichat" ) ) {
706 		trap_BotLibVarSet( "bot_testichat", "1" );
707 		BotChatTest( bs );
708 	}
709 	//NOTE: reschedule the bot thinking
710 	BotScheduleBotThink();
711 	//
712 	return qtrue;
713 }
714 
715 /*
716 ==============
717 BotAIShutdownClient
718 ==============
719 */
BotAIShutdownClient(int client)720 int BotAIShutdownClient( int client ) {
721 	bot_state_t *bs;
722 
723 	// Wolfenstein
724 	if ( g_entities[client].r.svFlags & SVF_CASTAI ) {
725 		AICast_ShutdownClient( client );
726 		return BLERR_NOERROR;
727 	}
728 	// done.
729 
730 	bs = botstates[client];
731 	if ( !bs || !bs->inuse ) {
732 		// BotAI_Print(PRT_ERROR, "client %d already shutdown\n", client);
733 		return BLERR_AICLIENTALREADYSHUTDOWN;
734 	}
735 
736 	if ( BotChat_ExitGame( bs ) ) {
737 		trap_BotEnterChat( bs->cs, bs->client, CHAT_ALL );
738 	}
739 
740 	trap_BotFreeMoveState( bs->ms );
741 	//free the goal state
742 	trap_BotFreeGoalState( bs->gs );
743 	//free the chat file
744 	trap_BotFreeChatState( bs->cs );
745 	//free the weapon weights
746 	trap_BotFreeWeaponState( bs->ws );
747 	//free the bot character
748 	trap_BotFreeCharacter( bs->character );
749 	//
750 	BotFreeWaypoints( bs->checkpoints );
751 	BotFreeWaypoints( bs->patrolpoints );
752 	//clear the bot state
753 	memset( bs, 0, sizeof( bot_state_t ) );
754 	//set the inuse flag to qfalse
755 	bs->inuse = qfalse;
756 	//there's one bot less
757 	numbots--;
758 	//everything went ok
759 	return BLERR_NOERROR;
760 }
761 
762 /*
763 ==============
764 BotResetState
765 
766 called when a bot enters the intermission or observer mode and
767 when the level is changed
768 ==============
769 */
BotResetState(bot_state_t * bs)770 void BotResetState( bot_state_t *bs ) {
771 	int client, entitynum, inuse;
772 	int movestate, goalstate, chatstate, weaponstate;
773 	bot_settings_t settings;
774 	int character;
775 	playerState_t ps;                           //current player state
776 	float entergame_time;
777 
778 	//save some things that should not be reset here
779 	memcpy( &settings, &bs->settings, sizeof( bot_settings_t ) );
780 	memcpy( &ps, &bs->cur_ps, sizeof( playerState_t ) );
781 	inuse = bs->inuse;
782 	client = bs->client;
783 	entitynum = bs->entitynum;
784 	character = bs->character;
785 	movestate = bs->ms;
786 	goalstate = bs->gs;
787 	chatstate = bs->cs;
788 	weaponstate = bs->ws;
789 	entergame_time = bs->entergame_time;
790 	//free checkpoints and patrol points
791 	BotFreeWaypoints( bs->checkpoints );
792 	BotFreeWaypoints( bs->patrolpoints );
793 	//reset the whole state
794 	memset( bs, 0, sizeof( bot_state_t ) );
795 	//copy back some state stuff that should not be reset
796 	bs->ms = movestate;
797 	bs->gs = goalstate;
798 	bs->cs = chatstate;
799 	bs->ws = weaponstate;
800 	memcpy( &bs->cur_ps, &ps, sizeof( playerState_t ) );
801 	memcpy( &bs->settings, &settings, sizeof( bot_settings_t ) );
802 	bs->inuse = inuse;
803 	bs->client = client;
804 	bs->entitynum = entitynum;
805 	bs->character = character;
806 	bs->entergame_time = entergame_time;
807 	//reset several states
808 	if ( bs->ms ) {
809 		trap_BotResetMoveState( bs->ms );
810 	}
811 	if ( bs->gs ) {
812 		trap_BotResetGoalState( bs->gs );
813 	}
814 	if ( bs->ws ) {
815 		trap_BotResetWeaponState( bs->ws );
816 	}
817 	if ( bs->gs ) {
818 		trap_BotResetAvoidGoals( bs->gs );
819 	}
820 	if ( bs->ms ) {
821 		trap_BotResetAvoidReach( bs->ms );
822 	}
823 }
824 
825 /*
826 ==============
827 BotAILoadMap
828 ==============
829 */
BotAILoadMap(int restart)830 int BotAILoadMap( int restart ) {
831 	int i;
832 	vmCvar_t mapname;
833 
834 	if ( !restart ) {
835 		trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
836 		trap_BotLibLoadMap( mapname.string );
837 	}
838 
839 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
840 		if ( botstates[i] && botstates[i]->inuse ) {
841 			BotResetState( botstates[i] );
842 			botstates[i]->setupcount = 4;
843 		}
844 	}
845 
846 	BotSetupDeathmatchAI();
847 
848 	return BLERR_NOERROR;
849 }
850 
851 /*
852 ==================
853 BotAIStartFrame
854 ==================
855 */
BotAIStartFrame(int time)856 int BotAIStartFrame( int time ) {
857 	int i;
858 	gentity_t   *ent;
859 	bot_entitystate_t state;
860 	//entityState_t entitystate;
861 	//vec3_t mins = {-15, -15, -24}, maxs = {15, 15, 32};
862 	int elapsed_time, thinktime;
863 	static int local_time;
864 	static int botlib_residual;
865 	static int lastbotthink_time;
866 
867 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
868 		G_CheckBotSpawn();
869 	}
870 
871 	trap_Cvar_Update( &bot_rocketjump );
872 	trap_Cvar_Update( &bot_grapple );
873 	trap_Cvar_Update( &bot_fastchat );
874 	trap_Cvar_Update( &bot_nochat );
875 	trap_Cvar_Update( &bot_testrchat );
876 	trap_Cvar_Update( &bot_thinktime );
877 	// Ridah, set the default AAS world
878 	trap_AAS_SetCurrentWorld( 0 );
879 	trap_Cvar_Update( &memorydump );
880 
881 	if ( memorydump.integer ) {
882 		trap_BotLibVarSet( "memorydump", "1" );
883 		trap_Cvar_Set( "memorydump", "0" );
884 	}
885 
886 	//if the bot think time changed we should reschedule the bots
887 	if ( bot_thinktime.integer != lastbotthink_time ) {
888 		lastbotthink_time = bot_thinktime.integer;
889 		BotScheduleBotThink();
890 	}
891 
892 	elapsed_time = time - local_time;
893 	local_time = time;
894 
895 	botlib_residual += elapsed_time;
896 
897 	if ( elapsed_time > bot_thinktime.integer ) {
898 		thinktime = elapsed_time;
899 	} else { thinktime = bot_thinktime.integer;}
900 
901 	// update the bot library
902 	if ( botlib_residual >= thinktime ) {
903 		botlib_residual -= thinktime;
904 
905 		trap_BotLibStartFrame( (float) time / 1000 );
906 
907 		// Ridah, only check the default world
908 		trap_AAS_SetCurrentWorld( 0 );
909 
910 		if ( !trap_AAS_Initialized() ) {
911 			return BLERR_NOERROR;
912 		}
913 
914 		//update entities in the botlib
915 		for ( i = 0; i < MAX_GENTITIES; i++ ) {
916 
917 			// Ridah, in single player, we only need client entity information
918 			if ( g_gametype.integer == GT_SINGLE_PLAYER && i > level.maxclients ) {
919 				break;
920 			}
921 
922 			ent = &g_entities[i];
923 			if ( !ent->inuse ) {
924 				continue;
925 			}
926 			if ( !ent->r.linked ) {
927 				continue;
928 			}
929 			if ( ent->r.svFlags & SVF_NOCLIENT ) {
930 				continue;
931 			}
932 			if ( ent->aiInactive ) {
933 				continue;
934 			}
935 			//
936 			memset( &state, 0, sizeof( bot_entitystate_t ) );
937 			//
938 			VectorCopy( ent->r.currentOrigin, state.origin );
939 			VectorCopy( ent->r.currentAngles, state.angles );
940 			VectorCopy( ent->s.origin2, state.old_origin );
941 			VectorCopy( ent->r.mins, state.mins );
942 			VectorCopy( ent->r.maxs, state.maxs );
943 			state.type = ent->s.eType;
944 			state.flags = ent->s.eFlags;
945 			if ( ent->r.bmodel ) {
946 				state.solid = SOLID_BSP;
947 			} else { state.solid = SOLID_BBOX;}
948 			state.groundent = ent->s.groundEntityNum;
949 			state.modelindex = ent->s.modelindex;
950 			state.modelindex2 = ent->s.modelindex2;
951 			state.frame = ent->s.frame;
952 			//state.event = ent->s.event;
953 			//state.eventParm = ent->s.eventParm;
954 			state.powerups = ent->s.powerups;
955 			state.legsAnim = ent->s.legsAnim;
956 			state.torsoAnim = ent->s.torsoAnim;
957 //			state.weapAnim = ent->s.weapAnim;	//----(SA)
958 //----(SA)	didn't want to comment in as I wasn't sure of any implications of changing the aas_entityinfo_t and bot_entitystate_t structures.
959 			state.weapon = ent->s.weapon;
960 			/*
961 			if (!BotAI_GetEntityState(i, &entitystate)) continue;
962 			//
963 			memset(&state, 0, sizeof(bot_entitystate_t));
964 			//
965 			VectorCopy(entitystate.pos.trBase, state.origin);
966 			VectorCopy(entitystate.angles, state.angles);
967 			VectorCopy(ent->s.origin2, state.old_origin);
968 			//VectorCopy(ent->r.mins, state.mins);
969 			//VectorCopy(ent->r.maxs, state.maxs);
970 			state.type = entitystate.eType;
971 			state.flags = entitystate.eFlags;
972 			if (ent->r.bmodel) state.solid = SOLID_BSP;
973 			else state.solid = SOLID_BBOX;
974 			state.modelindex = entitystate.modelindex;
975 			state.modelindex2 = entitystate.modelindex2;
976 			state.frame = entitystate.frame;
977 			state.event = entitystate.event;
978 			state.eventParm = entitystate.eventParm;
979 			state.powerups = entitystate.powerups;
980 			state.legsAnim = entitystate.legsAnim;
981 			state.torsoAnim = entitystate.torsoAnim;
982 			state.weapon = entitystate.weapon;
983 			*/
984 			//
985 			trap_BotLibUpdateEntity( i, &state );
986 		}
987 
988 		BotAIRegularUpdate();
989 
990 	}
991 
992 	// Ridah, in single player, don't need bot's thinking
993 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
994 		return BLERR_NOERROR;
995 	}
996 
997 	// execute scheduled bot AI
998 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
999 		if ( !botstates[i] || !botstates[i]->inuse ) {
1000 			continue;
1001 		}
1002 		// Ridah
1003 		if ( g_entities[i].r.svFlags & SVF_CASTAI ) {
1004 			continue;
1005 		}
1006 		// done.
1007 		//
1008 		botstates[i]->botthink_residual += elapsed_time;
1009 		//
1010 		if ( botstates[i]->botthink_residual >= thinktime ) {
1011 			botstates[i]->botthink_residual -= thinktime;
1012 
1013 			if ( !trap_AAS_Initialized() ) {
1014 				return BLERR_NOERROR;
1015 			}
1016 
1017 			if ( g_entities[i].client->pers.connected == CON_CONNECTED ) {
1018 				BotAI( i, (float) thinktime / 1000 );
1019 			}
1020 		}
1021 	}
1022 
1023 
1024 	// execute bot user commands every frame
1025 	for ( i = 0; i < MAX_CLIENTS; i++ ) {
1026 		if ( !botstates[i] || !botstates[i]->inuse ) {
1027 			continue;
1028 		}
1029 		// Ridah
1030 		if ( g_entities[i].r.svFlags & SVF_CASTAI ) {
1031 			continue;
1032 		}
1033 		// done.
1034 		if ( g_entities[i].client->pers.connected != CON_CONNECTED ) {
1035 			continue;
1036 		}
1037 
1038 		BotUpdateInput( botstates[i], time );
1039 		trap_BotUserCommand( botstates[i]->client, &botstates[i]->lastucmd );
1040 	}
1041 
1042 	return BLERR_NOERROR;
1043 }
1044 
1045 /*
1046 ==============
1047 BotInitLibrary
1048 ==============
1049 */
BotInitLibrary(void)1050 int BotInitLibrary( void ) {
1051 	char buf[144];
1052 
1053 	//set the maxclients and maxentities library variables before calling BotSetupLibrary
1054 	Com_sprintf( buf, sizeof( buf ), "%d", level.maxclients );
1055 	trap_BotLibVarSet( "maxclients", buf );
1056 	Com_sprintf( buf, sizeof( buf ), "%d", MAX_GENTITIES );
1057 	trap_BotLibVarSet( "maxentities", buf );
1058 	//bsp checksum
1059 	trap_Cvar_VariableStringBuffer( "sv_mapChecksum", buf, sizeof( buf ) );
1060 	if ( strlen( buf ) ) {
1061 		trap_BotLibVarSet( "sv_mapChecksum", buf );
1062 	}
1063 	//maximum number of aas links
1064 	trap_Cvar_VariableStringBuffer( "max_aaslinks", buf, sizeof( buf ) );
1065 	if ( strlen( buf ) ) {
1066 		trap_BotLibVarSet( "max_aaslinks", buf );
1067 	}
1068 	//maximum number of items in a level
1069 	trap_Cvar_VariableStringBuffer( "max_levelitems", buf, sizeof( buf ) );
1070 	if ( strlen( buf ) ) {
1071 		trap_BotLibVarSet( "max_levelitems", buf );
1072 	}
1073 	//automatically launch WinBSPC if AAS file not available
1074 	trap_Cvar_VariableStringBuffer( "autolaunchbspc", buf, sizeof( buf ) );
1075 	if ( strlen( buf ) ) {
1076 		trap_BotLibVarSet( "autolaunchbspc", "1" );
1077 	}
1078 	//
1079 	trap_Cvar_VariableStringBuffer( "g_gametype", buf, sizeof( buf ) );
1080 	if ( !strlen( buf ) ) {
1081 		strcpy( buf, "0" );
1082 	}
1083 	trap_BotLibVarSet( "g_gametype", buf );
1084 	//
1085 	// Rafael gameskill
1086 	trap_Cvar_VariableStringBuffer( "g_gameskill", buf, sizeof( buf ) );
1087 	if ( !strlen( buf ) ) {
1088 		strcpy( buf, "0" );
1089 	}
1090 	trap_BotLibVarSet( "g_gamekill", buf );
1091 	// done
1092 	//
1093 	trap_Cvar_VariableStringBuffer( "bot_developer", buf, sizeof( buf ) );
1094 	if ( !strlen( buf ) ) {
1095 		strcpy( buf, "0" );
1096 	}
1097 	trap_BotLibVarSet( "bot_developer", buf );
1098 	//log file
1099 	trap_Cvar_VariableStringBuffer( "bot_developer", buf, sizeof( buf ) );
1100 	if ( !strlen( buf ) ) {
1101 		strcpy( buf, "0" );
1102 	}
1103 	trap_Cvar_VariableStringBuffer( "logfile", buf, sizeof( buf ) );
1104 	trap_BotLibVarSet( "log", buf );
1105 	//no chatting
1106 	trap_Cvar_VariableStringBuffer( "bot_nochat", buf, sizeof( buf ) );
1107 	if ( strlen( buf ) ) {
1108 		trap_BotLibVarSet( "nochat", buf );
1109 	}
1110 	//forced clustering calculations
1111 	trap_Cvar_VariableStringBuffer( "forceclustering", buf, sizeof( buf ) );
1112 	if ( strlen( buf ) ) {
1113 		trap_BotLibVarSet( "forceclustering", buf );
1114 	}
1115 	//forced reachability calculations
1116 	trap_Cvar_VariableStringBuffer( "forcereachability", buf, sizeof( buf ) );
1117 	if ( strlen( buf ) ) {
1118 		trap_BotLibVarSet( "forcereachability", buf );
1119 	}
1120 	//force writing of AAS to file
1121 	trap_Cvar_VariableStringBuffer( "forcewrite", buf, sizeof( buf ) );
1122 	if ( strlen( buf ) ) {
1123 		trap_BotLibVarSet( "forcewrite", buf );
1124 	}
1125 	//no AAS optimization
1126 	trap_Cvar_VariableStringBuffer( "nooptimize", buf, sizeof( buf ) );
1127 	if ( strlen( buf ) ) {
1128 		trap_BotLibVarSet( "nooptimize", buf );
1129 	}
1130 	//number of reachabilities to calculate each frame
1131 	trap_Cvar_VariableStringBuffer( "framereachability", buf, sizeof( buf ) );
1132 	if ( !strlen( buf ) ) {
1133 		strcpy( buf, "20" );
1134 	}
1135 	trap_BotLibVarSet( "framereachability", buf );
1136 	//
1137 	trap_Cvar_VariableStringBuffer( "bot_reloadcharacters", buf, sizeof( buf ) );
1138 	if ( !strlen( buf ) ) {
1139 		strcpy( buf, "0" );
1140 	}
1141 	trap_BotLibVarSet( "bot_reloadcharacters", buf );
1142 	//base directory
1143 	trap_Cvar_VariableStringBuffer( "fs_basepath", buf, sizeof( buf ) );
1144 	if ( strlen( buf ) ) {
1145 		trap_BotLibVarSet( "basedir", buf );
1146 	}
1147 	//game directory
1148 	trap_Cvar_VariableStringBuffer( "fs_game", buf, sizeof( buf ) );
1149 	if ( strlen( buf ) ) {
1150 		trap_BotLibVarSet( "gamedir", buf );
1151 	}
1152 	//home directory
1153 	trap_Cvar_VariableStringBuffer( "fs_homepath", buf, sizeof( buf ) );
1154 	if ( strlen( buf ) ) {
1155 		trap_BotLibVarSet("homedir", buf);
1156 	}
1157 	//setup the bot library
1158 	return trap_BotLibSetup();
1159 }
1160 
1161 /*
1162 ==============
1163 BotAISetup
1164 ==============
1165 */
BotAISetup(int restart)1166 int BotAISetup( int restart ) {
1167 	int errnum;
1168 
1169 #ifdef RANDOMIZE
1170 	srand( (unsigned)time( NULL ) );
1171 #endif //RANDOMIZE
1172 
1173 	trap_Cvar_Register( &bot_thinktime, "bot_thinktime", "100", 0 );
1174 	trap_Cvar_Register( &memorydump, "memorydump", "0", 0 );
1175 
1176 	//if the game is restarted for a tournament
1177 	if ( restart ) {
1178 		return BLERR_NOERROR;
1179 	}
1180 
1181 	//initialize the bot states
1182 	memset( botstates, 0, sizeof( botstates ) );
1183 
1184 	trap_Cvar_Register( &bot_thinktime, "bot_thinktime", "100", 0 );
1185 
1186 	errnum = BotInitLibrary();
1187 	if ( errnum != BLERR_NOERROR ) {
1188 		return qfalse;
1189 	}
1190 	return BLERR_NOERROR;
1191 }
1192 
1193 /*
1194 ==============
1195 BotAIShutdown
1196 ==============
1197 */
BotAIShutdown(int restart)1198 int BotAIShutdown( int restart ) {
1199 
1200 	int i;
1201 
1202 	//if the game is restarted for a tournament
1203 	if ( restart ) {
1204 		//shutdown all the bots in the botlib
1205 		for ( i = 0; i < MAX_CLIENTS; i++ ) {
1206 			if ( botstates[i] && botstates[i]->inuse ) {
1207 				BotAIShutdownClient( botstates[i]->client );
1208 			}
1209 		}
1210 		//don't shutdown the bot library
1211 	} else {
1212 		trap_BotLibShutdown();
1213 	}
1214 	return qtrue;
1215 }
1216 
1217