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_cast_think.c
32 // Function:		Wolfenstein AI Character Thinking
33 // Programmer:		Ridah
34 // Tab Size:		4 (real tabs)
35 //===========================================================================
36 
37 #include "g_local.h"
38 #include "../qcommon/q_shared.h"
39 #include "../botlib/botlib.h"      //bot lib interface
40 #include "../botlib/be_aas.h"
41 #include "../botlib/be_ea.h"
42 #include "../botlib/be_ai_gen.h"
43 #include "../botlib/be_ai_goal.h"
44 #include "../botlib/be_ai_move.h"
45 #include "../botlib/botai.h"          //bot ai interface
46 
47 #include "ai_cast.h"
48 
49 /*
50 The lowest level of Cast AI thinking.
51 */
52 
53 /*
54 ============
55 AICast_ProcessAIFunctions
56 ============
57 */
AICast_ProcessAIFunctions(cast_state_t * cs,float thinktime)58 void AICast_ProcessAIFunctions( cast_state_t *cs, float thinktime ) {
59 	int i;
60 	char    *funcname;
61 
62 	//check for air
63 	BotCheckAir( cs->bs );
64 	//if the cast has no ai function
65 	if ( !cs->aifunc ) {
66 		AIFunc_DefaultStart( cs );
67 	}
68 	//
69 	// call AI funcs for this cast
70 	//
71 	AICast_DBG_InitAIFuncs();
72 	//
73 	// only allow looping in debug mode (since it's much slower)
74 	for ( i = 0; i < ( aicast_debug.integer ? MAX_AIFUNCS : 1 ); i++ )
75 	{
76 		if ( !( funcname = cs->aifunc( cs ) ) ) {
77 			break;
78 		} else {
79 			trap_BotResetAvoidReach( cs->bs->ms );    // reset avoidreach
80 			cs->thinkFuncChangeTime = level.time;
81 			AICast_DBG_AddAIFunc( cs, funcname );
82 		}
83 	}
84 	//
85 	//if the cast executed too many AI functions
86 	//
87 	if ( aicast_debug.integer && i >= MAX_AIFUNCS ) {
88 		AICast_DBG_ListAIFuncs( cs, 10 );   // print the last 10 funcs called
89 	}
90 }
91 
92 
93 /*
94 ==============
95 AICast_ChangeViewAngles
96 ==============
97 */
AICast_ChangeViewAngles(cast_state_t * cs,float thinktime)98 void AICast_ChangeViewAngles( cast_state_t *cs, float thinktime ) {
99 	float diff, factor, maxchange, anglespeed;
100 	int i;
101 	bot_state_t *bs;
102 
103 	bs = cs->bs;
104 	//
105 	// restore locked viewangles if required
106 	if ( cs->aiFlags & AIFL_VIEWLOCKED ) {
107 		VectorCopy( cs->viewlock_viewangles, cs->ideal_viewangles );
108 	} else {
109 		// check for playanim angles
110 		if ( cs->castScriptStatus.playAnimViewlockTime >= level.time ) {
111 			// check to make sure we are still playing a legs animation
112 			if ( !g_entities[cs->entityNum].client->ps.legsTimer ) {
113 				cs->castScriptStatus.playAnimViewlockTime = 0;
114 			} else {
115 				VectorCopy( cs->castScriptStatus.playanim_viewangles, cs->ideal_viewangles );
116 			}
117 		}
118 	}
119 	//
120 	if ( cs->ideal_viewangles[PITCH] > 180 ) {
121 		cs->ideal_viewangles[PITCH] -= 360;
122 	}
123 	//
124 	maxchange = cs->attributes[YAW_SPEED]; //300;
125 	if ( cs->aiState >= AISTATE_COMBAT ) {
126 		factor = 2.0;
127 		maxchange *= 2.0;
128 	} else {
129 		factor = 0.7;
130 	}
131 	//
132 	if ( cs->lockViewAnglesTime < level.time ) {
133 		maxchange *= thinktime;
134 		for ( i = 0; i < 3; i++ ) {
135 			diff = fabs( AngleDifference( cs->viewangles[i], cs->ideal_viewangles[i] ) );
136 			anglespeed = diff * factor;
137 			if ( cs->aiState >= AISTATE_COMBAT ) {
138 				if ( anglespeed < cs->attributes[YAW_SPEED] ) {
139 					anglespeed = cs->attributes[YAW_SPEED];
140 				}
141 			}
142 			if ( thinktime != 9999.0f ) {
143 				if ( anglespeed > maxchange ) {
144 					anglespeed = maxchange;
145 				}
146 			}
147 			cs->viewangles[i] = BotChangeViewAngle( cs->viewangles[i],
148 													cs->ideal_viewangles[i], anglespeed );
149 			//BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", cs->ideal_viewangles[0], cs->ideal_viewangles[1], cs->ideal_viewangles[2]);`
150 			//cs->viewangles[i] = cs->ideal_viewangles[i];
151 		}
152 	}
153 	if ( cs->viewangles[PITCH] > 180 ) {
154 		cs->viewangles[PITCH] -= 360;
155 	}
156 	//elementary action: view
157 	trap_EA_View( bs->client, cs->viewangles );
158 }
159 
160 
161 /*
162 ==============
163 AICast_InputToUserCommand
164 ==============
165 */
166 static int serverTime;
AICast_InputToUserCommand(cast_state_t * cs,bot_input_t * bi,usercmd_t * ucmd,int delta_angles[3])167 void AICast_InputToUserCommand( cast_state_t *cs, bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3] ) {
168 	vec3_t angles, forward, right, up;
169 	short temp;
170 	int j;
171 	signed char movechar;
172 
173 	//clear the whole structure
174 	memset( ucmd, 0, sizeof( usercmd_t ) );
175 	//
176 	//Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
177 	//the duration for the user command in milli seconds
178 	ucmd->serverTime = serverTime;
179 	//crouch/movedown
180 	if ( aiDefaults[cs->aiCharacter].attributes[ATTACK_CROUCH] ) {    // only crouch if this character is physically able to
181 		if ( cs->bs->cur_ps.groundEntityNum != ENTITYNUM_NONE && bi->actionflags & ACTION_CROUCH ) {
182 			ucmd->upmove -= 127;
183 		}
184 	}
185 	//
186 	// actions not effected by script pausing
187 	//
188 	// set zoom button
189 	if ( cs->aiFlags & AIFL_ZOOMING ) {
190 		ucmd->wbuttons |= WBUTTON_ZOOM;
191 	}
192 	//set the buttons
193 	if ( bi->actionflags & ACTION_ATTACK ) {
194 		vec3_t ofs;
195 		// don't fire if we are not facing the right direction yet
196 		if ( ( cs->triggerReleaseTime < level.time ) &&
197 			 (   ( cs->lockViewAnglesTime >= level.time ) ||
198 				 ( fabs( AngleDifference( cs->ideal_viewangles[YAW], cs->viewangles[YAW] ) ) < 20 ) ) &&
199 			 // check for radid luger firing by skilled users (release fire between shots)
200 			 ( ( ( level.time + cs->entityNum * 500 ) / 2000 ) % 2 || !( rand() % ( 1 + g_gameskill.integer ) ) || ( cs->attributes[ATTACK_SKILL] < 0.5 ) || ( cs->weaponNum != WP_LUGER ) || ( cs->bs->cur_ps.weaponTime == 0 ) || ( cs->bs->cur_ps.releasedFire ) ) ) {
201 			ucmd->buttons |= BUTTON_ATTACK;
202 			// do some swaying around for some weapons
203 			AICast_WeaponSway( cs, ofs );
204 			VectorAdd( bi->viewangles, ofs, bi->viewangles );
205 		}
206 	}
207 	//
208 	//set the view angles
209 	//NOTE: the ucmd->angles are the angles WITHOUT the delta angles
210 	ucmd->angles[PITCH] = ANGLE2SHORT( bi->viewangles[PITCH] );
211 	ucmd->angles[YAW] = ANGLE2SHORT( bi->viewangles[YAW] );
212 	ucmd->angles[ROLL] = ANGLE2SHORT( bi->viewangles[ROLL] );
213 	//subtract the delta angles
214 	for ( j = 0; j < 3; j++ ) {
215 		temp = ucmd->angles[j] - delta_angles[j];
216 		ucmd->angles[j] = temp;
217 	}
218 
219 
220 //----(SA)	modified slightly for DM/DK
221 	ucmd->weapon = bi->weapon;
222 	//
223 	// relaxed mode show no weapons
224 	if ( cs->aiState <= AISTATE_QUERY ) {
225 		if ( WEAPS_ONE_HANDED & ( 1 << ucmd->weapon ) ) { // one-handed wepons don't draw, others do
226 			ucmd->weapon = WP_NONE;
227 		}
228 	}
229 //----(SA)	end
230 
231 	//
232 	if ( bi->actionflags & ACTION_GESTURE ) {
233 		ucmd->buttons |= BUTTON_GESTURE;
234 	}
235 	if ( bi->actionflags & ACTION_RELOAD ) {
236 		ucmd->wbuttons |= WBUTTON_RELOAD;
237 	}
238 	//
239 	// if we are locked down, don't do anything
240 	//
241 	if ( cs->pauseTime > level.time ) {
242 		return;
243 	}
244 	//
245 	// if scripted pause, no movement
246 	//
247 	if ( cs->castScriptStatus.scriptNoMoveTime > level.time ) {
248 		return;
249 	}
250 	//
251 	// if viewlock, wait until we are facing ideal angles before we move
252 	if ( cs->aiFlags & AIFL_VIEWLOCKED ) {
253 		if ( fabs( AngleDifference( cs->ideal_viewangles[YAW], cs->viewangles[YAW] ) ) > 10 ) {
254 			return;
255 		}
256 	}
257 	//
258 	if ( bi->actionflags & ACTION_DELAYEDJUMP ) {
259 		bi->actionflags |= ACTION_JUMP;
260 		bi->actionflags &= ~ACTION_DELAYEDJUMP;
261 	}
262 	//
263 	// only move if we are in combat or we are facing where our ideal angles
264 	if ( bi->speed ) {
265 		if ( ( !( cs->aiFlags & AIFL_WALKFORWARD ) && cs->enemyNum >= 0 ) || ( ( ucmd->forwardmove >= 0 ) && fabs( AngleNormalize180( AngleDifference( cs->ideal_viewangles[YAW], cs->viewangles[YAW] ) ) ) < 60 ) ) {
266 			//NOTE: movement is relative to the REAL view angles
267 			//get the horizontal forward and right vector
268 			//get the pitch in the range [-180, 180]
269 			if ( bi->dir[2] ) {
270 				angles[PITCH] = bi->viewangles[PITCH];
271 			} else { angles[PITCH] = 0;}
272 			angles[YAW] = bi->viewangles[YAW];
273 			angles[ROLL] = 0;
274 			AngleVectors( angles, forward, right, up );
275 			//bot input speed is in the range [0, 400]
276 			bi->speed = bi->speed * 127 / 400;
277 			//set the view independent movement
278 			ucmd->forwardmove = DotProduct( forward, bi->dir ) * bi->speed;
279 			ucmd->rightmove = DotProduct( right, bi->dir ) * bi->speed;
280 
281 			// RF, changed this to fix stim soldier flying attack
282 			if ( !ucmd->upmove ) { // only change it if we don't already have an upmove set
283 				ucmd->upmove = DotProduct( up, bi->dir ) * bi->speed;
284 			}
285 			//if (!ucmd->upmove)	// only change it if we don't already have an upmove set
286 			//	ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
287 		}
288 	}
289 	//
290 	//normal keyboard movement
291 	if ( cs->actionFlags & CASTACTION_WALK ) {
292 		movechar = 70;
293 	} else {
294 		movechar = 127;
295 	}
296 	if ( bi->actionflags & ACTION_MOVEFORWARD ) {
297 		ucmd->forwardmove = movechar;
298 	}
299 	if ( !( cs->aiFlags & AIFL_WALKFORWARD ) || ( !cs->bs->cur_ps.groundEntityNum || cs->bs->cur_ps.groundEntityNum == ENTITYNUM_NONE ) ) {   // only do other movements if we are allowed to
300 		if ( bi->actionflags & ACTION_MOVEBACK ) {
301 			ucmd->forwardmove = -movechar;
302 		}
303 		if ( bi->actionflags & ACTION_MOVELEFT ) {
304 			ucmd->rightmove = -movechar;
305 		}
306 		if ( bi->actionflags & ACTION_MOVERIGHT ) {
307 			ucmd->rightmove = movechar;
308 		}
309 	}
310 	// prevent WALKFORWARD AI from moving backwards
311 	if ( cs->aiFlags & AIFL_WALKFORWARD ) {
312 		if ( ucmd->forwardmove < 0 ) {
313 			ucmd->forwardmove = 0;
314 		}
315 	}
316 	//jump/moveup
317 	if ( bi->actionflags & ACTION_JUMP ) {
318 		ucmd->upmove = 127;                                 // JUMP always takes preference over ducking
319 	}
320 	if ( bi->actionflags & ACTION_MOVEDOWN ) {
321 		ucmd->upmove = -127;                                    // JUMP always takes preference over ducking
322 	}
323 	if ( bi->actionflags & ACTION_MOVEUP ) {
324 		ucmd->upmove = 127;                                     // JUMP always takes preference over ducking
325 	}
326 	//
327 	//Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
328 }
329 
330 
331 /*
332 ==============
333 AICast_UpdateInput
334 ==============
335 */
AICast_UpdateInput(cast_state_t * cs,int time)336 void AICast_UpdateInput( cast_state_t *cs, int time ) {
337 	bot_input_t bi;
338 	bot_state_t *bs;
339 	int j;
340 	float speed;
341 
342 	bs = cs->bs;
343 
344 	//add the delta angles to the bot's current view angles
345 	for ( j = 0; j < 3; j++ ) {
346 		cs->viewangles[j] = AngleMod( cs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
347 	}
348 	//
349 	AICast_ChangeViewAngles( cs, (float) time / 1000 );
350 	//
351 	if ( cs->pauseTime > level.time ) {
352 		trap_EA_View( bs->client, cs->viewangles );
353 		trap_EA_GetInput( bs->client, (float) time / 1000, &bi );
354 		AICast_InputToUserCommand( cs, &bi, &cs->lastucmd, bs->cur_ps.delta_angles );
355 		g_entities[cs->bs->entitynum].client->ps.pm_flags &= ~PMF_RESPAWNED;
356 		//
357 		//subtract the delta angles
358 		for ( j = 0; j < 3; j++ ) {
359 			cs->viewangles[j] = AngleMod( cs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
360 		}
361 		//
362 		return;
363 	}
364 	//
365 	trap_EA_GetInput( bs->client, (float) time / 1000, &bi );
366 	//
367 	// restrict the speed according to the character and their current speedScale
368 	// HACK, don't slow down while crouching
369 	if ( bi.actionflags & ACTION_CROUCH && cs->speedScale < 1.0 ) {
370 		cs->speedScale = 1.0;
371 	}
372 	//
373 	// check some Cast AI specific movement flags
374 	if ( cs->actionFlags & CASTACTION_WALK ) {
375 		if ( cs->speedScale > ( cs->attributes[WALKING_SPEED] / cs->attributes[RUNNING_SPEED] ) ) {
376 			cs->speedScale = ( cs->attributes[WALKING_SPEED] / cs->attributes[RUNNING_SPEED] );
377 		}
378 	}
379 	// don't ever let the speed get too low
380 	if ( cs->speedScale < 0.25 ) {
381 		cs->speedScale = 0.25;
382 	}
383 	if ( cs->speedScale > 1.2 ) {
384 		cs->speedScale = 1.2;
385 	}
386 	//
387 	speed = cs->speedScale * cs->attributes[RUNNING_SPEED];
388 	//
389 	//if (speed <= (cs->attributes[WALKING_SPEED] + (cs->attributes[WALKING_SPEED] + 50 < cs->attributes[RUNNING_SPEED] ? 50 : -1)))	// do a fast shuffle if slightly over walking speed
390 	if ( speed <= cs->attributes[WALKING_SPEED] ) {
391 		cs->actionFlags |= CASTACTION_WALK;
392 	}
393 	//
394 	// we use 300 here, because the default player speed is 300, so Cast AI's can't move faster than that
395 	if ( ( bi.speed / 400.0 ) > ( speed / 300.0 ) ) {
396 		bi.speed = 400.0 * ( speed / 300.0 );
397 		if ( bi.speed > 400.0 ) {
398 			bi.speed = 400.0;   // just in case, we should never exceed this
399 		}
400 	}
401 	//
402 	// do a fast shuffle if slightly over walking speed
403 	if ( bi.speed <= ( 400.0 / 300.0 ) * ( cs->attributes[WALKING_SPEED] + ( cs->attributes[WALKING_SPEED] + 50 < cs->attributes[RUNNING_SPEED] ? 50 : -1 ) ) ) {
404 		cs->actionFlags |= CASTACTION_WALK;
405 	}
406 	//
407 	AICast_InputToUserCommand( cs, &bi, &cs->lastucmd, bs->cur_ps.delta_angles );
408 	//
409 	// check some Cast AI specific movement flags
410 	if ( cs->actionFlags & CASTACTION_WALK ) {
411 		cs->lastucmd.buttons |= BUTTON_WALKING; // play the walking animation
412 	}
413 	//
414 	//subtract the delta angles
415 	for ( j = 0; j < 3; j++ ) {
416 		cs->viewangles[j] = AngleMod( cs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) );
417 	}
418 	//
419 	// make sure the respawn flag is disabled (causes problems after multiple "map xxx" commands)
420 	g_entities[cs->bs->entitynum].client->ps.pm_flags &= ~PMF_RESPAWNED;
421 	// set the aiState
422 	g_entities[cs->bs->entitynum].client->ps.aiState = cs->aiState;
423 }
424 
425 /*
426 ============
427 AICast_Think
428 
429   entry point for all cast AI
430 ============
431 */
AICast_Think(int client,float thinktime)432 void AICast_Think( int client, float thinktime ) {
433 	gentity_t       *ent;
434 	cast_state_t    *cs;
435 	int i;
436 	int animIndex;
437 	animation_t     *anim;
438 
439 //	if (saveGamePending || (strlen( g_missionStats.string ) > 2 )) {
440 //		return;
441 //	}
442 
443 	//
444 	// get the cast ready for processing
445 	//
446 	cs = AICast_GetCastState( client );
447 	ent = &g_entities[client];
448 	//
449 	// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
450 	trap_AAS_SetCurrentWorld( cs->aasWorldIndex );
451 	//
452 	// make sure we have a valid navigation system
453 	//
454 	if ( !trap_AAS_Initialized() ) {
455 		return;
456 	}
457 	//
458 	trap_EA_ResetInput( client, NULL );
459 	cs->aiFlags &= ~AIFL_VIEWLOCKED;
460 	cs->aiFlags &= ~AIFL_SPECIAL_FUNC;
461 	//cs->weaponNum = ent->client->ps.weapon;
462 	//
463 	// turn off flags that are set each frame if needed
464 	ent->client->ps.eFlags &= ~( EF_NOSWINGANGLES | EF_MONSTER_EFFECT | EF_MONSTER_EFFECT2 | EF_MONSTER_EFFECT3 );
465 	// conditional flags
466 	if ( ent->aiCharacter == AICHAR_ZOMBIE ) {
467 		if ( COM_BitCheck( ent->client->ps.weapons, WP_MONSTER_ATTACK1 ) ) {
468 			cs->aiFlags |= AIFL_NO_FLAME_DAMAGE;
469 			SET_FLAMING_ZOMBIE( ent->s, 1 );
470 		} else {
471 			SET_FLAMING_ZOMBIE( ent->s, 0 );
472 		}
473 	}
474 	//
475 	// update bounding box
476 	AIChar_SetBBox( ent, cs, qtrue );
477 	//
478 	// set/disable these each frame as required
479 	//ent->r.svFlags |= SVF_BROADCAST;
480 	ent->client->ps.eFlags &= ~EF_FORCE_END_FRAME;
481 	//origin of the cast
482 	VectorCopy( ent->client->ps.origin, cs->bs->origin );
483 	//eye coordinates of the cast
484 	VectorCopy( ent->client->ps.origin, cs->bs->eye );
485 	cs->bs->eye[2] += ent->client->ps.viewheight;
486 	//get the area the cast is in
487 	cs->bs->areanum = BotPointAreaNum( cs->bs->origin );
488 	if ( cs->bs->areanum ) {
489 		cs->lastValidAreaNum[cs->aasWorldIndex] = cs->bs->areanum;
490 		cs->lastValidAreaTime[cs->aasWorldIndex] = level.time;
491 	}
492 	// if we're dead, do special stuff only
493 	if ( ent->health <= 0 || cs->revivingTime || cs->rebirthTime ) {
494 		//
495 		if ( cs->revivingTime && cs->revivingTime < level.time ) {
496 			// start us thinking again
497 			ent->client->ps.pm_type = PM_NORMAL;
498 			cs->revivingTime = 0;
499 		}
500 		//
501 		if ( cs->rebirthTime && cs->rebirthTime < level.time ) {
502 			vec3_t mins, maxs;
503 			int touch[10], numTouch;
504 			float oldmaxZ;
505 
506 			oldmaxZ = ent->r.maxs[2];
507 
508 			// make sure the area is clear
509 			AIChar_SetBBox( ent, cs, qfalse );
510 
511 			VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
512 			VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
513 			trap_UnlinkEntity( ent );
514 
515 			numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 );
516 
517 			if ( numTouch ) {
518 				for ( i = 0; i < numTouch; i++ ) {
519 					//if (!g_entities[touch[i]].client || g_entities[touch[i]].r.contents == CONTENTS_BODY)
520 					if ( g_entities[touch[i]].r.contents & MASK_PLAYERSOLID ) {
521 						break;
522 					}
523 				}
524 				if ( i == numTouch ) {
525 					numTouch = 0;
526 				}
527 			}
528 
529 			if ( numTouch == 0 ) {    // ok to spawn
530 
531 				// give them health when they start reviving, so we won't gib after
532 				// just a couple shots while reviving
533 				ent->health =
534 					ent->client->ps.stats[STAT_HEALTH] =
535 						ent->client->ps.stats[STAT_MAX_HEALTH] =
536 							( ( cs->attributes[STARTING_HEALTH] - 50 ) > 30 ? ( cs->attributes[STARTING_HEALTH] - 50 ) : 30 );
537 
538 				ent->r.contents = CONTENTS_BODY;
539 				ent->clipmask = MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP;
540 				ent->takedamage = qtrue;
541 				ent->waterlevel = 0;
542 				ent->watertype = 0;
543 				ent->flags = 0;
544 				ent->die = AICast_Die;
545 				ent->client->ps.eFlags &= ~EF_DEAD;
546 				ent->s.eFlags &= ~EF_DEAD;
547 
548 				cs->rebirthTime = 0;
549 				cs->deathTime = 0;
550 
551 				ent->client->ps.eFlags &= ~EF_DEATH_FRAME;
552 				ent->client->ps.eFlags &= ~EF_FORCE_END_FRAME;
553 				ent->client->ps.eFlags |= EF_NO_TURN_ANIM;
554 
555 				// play the revive animation
556 				cs->revivingTime = level.time + BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_REVIVE, qfalse, qtrue );;
557 			} else {
558 				// can't spawn yet, so set bbox back, and wait
559 				ent->r.maxs[2] = oldmaxZ;
560 				ent->client->ps.maxs[2] = ent->r.maxs[2];
561 			}
562 			trap_LinkEntity( ent );
563 		}
564 		// ZOMBIE should set effect flag if really dead
565 		if ( cs->aiCharacter == AICHAR_ZOMBIE && !ent->r.contents ) {
566 			ent->client->ps.eFlags |= EF_MONSTER_EFFECT2;
567 		}
568 		//
569 		if ( ent->health > GIB_HEALTH && cs->deathTime && cs->deathTime < ( level.time - 1000 ) ) {
570 			//ent->r.svFlags &= ~SVF_BROADCAST;
571 			if ( !ent->client->ps.torsoTimer && !ent->client->ps.legsTimer ) {
572 				ent->client->ps.eFlags |= EF_FORCE_END_FRAME;
573 			}
574 			// sink?
575 			if ( cs->deadSinkStartTime ) {
576 				ent->s.effect3Time = cs->deadSinkStartTime;
577 				// if they are gone
578 				if ( cs->deadSinkStartTime + DEAD_SINK_DURATION < level.time ) {
579 					trap_DropClient( cs->entityNum, "" );
580 					return;
581 				}
582 			}
583 			// if we've been dead for a while, stop head-checking
584 			if ( cs->deathTime < ( level.time - 5000 ) ) {
585 				ent->flags |= FL_NO_HEADCHECK;
586 			}
587 		}
588 		//
589 		// do some special handling for this character
590 		AICast_SpecialFunc( cs );
591 		//
592 		// no more thinking required
593 		return;
594 	}
595 	//
596 	// set some anim conditions
597 	if ( cs->secondDeadTime ) {
598 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qtrue, qfalse );
599 	} else {
600 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qfalse, qfalse );
601 	}
602 	// set health value
603 	if ( ent->health <= 0.25 * cs->attributes[STARTING_HEALTH] ) {
604 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 2, qfalse );
605 	} else if ( ent->health <= 0.5 * cs->attributes[STARTING_HEALTH] ) {
606 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 1, qfalse );
607 	} else {
608 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 0, qfalse );
609 	}
610 	// set enemy position
611 	if ( cs->enemyNum >= 0 ) {
612 		if ( infront( ent, &g_entities[cs->enemyNum] ) ) {
613 			BG_UpdateConditionValue( ent->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue );
614 		} else {
615 			BG_UpdateConditionValue( ent->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue );
616 		}
617 	} else {
618 		BG_UpdateConditionValue( ent->s.number, ANIM_COND_ENEMY_POSITION, POSITION_UNUSED, qtrue );
619 	}
620 	// set defense pose
621 	if ( ent->flags & FL_DEFENSE_CROUCH ) {
622 		BG_UpdateConditionValue( ent->s.number, ANIM_COND_DEFENSE, qtrue, qfalse );
623 	} else {
624 		BG_UpdateConditionValue( ent->s.number, ANIM_COND_DEFENSE, qfalse, qfalse );
625 	}
626 	//
627 	cs->speedScale = 1.0;           // reset each frame, set if required
628 	cs->actionFlags = 0;            // FIXME: move this to a Cast AI movement init function!
629 	//retrieve the current client state
630 	BotAI_GetClientState( client, &( cs->bs->cur_ps ) );
631 	//
632 	// setup movement speeds for the given state
633 	// walking
634 	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALK );
635 	if ( animIndex >= 0 ) {
636 		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
637 		cs->attributes[WALKING_SPEED] = anim->moveSpeed;
638 	}
639 	// crouching
640 	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALKCR );
641 	if ( animIndex >= 0 ) {
642 		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
643 		cs->attributes[CROUCHING_SPEED] = anim->moveSpeed;
644 	}
645 	// running
646 	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_RUN );
647 	if ( animIndex >= 0 ) {
648 		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
649 		cs->attributes[RUNNING_SPEED] = anim->moveSpeed;
650 	}
651 	// update crouch speed scale
652 	ent->client->ps.crouchSpeedScale = cs->attributes[CROUCHING_SPEED] / cs->attributes[RUNNING_SPEED];
653 	//
654 	// only enable headlook if we want to this frame
655 	ent->client->ps.eFlags &= ~EF_HEADLOOK;
656 	if ( cs->enemyNum >= 0 ) {
657 		ent->client->ps.eFlags &= ~EF_STAND_IDLE2;  // never use alt idle if fighting
658 	}
659 	//
660 	// check for dead leader
661 	if ( cs->leaderNum >= 0 && g_entities[cs->leaderNum].health <= 0 ) {
662 		cs->leaderNum = -1;
663 	}
664 	//
665 #if 0
666 	// HACK for village2, if they are stuck, find a good position (there is a friendly guy placed inside a table)
667 	{
668 		trace_t tr;
669 		vec3_t org;
670 		trap_Trace( &tr, cs->bs->cur_ps.origin, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, cs->bs->cur_ps.origin, cs->entityNum, CONTENTS_SOLID );
671 		while ( tr.startsolid ) {
672 			VectorCopy( cs->bs->cur_ps.origin, org );
673 			org[0] += 96 * crandom();
674 			org[1] += 96 * crandom();
675 			org[2] += 16 * crandom();
676 			trap_Trace( &tr, org, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, org, cs->entityNum, CONTENTS_SOLID );
677 			G_SetOrigin( &g_entities[cs->entityNum], org );
678 			VectorCopy( org, g_entities[cs->entityNum].client->ps.origin );
679 		}
680 	}
681 #endif
682 	//add the delta angles to the cast's current view angles
683 	for ( i = 0; i < 3; i++ ) {
684 		cs->viewangles[i] = AngleMod( cs->viewangles[i] + SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
685 	}
686 	//
687 	//increase the local time of the cast
688 	cs->bs->ltime += thinktime;
689 	//
690 	cs->bs->thinktime = thinktime;
691 	// clear flags each frame
692 	cs->bFlags = 0;
693 	//
694 	// check enemy health
695 	if ( cs->enemyNum >= 0 && g_entities[cs->enemyNum].health <= 0 ) {
696 		cs->enemyNum = -1;
697 	}
698 	//
699 	// if the previous movetype was temporary, set it back
700 	if ( cs->movestateType == MSTYPE_TEMPORARY ) {
701 		cs->movestate = MS_DEFAULT;
702 		cs->movestateType = MSTYPE_NONE;
703 	}
704 	// crouching?
705 	if (    ( cs->attackcrouch_time >= level.time ) /*&&
706 			((cs->lastAttackCrouch > level.time - 500) || (cs->thinkFuncChangeTime < level.time - 1000))*/) {
707 		// if we are not moving, and we are firing, always stand, unless we are allowed to crouch + fire
708 		if ( ( cs->lastucmd.forwardmove || cs->lastucmd.rightmove ) || ( cs->lastWeaponFired < level.time - 2000 ) || ( cs->aiFlags & AIFL_ATTACK_CROUCH ) ) {
709 			cs->lastAttackCrouch = level.time;
710 			trap_EA_Crouch( cs->bs->client );
711 		}
712 	}
713 	//
714 	//if (cs->enemyNum >= 0) {
715 	//update the attack inventory values
716 	AICast_UpdateBattleInventory( cs, cs->enemyNum );
717 	//}
718 	//
719 	// if we don't have ammo for the current weapon, get rid of it
720 	if ( !( COM_BitCheck( cs->bs->cur_ps.weapons, cs->weaponNum ) ) || !AICast_GotEnoughAmmoForWeapon( cs, cs->weaponNum ) ) {
721 		// select a weapon
722 		AICast_ChooseWeapon( cs, qfalse );
723 		// if still no ammo, select a blank weapon
724 		//if (!AICast_GotEnoughAmmoForWeapon( cs, cs->weaponNum )) {
725 		//	cs->weaponNum = WP_NONE;
726 		//}
727 	}
728 	//
729 	// in query mode, we do special handling (pause scripting, check for transition to alert/combat, etc)
730 	if ( cs->aiState == AISTATE_QUERY ) {
731 		AICast_QueryThink( cs );
732 	} else if ( cs->pauseTime < level.time )     {
733 		// do the thinking
734 		AICast_ProcessAIFunctions( cs, thinktime );
735 		//
736 		// make sure the correct weapon is selected
737 		trap_EA_SelectWeapon( cs->bs->client, cs->weaponNum );
738 		//
739 		// process current script if it exists
740 		cs->castScriptStatusCurrent = cs->castScriptStatus;
741 		AICast_ScriptRun( cs, qfalse );
742 		//
743 		// do some special handling for this character
744 		AICast_SpecialFunc( cs );
745 	}
746 	//
747 	// any anim playing on legs should prevent turn anims being played in the cgame
748 	if ( ent->client->ps.legsTimer ) {
749 		ent->client->ps.eFlags |= EF_NO_TURN_ANIM;
750 	} else {
751 		ent->client->ps.eFlags &= ~EF_NO_TURN_ANIM;
752 	}
753 	//
754 	// set special movestate if necessary
755 	if ( cs->movestateType != MSTYPE_NONE ) {
756 		switch ( cs->movestate ) {
757 		case MS_WALK:
758 			cs->actionFlags |= CASTACTION_WALK;
759 			break;
760 		case MS_CROUCH:
761 			trap_EA_Crouch( cs->entityNum );
762 			break;
763 		default:
764 			break;
765 		}
766 	}
767 	//
768 	// set our weapon in the old structure, in case it's needed elsewhere (?)
769 	cs->bs->weaponnum = cs->weaponNum;
770 	// see if we were recently firing
771 	if ( cs->lastWeaponFired && cs->lastWeaponFired > level.time - 2000 ) {
772 		ent->client->ps.eFlags |= EF_RECENTLY_FIRING;
773 	} else {
774 		ent->client->ps.eFlags &= ~EF_RECENTLY_FIRING;
775 	}
776 	//
777 	//subtract the delta angles
778 	for ( i = 0; i < 3; i++ ) {
779 		cs->viewangles[i] = AngleMod( cs->viewangles[i] - SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
780 	}
781 }
782 
783 /*
784 ============
785 AICast_StartFrame
786 
787   Think any clients that need thinking
788 ============
789 */
790 void CopyToBodyQue( gentity_t *ent );
791 
AICast_StartFrame(int time)792 void AICast_StartFrame( int time ) {
793 	int i, elapsed, count, clCount;
794 	cast_state_t    *cs;
795 	int castcount;
796 	static int lasttime, lastthink;
797 	static vmCvar_t aicast_disable;
798 	gentity_t *ent;
799 
800 	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
801 		return;
802 	}
803 
804 	if ( saveGamePending ) {
805 		return;
806 	}
807 
808 	// if waiting at intermission, don't think
809 	if ( strlen( g_missionStats.string ) > 1 ) {
810 		return;
811 	}
812 
813 	if ( !aicast_disable.handle ) {
814 		trap_Cvar_Register( &aicast_disable, "aicast_disable", "0", CVAR_CHEAT );
815 	} else
816 	{
817 		trap_Cvar_Update( &aicast_disable );
818 		if ( aicast_disable.integer ) {
819 			return;
820 		}
821 	}
822 
823 	trap_Cvar_Update( &aicast_debug );
824 	trap_Cvar_Update( &aicast_debugname );
825 	trap_Cvar_Update( &aicast_scripts );
826 
827 	// no need to think during the intermission
828 	if ( level.intermissiontime ) {
829 		return;
830 	}
831 	//
832 	// make sure the AAS gets updated
833 	trap_BotLibStartFrame( (float) time / 1000 );
834 	//
835 	//
836 	elapsed = time - lasttime;
837 	if ( elapsed == 0 ) {
838 		return;         // no time has elapsed
839 	}
840 
841 //G_Printf( "AI startframe: %i\n", time );
842 
843 	if ( elapsed < 0 ) {
844 		elapsed = 0;
845 		lasttime = time;
846 	}
847 	// don't let the SIGHTING framerate drop below 10 (too much sighting to process at once)
848 	if ( elapsed > 100 ) {
849 		elapsed = 100;
850 	}
851 	AICast_SightUpdate( (int)( (float)SIGHT_PER_SEC * ( (float)elapsed / 1000 ) ) );
852 	//
853 	// update the player's area, only update if it's valid
854 	for ( i = 0; i < 2; i++ ) {
855 		trap_AAS_SetCurrentWorld( i );
856 		castcount = BotPointAreaNum( g_entities[0].s.pos.trBase );
857 		if ( castcount ) {
858 			caststates[0].lastValidAreaNum[i] = castcount;
859 			caststates[0].lastValidAreaTime[i] = level.time;
860 		}
861 	}
862 	//
863 	count = 0;
864 	castcount = 0;
865 	clCount = 0;
866 	//
867 	if ( ++lastthink > level.maxclients ) {
868 		lastthink = 0;
869 	}
870 	//update the AI characters
871 	for ( i = lastthink, ent = &g_entities[lastthink]; clCount < level.numPlayingClients && count < aicast_maxthink; i++, ent++ )
872 	{
873 		if ( i >= level.maxclients ) {
874 			// rewind back to the start
875 			i = 0;
876 			ent = g_entities;
877 		}
878 		//
879 		lastthink = i;
880 		if ( !ent->inuse ) {
881 			continue;
882 		}
883 		if ( ent->client ) {
884 			clCount++;
885 		}
886 		//
887 		cs = AICast_GetCastState( i );
888 		// is this a cast AI?
889 		if ( cs->bs ) {
890 			if ( ent->aiInactive == qfalse ) {
891 				//
892 				elapsed = time - cs->lastThink;
893 				//
894 				// if they're moving/firing think every frame
895 				if ( ( elapsed && cs->scriptAnimTime && cs->scriptAnimTime >= ( level.time - 1000 ) ) ||
896 					 (   ( elapsed >= 50 ) &&
897 						 (   (   (   ( !VectorCompare( ent->client->ps.velocity, vec3_origin ) ) ||
898 									 ( cs->enemyNum >= 0 ) ||
899 									 ( cs->aiState >= AISTATE_COMBAT ) ||
900 									 ( cs->vislist[0].visible_timestamp && cs->vislist[0].visible_timestamp > level.time - 4000 ) ||
901 									 ( ent->client->buttons ) ||
902 									 ( elapsed >= aicast_thinktime ) ) /*&&
903 								(count <= aicast_maxthink)*/) ||
904 							( elapsed >= aicast_thinktime * 2 ) ) ) ) {
905 					// make it think now
906 					AICast_Think( i, (float)elapsed / 1000 );
907 					// did they drop?
908 					if ( !cs->bs || !cs->bs->inuse ) {
909 						break;  // get out of here, to be safe
910 					}
911 					cs->lastThink = time + rand() % 20;   // randomize this slightly to spread out thinks during high framerates
912 					//
913 					// only count live guys
914 					if ( ent->health > 0 ) {
915 						count++;
916 					}
917 				}
918 				// check for any debug info updates
919 				AICast_DebugFrame( cs );
920 			} else if ( cs->aiFlags & AIFL_WAITINGTOSPAWN ) {
921 				// check f the space is clear yet
922 				ent->AIScript_AlertEntity( ent );
923 			}
924 			//
925 			// see if we've checked all cast AI's
926 			if ( ++castcount >= numcast ) {
927 				break;
928 			}
929 		}
930 	}
931 	//
932 	lasttime = time;
933 }
934 
935 /*
936 ============
937 AICast_StartServerFrame
938 
939   Do movements, sighting, etc
940 ============
941 */
AICast_StartServerFrame(int time)942 void AICast_StartServerFrame( int time ) {
943 	int i, elapsed, activeCount;
944 	cast_state_t    *cs;
945 	int castcount;
946 	static int lasttime;
947 	static vmCvar_t aicast_disable;
948 	gentity_t *ent;
949 	cast_state_t *pcs;
950 	qboolean highPriority;
951 	int	oldLegsTimer;
952 
953 	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
954 		return;
955 	}
956 
957 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
958 		return;
959 	}
960 
961 	if ( saveGamePending ) {
962 		return;
963 	}
964 
965 	// if waiting at intermission, don't think
966 	if ( strlen( g_missionStats.string ) > 1 ) {
967 		return;
968 	}
969 
970 	if ( !aicast_disable.handle ) {
971 		trap_Cvar_Register( &aicast_disable, "aicast_disable", "0", CVAR_CHEAT );
972 	} else
973 	{
974 		trap_Cvar_Update( &aicast_disable );
975 		if ( aicast_disable.integer ) {
976 			return;
977 		}
978 	}
979 
980 	trap_Cvar_Update( &aicast_debug );
981 
982 	// no need to think during the intermission
983 	if ( level.intermissiontime ) {
984 		return;
985 	}
986 	//
987 	// make sure the AAS gets updated
988 	trap_BotLibStartFrame( (float) time / 1000 );
989 	//
990 	//
991 	elapsed = time - lasttime;
992 	if ( elapsed == 0 ) {
993 		return;         // no time has elapsed
994 	}
995 
996 	pcs = AICast_GetCastState( 0 );
997 
998 //	G_Printf( "AI startserverframe: %i\n", time );
999 
1000 	AICast_AgePlayTime( 0 );
1001 
1002 	if ( elapsed < 0 ) {
1003 //		elapsed = 0;
1004 		lasttime = time;
1005 	}
1006 	// don't let the framerate drop below 10
1007 //	if ( elapsed > 100 ) {
1008 //		elapsed = 100;
1009 //	}
1010 	//
1011 	// process player's current script if it exists
1012 	AICast_ScriptRun( AICast_GetCastState( 0 ), qfalse );
1013 	//
1014 //	AICast_SightUpdate( (int)((float)SIGHT_PER_SEC * ((float)elapsed / 1000)) );
1015 	//
1016 	castcount = 0;
1017 	activeCount = 0;
1018 	//
1019 	//update the AI characters
1020 	for ( i = 0, ent = g_entities; i < level.maxclients /*&& clCount < level.numPlayingClients*/; i++, ent++ )
1021 	{
1022 //		if ( ent->inuse && ent->client ) {
1023 //			clCount++;
1024 //		}
1025 		//
1026 		cs = AICast_GetCastState( i );
1027 		// is this a cast AI?
1028 		if ( cs->bs ) {
1029 			if ( ent->aiInactive == qfalse && ent->inuse ) {
1030 				//
1031 				elapsed = level.time - cs->lastMoveThink;
1032 				if ( cs->lastThink && elapsed > 0 ) {
1033 					highPriority = qfalse;
1034 					if ( ent->health > 0 ) {
1035 						highPriority = qtrue;
1036 					} else if ( cs->deathTime > ( level.time - 5000 ) ) {
1037 						highPriority = qtrue;
1038 					}
1039 					//
1040 					if ( highPriority ) {
1041 						activeCount++;
1042 					}
1043 					//
1044 					// optimization, if they're not in the player's PVS, and they aren't trying to move, then don't bother thinking
1045 					if (    ( highPriority && ( elapsed > 300 ) )
1046 							||  ( g_entities[0].client && g_entities[0].client->cameraPortal )
1047 							||  ( highPriority && ( cs->vislist[0].visible_timestamp == cs->vislist[0].lastcheck_timestamp ) )
1048 							||  ( highPriority && ( pcs->vislist[cs->entityNum].visible_timestamp == pcs->vislist[cs->entityNum].lastcheck_timestamp ) )
1049 							||  ( VectorLength( ent->client->ps.velocity ) > 0 )
1050 							||  ( highPriority && ( cs->lastucmd.forwardmove || cs->lastucmd.rightmove || cs->lastucmd.upmove > 0 || cs->lastucmd.buttons || cs->lastucmd.wbuttons ) )
1051 							// !!NOTE: always allow thinking if in PVS, otherwise bosses won't gib, and dead guys might not push away from clipped walls
1052 							||  ( trap_InPVS( cs->bs->origin, g_entities[0].s.pos.trBase ) ) ) { // do pvs check last, since it's the most expensive to call
1053 						oldLegsTimer = ent->client->ps.legsTimer;
1054 						//
1055 						// send it's movement commands
1056 						//
1057 						serverTime = time;
1058 						AICast_UpdateInput( cs, elapsed );
1059 						trap_BotUserCommand( cs->bs->client, &( cs->lastucmd ) );
1060 						cs->lastMoveThink = level.time;
1061 						//
1062 						// check for anim changes that may require us to stay still
1063 						//
1064 						if ( oldLegsTimer < ent->client->ps.legsTimer && ent->client->ps.groundEntityNum == ENTITYNUM_WORLD ) {
1065 							// dont move until they are finished
1066 							if ( cs->castScriptStatus.scriptNoMoveTime < level.time + ent->client->ps.legsTimer ) {
1067 								cs->castScriptStatus.scriptNoMoveTime = level.time + ent->client->ps.legsTimer;
1068 							}
1069 						}
1070 					}
1071 				}
1072 			} else {
1073 				trap_UnlinkEntity( ent );
1074 			}
1075 			//
1076 			// see if we've checked all cast AI's
1077 			if ( ++castcount >= numcast ) {
1078 				break;
1079 			}
1080 		}
1081 	}
1082 	//
1083 	lasttime = time;
1084 	//
1085 	if ( aicast_debug.integer == 3 ) {
1086 		G_Printf( "AI Active Count: %i\n", activeCount );
1087 	}
1088 }
1089 
1090 /*
1091 ==============
1092 AICast_PredictMovement
1093 
1094   Simulates movement over a number of frames, returning the end position
1095 ==============
1096 */
AICast_PredictMovement(cast_state_t * cs,int numframes,float frametime,aicast_predictmove_t * move,usercmd_t * ucmd,int checkHitEnt)1097 void AICast_PredictMovement( cast_state_t *cs, int numframes, float frametime, aicast_predictmove_t *move, usercmd_t *ucmd, int checkHitEnt ) {
1098 	int frame, i;
1099 	playerState_t ps;
1100 	pmove_t pm;
1101 	trace_t tr;
1102 	vec3_t end, startHitVec = { 0 }, thisHitVec, lastOrg, projPoint;
1103 	qboolean checkReachMarker;
1104 	gentity_t   *ent = &g_entities[cs->entityNum];
1105 	bot_input_t bi;
1106 
1107 //	int pretime = Sys_MilliSeconds();
1108 //	G_Printf("PredictMovement: %f duration, %i frames\n", frametime, numframes );
1109 
1110 	if ( cs->bs ) {
1111 		ps = cs->bs->cur_ps;
1112 		trap_EA_GetInput( cs->entityNum, (float) level.time / 1000, &bi );
1113 	} else {
1114 		ps = g_entities[cs->entityNum].client->ps;
1115 	}
1116 
1117 	ps.eFlags |= EF_DUMMY_PMOVE;
1118 
1119 	move->stopevent = PREDICTSTOP_NONE;
1120 
1121 	if ( checkHitEnt >= 0 && !Q_stricmp( g_entities[checkHitEnt].classname, "ai_marker" ) ) {
1122 		checkReachMarker = qtrue;
1123 		VectorSubtract( g_entities[checkHitEnt].r.currentOrigin, ps.origin, startHitVec );
1124 		VectorCopy( ps.origin, lastOrg );
1125 	} else {
1126 		checkReachMarker = qfalse;
1127 	}
1128 
1129 	// don't let the frametime be too high
1130 //	while (frametime > 0.2) {
1131 //		numframes *= 2;
1132 //		frametime /= 2;
1133 //	}
1134 
1135 	for ( frame = 0; frame < numframes; frame++ )
1136 	{
1137 		memset( &pm, 0, sizeof( pm ) );
1138 		pm.ps = &ps;
1139 		pm.cmd = *ucmd;
1140 		pm.oldcmd = *ucmd;
1141 		pm.ps->commandTime = 0;
1142 		pm.cmd.serverTime = (int)( 1000.0 * frametime );
1143 		pm.tracemask = g_entities[cs->entityNum].clipmask; //MASK_PLAYERSOLID;
1144 
1145 		pm.trace = trap_TraceCapsule; //trap_Trace;
1146 		pm.pointcontents = trap_PointContents;
1147 		pm.debugLevel = qfalse;
1148 		pm.noFootsteps = qtrue;
1149 		// RF, not needed for prediction
1150 		//pm.noWeapClips = qtrue;	// (SA) AI's ignore weapon clips
1151 
1152 		// perform a pmove
1153 		Pmove( &pm );
1154 
1155 		if ( checkHitEnt >= 0 ) {
1156 			// if we've hit the checkent, abort
1157 			if ( checkReachMarker ) {
1158 				VectorSubtract( g_entities[checkHitEnt].r.currentOrigin, pm.ps->origin, thisHitVec );
1159 				if ( DotProduct( startHitVec, thisHitVec ) < 0 ) {
1160 					// project the marker onto the movement vec, and check distance
1161 					ProjectPointOntoVector( g_entities[checkHitEnt].r.currentOrigin, lastOrg, pm.ps->origin, projPoint );
1162 					if ( VectorDistance( g_entities[checkHitEnt].r.currentOrigin, projPoint ) < 8 ) {
1163 						move->stopevent = PREDICTSTOP_HITENT;
1164 						goto done;
1165 					}
1166 				}
1167 				// use this position as the base for the next test
1168 				//VectorCopy( thisHitVec, startHitVec );
1169 				VectorCopy( pm.ps->origin, lastOrg );
1170 			}
1171 			// if we didn't reach the marker, then check for something that blocked us
1172 			for ( i = 0; i < pm.numtouch; i++ ) {
1173 				if ( pm.touchents[i] == pm.ps->groundEntityNum ) {
1174 					continue;
1175 				}
1176 				if ( pm.touchents[i] == checkHitEnt ) {
1177 					move->stopevent = PREDICTSTOP_HITENT;
1178 					goto done;
1179 				} else if ( pm.touchents[i] < MAX_CLIENTS ||
1180 							( pm.touchents[i] != ENTITYNUM_WORLD && ( g_entities[pm.touchents[i]].s.eType != ET_MOVER || g_entities[pm.touchents[i]].moverState != MOVER_POS1 ) ) ) {
1181 					// we have hit another entity, so abort
1182 					move->stopevent = PREDICTSTOP_HITCLIENT;
1183 					goto done;
1184 				} else if ( !Q_stricmp( g_entities[pm.touchents[i]].classname, "script_mover" ) ) {
1185 					// avoid script_mover's
1186 					move->stopevent = PREDICTSTOP_HITCLIENT;
1187 					goto done;
1188 				}
1189 			}
1190 			// if we are trying to get to something, we should keep adjusting our input to head towards them
1191 			if ( cs->bs && checkHitEnt >= 0 ) {
1192 				// keep walking straight to them
1193 				VectorSubtract( g_entities[checkHitEnt].r.currentOrigin, pm.ps->origin, bi.dir );
1194 				if ( !ent->waterlevel ) {
1195 					bi.dir[2] = 0;
1196 				}
1197 				VectorNormalize( bi.dir );
1198 				bi.actionflags = 0;
1199 				bi.speed = 400;
1200 				AICast_InputToUserCommand( cs, &bi, ucmd, ps.delta_angles );
1201 			}
1202 		}
1203 	}
1204 
1205 done:
1206 
1207 	// hack, if we are above ground, chances are it's because we only did one frame, and gravity isn't applied until
1208 	// after the frame, so try and drop us down some
1209 	if ( pm.ps->groundEntityNum == ENTITYNUM_NONE ) {
1210 		VectorCopy( pm.ps->origin, end );
1211 		end[2] -= 32;
1212 		trap_Trace( &tr, pm.ps->origin, pm.mins, pm.maxs, end, pm.ps->clientNum, pm.tracemask );
1213 		if ( !tr.startsolid && !tr.allsolid && tr.fraction < 1 ) {
1214 			VectorCopy( tr.endpos, pm.ps->origin );
1215 			pm.ps->groundEntityNum = tr.entityNum;
1216 		}
1217 	}
1218 
1219 	// copy off the results
1220 	VectorCopy( pm.ps->origin, move->endpos );
1221 	move->frames = frame;
1222 	//move->presencetype = cs->bs->presencetype;
1223 	VectorCopy( pm.ps->velocity, move->velocity );
1224 	move->numtouch = pm.numtouch;
1225 	memcpy( move->touchents, pm.touchents, sizeof( pm.touchents ) );
1226 	move->groundEntityNum = pm.ps->groundEntityNum;
1227 
1228 //G_Printf("PredictMovement: %i ms\n", -pretime + Sys_MilliSeconds() );
1229 }
1230 
1231 /*
1232 ============
1233 AICast_GetAvoid
1234 ============
1235 */
AICast_GetAvoid(cast_state_t * cs,bot_goal_t * goal,vec3_t outpos,qboolean reverse,int blockEnt)1236 qboolean AICast_GetAvoid( cast_state_t *cs, bot_goal_t *goal, vec3_t outpos, qboolean reverse, int blockEnt ) {
1237 	float yaw, oldyaw, distmoved, bestmoved;
1238 	vec3_t bestpos = { 0 };
1239 	aicast_predictmove_t castmove;
1240 	usercmd_t ucmd;
1241 	qboolean enemyVisible;
1242 	float angleDiff;
1243 	int starttraveltime = 0, traveltime;
1244 	int invert;
1245 	float inc;
1246 	qboolean averting = qfalse;
1247 	float maxYaw;
1248 	static int lastTime;
1249 	//
1250 	// if we are in the air, no chance of avoiding
1251 	if ( cs->bs->cur_ps.groundEntityNum == ENTITYNUM_NONE && g_entities[cs->entityNum].waterlevel <= 1 ) {
1252 		return qfalse;
1253 	}
1254 	//
1255 	if ( cs->lastAvoid > level.time - rand() % 500 ) {
1256 		return qfalse;
1257 	}
1258 	cs->lastAvoid = level.time + 50 + rand() % 500;
1259 	//
1260 	if ( lastTime == level.time ) {
1261 		return qfalse;
1262 	}
1263 	lastTime = level.time;
1264 
1265 	// if they have an enemy, and can currently see them, don't move out of their view
1266 	enemyVisible =  ( cs->enemyNum >= 0 ) &&
1267 				   ( AICast_CheckAttack( cs, cs->enemyNum, qfalse ) );
1268 	//
1269 	// look for a good direction to move out of the way
1270 	bestmoved = 0;
1271 	if ( goal ) {
1272 		starttraveltime = trap_AAS_AreaTravelTimeToGoalArea( cs->bs->areanum, cs->bs->origin, goal->areanum, cs->travelflags );
1273 	}
1274 	memcpy( &ucmd, &cs->lastucmd, sizeof( usercmd_t ) );
1275 	ucmd.forwardmove = 127;
1276 	ucmd.rightmove = 0;
1277 	ucmd.upmove = 0;
1278 	if ( cs->dangerEntity >= 0 && cs->dangerEntityValidTime >= level.time ) {
1279 		averting = qtrue;
1280 	} else if ( !goal ) {
1281 		averting = qtrue;   // not heading for a goal, so we must be getting out of someone's way
1282 	}
1283 	//
1284 	maxYaw = 0;
1285 	//
1286 	if ( averting ) {
1287 		// avoiding danger, go anywhere!
1288 		angleDiff = 300;
1289 		inc = 60;
1290 		invert = 1;
1291 	} else {
1292 		if ( level.time % 1000 < 500 ) {
1293 			invert = 1;
1294 		} else {
1295 			invert = -1;
1296 		}
1297 		angleDiff = 140;
1298 		inc = 35;
1299 	}
1300 	if ( blockEnt > aicast_maxclients ) {
1301 		maxYaw = angleDiff;
1302 	}
1303 	//
1304 	for ( yaw = -angleDiff * invert; yaw*invert <= maxYaw; yaw += inc * invert ) {
1305 		if ( !averting && !yaw ) {
1306 			continue;
1307 		}
1308 		oldyaw = cs->bs->cur_ps.viewangles[YAW];
1309 		cs->bs->cur_ps.viewangles[YAW] += yaw + reverse * 180;
1310 		//
1311 		ucmd.angles[YAW] = ANGLE2SHORT( AngleMod( cs->bs->cur_ps.viewangles[YAW] ) );
1312 		//
1313 		AICast_PredictMovement( cs, 5, 0.4, &castmove, &ucmd, -1 );
1314 		// if we have a danger entity, try and get away from it at all costs
1315 		if ( cs->dangerEntity >= 0 && cs->dangerEntityValidTime >= level.time ) {
1316 			distmoved = Distance( castmove.endpos, cs->dangerEntityPos );
1317 		} else if ( goal ) {
1318 			//distmoved = 99999 - trap_AAS_AreaTravelTimeToGoalArea( BotPointAreaNum(castmove.endpos), castmove.endpos, goal->areanum, cs->travelflags );
1319 			distmoved = 99999 - Distance( castmove.endpos, goal->origin );
1320 		} else {
1321 			distmoved = Distance( castmove.endpos, cs->bs->cur_ps.origin );
1322 		}
1323 		if (    ( distmoved > bestmoved )
1324 				//&&	((cs->bs->origin[2] - castmove.endpos[2]) < 64)	// allow up, but not down (falling)
1325 				&&  ( castmove.groundEntityNum != ENTITYNUM_NONE ) ) {
1326 			// they all passed, check any other stuff
1327 			if ( !enemyVisible || AICast_CheckAttackAtPos( cs->entityNum, cs->enemyNum, castmove.endpos, qfalse, qfalse ) ) {
1328 				if ( !goal || ( traveltime = trap_AAS_AreaTravelTimeToGoalArea( BotPointAreaNum( castmove.endpos ), castmove.endpos, goal->areanum, cs->travelflags ) ) < ( starttraveltime + 200 ) ) {
1329 					bestmoved = distmoved;
1330 					VectorCopy( castmove.endpos, bestpos );
1331 				}
1332 			}
1333 		}
1334 		//
1335 		cs->bs->cur_ps.viewangles[YAW] = oldyaw;
1336 	}
1337 	//
1338 	if ( bestmoved > 0 ) {
1339 		VectorCopy( bestpos, outpos );
1340 		return qtrue;
1341 	} else {
1342 		return qfalse;
1343 	}
1344 
1345 //G_Printf("GetAvoid: %i ms\n", -pretime + Sys_MilliSeconds() );
1346 }
1347 
1348 /*
1349 ============
1350 AICast_Blocked
1351 ============
1352 */
AICast_Blocked(cast_state_t * cs,bot_moveresult_t * moveresult,int activate,bot_goal_t * goal)1353 void AICast_Blocked( cast_state_t *cs, bot_moveresult_t *moveresult, int activate, bot_goal_t *goal ) {
1354 	vec3_t pos, dir;
1355 	aicast_predictmove_t move;
1356 	usercmd_t ucmd;
1357 	bot_input_t bi;
1358 	cast_state_t *ocs;
1359 	int i, blockEnt = -1;
1360 	bot_goal_t ogoal;
1361 
1362 	if ( cs->blockedAvoidTime < level.time ) {
1363 		if ( cs->blockedAvoidTime < level.time - 300 ) {
1364 			if ( VectorCompare( cs->bs->cur_ps.velocity, vec3_origin ) && !cs->lastucmd.forwardmove && !cs->lastucmd.rightmove ) {
1365 				// not moving, don't bother checking
1366 				cs->blockedAvoidTime = level.time - 1;
1367 				return;
1368 			}
1369 			// are we going to hit someone soon?
1370 			trap_EA_GetInput( cs->entityNum, (float) level.time / 1000, &bi );
1371 			AICast_InputToUserCommand( cs, &bi, &ucmd, cs->bs->cur_ps.delta_angles );
1372 			AICast_PredictMovement( cs, 1, 0.6, &move, &ucmd, ( goal && goal->entitynum > -1 ) ? goal->entitynum : cs->entityNum );
1373 
1374 			// blocked if we hit a client (or non-stationary mover) other than our enemy or goal
1375 			if ( move.stopevent != PREDICTSTOP_HITCLIENT ) {
1376 				// not blocked
1377 				cs->blockedAvoidTime = level.time - 1;
1378 				return;
1379 			}
1380 
1381 			// if we stopped passed our goal, ignore it
1382 			if ( goal ) {
1383 				if ( VectorDistance( cs->bs->origin, goal->origin ) < VectorDistance( cs->bs->origin, move.endpos ) ) {
1384 					vec3_t v1, v2;
1385 					VectorSubtract( goal->origin, cs->bs->origin, v1 );
1386 					VectorSubtract( goal->origin, move.endpos, v2 );
1387 					VectorNormalize( v1 );
1388 					VectorNormalize( v2 );
1389 					if ( DotProduct( v1, v2 ) < 0 ) {
1390 						// we went passed the goal, so assume we can reach it
1391 						cs->blockedAvoidTime = level.time - 1;
1392 						return;
1393 					}
1394 				}
1395 			}
1396 
1397 			// try and get them to move, in case we can't get around them
1398 			blockEnt = -1;
1399 			for ( i = 0; i < move.numtouch; i++ ) {
1400 				if ( move.touchents[i] >= MAX_CLIENTS ) {
1401 					if ( !Q_stricmp( g_entities[move.touchents[i]].classname, "script_mover" ) ) {
1402 						// avoid script_mover's
1403 						blockEnt = move.touchents[i];
1404 					}
1405 					// if we are close to the impact point, then avoid this entity
1406 					else if ( VectorDistance( cs->bs->origin, move.endpos ) < 10 ) {
1407 						//G_Printf("AI (%s) avoiding %s\n", g_entities[cs->entityNum].aiName, g_entities[move.touchents[i]].classname );
1408 						blockEnt = move.touchents[i];
1409 					}
1410 					continue;
1411 				}
1412 				//
1413 				ocs = AICast_GetCastState( move.touchents[i] );
1414 				if ( !ocs->bs ) {
1415 					blockEnt = move.touchents[i];
1416 				}
1417 				// reject this blocker if we are following or going to them
1418 				else if ( cs->followEntity != ocs->entityNum ) {
1419 					// if they are moving away from us already, let them go
1420 					if ( VectorLength( ocs->bs->cur_ps.velocity ) > 10 ) {
1421 						vec3_t v1, v2;
1422 
1423 						VectorSubtract( ocs->bs->origin, cs->bs->origin, v2 );
1424 						VectorNormalize( v2 );
1425 						VectorNormalize2( ocs->bs->cur_ps.velocity, v1 );
1426 
1427 						if ( DotProduct( v1, v2 ) > 0.0 ) {
1428 							continue;
1429 						}
1430 					}
1431 					//
1432 					// if they recently were asked to avoid us, then they're probably not listening
1433 					if ( ocs->obstructingTime > level.time - 500 ) {
1434 						blockEnt = move.touchents[i];
1435 					}
1436 					//
1437 					// if they are not avoiding, ignore
1438 					if ( !( ocs->aiFlags & AIFL_NOAVOID ) ) {
1439 						continue;
1440 					}
1441 					//
1442 					// they should avoid us
1443 					if ( ocs->leaderNum >= 0 ) {
1444 						ogoal.entitynum = ocs->leaderNum;
1445 						VectorCopy( g_entities[ocs->leaderNum].r.currentOrigin, ogoal.origin );
1446 						if ( AICast_GetAvoid( ocs, &ogoal, ocs->obstructingPos, qfalse, cs->entityNum ) ) {
1447 							// give them time to move somewhere else
1448 							ocs->obstructingTime = level.time + 1000;
1449 						} else {
1450 							// make sure they don't call GetAvoid() for another few frames to let others avoid also
1451 							ocs->obstructingTime = level.time - 1;
1452 							blockEnt = move.touchents[i];
1453 						}
1454 					} else {
1455 						if ( AICast_GetAvoid( ocs, NULL, ocs->obstructingPos, qfalse, cs->entityNum ) ) {
1456 							// give them time to move somewhere else
1457 							ocs->obstructingTime = level.time + 1000;
1458 						} else {
1459 							// make sure they don't call GetAvoid() for another few frames to let others avoid also
1460 							ocs->obstructingTime = level.time - 1;
1461 							blockEnt = move.touchents[i];
1462 						}
1463 					}
1464 				}
1465 			}
1466 
1467 		} else {
1468 			return;
1469 		}
1470 
1471 		if ( blockEnt < 0 ) {
1472 			// nothing found to be worth avoding
1473 			cs->blockedAvoidTime = level.time - 1;
1474 			return;
1475 		}
1476 
1477 		// something is blocking our path
1478 		if ( g_entities[blockEnt].aiName && g_entities[blockEnt].client && cs->bs
1479 			 && VectorDistance( cs->bs->origin, g_entities[blockEnt].r.currentOrigin ) < 128 ) {
1480 			int oldId = cs->castScriptStatus.scriptId;
1481 			AICast_ScriptEvent( cs, "blocked", g_entities[blockEnt].aiName );
1482 			if ( ( oldId != cs->castScriptStatus.scriptId ) || ( cs->aiFlags & AIFL_DENYACTION ) ) {
1483 				// the script has changed, so assume the scripting is handling the avoidance
1484 				return;
1485 			}
1486 		}
1487 
1488 		// avoid geometry and props, but assume clients will get out the way
1489 		if ( /*blockEnt > MAX_CLIENTS &&*/ AICast_GetAvoid( cs, goal, pos, qfalse, blockEnt ) ) {
1490 			VectorSubtract( pos, cs->bs->cur_ps.origin, dir );
1491 			VectorNormalize( dir );
1492 			cs->blockedAvoidYaw = vectoyaw( dir );
1493 			if ( blockEnt >= MAX_CLIENTS ) {
1494 				cs->blockedAvoidTime = level.time + 100 + rand() % 200;
1495 			} else {
1496 				cs->blockedAvoidTime = level.time + 300 + rand() % 400;
1497 			}
1498 		} else {
1499 			cs->blockedAvoidTime = level.time - 1;    // don't look again for another few frames
1500 			return;
1501 		}
1502 	}
1503 
1504 	VectorClear( pos );
1505 	pos[YAW] = cs->blockedAvoidYaw;
1506 	AngleVectors( pos, dir, NULL, NULL );
1507 
1508 	if ( moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE ) {
1509 		trap_EA_Jump( cs->bs->entitynum );
1510 	}
1511 
1512 	trap_EA_Move( cs->bs->entitynum, dir, 200 ); //400);
1513 
1514 	vectoangles( dir, cs->ideal_viewangles );
1515 	cs->ideal_viewangles[2] *= 0.5;
1516 }
1517 
1518 /*
1519 ================
1520 AICast_EvaluatePmove
1521 
1522   Avoidance after the event (leaders instruct AI's to get out the way, AI's instruct other non-moving AI's to get out the way)
1523 ================
1524 */
AICast_EvaluatePmove(int clientnum,pmove_t * pm)1525 void AICast_EvaluatePmove( int clientnum, pmove_t *pm ) {
1526 	cast_state_t    *cs, *ocs;
1527 	int i, ent;
1528 	bot_goal_t ogoal;
1529 
1530 	//vec3_t pos, dir;
1531 	cs = AICast_GetCastState( clientnum );
1532 	// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
1533 	trap_AAS_SetCurrentWorld( cs->aasWorldIndex );
1534 
1535 	// NOTE: this is only enabled for real clients, so their followers get out of their way
1536 	//if (cs->bs)
1537 	//	return;
1538 
1539 	// look through the touchent's to see if we've bumped into something we should avoid, or react to
1540 	for ( i = 0; i < pm->numtouch; i++ )
1541 	{
1542 		// mark the time, so they can deal with the obstruction in their own think functions
1543 		cs->blockedTime = level.time;
1544 
1545 		if ( pm->touchents[i] == pm->ps->groundEntityNum ) {
1546 			continue;
1547 		}
1548 
1549 		// if they are an AI Cast, inform them of our disposition, and hope that they are reasonable
1550 		// enough to assist us in our desire to move beyond our current position
1551 		if ( pm->touchents[i] < aicast_maxclients ) {
1552 			if ( !AICast_EntityVisible( cs, pm->touchents[i], qtrue ) ) {
1553 				continue;
1554 			}
1555 
1556 			// if we are inspecting the body, abort if we touch anything
1557 			if ( cs->bs && cs->enemyNum >= 0 && g_entities[cs->enemyNum].health <= 0 ) {
1558 				cs->enemyNum = -1;
1559 			}
1560 
1561 			// anything we touch, should see us
1562 			AICast_UpdateVisibility( &g_entities[pm->touchents[i]], &g_entities[cs->entityNum], qfalse, qtrue );
1563 
1564 			ocs = AICast_GetCastState( pm->touchents[i] );
1565 			if (    ( ocs->bs ) &&
1566 					( AICast_SameTeam( cs, ocs->entityNum ) ) &&
1567 					( !( ocs->aiFlags & AIFL_NOAVOID ) ) &&
1568 					( ( ocs->leaderNum == cs->entityNum ) || ( VectorLength( ocs->bs->velocity ) < 5 ) ) &&
1569 					( ocs->obstructingTime < ( level.time + 100 ) ) ) {
1570 				// if they are moving away from us already, let them go
1571 				if ( VectorLength( ocs->bs->cur_ps.velocity ) > 10 ) {
1572 					vec3_t v1, v2;
1573 
1574 					VectorSubtract( ocs->bs->origin, g_entities[clientnum].client->ps.velocity, v2 );
1575 					VectorNormalize( v2 );
1576 					VectorNormalize2( ocs->bs->cur_ps.velocity, v1 );
1577 
1578 					if ( DotProduct( v1, v2 ) > 0.0 ) {
1579 						continue;
1580 					}
1581 				}
1582 				if ( ocs->leaderNum >= 0 ) {
1583 					VectorCopy( g_entities[ocs->leaderNum].r.currentOrigin, ogoal.origin );
1584 					ogoal.areanum = BotPointAreaNum( ogoal.origin );
1585 					ogoal.entitynum = ocs->leaderNum;
1586 					if ( ocs->bs && AICast_GetAvoid( ocs, &ogoal, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
1587 						ocs->obstructingTime = level.time + 1000;
1588 					}
1589 				} else {
1590 					if ( ocs->bs && AICast_GetAvoid( ocs, NULL, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
1591 						ocs->obstructingTime = level.time + 1000;
1592 					}
1593 				}
1594 			}
1595 		} else if ( cs->bs ) {
1596 			// if we are blocked by a brush entity, see if we can activate it
1597 			ent = pm->touchents[i];
1598 			if ( g_entities[ent].s.modelindex > 0 && g_entities[ent].s.eType == ET_MOVER ) {
1599 				//find the bsp entity which should be activated in order to remove
1600 				//the blocking entity
1601 
1602 				if ( !g_entities[ent].isProp
1603 					 && Q_stricmp( g_entities[ent].classname, "func_static" )
1604 					 && Q_stricmp( g_entities[ent].classname, "func_button" )
1605 					 && Q_stricmp( g_entities[ent].classname, "func_tram" ) ) {
1606 					G_Activate( &g_entities[ent], &g_entities[cs->entityNum] );
1607 				}
1608 
1609 			}
1610 		}
1611 	}
1612 }
1613 
1614 /*
1615 ==============
1616 AICast_RequestCrouchAttack
1617 ==============
1618 */
AICast_RequestCrouchAttack(cast_state_t * cs,vec3_t org,float time)1619 qboolean AICast_RequestCrouchAttack( cast_state_t *cs, vec3_t org, float time ) {
1620 	if ( cs->attributes[ATTACK_CROUCH] > 0 && AICast_CheckAttackAtPos( cs->entityNum, cs->enemyNum, org, qtrue, qfalse ) ) {
1621 		if ( time ) {
1622 			cs->attackcrouch_time = level.time + (int)( time * 1000 );
1623 		}
1624 		return qtrue;
1625 	}
1626 
1627 	return qfalse;
1628 }
1629 
1630 /*
1631 ==============
1632 AICast_QueryThink
1633 ==============
1634 */
AICast_QueryThink(cast_state_t * cs)1635 void AICast_QueryThink( cast_state_t *cs ) {
1636 	gentity_t *ent;
1637 	qboolean visible;
1638 	cast_state_t *ocs;
1639 	vec3_t vec;
1640 
1641 	ent = &g_entities[cs->entityNum];
1642 	ocs = AICast_GetCastState( cs->enemyNum );
1643 
1644 	// never crouch while in this state (by choice anyway)
1645 	cs->attackcrouch_time = 0;
1646 
1647 	// look at where we last (thought we) saw them
1648 	VectorSubtract( cs->vislist[cs->enemyNum].visible_pos, cs->bs->origin, vec );
1649 	VectorNormalize( vec );
1650 	vectoangles( vec, cs->ideal_viewangles );
1651 
1652 	// are they visible now?
1653 	visible = AICast_VisibleFromPos( cs->bs->origin, cs->entityNum, g_entities[cs->enemyNum].r.currentOrigin, cs->enemyNum, qfalse );
1654 
1655 	// make sure we dont process the sighting of this enemy by going into query mode again, without them being visible again after we leave here
1656 	cs->vislist[cs->enemyNum].flags &= ~AIVIS_PROCESS_SIGHTING;
1657 
1658 	// look towards where we last saw them
1659 	AICast_AimAtEnemy( cs );
1660 
1661 	// if visible and alert time has expired, go POSTAL
1662 	if ( ( cs->queryAlertSightTime < 0 ) || ( ( cs->queryAlertSightTime < level.time ) && visible ) ) {
1663 		if ( !cs->queryAlertSightTime ) {
1664 			// set the "short reaction" condition
1665 			BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
1666 		}
1667 		AICast_StateChange( cs, AISTATE_COMBAT );
1668 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
1669 		AIFunc_BattleStart( cs );
1670 		return;
1671 	}
1672 
1673 	// if they've fired since the start of the query mode, go POSTAL
1674 	if ( ocs->lastWeaponFired > cs->queryStartTime ) {
1675 		// set the "short reaction" condition
1676 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
1677 		AICast_StateChange( cs, AISTATE_COMBAT );
1678 		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
1679 		AIFunc_BattleStart( cs );
1680 		return;
1681 	}
1682 
1683 	// if not visible, then kill the Lock On timer
1684 	if ( ( cs->queryAlertSightTime > 0 ) && !visible ) {
1685 		cs->queryAlertSightTime = 0;
1686 	}
1687 
1688 	// if the query has expired, go back to relaxed
1689 	if ( !ent->client->ps.legsTimer ) {
1690 		AICast_StateChange( cs, AISTATE_RELAXED );
1691 	}
1692 }
1693 
1694 /*
1695 ================
1696 AICast_DeadClipWalls
1697 ================
1698 */
AICast_DeadClipWalls(cast_state_t * cs)1699 void AICast_DeadClipWalls( cast_state_t *cs ) {
1700 /*
1701 	//animation_t *anim;
1702 	orientation_t or;
1703 	vec3_t	src, vel;
1704 	trace_t	tr;
1705 
1706 	// get the death animation we are currently playing
1707 	//anim = BG_GetAnimationForIndex( cs->entityNum, (cs->bs->cur_ps.torsoAnim & ~ANIM_TOGGLEBIT) );
1708 
1709 	// find the head position
1710 	trap_GetTag( cs->entityNum, "tag_head", &or );
1711 	// move up a tad
1712 	or.origin[2] += 3;
1713 
1714 	// trace from the base of our bounding box, to the head
1715 	VectorCopy( cs->bs->origin, src );
1716 	src[2] -= cs->bs->cur_ps.mins[2] + 3;
1717 	trap_Trace( &tr, src, vec3_origin, vec3_origin, or.origin, cs->entityNum, MASK_SOLID );
1718 
1719 	// if we hit something, move away from it
1720 	if (!tr.startsolid && !tr.allsolid && tr.fraction < 1.0) {
1721 		VectorScale( tr.plane.normal, 80, vel );
1722 		vel[2] = 0;
1723 		VectorAdd( g_entities[cs->entityNum].client->ps.velocity, vel, g_entities[cs->entityNum].client->ps.velocity );
1724 	}
1725 */
1726 }
1727 
1728 /*
1729 ==================
1730 AICast_IdleReload
1731 ==================
1732 */
AICast_IdleReload(cast_state_t * cs)1733 void AICast_IdleReload( cast_state_t *cs ) {
1734 	if ( AICast_NoReload( cs->entityNum ) ) {
1735 		return;
1736 	}
1737 	if ( cs->noReloadTime >= level.time ) {
1738 		return;
1739 	}
1740 	if ( !( ( cs->bs->cur_ps.ammoclip[BG_FindClipForWeapon( cs->bs->cur_ps.weapon )] < (int)( 0.75 * ammoTable[cs->bs->cur_ps.weapon].maxclip ) ) && cs->bs->cur_ps.ammo[BG_FindAmmoForWeapon( cs->bs->cur_ps.weapon )] ) ) {
1741 		return;
1742 	}
1743 	//
1744 	trap_EA_Reload( cs->entityNum );
1745 }
1746