1 
2 #include "g_local.h"
3 
4 //===============================
5 // BLOCKED Logic
6 //===============================
7 
8 /*
9 		gi.WriteByte (svc_temp_entity);
10 		gi.WriteByte (TE_DEBUGTRAIL);
11 		gi.WritePosition (pt1);
12 		gi.WritePosition (pt2);
13 		gi.multicast (pt1, MULTICAST_PVS);
14 
15 		self->nextthink = level.time + 10;
16 */
17 
18 // plat states, copied from g_func.c
19 
20 #define	STATE_TOP			0
21 #define	STATE_BOTTOM		1
22 #define STATE_UP			2
23 #define STATE_DOWN			3
24 
25 qboolean face_wall (edict_t *self);
26 void HuntTarget (edict_t *self);
27 
28 // PMM
29 qboolean parasite_drain_attack_ok (vec3_t start, vec3_t end);
30 
31 
32 // blocked_checkshot
33 //	shotchance: 0-1, chance they'll take the shot if it's clear.
blocked_checkshot(edict_t * self,float shotChance)34 qboolean blocked_checkshot (edict_t *self, float shotChance)
35 {
36 	qboolean	playerVisible;
37 
38 	if(!self->enemy)
39 		return false;
40 
41 	// blocked checkshot is only against players. this will
42 	// filter out player sounds and other shit they should
43 	// not be firing at.
44 	if(!(self->enemy->client))
45 		return false;
46 
47 	if (random() < shotChance)
48 		return false;
49 
50 	// PMM - special handling for the parasite
51 	if (!strcmp(self->classname, "monster_parasite"))
52 	{
53 		vec3_t	f, r, offset, start, end;
54 		trace_t	tr;
55 		AngleVectors (self->s.angles, f, r, NULL);
56 		VectorSet (offset, 24, 0, 6);
57 		G_ProjectSource (self->s.origin, offset, f, r, start);
58 
59 		VectorCopy (self->enemy->s.origin, end);
60 		if (!parasite_drain_attack_ok(start, end))
61 		{
62 			end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
63 			if (!parasite_drain_attack_ok(start, end))
64 			{
65 				end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
66 				if (!parasite_drain_attack_ok(start, end))
67 					return false;
68 			}
69 		}
70 		VectorCopy (self->enemy->s.origin, end);
71 
72 		tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
73 		if (tr.ent != self->enemy)
74 		{
75 			self->monsterinfo.aiflags |= AI_BLOCKED;
76 
77 			if(self->monsterinfo.attack)
78 				self->monsterinfo.attack(self);
79 
80 			self->monsterinfo.aiflags &= ~AI_BLOCKED;
81 			return true;
82 		}
83 	}
84 
85 	playerVisible = visible (self, self->enemy);
86 	// always shoot at teslas
87 	if(playerVisible)
88 	{
89 		if (!strcmp(self->enemy->classname, "tesla"))
90 		{
91 //			if(g_showlogic && g_showlogic->value)
92 //				gi.dprintf("blocked: taking a shot\n");
93 
94 			// turn on AI_BLOCKED to let the monster know the attack is being called
95 			// by the blocked functions...
96 			self->monsterinfo.aiflags |= AI_BLOCKED;
97 
98 			if(self->monsterinfo.attack)
99 				self->monsterinfo.attack(self);
100 
101 			self->monsterinfo.aiflags &= ~AI_BLOCKED;
102 			return true;
103 		}
104 	}
105 
106 	return false;
107 }
108 
109 // blocked_checkplat
110 //	dist: how far they are trying to walk.
blocked_checkplat(edict_t * self,float dist)111 qboolean blocked_checkplat (edict_t *self, float dist)
112 {
113 	int			playerPosition;
114 	trace_t		trace;
115 	vec3_t		pt1, pt2;
116 	vec3_t		forward;
117 	edict_t		*plat;
118 
119 	if(!self->enemy)
120 		return false;
121 
122 	// check player's relative altitude
123 	if(self->enemy->absmin[2] >= self->absmax[2])
124 		playerPosition = 1;
125 	else if(self->enemy->absmax[2] <= self->absmin[2])
126 		playerPosition = -1;
127 	else
128 		playerPosition = 0;
129 
130 	// if we're close to the same position, don't bother trying plats.
131 	if(playerPosition == 0)
132 		return false;
133 
134 	plat = NULL;
135 
136 	// see if we're already standing on a plat.
137 	if(self->groundentity && self->groundentity != world)
138 	{
139 		if(!strncmp(self->groundentity->classname, "func_plat", 8))
140 			plat = self->groundentity;
141 	}
142 
143 	// if we're not, check to see if we'll step onto one with this move
144 	if(!plat)
145 	{
146 		AngleVectors (self->s.angles, forward, NULL, NULL);
147 		VectorMA(self->s.origin, dist, forward, pt1);
148 		VectorCopy (pt1, pt2);
149 		pt2[2] -= 384;
150 
151 		trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID);
152 		if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
153 		{
154 			if(!strncmp(trace.ent->classname, "func_plat", 8))
155 			{
156 				plat = trace.ent;
157 			}
158 		}
159 	}
160 
161 	// if we've found a plat, trigger it.
162 	if(plat && plat->use)
163 	{
164 		if (playerPosition == 1)
165 		{
166 			if((self->groundentity == plat && plat->moveinfo.state == STATE_BOTTOM) ||
167 				(self->groundentity != plat && plat->moveinfo.state == STATE_TOP))
168 			{
169 //				if(g_showlogic && g_showlogic->value)
170 //					gi.dprintf("player above, and plat will raise. using!\n");
171 				plat->use (plat, self, self);
172 				return true;
173 			}
174 		}
175 		else if(playerPosition == -1)
176 		{
177 			if((self->groundentity == plat && plat->moveinfo.state == STATE_TOP) ||
178 				(self->groundentity != plat && plat->moveinfo.state == STATE_BOTTOM))
179 			{
180 //				if(g_showlogic && g_showlogic->value)
181 //					gi.dprintf("player below, and plat will lower. using!\n");
182 				plat->use (plat, self, self);
183 				return true;
184 			}
185 		}
186 //		if(g_showlogic && g_showlogic->value)
187 //			gi.dprintf("hit a plat, not using. ppos: %d   plat: %d\n", playerPosition, plat->moveinfo.state);
188 	}
189 
190 	return false;
191 }
192 
193 // blocked_checkjump
194 //	dist: how far they are trying to walk.
195 //  maxDown/maxUp: how far they'll ok a jump for. set to 0 to disable that direction.
blocked_checkjump(edict_t * self,float dist,float maxDown,float maxUp)196 qboolean blocked_checkjump (edict_t *self, float dist, float maxDown, float maxUp)
197 {
198 	int			playerPosition;
199 	trace_t		trace;
200 	vec3_t		pt1, pt2;
201 	vec3_t		forward, up;
202 
203 	if(!self->enemy)
204 		return false;
205 
206 	AngleVectors (self->s.angles, forward, NULL, up);
207 
208 	if(self->enemy->absmin[2] > (self->absmin[2] + 16))
209 		playerPosition = 1;
210 	else if(self->enemy->absmin[2] < (self->absmin[2] - 16))
211 		playerPosition = -1;
212 	else
213 		playerPosition = 0;
214 
215 	if(playerPosition == -1 && maxDown)
216 	{
217 		// check to make sure we can even get to the spot we're going to "fall" from
218 		VectorMA(self->s.origin, 48, forward, pt1);
219 		trace = gi.trace(self->s.origin, self->mins, self->maxs, pt1, self, MASK_MONSTERSOLID);
220 		if(trace.fraction < 1)
221 		{
222 //			gi.dprintf("can't get thar from hear...\n");
223 			return false;
224 		}
225 
226 		VectorCopy (pt1, pt2);
227 		pt2[2] = self->mins[2] - maxDown - 1;
228 
229 		trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
230 		if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
231 		{
232 			if((self->absmin[2] - trace.endpos[2]) >= 24 && trace.contents & MASK_SOLID)
233 			{
234 				if( (self->enemy->absmin[2] - trace.endpos[2]) > 32)
235 				{
236 //					if(g_showlogic && g_showlogic->value)
237 //						gi.dprintf("That'll take me too far down...%0.1f\n", (self->enemy->absmin[2] - trace.endpos[2]));
238 					return false;
239 				}
240 
241 				if(trace.plane.normal[2] < 0.9)
242 				{
243 //					gi.dprintf("Floor angle too much! %s\n", vtos(trace.plane.normal));
244 					return false;
245 				}
246 //				if(g_showlogic && g_showlogic->value)
247 //					gi.dprintf("Geronimo! %0.1f\n", (self->absmin[2] - trace.endpos[2]));
248 				return true;
249 			}
250 //			else if(g_showlogic && g_showlogic->value)
251 //			{
252 //				if(!(trace.contents & MASK_SOLID))
253 //					gi.dprintf("Ooooh... Bad stuff down there...\n");
254 //				else
255 //					gi.dprintf("Too far to fall\n");
256 //			}
257 		}
258 //		else if(g_showlogic && g_showlogic->value)
259 //			gi.dprintf("Ooooh... Too far to fall...\n");
260 	}
261 	else if(playerPosition == 1 && maxUp)
262 	{
263 		VectorMA(self->s.origin, 48, forward, pt1);
264 		VectorCopy(pt1, pt2);
265 		pt1[2] = self->absmax[2] + maxUp;
266 
267 		trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
268 		if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
269 		{
270 			if((trace.endpos[2] - self->absmin[2]) <= maxUp && trace.contents & MASK_SOLID)
271 			{
272 //				if(g_showlogic && g_showlogic->value)
273 //					gi.dprintf("Jumping Up! %0.1f\n", (trace.endpos[2] - self->absmin[2]));
274 
275 				face_wall(self);
276 				return true;
277 			}
278 //			else if(g_showlogic && g_showlogic->value)
279 //				gi.dprintf("Too high to jump %0.1f\n", (trace.endpos[2] - self->absmin[2]));
280 		}
281 //		else if(g_showlogic && g_showlogic->value)
282 //				gi.dprintf("Not something I could jump onto\n");
283 	}
284 //	else if(g_showlogic && g_showlogic->value)
285 //		gi.dprintf("Player at similar level. No need to jump up?\n");
286 
287 	return false;
288 }
289 
290 // checks to see if another coop player is nearby, and will switch.
blocked_checknewenemy(edict_t * self)291 qboolean blocked_checknewenemy (edict_t *self)
292 {
293 /*
294 	int		player;
295 	edict_t *ent;
296 
297 	if (!(coop->value))
298 		return false;
299 
300 	for (player = 1; player <= game.maxclients; player++)
301 	{
302 		ent = &g_edicts[player];
303 		if (!ent->inuse)
304 			continue;
305 		if (!ent->client)
306 			continue;
307 		if (ent == self->enemy)
308 			continue;
309 
310 		if (visible (self, ent))
311 		{
312 			if (g_showlogic && g_showlogic->value)
313 				gi.dprintf ("B_CNE: %s acquired new enemy %s\n", self->classname, ent->client->pers.netname);
314 
315 			self->enemy = ent;
316 			FoundTarget (self);
317 			return true;
318 		}
319 	}
320 
321 	return false;
322 */
323 	return false;
324 }
325 
326 // *************************
327 // HINT PATHS
328 // *************************
329 
330 #define HINT_ENDPOINT		0x0001
331 #define	MAX_HINT_CHAINS		100
332 
333 int	hint_paths_present;
334 edict_t *hint_path_start[MAX_HINT_CHAINS];
335 int	num_hint_paths;
336 
337 //
338 // AI code
339 //
340 
341 // =============
342 // hintpath_findstart - given any hintpath node, finds the start node
343 // =============
hintpath_findstart(edict_t * ent)344 edict_t	*hintpath_findstart(edict_t *ent)
345 {
346 	edict_t		*e;
347 	edict_t		*last;
348 	int			field;
349 
350 	if(ent->target)		// starting point
351 	{
352 		last = world;
353 		field = FOFS(targetname);
354 		e = G_Find(NULL, field, ent->target);
355 		while(e)
356 		{
357 			last = e;
358 			if(!e->target)
359 				break;
360 			e = G_Find(NULL, field, e->target);
361 		}
362 	}
363 	else				// end point
364 	{
365 		last = world;
366 		field = FOFS(target);
367 		e = G_Find(NULL, field, ent->targetname);
368 		while(e)
369 		{
370 			last = e;
371 			if(!e->targetname)
372 				break;
373 			e = G_Find(NULL, field, e->targetname);
374 		}
375 	}
376 
377 	if(!(last->spawnflags & HINT_ENDPOINT))
378 	{
379 //		gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
380 		return NULL;
381 	}
382 
383 	if(last == world)
384 		last = NULL;
385 	return last;
386 }
387 
388 // =============
389 // hintpath_other_end - given one endpoint of a hintpath, returns the other end.
390 // =============
hintpath_other_end(edict_t * ent)391 edict_t	*hintpath_other_end(edict_t *ent)
392 {
393 	edict_t		*e;
394 	edict_t		*last;
395 	int			field;
396 
397 	if(ent->target)		// starting point
398 	{
399 		last = world;
400 		field = FOFS(targetname);
401 		e = G_Find(NULL, field, ent->target);
402 		while(e)
403 		{
404 			last = e;
405 			if(!e->target)
406 				break;
407 			e = G_Find(NULL, field, e->target);
408 		}
409 	}
410 	else				// end point
411 	{
412 		last = world;
413 		field = FOFS(target);
414 		e = G_Find(NULL, field, ent->targetname);
415 		while(e)
416 		{
417 			last = e;
418 			if(!e->targetname)
419 				break;
420 			e = G_Find(NULL, field, e->targetname);
421 		}
422 	}
423 
424 	if(!(last->spawnflags & HINT_ENDPOINT))
425 	{
426 //		gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
427 		return NULL;
428 	}
429 
430 	if(last == world)
431 		last = NULL;
432 	return last;
433 }
434 
435 // =============
436 // hintpath_go - starts a monster (self) moving towards the hintpath (point)
437 //		disables all contrary AI flags.
438 // =============
hintpath_go(edict_t * self,edict_t * point)439 void hintpath_go (edict_t *self, edict_t *point)
440 {
441 	vec3_t	dir;
442 	vec3_t	angles;
443 
444 	VectorSubtract(point->s.origin, self->s.origin, dir);
445 	vectoangles2(dir, angles);
446 
447 	self->ideal_yaw = angles[YAW];
448 	self->goalentity = self->movetarget = point;
449 	self->monsterinfo.pausetime = 0;
450 	self->monsterinfo.aiflags |= AI_HINT_PATH;
451 	self->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP);
452 	// run for it
453 	self->monsterinfo.search_time = level.time;
454 	self->monsterinfo.run (self);
455 }
456 
457 // =============
458 // hintpath_stop - bails a monster out of following hint paths
459 // =============
hintpath_stop(edict_t * self)460 void hintpath_stop (edict_t *self)
461 {
462 	self->goalentity = NULL;
463 	self->movetarget = NULL;
464 //	self->monsterinfo.last_hint = NULL;
465 	self->monsterinfo.last_hint_time = level.time;
466 	self->monsterinfo.goal_hint = NULL;
467 	self->monsterinfo.aiflags &= ~AI_HINT_PATH;
468 	if (has_valid_enemy(self))
469 	{
470 		// if we can see our target, go nuts
471 		if (visible(self, self->enemy))
472 		{
473 			FoundTarget (self);
474 			return;
475 		}
476 		// otherwise, keep chasing
477 		HuntTarget (self);
478 		return;
479 	}
480 	// if our enemy is no longer valid, forget about our enemy and go into stand
481 	self->enemy = NULL;
482 		// we need the pausetime otherwise the stand code
483 		// will just revert to walking with no target and
484 		// the monsters will wonder around aimlessly trying
485 		// to hunt the world entity
486 	self->monsterinfo.pausetime = level.time + 100000000;
487 	self->monsterinfo.stand (self);
488 }
489 
490 // =============
491 // monsterlost_checkhint - the monster (self) will check around for valid hintpaths.
492 //		a valid hintpath is one where the two endpoints can see both the monster
493 //		and the monster's enemy. if only one person is visible from the endpoints,
494 //		it will not go for it.
495 // =============
496 qboolean monsterlost_checkhint2 (edict_t *self);
497 
monsterlost_checkhint(edict_t * self)498 qboolean monsterlost_checkhint (edict_t *self)
499 {
500 	edict_t		*e, *monster_pathchain, *target_pathchain, *checkpoint;
501 	edict_t		*closest;
502 	float		closest_range = 1000000;
503 	edict_t		*start, *destination;
504 	int			field;
505 	int			count1=0, count2=0, count3=0, count4=0, count5=0;
506 	float		r;
507 	int			i;
508 	qboolean	hint_path_represented[MAX_HINT_CHAINS];
509 
510 	// if there are no hint paths on this map, exit immediately.
511 	if(!hint_paths_present)
512 		return false;
513 
514 	if(!self->enemy)
515 		return false;
516 
517 	if (self->monsterinfo.aiflags & AI_STAND_GROUND)
518 		return false;
519 
520 	if (!strcmp(self->classname, "monster_turret"))
521 		return false;
522 
523 	monster_pathchain = NULL;
524 
525 	field = FOFS(classname);
526 
527 	// find all the hint_paths.
528 	// FIXME - can we not do this every time?
529 	for (i=0; i < num_hint_paths; i++)
530 	{
531 		e = hint_path_start[i];
532 		while(e)
533 		{
534 			count1++;
535 			if (e->monster_hint_chain)
536 			{
537 //				gi.dprintf ("uh, oh, I didn't clean up after myself\n");
538 				e->monster_hint_chain = NULL;
539 			}
540 			if (monster_pathchain)
541 			{
542 				checkpoint->monster_hint_chain = e;
543 				checkpoint = e;
544 			}
545 			else
546 			{
547 				monster_pathchain = e;
548 				checkpoint = e;
549 			}
550 			e = e->hint_chain;
551 		}
552 	}
553 
554 	// filter them by distance and visibility to the monster
555 	e = monster_pathchain;
556 	checkpoint = NULL;
557 	while (e)
558 	{
559 		r = realrange (self, e);
560 
561 //		if (r > 512)
562 //			count3++;
563 
564 		if (r > 512)
565 		{
566 			count2++;
567 //			if (g_showlogic && g_showlogic->value)
568 //			{
569 //				gi.dprintf ("MONSTER (%s) DISTANCE:  ", self->classname);
570 //				if (e->targetname)
571 //					gi.dprintf ("targetname %s\n", e->targetname);
572 //				else
573 //					gi.dprintf ("start -> %s\n", e->target);
574 //			}
575 			if (checkpoint)
576 			{
577 				checkpoint->monster_hint_chain = e->monster_hint_chain;
578 				e->monster_hint_chain = NULL;
579 				e = checkpoint->monster_hint_chain;
580 				continue;
581 			}
582 			else
583 			{
584 				// use checkpoint as temp pointer
585 				checkpoint = e;
586 				e = e->monster_hint_chain;
587 				checkpoint->monster_hint_chain = NULL;
588 				// and clear it again
589 				checkpoint = NULL;
590 				// since we have yet to find a valid one (or else checkpoint would be set) move the
591 				// start of monster_pathchain
592 				monster_pathchain = e;
593 				continue;
594 			}
595 		}
596 		if (!visible(self, e))
597 		{
598 			count4++;
599 //			if (g_showlogic && g_showlogic->value)
600 //			{
601 //				gi.dprintf ("MONSTER (%s) VISIBILITY:  ", self->classname);
602 //				if (e->targetname)
603 //					gi.dprintf ("targetname %s\n", e->targetname);
604 //				else
605 //					gi.dprintf ("start -> %s\n", e->target);
606 //			}
607 			if (checkpoint)
608 			{
609 				checkpoint->monster_hint_chain = e->monster_hint_chain;
610 				e->monster_hint_chain = NULL;
611 				e = checkpoint->monster_hint_chain;
612 				continue;
613 			}
614 			else
615 			{
616 				// use checkpoint as temp pointer
617 				checkpoint = e;
618 				e = e->monster_hint_chain;
619 				checkpoint->monster_hint_chain = NULL;
620 				// and clear it again
621 				checkpoint = NULL;
622 				// since we have yet to find a valid one (or else checkpoint would be set) move the
623 				// start of monster_pathchain
624 				monster_pathchain = e;
625 				continue;
626 			}
627 		}
628 		// if it passes all the tests, it's a keeper
629 //		if (g_showlogic && g_showlogic->value)
630 //		{
631 //			gi.dprintf ("MONSTER (%s) ACCEPT:  ", self->classname);
632 //			if (e->targetname)
633 //				gi.dprintf ("targetname %s\n", e->targetname);
634 //			else
635 //				gi.dprintf ("start -> %s\n", e->target);
636 //		}
637 		count5++;
638 		checkpoint = e;
639 		e = e->monster_hint_chain;
640 	}
641 
642 	// at this point, we have a list of all of the eligible hint nodes for the monster
643 	// we now take them, figure out what hint chains they're on, and traverse down those chains,
644 	// seeing whether any can see the player
645 	//
646 	// first, we figure out which hint chains we have represented in monster_pathchain
647 	if (count5 == 0)
648 	{
649 //		if ((g_showlogic) && (g_showlogic->value))
650 //			gi.dprintf ("No eligible hint paths found.\n");
651 		return false;
652 	}
653 
654 	for (i=0; i < num_hint_paths; i++)
655 	{
656 		hint_path_represented[i] = false;
657 	}
658 	e = monster_pathchain;
659 	checkpoint = NULL;
660 	while (e)
661 	{
662 		if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
663 		{
664 //			if (g_showlogic && g_showlogic->value)
665 //				gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
666 			return false;
667 		}
668 		hint_path_represented[e->hint_chain_id] = true;
669 		e = e->monster_hint_chain;
670 	}
671 
672 	count1 = 0;
673 	count2 = 0;
674 	count3 = 0;
675 	count4 = 0;
676 	count5 = 0;
677 
678 	// now, build the target_pathchain which contains all of the hint_path nodes we need to check for
679 	// validity (within range, visibility)
680 	target_pathchain = NULL;
681 	checkpoint = NULL;
682 	for (i=0; i < num_hint_paths; i++)
683 	{
684 		// if this hint chain is represented in the monster_hint_chain, add all of it's nodes to the target_pathchain
685 		// for validity checking
686 		if (hint_path_represented[i])
687 		{
688 			e = hint_path_start[i];
689 			while (e)
690 			{
691 				if (target_pathchain)
692 				{
693 					checkpoint->target_hint_chain = e;
694 					checkpoint = e;
695 				}
696 				else
697 				{
698 					target_pathchain = e;
699 					checkpoint = e;
700 				}
701 				e = e->hint_chain;
702 			}
703 		}
704 	}
705 
706 	// target_pathchain is a list of all of the hint_path nodes we need to check for validity relative to the target
707 	e = target_pathchain;
708 	checkpoint = NULL;
709 	while (e)
710 	{
711 		r = realrange (self->enemy, e);
712 
713 //		if (r > 512)
714 //			count3++;
715 
716 		if (r > 512)
717 		{
718 			count2++;
719 //			if (g_showlogic && g_showlogic->value)
720 //			{
721 //				gi.dprintf ("TARGET RANGE:  ");
722 //				if (e->targetname)
723 //					gi.dprintf ("targetname %s\n", e->targetname);
724 //				else
725 //					gi.dprintf ("start -> %s\n", e->target);
726 //			}
727 			if (checkpoint)
728 			{
729 				checkpoint->target_hint_chain = e->target_hint_chain;
730 				e->target_hint_chain = NULL;
731 				e = checkpoint->target_hint_chain;
732 				continue;
733 			}
734 			else
735 			{
736 				// use checkpoint as temp pointer
737 				checkpoint = e;
738 				e = e->target_hint_chain;
739 				checkpoint->target_hint_chain = NULL;
740 				// and clear it again
741 				checkpoint = NULL;
742 				target_pathchain = e;
743 				continue;
744 			}
745 		}
746 		if (!visible(self->enemy, e))
747 		{
748 			count4++;
749 //			if (g_showlogic && g_showlogic->value)
750 //			{
751 //				gi.dprintf ("TARGET VISIBILITY:  ");
752 //				if (e->targetname)
753 //					gi.dprintf ("targetname %s\n", e->targetname);
754 //				else
755 //					gi.dprintf ("start -> %s\n", e->target);
756 //			}
757 			if (checkpoint)
758 			{
759 				checkpoint->target_hint_chain = e->target_hint_chain;
760 				e->target_hint_chain = NULL;
761 				e = checkpoint->target_hint_chain;
762 				continue;
763 			}
764 			else
765 			{
766 				// use checkpoint as temp pointer
767 				checkpoint = e;
768 				e = e->target_hint_chain;
769 				checkpoint->target_hint_chain = NULL;
770 				// and clear it again
771 				checkpoint = NULL;
772 				target_pathchain = e;
773 				continue;
774 			}
775 		}
776 		// if it passes all the tests, it's a keeper
777 //		if (g_showlogic && g_showlogic->value)
778 //		{
779 //			gi.dprintf ("TARGET ACCEPT:  ");
780 //			if (e->targetname)
781 //				gi.dprintf ("targetname %s\n", e->targetname);
782 //			else
783 //				gi.dprintf ("start -> %s\n", e->target);
784 //		}
785 		count5++;
786 		checkpoint = e;
787 		e = e->target_hint_chain;
788 	}
789 
790 	// at this point we should have:
791 	// monster_pathchain - a list of "monster valid" hint_path nodes linked together by monster_hint_chain
792 	// target_pathcain - a list of "target valid" hint_path nodes linked together by target_hint_chain.  these
793 	//                   are filtered such that only nodes which are on the same chain as "monster valid" nodes
794 	//
795 	// Now, we figure out which "monster valid" node we want to use
796 	//
797 	// To do this, we first off make sure we have some target nodes.  If we don't, there are no valid hint_path nodes
798 	// for us to take
799 	//
800 	// If we have some, we filter all of our "monster valid" nodes by which ones have "target valid" nodes on them
801 	//
802 	// Once this filter is finished, we select the closest "monster valid" node, and go to it.
803 
804 	if (count5 == 0)
805 	{
806 //		if ((g_showlogic) && (g_showlogic->value))
807 //			gi.dprintf ("No valid target nodes found\n");
808 		return false;
809 	}
810 
811 	// reuse the hint_chain_represented array, this time to see which chains are represented by the target
812 	for (i=0; i < num_hint_paths; i++)
813 	{
814 		hint_path_represented[i] = false;
815 	}
816 
817 	e = target_pathchain;
818 	checkpoint = NULL;
819 	while (e)
820 	{
821 		if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
822 		{
823 //			gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
824 			return false;
825 		}
826 		hint_path_represented[e->hint_chain_id] = true;
827 		e = e->target_hint_chain;
828 	}
829 
830 	// traverse the monster_pathchain - if the hint_node isn't represented in the "target valid" chain list,
831 	// remove it
832 	// if it is on the list, check it for range from the monster.  If the range is the closest, keep it
833 	//
834 	closest = NULL;
835 	e = monster_pathchain;
836 	while (e)
837 	{
838 		if (!(hint_path_represented[e->hint_chain_id]))
839 		{
840 			checkpoint = e->monster_hint_chain;
841 			e->monster_hint_chain = NULL;
842 			e = checkpoint;
843 			continue;
844 		}
845 		r = realrange(self, e);
846 		if (r < closest_range)
847 			closest = e;
848 		e = e->monster_hint_chain;
849 	}
850 
851 	if (!closest)
852 	{
853 //		if ((g_showlogic) && (g_showlogic->value))
854 //			gi.dprintf ("Failed to find closest node for monster.  Shouldn't happen.\n");
855 		return false;
856 	}
857 
858 	start = closest;
859 	// now we know which one is the closest to the monster .. this is the one the monster will go to
860 	// we need to finally determine what the DESTINATION node is for the monster .. walk down the hint_chain,
861 	// and find the closest one to the player
862 
863 	closest = NULL;
864 	closest_range = 10000000;
865 	e = target_pathchain;
866 	while (e)
867 	{
868 		if (start->hint_chain_id == e->hint_chain_id)
869 		{
870 			r = realrange(self, e);
871 			if (r < closest_range)
872 				closest = e;
873 		}
874 		e = e->target_hint_chain;
875 	}
876 
877 	if (!closest)
878 	{
879 //		if ((g_showlogic) && (g_showlogic->value))
880 //			gi.dprintf ("Failed to find closest node for target.  Shouldn't happen.\n");
881 		return false;
882 	}
883 
884 	destination = closest;
885 
886 	self->monsterinfo.goal_hint = destination;
887 //	self->monsterinfo.last_hint = NULL;
888 	hintpath_go(self, start);
889 
890 //	if(g_showlogic && g_showlogic->value)
891 //	{
892 //		gi.dprintf ("found path.  proceed to ");
893 //		if (start->targetname)
894 //			gi.dprintf ("%s to get to ", start->targetname);
895 //		else
896 //			gi.dprintf ("start (->%s) to get to ", start->target);
897 //		if (destination->targetname)
898 //			gi.dprintf ("%s.", destination->targetname);
899 //		else
900 //			gi.dprintf ("start (->%s)", destination->target);
901 //	}
902 //		gi.dprintf("found path. proceed to %s to get to %s\n", vtos(start->s.origin), vtos(destination->s.origin));
903 
904 	return true;
905 }
906 /*
907 qboolean monsterlost_checkhint2 (edict_t *self)
908 {
909 	edict_t		*e, *e2, *goPoint;
910 	int			field;
911 	int			playerVisible, selfVisible;
912 
913 	// if there are no hint paths on this map, exit immediately.
914 	if(!hint_paths_present)
915 		return false;
916 
917 	if(!self->enemy)
918 		return false;
919 
920 	goPoint = NULL;
921 	field = FOFS(classname);
922 
923 	// check all the hint_paths.
924 	e = G_Find(NULL, field, "hint_path");
925 	while(e)
926 	{
927 		// if it's an endpoint, check for "validity"
928 		if(e->spawnflags & HINT_ENDPOINT)
929 		{
930 			// check visibility from this spot
931 			selfVisible = visible(e, self);
932 			playerVisible = visible(e, self->enemy);
933 //			gi.dprintf("checking endpoint at %s %d %d\n", vtos(e->s.origin),selfVisible,playerVisible);
934 
935 			// at least one of us is visible from this endpoint.
936 			// now check the other one if needed.
937 			if(selfVisible || playerVisible)
938 			{
939 				// if endpoint 1 saw me, set my destination to it.
940 				if(selfVisible)
941 					goPoint = e;
942 
943 				// if both aren't visible, try the other endpoint
944 				if(!selfVisible || !playerVisible)
945 				{
946 					e2 = hintpath_other_end(e);
947 					if(!e2)		// could not connect to the other endpoint
948 					{
949 						gi.dprintf("Unlinked hint paths!\n");
950 						return false;
951 					}
952 
953 					// if endpoint 1 saw the enemy, see if endpoint 2 sees me
954 					if(!selfVisible)
955 						selfVisible = visible(e2, self);
956 					// if endpoint 1 saw me, see if endpoint 2 sees the enemy
957 					else if(!playerVisible)
958 						playerVisible = visible(e2, self->enemy);
959 
960 					// if endpoint 2 saw me, set my destination to it.
961 					if(!goPoint && selfVisible)
962 						goPoint = e2;
963 
964 //					gi.dprintf("checking other endpoint at %s %d %d\n", vtos(e2->s.origin),selfVisible,playerVisible);
965 				}
966 
967 				// if both are visible from at least one endpoint,
968 				// go for it.
969 				if(selfVisible && playerVisible)
970 				{
971 					// set me to go to goPoint
972 					if(g_showlogic && g_showlogic->value)
973 						gi.dprintf("found path. proceed to %s\n", vtos(goPoint->s.origin));
974 
975 					// since this is a new hint path trip, set last_hint to NULL
976 					self->monsterinfo.last_hint = NULL;
977 					hintpath_go(self, goPoint);
978 					return true;
979 				}
980 			}
981 		}
982 		e = G_Find(e, field, "hint_path");
983 	}
984 
985 	// if we got here, we didn't find a valid path
986 	if(g_showlogic && g_showlogic->value)
987 		gi.dprintf("blocked_checkhint: found no paths\n");
988 	return false;
989 }
990 */
991 //
992 // Path code
993 //
994 
995 // =============
996 // hint_path_touch - someone's touched the hint_path
997 // =============
hint_path_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)998 void hint_path_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
999 {
1000 	edict_t		*e, *goal, *next;
1001 //	int			chain;			 // direction - (-1) = upstream, (1) = downstream, (0) = done
1002 	qboolean	goalFound = false;
1003 
1004 	// make sure we're the target of it's obsession
1005 	if(other->movetarget == self)
1006 	{
1007 		goal = other->monsterinfo.goal_hint;
1008 
1009 		// if the monster is where he wants to be
1010 		if (goal == self)
1011 		{
1012 //			if(g_showlogic && g_showlogic->value)
1013 //				gi.dprintf("Got to goal, detatching\n");
1014 			hintpath_stop (other);
1015 			return;
1016 		}
1017 		else
1018 		{
1019 			// if we aren't, figure out which way we want to go
1020 			e = hint_path_start[self->hint_chain_id];
1021 			while (e)
1022 			{
1023 				// if we get up to ourselves on the hint chain, we're going down it
1024 				if (e == self)
1025 				{
1026 					next = e->hint_chain;
1027 					break;
1028 				}
1029 				if (e == goal)
1030 					goalFound = true;
1031 				// if we get to where the next link on the chain is this hint_path and have found the goal on the way
1032 				// we're going upstream, so remember who the previous link is
1033 				if ((e->hint_chain == self) && goalFound)
1034 				{
1035 					next = e;
1036 					break;
1037 				}
1038 				e = e->hint_chain;
1039 			}
1040 		}
1041 
1042 		// if we couldn't find it, have the monster go back to normal hunting.
1043 		if(!next)
1044 		{
1045 //			if(g_showlogic && g_showlogic->value)
1046 //				gi.dprintf("couldn't figure out next node, dropping hint path\n");
1047 			hintpath_stop(other);
1048 			return;
1049 		}
1050 
1051 		// set the last_hint entry to this hint_path, and
1052 		// send him on his way
1053 //		other->monsterinfo.last_hint = self;
1054 //		if(g_showlogic && g_showlogic->value)
1055 //		{
1056 //			gi.dprintf("moving to next point, ");
1057 //			if (next->targetname)
1058 //				gi.dprintf ("targetname %s\n", next->targetname);
1059 //			else
1060 //				gi.dprintf ("start -> %s\n", next->target);
1061 //		}
1062 		hintpath_go(other, next);
1063 
1064 		// have the monster freeze if the hint path we just touched has a wait time
1065 		// on it, for example, when riding a plat.
1066 		if(self->wait)
1067 		{
1068 //			if(g_showlogic && g_showlogic->value)
1069 //				gi.dprintf("monster waiting %0.1f\n", self->wait);
1070 			other->nextthink = level.time + self->wait;
1071 		}
1072 	}
1073 }
1074 /*
1075 void hint_path_touch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1076 {
1077 	edict_t		*next, *last;
1078 	int			chain;
1079 
1080 	// make sure we're the target of it's obsession
1081 	if(other->movetarget == self)
1082 	{
1083 		chain = 0;		// direction the monster is going in the chain
1084 		next = NULL;	// next hint_path
1085 
1086 //		gi.dprintf("hint_path %s\n", vtos(self->s.origin));
1087 		// is this the first hintpath targeted? if so, we can do this easily.
1088 		if(other->monsterinfo.last_hint == NULL)
1089 		{
1090 			if(self->target)		// forward chaining
1091 				chain = 1;
1092 			else					// backward chaining
1093 				chain = -1;
1094 		}
1095 		else
1096 		{
1097 			// shortcut to last_hint
1098 			last = other->monsterinfo.last_hint;
1099 
1100 			// make sure it's valid...
1101 			if ( (last < g_edicts) || (last >= &g_edicts[globals.num_edicts]))
1102 			{
1103 				if(g_showlogic && g_showlogic->value)
1104 				{
1105 					gi.dprintf("bogus last_hint encountered.\n");
1106 					gi.dprintf("detaching from hint path %d\n", chain);
1107 				}
1108 				hintpath_stop (other);
1109 				return;
1110 			}
1111 
1112 			// if we're an endpoint, then the monster is done moving.
1113 			if(self->spawnflags & HINT_ENDPOINT)
1114 			{
1115 				chain = 0;
1116 			}
1117 			// if last hint's target is our targetname, it's forward chaining.
1118 			else if(last->target && self->targetname && !strcmp(last->target, self->targetname))
1119 			{
1120 				chain = 1;
1121 			}
1122 			// if last hint's targetname is our target, it's backward chaining.
1123 			// FIXME - last->targetname was 1, not NULL ????  was a screwed up hintpath
1124 			else if(self->target && last->targetname && !strcmp(last->targetname, self->target))
1125 			{
1126 				chain = -1;
1127 			}
1128 			else	// if it gets here, i'm not sure how
1129 			{
1130 				gi.dprintf("hit an uncovered possibility in hint_path_touch\n");
1131 				chain = 0;
1132 			}
1133 		}
1134 
1135 		// find the "next" hint_path
1136 		if(chain == 1 && self->target)						// forward chaining
1137 			next = G_Find(NULL, FOFS(targetname), self->target);
1138 		else if(chain == -1 && self->targetname)			// backward chaining
1139 			next = G_Find(NULL, FOFS(target), self->targetname);
1140 
1141 		// if we couldn't find it, have the monster go back to normal hunting.
1142 		if(!next)
1143 		{
1144 			if(g_showlogic && g_showlogic->value)
1145 				gi.dprintf("detaching from hint path %d\n", chain);
1146 			hintpath_stop(other);
1147 			return;
1148 		}
1149 
1150 		// set the last_hint entry to this hint_path, and
1151 		// send him on his way
1152 		other->monsterinfo.last_hint = self;
1153 		if(g_showlogic && g_showlogic->value)
1154 			gi.dprintf("moving to next point, %s\n", vtos(next->s.origin));
1155 		hintpath_go(other, next);
1156 
1157 		// have the monster freeze if the hint path we just touched has a wait time
1158 		// on it, for example, when riding a plat.
1159 		if(self->wait)
1160 		{
1161 			if(g_showlogic && g_showlogic->value)
1162 				gi.dprintf("monster waiting %0.1f\n", self->wait);
1163 			other->nextthink = level.time + self->wait;
1164 		}
1165 	}
1166 }
1167 */
1168 
1169 /*QUAKED hint_path (.5 .3 0) (-8 -8 -8) (8 8 8) END
1170 Target: next hint path
1171 
1172 END - set this flag on the endpoints of each hintpath.
1173 
1174 "wait" - set this if you want the monster to freeze when they touch this hintpath
1175 */
SP_hint_path(edict_t * self)1176 void SP_hint_path (edict_t *self)
1177 {
1178 	if (deathmatch->value)
1179 	{
1180 		G_FreeEdict(self);
1181 		return;
1182 	}
1183 
1184 	if (!self->targetname && !self->target)
1185 	{
1186 		gi.dprintf ("unlinked hint_path at %s\n", vtos(self->s.origin));
1187 		G_FreeEdict (self);
1188 		return;
1189 	}
1190 
1191 	self->solid = SOLID_TRIGGER;
1192 	self->touch = hint_path_touch;
1193 	VectorSet (self->mins, -8, -8, -8);
1194 	VectorSet (self->maxs, 8, 8, 8);
1195 	self->svflags |= SVF_NOCLIENT;
1196 	gi.linkentity (self);
1197 }
1198 
1199 //int	hint_paths_present;
1200 //edict_t *hint_path_start[100];
1201 //int	num_hint_paths;
1202 
1203 // ============
1204 // InitHintPaths - Called by InitGame (g_save) to enable quick exits if valid
1205 // ============
InitHintPaths(void)1206 void InitHintPaths (void)
1207 {
1208 	edict_t		*e, *current;
1209 	int			field, i, count2;
1210 	qboolean	errors = false;
1211 
1212 	hint_paths_present = 0;
1213 
1214 	// check all the hint_paths.
1215 	field = FOFS(classname);
1216 	e = G_Find(NULL, field, "hint_path");
1217 	if(e)
1218 	{
1219 //		gi.dprintf("hint paths present on map\n");
1220 		hint_paths_present = 1;
1221 	}
1222 	else
1223 	{
1224 //		if ((g_showlogic) && (g_showlogic->value))
1225 //			gi.dprintf ("hint paths not present on map\n");
1226 		return;
1227 	}
1228 
1229 	memset (hint_path_start, 0, MAX_HINT_CHAINS*sizeof (edict_t *));
1230 	num_hint_paths = 0;
1231 	while(e)
1232 	{
1233 		if(e->spawnflags & HINT_ENDPOINT)
1234 		{
1235 			if (e->target) // start point
1236 			{
1237 				if (e->targetname) // this is a bad end, ignore it
1238 				{
1239 					gi.dprintf ("Hint path at %s marked as endpoint with both target (%s) and targetname (%s)\n",
1240 						vtos (e->s.origin), e->target, e->targetname);
1241 					errors = true;
1242 				}
1243 				else
1244 				{
1245 					if (num_hint_paths >= MAX_HINT_CHAINS)
1246 					{
1247 //						gi.dprintf ("Only %d hint chains allowed.  Connect some together!\n", MAX_HINT_CHAINS);
1248 						break;
1249 					}
1250 					hint_path_start[num_hint_paths++] = e;
1251 				}
1252 			}
1253 		}
1254 		e = G_Find(e, field, "hint_path");
1255 	}
1256 
1257 	field = FOFS(targetname);
1258 	for (i=0; i< num_hint_paths; i++)
1259 	{
1260 		count2 = 1;
1261 		current = hint_path_start[i];
1262 		current->hint_chain_id = i;
1263 //		gi.dprintf ("start ");
1264 		e = G_Find(NULL, field, current->target);
1265 		if (G_Find(e, field, current->target))
1266 		{
1267 			gi.dprintf ("\nForked hint path at %s detected for chain %d, target %s\n",
1268 				vtos (current->s.origin), num_hint_paths, current->target);
1269 			hint_path_start[i]->hint_chain = NULL;
1270 			count2 = 0;
1271 			errors = true;
1272 			continue;
1273 		}
1274 		while (e)
1275 		{
1276 			if (e->hint_chain)
1277 			{
1278 				gi.dprintf ("\nCircular hint path at %s detected for chain %d, targetname %s\n",
1279 					vtos (e->s.origin), num_hint_paths, e->targetname);
1280 				hint_path_start[i]->hint_chain = NULL;
1281 				count2 = 0;
1282 				errors = true;
1283 				break;
1284 			}
1285 			count2++;
1286 			current->hint_chain = e;
1287 			current = e;
1288 			current->hint_chain_id = i;
1289 //			gi.dprintf ("-> %s ", current->targetname);
1290 			if (!current->target)
1291 				break;
1292 			e = G_Find(NULL, field, current->target);
1293 			if (G_Find(e, field, current->target))
1294 			{
1295 				gi.dprintf ("\nForked hint path at %s detected for chain %d, target %s\n",
1296 					vtos (current->s.origin), num_hint_paths, current->target);
1297 				hint_path_start[i]->hint_chain = NULL;
1298 				count2 = 0;
1299 				break;
1300 			}
1301 		}
1302 //		if ((g_showlogic) && (g_showlogic->value))
1303 //			if (count2)
1304 //			{
1305 //				goodcount++;
1306 //				gi.dprintf ("\nhint_path #%d, %d elements\n\n", i, count2);
1307 //			}
1308 //			else
1309 //				gi.dprintf ("\nhint_path #%d invalid\n\n", i);
1310 	}
1311 //	if (errors)
1312 //		gi.error ("hint_path processing failed, fix errors\n");
1313 //	if ((g_showlogic) && (g_showlogic->value))
1314 //		gi.dprintf ("hint_path processing done, %d hint paths linked\n", num_hint_paths);
1315 }
1316 
1317 // *****************************
1318 //	MISCELLANEOUS STUFF
1319 // *****************************
1320 
1321 // PMM - inback
1322 // use to see if opponent is behind you (not to side)
1323 // if it looks a lot like infront, well, there's a reason
1324 
inback(edict_t * self,edict_t * other)1325 qboolean inback (edict_t *self, edict_t *other)
1326 {
1327 	vec3_t	vec;
1328 	float	dot;
1329 	vec3_t	forward;
1330 
1331 	AngleVectors (self->s.angles, forward, NULL, NULL);
1332 	VectorSubtract (other->s.origin, self->s.origin, vec);
1333 	VectorNormalize (vec);
1334 	dot = DotProduct (vec, forward);
1335 
1336 	if (dot < -0.3)
1337 		return true;
1338 	return false;
1339 }
1340 
realrange(edict_t * self,edict_t * other)1341 float realrange (edict_t *self, edict_t *other)
1342 {
1343 	vec3_t dir;
1344 
1345 	VectorSubtract (self->s.origin, other->s.origin, dir);
1346 
1347 	return VectorLength(dir);
1348 }
1349 
face_wall(edict_t * self)1350 qboolean face_wall (edict_t *self)
1351 {
1352 	vec3_t	pt;
1353 	vec3_t	forward;
1354 	vec3_t	ang;
1355 	trace_t	tr;
1356 
1357 	AngleVectors (self->s.angles, forward, NULL, NULL);
1358 	VectorMA(self->s.origin, 64, forward, pt);
1359 	tr = gi.trace(self->s.origin, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
1360 	if(tr.fraction < 1 && !tr.allsolid && !tr.startsolid)
1361 	{
1362 		vectoangles2(tr.plane.normal, ang);
1363 		self->ideal_yaw = ang[YAW] + 180;
1364 		if(self->ideal_yaw > 360)
1365 			self->ideal_yaw -= 360;
1366 
1367 //		if(g_showlogic && g_showlogic->value)
1368 //			gi.dprintf("facing wall, dir %0.1f/%0.1f\n", ang[YAW], self->ideal_yaw);
1369 		M_ChangeYaw(self);
1370 		return true;
1371 	}
1372 
1373 	return false;
1374 }
1375 
1376 //
1377 // Monster "Bad" Areas
1378 //
1379 
badarea_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1380 void badarea_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1381 {
1382 //	drawbbox(ent);
1383 }
1384 
SpawnBadArea(vec3_t mins,vec3_t maxs,float lifespan,edict_t * owner)1385 edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
1386 {
1387 	edict_t *badarea;
1388 	vec3_t	origin;
1389 
1390 	VectorAdd(mins, maxs, origin);
1391 	VectorScale(origin, 0.5, origin);
1392 
1393 	VectorSubtract(maxs, origin, maxs);
1394 	VectorSubtract(mins, origin, mins);
1395 
1396 	badarea = G_Spawn();
1397 	VectorCopy(origin, badarea->s.origin);
1398 	VectorCopy(maxs, badarea->maxs);
1399 	VectorCopy(mins, badarea->mins);
1400 	badarea->touch = badarea_touch;
1401 	badarea->movetype = MOVETYPE_NONE;
1402 	badarea->solid = SOLID_TRIGGER;
1403 	badarea->classname = "bad_area";
1404 	gi.linkentity (badarea);
1405 
1406 //	gi.dprintf("(%s)-(%s)\n", vtos(badarea->absmin), vtos(badarea->absmax));
1407 
1408 	if(lifespan)
1409 	{
1410 		badarea->think = G_FreeEdict;
1411 		badarea->nextthink = level.time + lifespan;
1412 	}
1413 	if(owner)
1414 	{
1415 		badarea->owner = owner;
1416 	}
1417 
1418 //	drawbbox(badarea);
1419 	return badarea;
1420 }
1421 
1422 // CheckForBadArea
1423 //		This is a customized version of G_TouchTriggers that will check
1424 //		for bad area triggers and return them if they're touched.
CheckForBadArea(edict_t * ent)1425 edict_t *CheckForBadArea(edict_t *ent)
1426 {
1427 	int			i, num;
1428 	edict_t		*touch[MAX_EDICTS], *hit;
1429 	vec3_t		mins, maxs;
1430 
1431 	VectorAdd(ent->s.origin, ent->mins, mins);
1432 	VectorAdd(ent->s.origin, ent->maxs, maxs);
1433 
1434 	num = gi.BoxEdicts (mins, maxs, touch, MAX_EDICTS, AREA_TRIGGERS);
1435 
1436 //	drawbbox(ent);
1437 
1438 	// be careful, it is possible to have an entity in this
1439 	// list removed before we get to it (killtriggered)
1440 	for (i=0 ; i<num ; i++)
1441 	{
1442 		hit = touch[i];
1443 		if (!hit->inuse)
1444 			continue;
1445 		if (hit->touch == badarea_touch)
1446 		{
1447 			return hit;
1448 		}
1449 	}
1450 
1451 	return NULL;
1452 }
1453 
1454 #define TESLA_DAMAGE_RADIUS		128
1455 
MarkTeslaArea(edict_t * self,edict_t * tesla)1456 qboolean MarkTeslaArea(edict_t *self, edict_t *tesla)
1457 {
1458 	vec3_t	mins, maxs;
1459 	edict_t *e;
1460 	edict_t *tail;
1461 	edict_t *area;
1462 
1463 	if(!tesla || !self)
1464 		return false;
1465 
1466 	area = NULL;
1467 
1468 	// make sure this tesla doesn't have a bad area around it already...
1469 	e = tesla->teamchain;
1470 	tail = tesla;
1471 	while (e)
1472 	{
1473 		tail = tail->teamchain;
1474 		if(!strcmp(e->classname, "bad_area"))
1475 		{
1476 //			gi.dprintf("tesla already has a bad area marked\n");
1477 			return false;
1478 		}
1479 		e = e->teamchain;
1480 	}
1481 
1482 	// see if we can grab the trigger directly
1483 	if(tesla->teamchain && tesla->teamchain->inuse)
1484 	{
1485 		edict_t *trigger;
1486 
1487 		trigger = tesla->teamchain;
1488 
1489 //		VectorAdd (trigger->s.origin, trigger->mins, mins);
1490 //		VectorAdd (trigger->s.origin, trigger->maxs, maxs);
1491 		VectorCopy(trigger->absmin, mins);
1492 		VectorCopy(trigger->absmax, maxs);
1493 
1494 		if(tesla->air_finished)
1495 			area = SpawnBadArea (mins, maxs, tesla->air_finished, tesla);
1496 		else
1497 			area = SpawnBadArea (mins, maxs, tesla->nextthink, tesla);
1498 	}
1499 	// otherwise we just guess at how long it'll last.
1500 	else
1501 	{
1502 
1503 		VectorSet (mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, tesla->mins[2]);
1504 		VectorSet (maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
1505 
1506 		area = SpawnBadArea(mins, maxs, 30, tesla);
1507 	}
1508 
1509 	// if we spawned a bad area, then link it to the tesla
1510 	if(area)
1511 	{
1512 //		gi.dprintf("bad area marker spawned and linked to tesla\n");
1513 		tail->teamchain = area;
1514 	}
1515 	return true;
1516 }
1517 
1518 // predictive calculator
1519 // target is who you want to shoot
1520 // start is where the shot comes from
1521 // bolt_speed is how fast the shot is
1522 // eye_height is a boolean to say whether or not to adjust to targets eye_height
1523 // offset is how much time to miss by
1524 // aimdir is the resulting aim direction (pass in NULL if you don't want it)
1525 // aimpoint is the resulting aimpoint (pass in NULL if don't want it)
PredictAim(edict_t * target,vec3_t start,float bolt_speed,qboolean eye_height,float offset,vec3_t aimdir,vec3_t aimpoint)1526 void PredictAim (edict_t *target, vec3_t start, float bolt_speed, qboolean eye_height, float offset, vec3_t aimdir, vec3_t aimpoint)
1527 {
1528 	vec3_t dir, vec;
1529 	float dist, time;
1530 
1531 	if (!target || !target->inuse)
1532 	{
1533 		VectorCopy (vec3_origin, aimdir);
1534 		return;
1535 	}
1536 
1537 	VectorSubtract(target->s.origin, start, dir);
1538 	if (eye_height)
1539 		dir[2] += target->viewheight;
1540 	dist = VectorLength(dir);
1541 	time = dist / bolt_speed;
1542 
1543 
1544 	VectorMA(target->s.origin, time - offset, target->velocity, vec);
1545 
1546 	if (eye_height)
1547 		vec[2] += target->viewheight;
1548 
1549 	if (aimdir)
1550 	{
1551 		VectorSubtract (vec, start, aimdir);
1552 		VectorNormalize (aimdir);
1553 	}
1554 
1555 	if (aimpoint)
1556 	{
1557 		VectorCopy (vec, aimpoint);
1558 	}
1559 }
1560 
1561 
below(edict_t * self,edict_t * other)1562 qboolean below (edict_t *self, edict_t *other)
1563 {
1564 	vec3_t	vec;
1565 	float	dot;
1566 	vec3_t	down;
1567 
1568 	VectorSubtract (other->s.origin, self->s.origin, vec);
1569 	VectorNormalize (vec);
1570 	VectorSet (down, 0, 0, -1);
1571 	dot = DotProduct (vec, down);
1572 
1573 	if (dot > 0.95)  // 18 degree arc below
1574 		return true;
1575 	return false;
1576 }
1577 
drawbbox(edict_t * self)1578 void drawbbox (edict_t *self)
1579 {
1580 	int	lines[4][3] = {
1581 		{1, 2, 4},
1582 		{1, 2, 7},
1583 		{1, 4, 5},
1584 		{2, 4, 7}
1585 	};
1586 
1587 	int starts[4] = {0, 3, 5, 6};
1588 
1589 	vec3_t pt[8];
1590 	int i, j, k;
1591 	vec3_t coords[2];
1592 	vec3_t newbox;
1593 	vec3_t f,r,u, dir;
1594 
1595 	VectorCopy (self->absmin, coords[0]);
1596 	VectorCopy (self->absmax, coords[1]);
1597 
1598 	for (i=0; i<=1; i++)
1599 	{
1600 		for (j=0; j<=1; j++)
1601 		{
1602 			for (k=0; k<=1; k++)
1603 			{
1604 				pt[4*i+2*j+k][0] = coords[i][0];
1605 				pt[4*i+2*j+k][1] = coords[j][1];
1606 				pt[4*i+2*j+k][2] = coords[k][2];
1607 			}
1608 		}
1609 	}
1610 
1611 	for (i=0; i<= 3; i++)
1612 	{
1613 		for (j=0; j<= 2; j++)
1614 		{
1615 			gi.WriteByte (svc_temp_entity);
1616 			gi.WriteByte (TE_DEBUGTRAIL);
1617 			gi.WritePosition (pt[starts[i]]);
1618 			gi.WritePosition (pt[lines[i][j]]);
1619 			gi.multicast (pt[starts[i]], MULTICAST_ALL);
1620 		}
1621 	}
1622 
1623 	vectoangles2 (self->s.angles, dir);
1624 	AngleVectors (dir, f, r, u);
1625 
1626 	VectorMA (self->s.origin, 50, f, newbox);
1627 	gi.WriteByte (svc_temp_entity);
1628 	gi.WriteByte (TE_DEBUGTRAIL);
1629 	gi.WritePosition (self->s.origin);
1630 	gi.WritePosition (newbox);
1631 	gi.multicast (self->s.origin, MULTICAST_PVS);
1632 	VectorClear (newbox);
1633 
1634 	VectorMA (self->s.origin, 50, r, newbox);
1635 	gi.WriteByte (svc_temp_entity);
1636 	gi.WriteByte (TE_DEBUGTRAIL);
1637 	gi.WritePosition (self->s.origin);
1638 	gi.WritePosition (newbox);
1639 	gi.multicast (self->s.origin, MULTICAST_PVS);
1640 	VectorClear (newbox);
1641 
1642 	VectorMA (self->s.origin, 50, u, newbox);
1643 	gi.WriteByte (svc_temp_entity);
1644 	gi.WriteByte (TE_DEBUGTRAIL);
1645 	gi.WritePosition (self->s.origin);
1646 	gi.WritePosition (newbox);
1647 	gi.multicast (self->s.origin, MULTICAST_PVS);
1648 	VectorClear (newbox);
1649 }
1650 
1651 //
1652 // New dodge code
1653 //
M_MonsterDodge(edict_t * self,edict_t * attacker,float eta,trace_t * tr)1654 void M_MonsterDodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
1655 {
1656 	float	r = random();
1657 	float	height;
1658 	qboolean	ducker = false, dodger = false;
1659 
1660 	// this needs to be here since this can be called after the monster has "died"
1661 	if (self->health < 1)
1662 		return;
1663 
1664 	if ((self->monsterinfo.duck) && (self->monsterinfo.unduck))
1665 		ducker = true;
1666 	if ((self->monsterinfo.sidestep) && !(self->monsterinfo.aiflags & AI_STAND_GROUND))
1667 		dodger = true;
1668 
1669 	if ((!ducker) && (!dodger))
1670 		return;
1671 
1672 //	if ((g_showlogic) && (g_showlogic->value))
1673 //	{
1674 //		if (self->monsterinfo.aiflags & AI_DODGING)
1675 //			gi.dprintf ("dodging - ");
1676 //		if (self->monsterinfo.aiflags & AI_DUCKED)
1677 //			gi.dprintf ("ducked - ");
1678 //	}
1679 	if (!self->enemy)
1680 	{
1681 		self->enemy = attacker;
1682 		FoundTarget (self);
1683 	}
1684 
1685 	// PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
1686 	// seeing numbers like 13 and 14)
1687 	if ((eta < 0.1) || (eta > 5))
1688 	{
1689 //		if ((g_showlogic) && (g_showlogic->value))
1690 //			gi.dprintf ("timeout\n");
1691 		return;
1692 	}
1693 
1694 	// skill level determination..
1695 	if (r > (0.25*((skill->value)+1)))
1696 	{
1697 //		if ((g_showlogic) && (g_showlogic->value))
1698 //			gi.dprintf ("skillout\n");
1699 		return;
1700 	}
1701 
1702 	// stop charging, since we're going to dodge (somehow) instead
1703 //	soldier_stop_charge (self);
1704 
1705 	if (ducker)
1706 	{
1707 		height = self->absmax[2]-32-1;  // the -1 is because the absmax is s.origin + maxs + 1
1708 
1709 		// FIXME, make smarter
1710 		// if we only duck, and ducking won't help or we're already ducking, do nothing
1711 		//
1712 		// need to add monsterinfo.abort_duck() and monsterinfo.next_duck_time
1713 
1714 		if ((!dodger) && ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED)))
1715 			return;
1716 	}
1717 	else
1718 		height = self->absmax[2];
1719 
1720 	if (dodger)
1721 	{
1722 		// if we're already dodging, just finish the sequence, i.e. don't do anything else
1723 		if (self->monsterinfo.aiflags & AI_DODGING)
1724 		{
1725 //			if ((g_showlogic) && (g_showlogic->value))
1726 //				gi.dprintf ("already dodging\n");
1727 			return;
1728 		}
1729 
1730 		// if we're ducking already, or the shot is at our knees
1731 		if ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED))
1732 		{
1733 			vec3_t right, diff;
1734 
1735 			AngleVectors (self->s.angles, NULL, right, NULL);
1736 			VectorSubtract (tr->endpos, self->s.origin, diff);
1737 
1738 			if (DotProduct (right, diff) < 0)
1739 			{
1740 				self->monsterinfo.lefty = 0;
1741 //				gi.dprintf ("left\n");
1742 			} else {
1743 				self->monsterinfo.lefty = 1;
1744 //				gi.dprintf ("right\n");
1745 			}
1746 
1747 			// if we are currently ducked, unduck
1748 
1749 			if ((ducker) && (self->monsterinfo.aiflags & AI_DUCKED))
1750 			{
1751 //				if ((g_showlogic) && (g_showlogic->value))
1752 //					gi.dprintf ("unducking - ");
1753 				self->monsterinfo.unduck(self);
1754 			}
1755 
1756 			self->monsterinfo.aiflags |= AI_DODGING;
1757 			self->monsterinfo.attack_state = AS_SLIDING;
1758 
1759 			// call the monster specific code here
1760 			self->monsterinfo.sidestep (self);
1761 			return;
1762 		}
1763 	}
1764 
1765 	if (ducker)
1766 	{
1767 		if (self->monsterinfo.next_duck_time > level.time)
1768 		{
1769 //			if ((g_showlogic) && (g_showlogic->value))
1770 //				gi.dprintf ("ducked too often, not ducking\n");
1771 			return;
1772 		}
1773 
1774 //		if ((g_showlogic) && (g_showlogic->value))
1775 //			gi.dprintf ("ducking!\n");
1776 
1777 		monster_done_dodge (self);
1778 		// set this prematurely; it doesn't hurt, and prevents extra iterations
1779 		self->monsterinfo.aiflags |= AI_DUCKED;
1780 
1781 		self->monsterinfo.duck (self, eta);
1782 	}
1783 }
1784 
monster_duck_down(edict_t * self)1785 void monster_duck_down (edict_t *self)
1786 {
1787 //	if (self->monsterinfo.aiflags & AI_DUCKED)
1788 //		return;
1789 	self->monsterinfo.aiflags |= AI_DUCKED;
1790 
1791 //	if ((g_showlogic) && (g_showlogic->value))
1792 //		gi.dprintf ("duck down!\n");
1793 //	self->maxs[2] -= 32;
1794 	self->maxs[2] = self->monsterinfo.base_height - 32;
1795 	self->takedamage = DAMAGE_YES;
1796 	if (self->monsterinfo.duck_wait_time < level.time)
1797 		self->monsterinfo.duck_wait_time = level.time + 1;
1798 	gi.linkentity (self);
1799 }
1800 
monster_duck_hold(edict_t * self)1801 void monster_duck_hold (edict_t *self)
1802 {
1803 	if (level.time >= self->monsterinfo.duck_wait_time)
1804 		self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
1805 	else
1806 		self->monsterinfo.aiflags |= AI_HOLD_FRAME;
1807 }
1808 
monster_duck_up(edict_t * self)1809 void monster_duck_up (edict_t *self)
1810 {
1811 	self->monsterinfo.aiflags &= ~AI_DUCKED;
1812 //	self->maxs[2] += 32;
1813 	self->maxs[2] = self->monsterinfo.base_height;
1814 	self->takedamage = DAMAGE_AIM;
1815 	self->monsterinfo.next_duck_time = level.time + DUCK_INTERVAL;
1816 	gi.linkentity (self);
1817 }
1818 
1819 //=========================
1820 //=========================
has_valid_enemy(edict_t * self)1821 qboolean has_valid_enemy (edict_t *self)
1822 {
1823 	if (!self->enemy)
1824 		return false;
1825 
1826 	if (!self->enemy->inuse)
1827 		return false;
1828 
1829 	if (self->enemy->health < 1)
1830 		return false;
1831 
1832 	return true;
1833 }
1834 
TargetTesla(edict_t * self,edict_t * tesla)1835 void TargetTesla (edict_t *self, edict_t *tesla)
1836 {
1837 	if ((!self) || (!tesla))
1838 		return;
1839 
1840 	// PMM - medic bails on healing things
1841 	if (self->monsterinfo.aiflags & AI_MEDIC)
1842 	{
1843 		if (self->enemy)
1844 			cleanupHealTarget(self->enemy);
1845 		self->monsterinfo.aiflags &= ~AI_MEDIC;
1846 	}
1847 
1848 	// store the player enemy in case we lose track of him.
1849 	if(self->enemy && self->enemy->client)
1850 		self->monsterinfo.last_player_enemy = self->enemy;
1851 
1852 	if(self->enemy != tesla)
1853 	{
1854 		self->oldenemy = self->enemy;
1855 		self->enemy = tesla;
1856 		if(self->monsterinfo.attack)
1857 		{
1858 			if (self->health <= 0)
1859 			{
1860 //				if ((g_showlogic) && (g_showlogic->value))
1861 //					gi.dprintf ("bad tesla attack avoided!\n");
1862 				return;
1863 			}
1864 			self->monsterinfo.attack(self);
1865 		}
1866 		else
1867 		{
1868 			FoundTarget(self);
1869 		}
1870 	}
1871 }
1872 
1873 // this returns a randomly selected coop player who is visible to self
1874 // returns NULL if bad
1875 
PickCoopTarget(edict_t * self)1876 edict_t * PickCoopTarget (edict_t *self)
1877 {
1878 	// no more than 4 players in coop, so..
1879 	edict_t *targets[4];
1880 	int		num_targets = 0, targetID;
1881 	edict_t *ent;
1882 	int		player;
1883 
1884 	// if we're not in coop, this is a noop
1885 	if (!coop || !coop->value)
1886 		return NULL;
1887 
1888 	memset (targets, 0, 4*sizeof(edict_t *));
1889 
1890 	for (player = 1; player <= game.maxclients; player++)
1891 	{
1892 		ent = &g_edicts[player];
1893 		if (!ent->inuse)
1894 			continue;
1895 		if (!ent->client)
1896 			continue;
1897 		if (visible(self, ent))
1898 		{
1899 //			if ((g_showlogic) && (g_showlogic->value))
1900 //				gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
1901 			targets[num_targets++] = ent;
1902 		}
1903 	}
1904 
1905 /*
1906 	ent = g_edicts+1; // skip the worldspawn
1907 	// cycle through players
1908 	while (ent)
1909 	{
1910 		if ((ent->client) && (ent->inuse))
1911 		{
1912 			if (visible(self, ent))
1913 			{
1914 				if ((g_showlogic) && (g_showlogic->value))
1915 					gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
1916 				targets[num_targets++] = ent;
1917 			}
1918 			ent++;
1919 		}
1920 		else
1921 			ent = NULL;
1922 	}
1923 */
1924 
1925 	if (!num_targets)
1926 		return NULL;
1927 
1928 	// get a number from 0 to (num_targets-1)
1929 	targetID = (random() * (float)num_targets);
1930 
1931 	// just in case we got a 1.0 from random
1932 	if (targetID == num_targets)
1933 		targetID--;
1934 
1935 //	if (g_showlogic && g_showlogic->value)
1936 //		gi.dprintf ("using player %s\n", targets[targetID]->client->pers.netname);
1937 	return targets[targetID];
1938 }
1939 
1940 // only meant to be used in coop
CountPlayers(void)1941 int CountPlayers (void)
1942 {
1943 	edict_t *ent;
1944 	int		count = 0;
1945 	int		player;
1946 
1947 	// if we're not in coop, this is a noop
1948 	if (!coop || !coop->value)
1949 		return 1;
1950 
1951 	for (player = 1; player <= game.maxclients; player++)
1952 	{
1953 		ent = &g_edicts[player];
1954 		if (!ent->inuse)
1955 			continue;
1956 		if (!ent->client)
1957 			continue;
1958 		count++;
1959 	}
1960 /*
1961 	ent = g_edicts+1; // skip the worldspawn
1962 	while (ent)
1963 	{
1964 		if ((ent->client) && (ent->inuse))
1965 		{
1966 			ent++;
1967 			count++;
1968 		}
1969 		else
1970 			ent = NULL;
1971 	}
1972 */
1973 	return count;
1974 }
1975 
1976 //*******************
1977 // JUMPING AIDS
1978 //*******************
1979 
monster_jump_start(edict_t * self)1980 void monster_jump_start (edict_t *self)
1981 {
1982 	self->timestamp = level.time;
1983 }
1984 
monster_jump_finished(edict_t * self)1985 qboolean monster_jump_finished (edict_t *self)
1986 {
1987 	if ((level.time - self->timestamp) > 3)
1988 	{
1989 //		if (g_showlogic && g_showlogic->value)
1990 //		{
1991 //			gi.dprintf("%s jump timed out!\n", self->classname);
1992 //		}
1993 		return true;
1994 	}
1995 }
1996 
1997 
1998 
1999