1 // g_ai.c
2 
3 #include "g_local.h"
4 
5 qboolean FindTarget (edict_t *self);
6 extern cvar_t	*maxclients;
7 
8 qboolean ai_checkattack (edict_t *self, float dist);
9 
10 qboolean	enemy_vis;
11 qboolean	enemy_infront;
12 int			enemy_range;
13 float		enemy_yaw;
14 
15 // ROGUE STUFF
16 #define SLIDING_TROOPS	1
17 #define	MAX_SIDESTEP	8.0
18 //
19 
20 //============================================================================
21 
22 
23 /*
24 =================
25 AI_SetSightClient
26 
27 Called once each frame to set level.sight_client to the
28 player to be checked for in findtarget.
29 
30 If all clients are either dead or in notarget, sight_client
31 will be null.
32 
33 In coop games, sight_client will cycle between the clients.
34 =================
35 */
AI_SetSightClient(void)36 void AI_SetSightClient (void)
37 {
38 	edict_t	*ent;
39 	int		start, check;
40 
41 	if (level.sight_client == NULL)
42 		start = 1;
43 	else
44 		start = level.sight_client - g_edicts;
45 
46 	check = start;
47 	while (1)
48 	{
49 		check++;
50 		if (check > game.maxclients)
51 			check = 1;
52 		ent = &g_edicts[check];
53 		if (ent->inuse
54 			&& ent->health > 0
55 			&& !(ent->flags & (FL_NOTARGET|FL_DISGUISED)) )
56 		{
57 			level.sight_client = ent;
58 			return;		// got one
59 		}
60 		if (check == start)
61 		{
62 			level.sight_client = NULL;
63 			return;		// nobody to see
64 		}
65 	}
66 }
67 
68 //============================================================================
69 
70 /*
71 =============
72 ai_move
73 
74 Move the specified distance at current facing.
75 This replaces the QC functions: ai_forward, ai_back, ai_pain, and ai_painforward
76 ==============
77 */
ai_move(edict_t * self,float dist)78 void ai_move (edict_t *self, float dist)
79 {
80 	M_walkmove (self, self->s.angles[YAW], dist);
81 }
82 
83 
84 /*
85 =============
86 ai_stand
87 
88 Used for standing around and looking for players
89 Distance is for slight position adjustments needed by the animations
90 ==============
91 */
ai_stand(edict_t * self,float dist)92 void ai_stand (edict_t *self, float dist)
93 {
94 	vec3_t	v;
95 	// PMM
96 	qboolean retval;
97 
98 	if (dist)
99 		M_walkmove (self, self->s.angles[YAW], dist);
100 
101 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
102 	{
103 		if (self->enemy)
104 		{
105 			VectorSubtract (self->enemy->s.origin, self->s.origin, v);
106 			self->ideal_yaw = vectoyaw(v);
107 			if (self->s.angles[YAW] != self->ideal_yaw && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
108 			{
109 				self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
110 				self->monsterinfo.run (self);
111 			}
112 			if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
113 				M_ChangeYaw (self);
114 			// PMM
115 			// find out if we're going to be shooting
116 			retval = ai_checkattack (self, 0);
117 			// record sightings of player
118 			if ((self->enemy) && (self->enemy->inuse) && (visible(self, self->enemy)))
119 			{
120 				self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
121 				VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
122 				VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
123 				self->monsterinfo.trail_time = level.time;
124 				self->monsterinfo.blind_fire_delay = 0;
125 			}
126 			// check retval to make sure we're not blindfiring
127 			else if (!retval)
128 			{
129 				FindTarget (self);
130 				return;
131 			}
132 //			ai_checkattack (self, 0);
133 			// pmm
134 		}
135 		else
136 			FindTarget (self);
137 		return;
138 	}
139 
140 	if (FindTarget (self))
141 		return;
142 
143 	if (level.time > self->monsterinfo.pausetime)
144 	{
145 		self->monsterinfo.walk (self);
146 		return;
147 	}
148 
149 	if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time))
150 	{
151 		if (self->monsterinfo.idle_time)
152 		{
153 			self->monsterinfo.idle (self);
154 			self->monsterinfo.idle_time = level.time + 15 + random() * 15;
155 		}
156 		else
157 		{
158 			self->monsterinfo.idle_time = level.time + random() * 15;
159 		}
160 	}
161 }
162 
163 
164 /*
165 =============
166 ai_walk
167 
168 The monster is walking it's beat
169 =============
170 */
ai_walk(edict_t * self,float dist)171 void ai_walk (edict_t *self, float dist)
172 {
173 	M_MoveToGoal (self, dist);
174 
175 	// check for noticing a player
176 	if (FindTarget (self))
177 		return;
178 
179 	if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time))
180 	{
181 		if (self->monsterinfo.idle_time)
182 		{
183 			self->monsterinfo.search (self);
184 			self->monsterinfo.idle_time = level.time + 15 + random() * 15;
185 		}
186 		else
187 		{
188 			self->monsterinfo.idle_time = level.time + random() * 15;
189 		}
190 	}
191 }
192 
193 
194 /*
195 =============
196 ai_charge
197 
198 Turns towards target and advances
199 Use this call with a distance of 0 to replace ai_face
200 ==============
201 */
ai_charge(edict_t * self,float dist)202 void ai_charge (edict_t *self, float dist)
203 {
204 	vec3_t	v;
205 	// PMM
206 	float	ofs;
207 	// PMM
208 
209 	// PMM - made AI_MANUAL_STEERING affect things differently here .. they turn, but
210 	// don't set the ideal_yaw
211 
212 	// This is put in there so monsters won't move towards the origin after killing
213 	// a tesla. This could be problematic, so keep an eye on it.
214 	if(!self->enemy || !self->enemy->inuse)		//PGM
215 		return;									//PGM
216 
217 	// PMM - save blindfire target
218 	if (visible(self, self->enemy))
219 		VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
220 	// pmm
221 
222 	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
223 	{
224 		VectorSubtract (self->enemy->s.origin, self->s.origin, v);
225 		self->ideal_yaw = vectoyaw(v);
226 //		gi.dprintf ("enemy = %s\n", vtos (self->enemy->s.origin));
227 //		gi.dprintf ("enemy: ideal yaw is %f\n", self->ideal_yaw);
228 	}
229 //	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
230 	M_ChangeYaw (self);
231 // PMM
232 //	if (dist)
233 //		M_walkmove (self, self->s.angles[YAW], dist);
234 
235 	if (dist)
236 	{
237 		if (self->monsterinfo.aiflags & AI_CHARGING)
238 		{
239 			M_MoveToGoal (self, dist);
240 			return;
241 		}
242 		// circle strafe support
243 		if (self->monsterinfo.attack_state == AS_SLIDING)
244 		{
245 			// if we're fighting a tesla, NEVER circle strafe
246 			if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")))
247 				ofs = 0;
248 			else if (self->monsterinfo.lefty)
249 				ofs = 90;
250 			else
251 				ofs = -90;
252 
253 			if (M_walkmove (self, self->ideal_yaw + ofs, dist))
254 				return;
255 
256 			self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
257 			M_walkmove (self, self->ideal_yaw - ofs, dist);
258 		}
259 		else
260 			M_walkmove (self, self->s.angles[YAW], dist);
261 	}
262 // PMM
263 }
264 
265 
266 /*
267 =============
268 ai_turn
269 
270 don't move, but turn towards ideal_yaw
271 Distance is for slight position adjustments needed by the animations
272 =============
273 */
ai_turn(edict_t * self,float dist)274 void ai_turn (edict_t *self, float dist)
275 {
276 	if (dist)
277 		M_walkmove (self, self->s.angles[YAW], dist);
278 
279 	if (FindTarget (self))
280 		return;
281 
282 	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
283 		M_ChangeYaw (self);
284 }
285 
286 
287 /*
288 
289 .enemy
290 Will be world if not currently angry at anyone.
291 
292 .movetarget
293 The next path spot to walk toward.  If .enemy, ignore .movetarget.
294 When an enemy is killed, the monster will try to return to it's path.
295 
296 .hunt_time
297 Set to time + something when the player is in sight, but movement straight for
298 him is blocked.  This causes the monster to use wall following code for
299 movement direction instead of sighting on the player.
300 
301 .ideal_yaw
302 A yaw angle of the intended direction, which will be turned towards at up
303 to 45 deg / state.  If the enemy is in view and hunt_time is not active,
304 this will be the exact line towards the enemy.
305 
306 .pausetime
307 A monster will leave it's stand state and head towards it's .movetarget when
308 time > .pausetime.
309 
310 walkmove(angle, speed) primitive is all or nothing
311 */
312 
313 /*
314 =============
315 range
316 
317 returns the range catagorization of an entity reletive to self
318 0	melee range, will become hostile even if back is turned
319 1	visibility and infront, or visibility and show hostile
320 2	infront and show hostile
321 3	only triggered by damage
322 =============
323 */
range(edict_t * self,edict_t * other)324 int range (edict_t *self, edict_t *other)
325 {
326 	vec3_t	v;
327 	float	len;
328 
329 	VectorSubtract (self->s.origin, other->s.origin, v);
330 	len = VectorLength (v);
331 	if (len < MELEE_DISTANCE)
332 		return RANGE_MELEE;
333 	if (len < 500)
334 		return RANGE_NEAR;
335 	if (len < 1000)
336 		return RANGE_MID;
337 	return RANGE_FAR;
338 }
339 
340 /*
341 =============
342 visible
343 
344 returns 1 if the entity is visible to self, even if not infront ()
345 =============
346 */
visible(edict_t * self,edict_t * other)347 qboolean visible (edict_t *self, edict_t *other)
348 {
349 	vec3_t	spot1;
350 	vec3_t	spot2;
351 	trace_t	trace;
352 
353 	VectorCopy (self->s.origin, spot1);
354 	spot1[2] += self->viewheight;
355 	VectorCopy (other->s.origin, spot2);
356 	spot2[2] += other->viewheight;
357 	trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
358 
359 	if (trace.fraction == 1.0 || trace.ent == other)		// PGM
360 		return true;
361 	return false;
362 }
363 
364 
365 /*
366 =============
367 infront
368 
369 returns 1 if the entity is in front (in sight) of self
370 =============
371 */
infront(edict_t * self,edict_t * other)372 qboolean infront (edict_t *self, edict_t *other)
373 {
374 	vec3_t	vec;
375 	float	dot;
376 	vec3_t	forward;
377 
378 	AngleVectors (self->s.angles, forward, NULL, NULL);
379 	VectorSubtract (other->s.origin, self->s.origin, vec);
380 	VectorNormalize (vec);
381 	dot = DotProduct (vec, forward);
382 
383 	if (dot > 0.3)
384 		return true;
385 	return false;
386 }
387 
388 
389 //============================================================================
390 
HuntTarget(edict_t * self)391 void HuntTarget (edict_t *self)
392 {
393 	vec3_t	vec;
394 
395 	self->goalentity = self->enemy;
396 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
397 		self->monsterinfo.stand (self);
398 	else
399 		self->monsterinfo.run (self);
400 	VectorSubtract (self->enemy->s.origin, self->s.origin, vec);
401 	self->ideal_yaw = vectoyaw(vec);
402 	// wait a while before first attack
403 	if (!(self->monsterinfo.aiflags & AI_STAND_GROUND))
404 		AttackFinished (self, 1);
405 }
406 
FoundTarget(edict_t * self)407 void FoundTarget (edict_t *self)
408 {
409 	// let other monsters see this monster for a while
410 	if (self->enemy->client)
411 	{
412 		if(self->enemy->flags & FL_DISGUISED)
413 		{
414 //			level.disguise_violator = self->enemy;
415 //			level.disguise_violation_framenum = level.framenum + 5;
416 			self->enemy->flags &= ~FL_DISGUISED;
417 		}
418 
419 		level.sight_entity = self;
420 		level.sight_entity_framenum = level.framenum;
421 		level.sight_entity->light_level = 128;
422 	}
423 
424 	self->show_hostile = level.time + 1;		// wake up other monsters
425 
426 	VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting);
427 	self->monsterinfo.trail_time = level.time;
428 	// PMM
429 	VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
430 	self->monsterinfo.blind_fire_delay = 0;
431 	// PMM
432 
433 	if (!self->combattarget)
434 	{
435 		HuntTarget (self);
436 		return;
437 	}
438 
439 	self->goalentity = self->movetarget = G_PickTarget(self->combattarget);
440 	if (!self->movetarget)
441 	{
442 		self->goalentity = self->movetarget = self->enemy;
443 		HuntTarget (self);
444 		gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget);
445 		return;
446 	}
447 
448 	// clear out our combattarget, these are a one shot deal
449 	self->combattarget = NULL;
450 	self->monsterinfo.aiflags |= AI_COMBAT_POINT;
451 
452 	// clear the targetname, that point is ours!
453 	self->movetarget->targetname = NULL;
454 	self->monsterinfo.pausetime = 0;
455 
456 	// run for it
457 	self->monsterinfo.run (self);
458 }
459 
460 
461 /*
462 ===========
463 FindTarget
464 
465 Self is currently not attacking anything, so try to find a target
466 
467 Returns TRUE if an enemy was sighted
468 
469 When a player fires a missile, the point of impact becomes a fakeplayer so
470 that monsters that see the impact will respond as if they had seen the
471 player.
472 
473 To avoid spending too much time, only a single client (or fakeclient) is
474 checked each frame.  This means multi player games will have slightly
475 slower noticing monsters.
476 ============
477 */
FindTarget(edict_t * self)478 qboolean FindTarget (edict_t *self)
479 {
480 	edict_t		*client;
481 	qboolean	heardit;
482 	int			r;
483 
484 	if (self->monsterinfo.aiflags & AI_GOOD_GUY)
485 	{
486 		if (self->goalentity && self->goalentity->inuse && self->goalentity->classname)
487 		{
488 			if (strcmp(self->goalentity->classname, "target_actor") == 0)
489 				return false;
490 		}
491 
492 		//FIXME look for monsters?
493 		return false;
494 	}
495 
496 	// if we're going to a combat point, just proceed
497 	if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
498 		return false;
499 
500 // if the first spawnflag bit is set, the monster will only wake up on
501 // really seeing the player, not another monster getting angry or hearing
502 // something
503 
504 // revised behavior so they will wake up if they "see" a player make a noise
505 // but not weapon impact/explosion noises
506 
507 	heardit = false;
508 	if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
509 	{
510 		client = level.sight_entity;
511 		if (client->enemy == self->enemy)
512 		{
513 			return false;
514 		}
515 	}
516 	else if (level.disguise_violation_framenum > level.framenum)
517 	{
518 		client = level.disguise_violator;
519 	}
520 	else if (level.sound_entity_framenum >= (level.framenum - 1))
521 	{
522 		client = level.sound_entity;
523 		heardit = true;
524 	}
525 	else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1) )
526 	{
527 		client = level.sound2_entity;
528 		heardit = true;
529 	}
530 	else
531 	{
532 		client = level.sight_client;
533 		if (!client)
534 			return false;	// no clients to get mad at
535 	}
536 
537 	// if the entity went away, forget it
538 	if (!client->inuse)
539 		return false;
540 
541 	if (client == self->enemy)
542 		return true;	// JDC false;
543 
544 	//PMM - hintpath coop fix
545 	if ((self->monsterinfo.aiflags & AI_HINT_PATH) && (coop) && (coop->value))
546 	{
547 //		if ((heardit) && (g_showlogic) && (g_showlogic->value))
548 //			gi.dprintf ("ignoring coop sound target\n");
549 		heardit = false;
550 	}
551 	// pmm
552 
553 	if (client->client)
554 	{
555 		if (client->flags & FL_NOTARGET)
556 			return false;
557 	}
558 	else if (client->svflags & SVF_MONSTER)
559 	{
560 		if (!client->enemy)
561 			return false;
562 		if (client->enemy->flags & FL_NOTARGET)
563 			return false;
564 	}
565 	else if (heardit)
566 	{
567 		// pgm - a little more paranoia won't hurt....
568 		if ((client->owner) && (client->owner->flags & FL_NOTARGET))
569 			return false;
570 	}
571 	else
572 		return false;
573 
574 	if (!heardit)
575 	{
576 		r = range (self, client);
577 
578 		if (r == RANGE_FAR)
579 			return false;
580 
581 // this is where we would check invisibility
582 
583 		// is client in an spot too dark to be seen?
584 		if (client->light_level <= 5)
585 			return false;
586 
587 		if (!visible (self, client))
588 		{
589 			return false;
590 		}
591 
592 		if (r == RANGE_NEAR)
593 		{
594 			if (client->show_hostile < level.time && !infront (self, client))
595 			{
596 				return false;
597 			}
598 		}
599 		else if (r == RANGE_MID)
600 		{
601 			if (!infront (self, client))
602 			{
603 				return false;
604 			}
605 		}
606 
607 		self->enemy = client;
608 
609 		if (strcmp(self->enemy->classname, "player_noise") != 0)
610 		{
611 			self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
612 
613 			if (!self->enemy->client)
614 			{
615 				self->enemy = self->enemy->enemy;
616 				if (!self->enemy->client)
617 				{
618 					self->enemy = NULL;
619 					return false;
620 				}
621 			}
622 		}
623 	}
624 	else	// heardit
625 	{
626 		vec3_t	temp;
627 
628 		if (self->spawnflags & 1)
629 		{
630 			if (!visible (self, client))
631 				return false;
632 		}
633 		else
634 		{
635 			if (!gi.inPHS(self->s.origin, client->s.origin))
636 				return false;
637 		}
638 
639 		VectorSubtract (client->s.origin, self->s.origin, temp);
640 
641 		if (VectorLength(temp) > 1000)	// too far to hear
642 		{
643 			return false;
644 		}
645 
646 		// check area portals - if they are different and not connected then we can't hear it
647 		if (client->areanum != self->areanum)
648 			if (!gi.AreasConnected(self->areanum, client->areanum))
649 				return false;
650 
651 		self->ideal_yaw = vectoyaw(temp);
652 		if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
653 			M_ChangeYaw (self);
654 
655 		// hunt the sound for a bit; hopefully find the real player
656 		self->monsterinfo.aiflags |= AI_SOUND_TARGET;
657 		self->enemy = client;
658 	}
659 
660 //
661 // got one
662 //
663 	// PMM - if we got an enemy, we need to bail out of hint paths, so take over here
664 	if (self->monsterinfo.aiflags & AI_HINT_PATH)
665 	{
666 //		if(g_showlogic && g_showlogic->value)
667 //			gi.dprintf("stopped following hint paths in FindTarget\n");
668 
669 		// this calls foundtarget for us
670 		hintpath_stop (self);
671 	}
672 	else
673 	{
674 		FoundTarget (self);
675 	}
676 	// pmm
677 	if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight))
678 		self->monsterinfo.sight (self, self->enemy);
679 
680 	return true;
681 }
682 
683 
684 //=============================================================================
685 
686 /*
687 ============
688 FacingIdeal
689 
690 ============
691 */
FacingIdeal(edict_t * self)692 qboolean FacingIdeal(edict_t *self)
693 {
694 	float	delta;
695 
696 	delta = anglemod(self->s.angles[YAW] - self->ideal_yaw);
697 	if (delta > 45 && delta < 315)
698 		return false;
699 	return true;
700 }
701 
702 
703 //=============================================================================
704 
M_CheckAttack(edict_t * self)705 qboolean M_CheckAttack (edict_t *self)
706 {
707 	vec3_t	spot1, spot2;
708 	float	chance;
709 	trace_t	tr;
710 
711 	if (self->enemy->health > 0)
712 	{
713 	// see if any entities are in the way of the shot
714 		VectorCopy (self->s.origin, spot1);
715 		spot1[2] += self->viewheight;
716 		VectorCopy (self->enemy->s.origin, spot2);
717 		spot2[2] += self->enemy->viewheight;
718 
719 		tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW);
720 
721 		// do we have a clear shot?
722 		if (tr.ent != self->enemy)
723 		{
724 			// PGM - we want them to go ahead and shoot at info_notnulls if they can.
725 			if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0)		//PGM
726 			{
727 				// PMM - if we can't see our target, and we're not blocked by a monster, go into blind fire if available
728 				if ((!(tr.ent->svflags & SVF_MONSTER)) && (!visible(self, self->enemy)))
729 				{
730 					if ((self->monsterinfo.blindfire) && (self->monsterinfo.blind_fire_delay <= 20.0))
731 					{
732 						if (level.time < self->monsterinfo.attack_finished)
733 						{
734 							return false;
735 						}
736 						if (level.time < (self->monsterinfo.trail_time + self->monsterinfo.blind_fire_delay))
737 						{
738 							// wait for our time
739 							return false;
740 						}
741 						else
742 						{
743 //	gi.WriteByte (svc_temp_entity);
744 //	gi.WriteByte (TE_DEBUGTRAIL);
745 //	gi.WritePosition (spot1);
746 //	gi.WritePosition (self->monsterinfo.blind_fire_target);
747 //	gi.multicast (self->s.origin, MULTICAST_ALL);
748 							// make sure we're not going to shoot a monster
749 							tr = gi.trace (spot1, NULL, NULL, self->monsterinfo.blind_fire_target, self, CONTENTS_MONSTER);
750 							if (tr.allsolid || tr.startsolid || ((tr.fraction < 1.0) && (tr.ent != self->enemy)))
751 							{
752 //								if ((g_showlogic) && (g_showlogic->value))
753 //									gi.dprintf ("blindfire blocked\n");
754 								return false;
755 							}
756 
757 							self->monsterinfo.attack_state = AS_BLIND;
758 							return true;
759 						}
760 					}
761 				}
762 				// pmm
763 				return false;
764 			}
765 		}
766 	}
767 
768 	// melee attack
769 	if (enemy_range == RANGE_MELEE)
770 	{
771 		// don't always melee in easy mode
772 		if (skill->value == 0 && (rand()&3) )
773 		{
774 			// PMM - fix for melee only monsters & strafing
775 			self->monsterinfo.attack_state = AS_STRAIGHT;
776 			return false;
777 		}
778 		if (self->monsterinfo.melee)
779 			self->monsterinfo.attack_state = AS_MELEE;
780 		else
781 			self->monsterinfo.attack_state = AS_MISSILE;
782 		return true;
783 	}
784 
785 // missile attack
786 	if (!self->monsterinfo.attack)
787 	{
788 		// PMM - fix for melee only monsters & strafing
789 		self->monsterinfo.attack_state = AS_STRAIGHT;
790 		return false;
791 	}
792 
793 	if (level.time < self->monsterinfo.attack_finished)
794 		return false;
795 
796 	if (enemy_range == RANGE_FAR)
797 		return false;
798 
799 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
800 	{
801 		chance = 0.4;
802 	}
803 	else if (enemy_range == RANGE_MELEE)
804 	{
805 		chance = 0.2;
806 	}
807 	else if (enemy_range == RANGE_NEAR)
808 	{
809 		chance = 0.1;
810 	}
811 	else if (enemy_range == RANGE_MID)
812 	{
813 		chance = 0.02;
814 	}
815 	else
816 	{
817 		return false;
818 	}
819 
820 	if (skill->value == 0)
821 		chance *= 0.5;
822 	else if (skill->value >= 2)
823 		chance *= 2;
824 
825 	// PGM - go ahead and shoot every time if it's a info_notnull
826 	if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
827 	{
828 		self->monsterinfo.attack_state = AS_MISSILE;
829 		self->monsterinfo.attack_finished = level.time + 2*random();
830 		return true;
831 	}
832 
833 	// PMM -daedalus should strafe more .. this can be done here or in a customized
834 	// check_attack code for the hover.
835 	if (self->flags & FL_FLY)
836 	{
837 		// originally, just 0.3
838 		float strafe_chance;
839 		if (!(strcmp(self->classname, "monster_daedalus")))
840 			strafe_chance = 0.8;
841 		else
842 			strafe_chance = 0.6;
843 
844 		// if enemy is tesla, never strafe
845 		if ((self->enemy) && (self->enemy->classname) && (!strcmp(self->enemy->classname, "tesla")))
846 			strafe_chance = 0;
847 
848 		if (random() < strafe_chance)
849 			self->monsterinfo.attack_state = AS_SLIDING;
850 		else
851 			self->monsterinfo.attack_state = AS_STRAIGHT;
852 	}
853 // do we want the monsters strafing?
854 #ifdef SLIDING_TROOPS
855 	else
856 	{
857 		if (random() < 0.4)
858 			self->monsterinfo.attack_state = AS_SLIDING;
859 		else
860 			self->monsterinfo.attack_state = AS_STRAIGHT;
861 	}
862 #endif
863 //-PMM
864 
865 	return false;
866 }
867 
868 
869 /*
870 =============
871 ai_run_melee
872 
873 Turn and close until within an angle to launch a melee attack
874 =============
875 */
ai_run_melee(edict_t * self)876 void ai_run_melee(edict_t *self)
877 {
878 	self->ideal_yaw = enemy_yaw;
879 	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
880 		M_ChangeYaw (self);
881 
882 	if (FacingIdeal(self))
883 	{
884 		self->monsterinfo.melee (self);
885 		self->monsterinfo.attack_state = AS_STRAIGHT;
886 	}
887 }
888 
889 
890 /*
891 =============
892 ai_run_missile
893 
894 Turn in place until within an angle to launch a missile attack
895 =============
896 */
ai_run_missile(edict_t * self)897 void ai_run_missile(edict_t *self)
898 {
899 	self->ideal_yaw = enemy_yaw;
900 	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
901 		M_ChangeYaw (self);
902 
903 	if (FacingIdeal(self))
904 	{
905 		self->monsterinfo.attack (self);
906 //		if (self->monsterinfo.attack_state == AS_MISSILE)
907 		if ((self->monsterinfo.attack_state == AS_MISSILE) || (self->monsterinfo.attack_state == AS_BLIND))
908 			self->monsterinfo.attack_state = AS_STRAIGHT;
909 //		else if (self->monsterinfo.attack_state != AS_SLIDING)
910 //			gi.dprintf ("ai_run_missile: Unexpected attack state %d !\n", self->monsterinfo.attack_state);
911 	}
912 };
913 
914 
915 /*
916 =============
917 ai_run_slide
918 
919 Strafe sideways, but stay at aproximately the same range
920 =============
921 */
ai_run_slide(edict_t * self,float distance)922 void ai_run_slide(edict_t *self, float distance)
923 {
924 	float	ofs;
925 	float	angle;
926 
927 	self->ideal_yaw = enemy_yaw;
928 
929 //	if (self->flags & FL_FLY)
930 //		angle = 90;
931 //	else
932 //		angle = 45;
933 
934 	angle = 90;
935 
936 	if (self->monsterinfo.lefty)
937 		ofs = angle;
938 	else
939 		ofs = -angle;
940 //
941 //	if (!(self->flags & FL_FLY))
942 //	{
943 //		// non fliers should actually turn towards the direction their trying to run
944 //		self->ideal_yaw += ofs;
945 //	}
946 //
947 	if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
948 		M_ChangeYaw (self);
949 
950 	/*
951 	if (!(self->flags & FL_FLY))
952 	{
953 		if (M_walkmove (self, self->ideal_yaw + ofs, distance))
954 			return;
955 	}
956 	else
957 	{
958 		if (M_walkmove (self, self->ideal_yaw, distance))
959 			return;
960 	}
961 	*/
962 	// PMM - clamp maximum sideways move for non flyers to make them look less jerky
963 	if (!self->flags & FL_FLY)
964 		distance = min (distance, MAX_SIDESTEP);
965 	if (M_walkmove (self, self->ideal_yaw + ofs, distance))
966 		return;
967 	// PMM - if we're dodging, give up on it and go straight
968 	if (self->monsterinfo.aiflags & AI_DODGING)
969 	{
970 		monster_done_dodge (self);
971 		// by setting as_straight, caller will know to try straight move
972 		self->monsterinfo.attack_state = AS_STRAIGHT;
973 		return;
974 	}
975 
976 	self->monsterinfo.lefty = 1 - self->monsterinfo.lefty;
977 	if (M_walkmove (self, self->ideal_yaw - ofs, distance))
978 		return;
979 	// PMM - if we're dodging, give up on it and go straight
980 	if (self->monsterinfo.aiflags & AI_DODGING)
981 		monster_done_dodge (self);
982 
983 	// PMM - the move failed, so signal the caller (ai_run) to try going straight
984 	self->monsterinfo.attack_state = AS_STRAIGHT;
985 	/*
986 	if (!(self->flags & FL_FLY))
987 	{
988 		M_walkmove (self, self->ideal_yaw + ofs, distance);
989 	}
990 	else
991 	{
992 		M_walkmove (self, self->ideal_yaw, distance);
993 	}*/
994 }
995 
996 
997 /*
998 =============
999 ai_checkattack
1000 
1001 Decides if we're going to attack or do something else
1002 used by ai_run and ai_stand
1003 =============
1004 */
ai_checkattack(edict_t * self,float dist)1005 qboolean ai_checkattack (edict_t *self, float dist)
1006 {
1007 	vec3_t		temp;
1008 	qboolean	hesDeadJim;
1009 	// PMM
1010 	qboolean	retval;
1011 
1012 // this causes monsters to run blindly to the combat point w/o firing
1013 	if (self->goalentity)
1014 	{
1015 		if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
1016 			return false;
1017 
1018 		if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
1019 		{
1020 			if ((level.time - self->enemy->teleport_time) > 5.0)
1021 			{
1022 				if (self->goalentity == self->enemy)
1023 					if (self->movetarget)
1024 						self->goalentity = self->movetarget;
1025 					else
1026 						self->goalentity = NULL;
1027 				self->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
1028 				if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND)
1029 					self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
1030 			}
1031 			else
1032 			{
1033 				self->show_hostile = level.time + 1;
1034 				return false;
1035 			}
1036 		}
1037 	}
1038 
1039 	enemy_vis = false;
1040 
1041 // see if the enemy is dead
1042 	hesDeadJim = false;
1043 	if ((!self->enemy) || (!self->enemy->inuse))
1044 	{
1045 		hesDeadJim = true;
1046 	}
1047 	else if (self->monsterinfo.aiflags & AI_MEDIC)
1048 	{
1049 		if (!(self->enemy->inuse) || (self->enemy->health > 0))
1050 		{
1051 			hesDeadJim = true;
1052 //			self->monsterinfo.aiflags &= ~AI_MEDIC;
1053 		}
1054 	}
1055 	else
1056 	{
1057 		if (self->monsterinfo.aiflags & AI_BRUTAL)
1058 		{
1059 			if (self->enemy->health <= -80)
1060 				hesDeadJim = true;
1061 		}
1062 		else
1063 		{
1064 			if (self->enemy->health <= 0)
1065 				hesDeadJim = true;
1066 		}
1067 	}
1068 
1069 	if (hesDeadJim)
1070 	{
1071 		self->monsterinfo.aiflags &= ~AI_MEDIC;
1072 		self->enemy = NULL;
1073 	// FIXME: look all around for other targets
1074 		if (self->oldenemy && self->oldenemy->health > 0)
1075 		{
1076 			self->enemy = self->oldenemy;
1077 			self->oldenemy = NULL;
1078 			HuntTarget (self);
1079 		}
1080 //ROGUE - multiple teslas make monsters lose track of the player.
1081 		else if(self->monsterinfo.last_player_enemy && self->monsterinfo.last_player_enemy->health > 0)
1082 		{
1083 //			if ((g_showlogic) && (g_showlogic->value))
1084 //				gi.dprintf("resorting to last_player_enemy...\n");
1085 			self->enemy = self->monsterinfo.last_player_enemy;
1086 			self->oldenemy = NULL;
1087 			self->monsterinfo.last_player_enemy = NULL;
1088 			HuntTarget (self);
1089 		}
1090 //ROGUE
1091 		else
1092 		{
1093 			if (self->movetarget)
1094 			{
1095 				self->goalentity = self->movetarget;
1096 				self->monsterinfo.walk (self);
1097 			}
1098 			else
1099 			{
1100 				// we need the pausetime otherwise the stand code
1101 				// will just revert to walking with no target and
1102 				// the monsters will wonder around aimlessly trying
1103 				// to hunt the world entity
1104 				self->monsterinfo.pausetime = level.time + 100000000;
1105 				self->monsterinfo.stand (self);
1106 			}
1107 			return true;
1108 		}
1109 	}
1110 
1111 	self->show_hostile = level.time + 1;		// wake up other monsters
1112 
1113 // check knowledge of enemy
1114 	enemy_vis = visible(self, self->enemy);
1115 	if (enemy_vis)
1116 	{
1117 		self->monsterinfo.search_time = level.time + 5;
1118 		VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
1119 		// PMM
1120 		self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
1121 		self->monsterinfo.trail_time = level.time;
1122 		VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
1123 		self->monsterinfo.blind_fire_delay = 0;
1124 		// pmm
1125 	}
1126 
1127 // look for other coop players here
1128 //	if (coop && self->monsterinfo.search_time < level.time)
1129 //	{
1130 //		if (FindTarget (self))
1131 //			return true;
1132 //	}
1133 
1134 	enemy_infront = infront(self, self->enemy);
1135 	enemy_range = range(self, self->enemy);
1136 	VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
1137 	enemy_yaw = vectoyaw(temp);
1138 
1139 
1140 	// JDC self->ideal_yaw = enemy_yaw;
1141 
1142 	// PMM -- reordered so the monster specific checkattack is called before the run_missle/melee/checkvis
1143 	// stuff .. this allows for, among other things, circle strafing and attacking while in ai_run
1144 	retval = self->monsterinfo.checkattack (self);
1145 	if (retval)
1146 	{
1147 		// PMM
1148 		if (self->monsterinfo.attack_state == AS_MISSILE)
1149 		{
1150 			ai_run_missile (self);
1151 			return true;
1152 		}
1153 		if (self->monsterinfo.attack_state == AS_MELEE)
1154 		{
1155 			ai_run_melee (self);
1156 			return true;
1157 		}
1158 		// PMM -- added so monsters can shoot blind
1159 		if (self->monsterinfo.attack_state == AS_BLIND)
1160 		{
1161 			ai_run_missile (self);
1162 			return true;
1163 		}
1164 		// pmm
1165 
1166 		// if enemy is not currently visible, we will never attack
1167 		if (!enemy_vis)
1168 			return false;
1169 		// PMM
1170 	}
1171 	return retval;
1172 	// PMM
1173 //	return self->monsterinfo.checkattack (self);
1174 }
1175 
1176 
1177 /*
1178 =============
1179 ai_run
1180 
1181 The monster has an enemy it is trying to kill
1182 =============
1183 */
ai_run(edict_t * self,float dist)1184 void ai_run (edict_t *self, float dist)
1185 {
1186 	vec3_t		v;
1187 	edict_t		*tempgoal;
1188 	edict_t		*save;
1189 	qboolean	new;
1190 	edict_t		*marker;
1191 	float		d1, d2;
1192 	trace_t		tr;
1193 	vec3_t		v_forward, v_right;
1194 	float		left, center, right;
1195 	vec3_t		left_target, right_target;
1196 	//PMM
1197 	qboolean	retval;
1198 	qboolean	alreadyMoved = false;
1199 	qboolean	gotcha = false;
1200 	edict_t		*realEnemy;
1201 
1202 	// if we're going to a combat point, just proceed
1203 	if (self->monsterinfo.aiflags & AI_COMBAT_POINT)
1204 	{
1205 		M_MoveToGoal (self, dist);
1206 		return;
1207 	}
1208 
1209 	// PMM
1210 	if (self->monsterinfo.aiflags & AI_DUCKED)
1211 	{
1212 //		if ((g_showlogic) && (g_showlogic->value))
1213 //			gi.dprintf ("%s - duck flag cleaned up!\n", self->classname);
1214 		self->monsterinfo.aiflags &= ~AI_DUCKED;
1215 	}
1216 	if (self->maxs[2] != self->monsterinfo.base_height)
1217 	{
1218 //		if ((g_showlogic) && (g_showlogic->value))
1219 //			gi.dprintf ("%s - ducked height corrected!\n", self->classname);
1220 		monster_duck_up (self);
1221 	}
1222 //	if ((self->monsterinfo.aiflags & AI_MANUAL_STEERING) && (strcmp(self->classname, "monster_turret")))
1223 //	{
1224 //		if ((g_showlogic) && (g_showlogic->value))
1225 //			gi.dprintf ("%s - manual steering in ai_run!\n", self->classname);
1226 //	}
1227 	// pmm
1228 
1229 //==========
1230 //PGM
1231 	// if we're currently looking for a hint path
1232 	if (self->monsterinfo.aiflags & AI_HINT_PATH)
1233 	{
1234 		// determine direction to our destination hintpath.
1235 		// FIXME - is this needed EVERY time? I was having trouble with them
1236 		// sometimes not going after it, and this fixed it.
1237 //		VectorSubtract(self->movetarget->s.origin, self->s.origin, v);
1238 //		vectoangles(v, v_forward);
1239 //		self->ideal_yaw = v_forward[YAW];
1240 //		gi.dprintf("seeking hintpath. origin: %s %0.1f\n", vtos(v), self->ideal_yaw);
1241 		M_MoveToGoal (self, dist);
1242 		if(!self->inuse)
1243 			return;			// PGM - g_touchtrigger free problem
1244 //		return;
1245 
1246 		// if we've already seen the player, and can't see him now, return
1247 //		if(self->enemy && !visible(self, self->enemy))
1248 //			return;
1249 
1250 		// if not and can't find the player, return
1251 //		if(!FindTarget(self))
1252 //			return;
1253 
1254 		// first off, make sure we're looking for the player, not a noise he made
1255 		if (self->enemy)
1256 		{
1257 			if (self->enemy->inuse)
1258 			{
1259 				if (strcmp(self->enemy->classname, "player_noise") != 0)
1260 					realEnemy = self->enemy;
1261 				else if (self->enemy->owner)
1262 					realEnemy = self->enemy->owner;
1263 				else // uh oh, can't figure out enemy, bail
1264 				{
1265 					self->enemy = NULL;
1266 					hintpath_stop (self);
1267 					return;
1268 				}
1269 			}
1270 			else
1271 			{
1272 				self->enemy = NULL;
1273 				hintpath_stop (self);
1274 				return;
1275 			}
1276 		}
1277 		else
1278 		{
1279 			hintpath_stop (self);
1280 			return;
1281 		}
1282 
1283 		if (coop && coop->value)
1284 		{
1285 			// if we're in coop, check my real enemy first .. if I SEE him, set gotcha to true
1286 			if (self->enemy && visible(self, realEnemy))
1287 				gotcha = true;
1288 			else // otherwise, let FindTarget bump us out of hint paths, if appropriate
1289 				FindTarget(self);
1290 		}
1291 		else
1292 		{
1293 			if(self->enemy && visible(self, realEnemy))
1294 				gotcha = true;
1295 		}
1296 
1297 		// if we see the player, stop following hintpaths.
1298 		if (gotcha)
1299 		{
1300 //			if(g_showlogic && g_showlogic->value)
1301 //				gi.dprintf("stopped following hint paths in ai_run\n");
1302 
1303 			// disconnect from hintpaths and start looking normally for players.
1304 			hintpath_stop (self);
1305 			// pmm - no longer needed, since hintpath_stop does it
1306 //			HuntTarget(self);
1307 		}
1308 		return;
1309 	}
1310 //PGM
1311 //==========
1312 
1313 	if (self->monsterinfo.aiflags & AI_SOUND_TARGET)
1314 	{
1315 		// PMM - paranoia checking
1316 		if (self->enemy)
1317 			VectorSubtract (self->s.origin, self->enemy->s.origin, v);
1318 
1319 		if ((!self->enemy) || (VectorLength(v) < 64))
1320 		// pmm
1321 		{
1322 			self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND);
1323 			self->monsterinfo.stand (self);
1324 			return;
1325 		}
1326 
1327 		M_MoveToGoal (self, dist);
1328 		// PMM - prevent double moves for sound_targets
1329 		alreadyMoved = true;
1330 		// pmm
1331 		if(!self->inuse)
1332 			return;			// PGM - g_touchtrigger free problem
1333 
1334 		if (!FindTarget (self))
1335 			return;
1336 	}
1337 
1338 	// PMM -- moved ai_checkattack up here so the monsters can attack while strafing or charging
1339 
1340 	// PMM -- if we're dodging, make sure to keep the attack_state AS_SLIDING
1341 
1342 	retval = ai_checkattack (self, dist);
1343 
1344 	// PMM - don't strafe if we can't see our enemy
1345 	if ((!enemy_vis) && (self->monsterinfo.attack_state == AS_SLIDING))
1346 		self->monsterinfo.attack_state = AS_STRAIGHT;
1347 	// unless we're dodging (dodging out of view looks smart)
1348 	if (self->monsterinfo.aiflags & AI_DODGING)
1349 		self->monsterinfo.attack_state = AS_SLIDING;
1350 	// pmm
1351 
1352 	if (self->monsterinfo.attack_state == AS_SLIDING)
1353 	{
1354 		// PMM - protect against double moves
1355 		if (!alreadyMoved)
1356 			ai_run_slide (self, dist);
1357 		// PMM
1358 		// we're using attack_state as the return value out of ai_run_slide to indicate whether or not the
1359 		// move succeeded.  If the move succeeded, and we're still sliding, we're done in here (since we've
1360 		// had our chance to shoot in ai_checkattack, and have moved).
1361 		// if the move failed, our state is as_straight, and it will be taken care of below
1362 		if ((!retval) && (self->monsterinfo.attack_state == AS_SLIDING))
1363 			return;
1364 	}
1365 	else if (self->monsterinfo.aiflags & AI_CHARGING)
1366 	{
1367 		self->ideal_yaw = enemy_yaw;
1368 		if (!(self->monsterinfo.aiflags & AI_MANUAL_STEERING))
1369 			M_ChangeYaw (self);
1370 	}
1371 	if (retval)
1372 	{
1373 		// PMM - is this useful?  Monsters attacking usually call the ai_charge routine..
1374 		// the only monster this affects should be the soldier
1375 		if ((dist != 0) && (!alreadyMoved) && (self->monsterinfo.attack_state == AS_STRAIGHT) && (!(self->monsterinfo.aiflags & AI_STAND_GROUND)))
1376 		{
1377 			M_MoveToGoal (self, dist);
1378 		}
1379 		if ((self->enemy) && (self->enemy->inuse) && (enemy_vis))
1380 		{
1381 			self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
1382 			VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
1383 			self->monsterinfo.trail_time = level.time;
1384 			//PMM
1385 			VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
1386 			self->monsterinfo.blind_fire_delay = 0;
1387 			//pmm
1388 		}
1389 		return;
1390 	}
1391 	//PMM
1392 //	if (ai_checkattack (self, dist))
1393 //		return;
1394 
1395 //	if (self->monsterinfo.attack_state == AS_SLIDING)
1396 //	{
1397 //		ai_run_slide (self, dist);
1398 //		return;
1399 //	}
1400 
1401 	// PGM - added a little paranoia checking here... 9/22/98
1402 	if ((self->enemy) && (self->enemy->inuse) && (enemy_vis))
1403 	{
1404 //		if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
1405 //			gi.dprintf("regained sight\n");
1406 		// PMM - check for alreadyMoved
1407 		if (!alreadyMoved)
1408 			M_MoveToGoal (self, dist);
1409 		if(!self->inuse)
1410 			return;			// PGM - g_touchtrigger free problem
1411 
1412 		self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
1413 		VectorCopy (self->enemy->s.origin, self->monsterinfo.last_sighting);
1414 		self->monsterinfo.trail_time = level.time;
1415 		// PMM
1416 		VectorCopy (self->enemy->s.origin, self->monsterinfo.blind_fire_target);
1417 		self->monsterinfo.blind_fire_delay = 0;
1418 		// pmm
1419 		return;
1420 	}
1421 
1422 //=======
1423 //PGM
1424 	// if we've been looking (unsuccessfully) for the player for 10 seconds
1425 	// PMM - reduced to 5, makes them much nastier
1426 	if((self->monsterinfo.trail_time + 5) <= level.time)
1427 	{
1428 		// and we haven't checked for valid hint paths in the last 10 seconds
1429 		if((self->monsterinfo.last_hint_time + 10) <= level.time)
1430 		{
1431 			// check for hint_paths.
1432 			self->monsterinfo.last_hint_time = level.time;
1433 			if(monsterlost_checkhint(self))
1434 				return;
1435 		}
1436 	}
1437 //PGM
1438 //=======
1439 
1440 // PMM - moved down here to allow monsters to get on hint paths
1441 	// coop will change to another enemy if visible
1442 	if (coop->value)
1443 	{	// FIXME: insane guys get mad with this, which causes crashes!
1444 		if (FindTarget (self))
1445 			return;
1446 	}
1447 // pmm
1448 
1449 	if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20)))
1450 	{
1451 		// PMM - double move protection
1452 		if (!alreadyMoved)
1453 			M_MoveToGoal (self, dist);
1454 		self->monsterinfo.search_time = 0;
1455 //		gi.dprintf("search timeout\n");
1456 		return;
1457 	}
1458 
1459 	save = self->goalentity;
1460 	tempgoal = G_Spawn();
1461 	self->goalentity = tempgoal;
1462 
1463 	new = false;
1464 
1465 	if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT))
1466 	{
1467 		// just lost sight of the player, decide where to go first
1468 //		gi.dprintf("lost sight of player, last seen at %s\n", vtos(self->monsterinfo.last_sighting));
1469 		self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN);
1470 		self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP);
1471 		new = true;
1472 	}
1473 
1474 	if (self->monsterinfo.aiflags & AI_PURSUE_NEXT)
1475 	{
1476 //		vec3_t	debug_vec;
1477 
1478 		self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT;
1479 //		VectorSubtract(self->monsterinfo.last_sighting, self->s.origin, debug_vec);
1480 //		gi.dprintf("reached current goal: %s %s %f", vtos(self->s.origin), vtos(self->monsterinfo.last_sighting), VectorLength(debug_vec));
1481 
1482 		// give ourself more time since we got this far
1483 		self->monsterinfo.search_time = level.time + 5;
1484 
1485 		if (self->monsterinfo.aiflags & AI_PURSUE_TEMP)
1486 		{
1487 //			gi.dprintf("was temp goal; retrying original\n");
1488 			self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP;
1489 			marker = NULL;
1490 			VectorCopy (self->monsterinfo.saved_goal, self->monsterinfo.last_sighting);
1491 			new = true;
1492 		}
1493 		else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN)
1494 		{
1495 			self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN;
1496 			marker = PlayerTrail_PickFirst (self);
1497 		}
1498 		else
1499 		{
1500 			marker = PlayerTrail_PickNext (self);
1501 		}
1502 
1503 		if (marker)
1504 		{
1505 			VectorCopy (marker->s.origin, self->monsterinfo.last_sighting);
1506 			self->monsterinfo.trail_time = marker->timestamp;
1507 			self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW];
1508 //			gi.dprintf("heading is %0.1f\n", self->ideal_yaw);
1509 
1510 //			debug_drawline(self.origin, self.last_sighting, 52);
1511 			new = true;
1512 		}
1513 	}
1514 
1515 	VectorSubtract (self->s.origin, self->monsterinfo.last_sighting, v);
1516 	d1 = VectorLength(v);
1517 	if (d1 <= dist)
1518 	{
1519 		self->monsterinfo.aiflags |= AI_PURSUE_NEXT;
1520 		dist = d1;
1521 	}
1522 
1523 	VectorCopy (self->monsterinfo.last_sighting, self->goalentity->s.origin);
1524 
1525 	if (new)
1526 	{
1527 //		gi.dprintf("checking for course correction\n");
1528 
1529 		tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID);
1530 		if (tr.fraction < 1)
1531 		{
1532 			VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
1533 			d1 = VectorLength(v);
1534 			center = tr.fraction;
1535 			d2 = d1 * ((center+1)/2);
1536 			self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
1537 			AngleVectors(self->s.angles, v_forward, v_right, NULL);
1538 
1539 			VectorSet(v, d2, -16, 0);
1540 			G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
1541 			tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID);
1542 			left = tr.fraction;
1543 
1544 			VectorSet(v, d2, 16, 0);
1545 			G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
1546 			tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID);
1547 			right = tr.fraction;
1548 
1549 			center = (d1*center)/d2;
1550 			if (left >= center && left > right)
1551 			{
1552 				if (left < 1)
1553 				{
1554 					VectorSet(v, d2 * left * 0.5, -16, 0);
1555 					G_ProjectSource (self->s.origin, v, v_forward, v_right, left_target);
1556 //					gi.dprintf("incomplete path, go part way and adjust again\n");
1557 				}
1558 				VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
1559 				self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
1560 				VectorCopy (left_target, self->goalentity->s.origin);
1561 				VectorCopy (left_target, self->monsterinfo.last_sighting);
1562 				VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
1563 				self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
1564 //				gi.dprintf("adjusted left\n");
1565 //				debug_drawline(self.origin, self.last_sighting, 152);
1566 			}
1567 			else if (right >= center && right > left)
1568 			{
1569 				if (right < 1)
1570 				{
1571 					VectorSet(v, d2 * right * 0.5, 16, 0);
1572 					G_ProjectSource (self->s.origin, v, v_forward, v_right, right_target);
1573 //					gi.dprintf("incomplete path, go part way and adjust again\n");
1574 				}
1575 				VectorCopy (self->monsterinfo.last_sighting, self->monsterinfo.saved_goal);
1576 				self->monsterinfo.aiflags |= AI_PURSUE_TEMP;
1577 				VectorCopy (right_target, self->goalentity->s.origin);
1578 				VectorCopy (right_target, self->monsterinfo.last_sighting);
1579 				VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
1580 				self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v);
1581 //				gi.dprintf("adjusted right\n");
1582 //				debug_drawline(self.origin, self.last_sighting, 152);
1583 			}
1584 		}
1585 //		else gi.dprintf("course was fine\n");
1586 	}
1587 
1588 	M_MoveToGoal (self, dist);
1589 	if(!self->inuse)
1590 		return;			// PGM - g_touchtrigger free problem
1591 
1592 	G_FreeEdict(tempgoal);
1593 
1594 	if (self)
1595 		self->goalentity = save;
1596 }
1597