1 // m_move.c -- monster movement
2 
3 #include "g_local.h"
4 
5 #define	STEPSIZE	18
6 
7 // this is used for communications out of sv_movestep to say what entity
8 // is blocking us
9 edict_t		*new_bad;			//pmm
10 
11 /*
12 =============
13 M_CheckBottom
14 
15 Returns false if any part of the bottom of the entity is off an edge that
16 is not a staircase.
17 
18 =============
19 */
20 int c_yes, c_no;
21 
M_CheckBottom(edict_t * ent)22 qboolean M_CheckBottom (edict_t *ent)
23 {
24 	vec3_t	mins, maxs, start, stop;
25 	trace_t	trace;
26 	int		x, y;
27 	float	mid, bottom;
28 
29 	VectorAdd (ent->s.origin, ent->mins, mins);
30 	VectorAdd (ent->s.origin, ent->maxs, maxs);
31 
32 // if all of the points under the corners are solid world, don't bother
33 // with the tougher checks
34 // the corners must be within 16 of the midpoint
35 
36 //PGM
37 #ifdef ROGUE_GRAVITY
38 	// FIXME - this will only handle 0,0,1 and 0,0,-1 gravity vectors
39 	start[2] = mins[2] - 1;
40 	if(ent->gravityVector[2] > 0)
41 		start[2] = maxs[2] + 1;
42 #else
43 	start[2] = mins[2] - 1;
44 #endif
45 //PGM
46 
47 	for	(x=0 ; x<=1 ; x++)
48 		for	(y=0 ; y<=1 ; y++)
49 		{
50 			start[0] = x ? maxs[0] : mins[0];
51 			start[1] = y ? maxs[1] : mins[1];
52 			if (gi.pointcontents (start) != CONTENTS_SOLID)
53 				goto realcheck;
54 		}
55 
56 	c_yes++;
57 	return true;		// we got out easy
58 
59 realcheck:
60 	c_no++;
61 //
62 // check it for real...
63 //
64 	start[2] = mins[2];
65 
66 // the midpoint must be within 16 of the bottom
67 	start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
68 	start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
69 
70 //PGM
71 #ifdef ROGUE_GRAVITY
72 	if(ent->gravityVector[2] < 0)
73 	{
74 		start[2] = mins[2];
75 		stop[2] = start[2] - STEPSIZE - STEPSIZE;
76 	}
77 	else
78 	{
79 		start[2] = maxs[2];
80 		stop[2] = start[2] + STEPSIZE + STEPSIZE;
81 	}
82 #else
83 	stop[2] = start[2] - 2*STEPSIZE;
84 #endif
85 //PGM
86 
87 	trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
88 
89 	if (trace.fraction == 1.0)
90 		return false;
91 	mid = bottom = trace.endpos[2];
92 
93 // the corners must be within 16 of the midpoint
94 	for	(x=0 ; x<=1 ; x++)
95 		for	(y=0 ; y<=1 ; y++)
96 		{
97 			start[0] = stop[0] = x ? maxs[0] : mins[0];
98 			start[1] = stop[1] = y ? maxs[1] : mins[1];
99 
100 			trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
101 
102 //PGM
103 #ifdef ROGUE_GRAVITY
104 			// FIXME - this will only handle 0,0,1 and 0,0,-1 gravity vectors
105 			if(ent->gravityVector[2] > 0)
106 			{
107 				if (trace.fraction != 1.0 && trace.endpos[2] < bottom)
108 					bottom = trace.endpos[2];
109 				if (trace.fraction == 1.0 || trace.endpos[2] - mid > STEPSIZE)
110 					return false;
111 			}
112 			else
113 			{
114 				if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
115 					bottom = trace.endpos[2];
116 				if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
117 					return false;
118 			}
119 #else
120 			if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
121 				bottom = trace.endpos[2];
122 			if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
123 				return false;
124 #endif
125 //PGM
126 		}
127 
128 	c_yes++;
129 	return true;
130 }
131 
132 //============
133 // ROGUE
IsBadAhead(edict_t * self,edict_t * bad,vec3_t move)134 qboolean IsBadAhead (edict_t *self, edict_t *bad, vec3_t move)
135 {
136 	vec3_t	dir;
137 	vec3_t	forward;
138 	float	dp_bad, dp_move;
139 	vec3_t	move_copy;
140 
141 	VectorCopy (move, move_copy);
142 
143 	VectorSubtract (bad->s.origin, self->s.origin, dir);
144 	VectorNormalize (dir);
145 	AngleVectors (self->s.angles, forward, NULL, NULL);
146 	dp_bad = DotProduct (forward, dir);
147 
148 	VectorNormalize (move_copy);
149 	AngleVectors (self->s.angles, forward, NULL, NULL);
150 	dp_move = DotProduct (forward, move_copy);
151 
152 	if ((dp_bad < 0) && (dp_move < 0))
153 		return true;
154 	if ((dp_bad > 0) && (dp_move > 0))
155 		return true;
156 
157 	return false;
158 /*
159 	if(DotProduct(forward, dir) > 0)
160 	{
161 //		gi.dprintf ("bad ahead...\n");
162 		return true;
163 	}
164 
165 //	gi.dprintf ("bad behind...\n");
166 	return false;
167 	*/
168 }
169 // ROGUE
170 //============
171 
172 /*
173 =============
174 SV_movestep
175 
176 Called by monster program code.
177 The move will be adjusted for slopes and stairs, but if the move isn't
178 possible, no move is done, false is returned, and
179 pr_global_struct->trace_normal is set to the normal of the blocking wall
180 =============
181 */
182 //FIXME since we need to test end position contents here, can we avoid doing
183 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)184 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
185 {
186 	float		dz;
187 	vec3_t		oldorg, neworg, end;
188 	trace_t		trace;
189 	int			i;
190 	float		stepsize;
191 	vec3_t		test;
192 	int			contents;
193 	edict_t		*current_bad;		// PGM
194 	float		minheight;			// pmm
195 
196 //======
197 //PGM
198 
199 	// PMM - who cares about bad areas if you're dead?
200 	if (ent->health > 0)
201 	{
202 		current_bad = CheckForBadArea(ent);
203 		if(current_bad)
204 		{
205 			ent->bad_area = current_bad;
206 
207 			if(ent->enemy && !strcmp(ent->enemy->classname, "tesla"))
208 			{
209 				// if the tesla is in front of us, back up...
210 				if (IsBadAhead (ent, current_bad, move))
211 					VectorScale(move, -1, move);
212 			}
213 		}
214 		else if(ent->bad_area)
215 		{
216 			// if we're no longer in a bad area, get back to business.
217 			ent->bad_area = NULL;
218 			if(ent->oldenemy)// && ent->bad_area->owner == ent->enemy)
219 			{
220 	//			gi.dprintf("resuming being pissed at %s\n", ent->oldenemy->classname);
221 				ent->enemy = ent->oldenemy;
222 				ent->goalentity = ent->oldenemy;
223 				FoundTarget(ent);
224 	// FIXME - remove this when ready!!!
225 //	if (ent->lastMoveTime == level.time)
226 //		if ((g_showlogic) && (g_showlogic->value))
227 //			gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
228 //	ent->lastMoveTime = level.time;
229 	// FIXME
230 
231 				return true;
232 			}
233 		}
234 	}
235 //PGM
236 //======
237 
238 // try the move
239 	VectorCopy (ent->s.origin, oldorg);
240 	VectorAdd (ent->s.origin, move, neworg);
241 
242 // flying monsters don't step up
243 	if ( ent->flags & (FL_SWIM | FL_FLY) )
244 	{
245 	// try one move with vertical motion, then one without
246 		for (i=0 ; i<2 ; i++)
247 		{
248 			VectorAdd (ent->s.origin, move, neworg);
249 			if (i == 0 && ent->enemy)
250 			{
251 				if (!ent->goalentity)
252 					ent->goalentity = ent->enemy;
253 				dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
254 				if (ent->goalentity->client)
255 				{
256 					// we want the carrier to stay a certain distance off the ground, to help prevent him
257 					// from shooting his fliers, who spawn in below him
258 					//
259 					if (!strcmp(ent->classname, "monster_carrier"))
260 						minheight = 104;
261 					else
262 						minheight = 40;
263 //					if (dz > 40)
264 					if (dz > minheight)
265 //	pmm
266 						neworg[2] -= 8;
267 					if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
268 						if (dz < (minheight - 10))
269 							neworg[2] += 8;
270 				}
271 				else
272 				{
273 					if (dz > 8)
274 						neworg[2] -= 8;
275 					else if (dz > 0)
276 						neworg[2] -= dz;
277 					else if (dz < -8)
278 						neworg[2] += 8;
279 					else
280 						neworg[2] += dz;
281 				}
282 			}
283 
284 			trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
285 
286 			// fly monsters don't enter water voluntarily
287 			if (ent->flags & FL_FLY)
288 			{
289 				if (!ent->waterlevel)
290 				{
291 					test[0] = trace.endpos[0];
292 					test[1] = trace.endpos[1];
293 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
294 					contents = gi.pointcontents(test);
295 					if (contents & MASK_WATER)
296 						return false;
297 				}
298 			}
299 
300 			// swim monsters don't exit water voluntarily
301 			if (ent->flags & FL_SWIM)
302 			{
303 				if (ent->waterlevel < 2)
304 				{
305 					test[0] = trace.endpos[0];
306 					test[1] = trace.endpos[1];
307 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
308 					contents = gi.pointcontents(test);
309 					if (!(contents & MASK_WATER))
310 						return false;
311 				}
312 			}
313 
314 //			if (trace.fraction == 1)
315 
316 			// PMM - changed above to this
317 			if ((trace.fraction == 1) && (!trace.allsolid) && (!trace.startsolid))
318 			{
319 				VectorCopy (trace.endpos, ent->s.origin);
320 //=====
321 //PGM
322 				if(!current_bad && CheckForBadArea(ent))
323 				{
324 //						gi.dprintf("Oooh! Bad Area!\n");
325 					VectorCopy (oldorg, ent->s.origin);
326 				}
327 				else
328 				{
329 					if (relink)
330 					{
331 						gi.linkentity (ent);
332 						G_TouchTriggers (ent);
333 					}
334 	// FIXME - remove this when ready!!!
335 //	if (ent->lastMoveTime == level.time)
336 //		if ((g_showlogic) && (g_showlogic->value))
337 //			gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
338 //	ent->lastMoveTime = level.time;
339 	// FIXME
340 
341 					return true;
342 				}
343 //PGM
344 //=====
345 			}
346 
347 			if (!ent->enemy)
348 				break;
349 		}
350 
351 		return false;
352 	}
353 
354 // push down from a step height above the wished position
355 	if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
356 		stepsize = STEPSIZE;
357 	else
358 		stepsize = 1;
359 
360 //PGM
361 #ifdef ROGUE_GRAVITY
362 	// trace from 1 stepsize gravityUp to 2 stepsize gravityDown.
363 	VectorMA(neworg, -1 * stepsize, ent->gravityVector, neworg);
364 	VectorMA(neworg, 2 * stepsize, ent->gravityVector, end);
365 #else
366 	neworg[2] += stepsize;
367 	VectorCopy (neworg, end);
368 	end[2] -= stepsize*2;
369 #endif
370 //PGM
371 
372 	trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
373 
374 	if (trace.allsolid)
375 		return false;
376 
377 	if (trace.startsolid)
378 	{
379 		neworg[2] -= stepsize;
380 		trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
381 		if (trace.allsolid || trace.startsolid)
382 			return false;
383 	}
384 
385 
386 	// don't go in to water
387 	if (ent->waterlevel == 0)
388 	{
389 //PGM
390 #ifdef ROGUE_GRAVITY
391 		test[0] = trace.endpos[0];
392 		test[1] = trace.endpos[1];
393 		if(ent->gravityVector[2] > 0)
394 			test[2] = trace.endpos[2] + ent->maxs[2] - 1;
395 		else
396 			test[2] = trace.endpos[2] + ent->mins[2] + 1;
397 #else
398 		test[0] = trace.endpos[0];
399 		test[1] = trace.endpos[1];
400 		test[2] = trace.endpos[2] + ent->mins[2] + 1;
401 #endif
402 //PGM
403 
404 		contents = gi.pointcontents(test);
405 
406 		if (contents & MASK_WATER)
407 			return false;
408 	}
409 
410 	if (trace.fraction == 1)
411 	{
412 	// if monster had the ground pulled out, go ahead and fall
413 		if ( ent->flags & FL_PARTIALGROUND )
414 		{
415 			VectorAdd (ent->s.origin, move, ent->s.origin);
416 			if (relink)
417 			{
418 				gi.linkentity (ent);
419 				G_TouchTriggers (ent);
420 			}
421 			ent->groundentity = NULL;
422 	// FIXME - remove this when ready!!!
423 //	if (ent->lastMoveTime == level.time)
424 //		if ((g_showlogic) && (g_showlogic->value))
425 //			gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
426 //	ent->lastMoveTime = level.time;
427 	// FIXME
428 
429 			return true;
430 		}
431 
432 		return false;		// walked off an edge
433 	}
434 
435 // check point traces down for dangling corners
436 	VectorCopy (trace.endpos, ent->s.origin);
437 
438 //PGM
439 	// PMM - don't bother with bad areas if we're dead
440 	if (ent->health > 0)
441 	{
442 		// use AI_BLOCKED to tell the calling layer that we're now mad at a tesla
443 		new_bad = CheckForBadArea(ent);
444 		if(!current_bad && new_bad)
445 		{
446 			if (new_bad->owner)
447 			{
448 //				if ((g_showlogic) && (g_showlogic->value))
449 //					gi.dprintf("Blocked -");
450 				if (!strcmp(new_bad->owner->classname, "tesla"))
451 				{
452 //					if ((g_showlogic) && (g_showlogic->value))
453 //						gi.dprintf ("it's a tesla -");
454 					if ((!(ent->enemy)) || (!(ent->enemy->inuse)))
455 					{
456 //						if ((g_showlogic) && (g_showlogic->value))
457 //							gi.dprintf ("I don't have a valid enemy, attacking tesla!\n");
458 						TargetTesla (ent, new_bad->owner);
459 						ent->monsterinfo.aiflags |= AI_BLOCKED;
460 					}
461 					else if (!strcmp(ent->enemy->classname, "telsa"))
462 					{
463 //						if ((g_showlogic) && (g_showlogic->value))
464 //							gi.dprintf ("but we're already mad at a tesla\n");
465 					}
466 					else if ((ent->enemy) && (ent->enemy->client))
467 					{
468 //						if ((g_showlogic) && (g_showlogic->value))
469 //							gi.dprintf ("we have a player enemy -");
470 						if (visible(ent, ent->enemy))
471 						{
472 //							if ((g_showlogic) && (g_showlogic->value))
473 //								gi.dprintf ("we can see him -");
474 						}
475 						else
476 						{
477 //							if ((g_showlogic) && (g_showlogic->value))
478 //								gi.dprintf ("can't see him, kill the tesla! -");
479 							TargetTesla (ent, new_bad->owner);
480 							ent->monsterinfo.aiflags |= AI_BLOCKED;
481 						}
482 					}
483 					else
484 					{
485 //						if ((g_showlogic) && (g_showlogic->value))
486 //							gi.dprintf ("the enemy isn't a player, killing tesla -");
487 						TargetTesla (ent, new_bad->owner);
488 						ent->monsterinfo.aiflags |= AI_BLOCKED;
489 					}
490 				}
491 //				else if ((g_showlogic) && (g_showlogic->value))
492 //				{
493 //					gi.dprintf(" by non-tesla bad area!");
494 //				}
495 			}
496 //			gi.dprintf ("\n");
497 
498 			VectorCopy (oldorg, ent->s.origin);
499 			return false;
500 		}
501 	}
502 //PGM
503 
504 	if (!M_CheckBottom (ent))
505 	{
506 		if ( ent->flags & FL_PARTIALGROUND )
507 		{	// entity had floor mostly pulled out from underneath it
508 			// and is trying to correct
509 			if (relink)
510 			{
511 				gi.linkentity (ent);
512 				G_TouchTriggers (ent);
513 			}
514 	// FIXME - remove this when ready!!!
515 //	if (ent->lastMoveTime == level.time)
516 //		if ((g_showlogic) && (g_showlogic->value))
517 //			gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
518 //	ent->lastMoveTime = level.time;
519 	// FIXME
520 
521 			return true;
522 		}
523 		VectorCopy (oldorg, ent->s.origin);
524 		return false;
525 	}
526 
527 	if ( ent->flags & FL_PARTIALGROUND )
528 	{
529 		ent->flags &= ~FL_PARTIALGROUND;
530 	}
531 	ent->groundentity = trace.ent;
532 	ent->groundentity_linkcount = trace.ent->linkcount;
533 
534 // the move is ok
535 	if (relink)
536 	{
537 		gi.linkentity (ent);
538 		G_TouchTriggers (ent);
539 	}
540 	// FIXME - remove this when ready!!!
541 //	if (ent->lastMoveTime == level.time)
542 //		if ((g_showlogic) && (g_showlogic->value))
543 //			gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
544 //	ent->lastMoveTime = level.time;
545 	// FIXME
546 
547 	return true;
548 }
549 
550 
551 //============================================================================
552 
553 /*
554 ===============
555 M_ChangeYaw
556 
557 ===============
558 */
M_ChangeYaw(edict_t * ent)559 void M_ChangeYaw (edict_t *ent)
560 {
561 	float	ideal;
562 	float	current;
563 	float	move;
564 	float	speed;
565 
566 	current = anglemod(ent->s.angles[YAW]);
567 	ideal = ent->ideal_yaw;
568 
569 	if (current == ideal)
570 		return;
571 
572 	move = ideal - current;
573 	speed = ent->yaw_speed;
574 	if (ideal > current)
575 	{
576 		if (move >= 180)
577 			move = move - 360;
578 	}
579 	else
580 	{
581 		if (move <= -180)
582 			move = move + 360;
583 	}
584 	if (move > 0)
585 	{
586 		if (move > speed)
587 			move = speed;
588 	}
589 	else
590 	{
591 		if (move < -speed)
592 			move = -speed;
593 	}
594 
595 	ent->s.angles[YAW] = anglemod (current + move);
596 }
597 
598 
599 /*
600 ======================
601 SV_StepDirection
602 
603 Turns to the movement direction, and walks the current distance if
604 facing it.
605 
606 ======================
607 */
SV_StepDirection(edict_t * ent,float yaw,float dist)608 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
609 {
610 	vec3_t		move, oldorigin;
611 	float		delta;
612 
613 	if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem
614 
615 	ent->ideal_yaw = yaw;
616 	M_ChangeYaw (ent);
617 
618 	yaw = yaw*M_PI*2 / 360;
619 	move[0] = cos(yaw)*dist;
620 	move[1] = sin(yaw)*dist;
621 	move[2] = 0;
622 
623 	VectorCopy (ent->s.origin, oldorigin);
624 	if (SV_movestep (ent, move, false))
625 	{
626 		ent->monsterinfo.aiflags &= ~AI_BLOCKED;
627 		if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem
628 
629 		delta = ent->s.angles[YAW] - ent->ideal_yaw;
630 		if (strncmp(ent->classname, "monster_widow", 13))
631 		{
632 			if (delta > 45 && delta < 315)
633 			{		// not turned far enough, so don't take the step
634 				VectorCopy (oldorigin, ent->s.origin);
635 			}
636 		}
637 		gi.linkentity (ent);
638 		G_TouchTriggers (ent);
639 		return true;
640 	}
641 	gi.linkentity (ent);
642 	G_TouchTriggers (ent);
643 	return false;
644 }
645 
646 /*
647 ======================
648 SV_FixCheckBottom
649 
650 ======================
651 */
SV_FixCheckBottom(edict_t * ent)652 void SV_FixCheckBottom (edict_t *ent)
653 {
654 	ent->flags |= FL_PARTIALGROUND;
655 }
656 
657 
658 
659 /*
660 ================
661 SV_NewChaseDir
662 
663 ================
664 */
665 #define	DI_NODIR	-1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)666 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
667 {
668 	float	deltax,deltay;
669 	float	d[3];
670 	float	tdir, olddir, turnaround;
671 
672 	//FIXME: how did we get here with no enemy
673 	if (!enemy)
674 		return;
675 
676 	olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
677 	turnaround = anglemod(olddir - 180);
678 
679 	deltax = enemy->s.origin[0] - actor->s.origin[0];
680 	deltay = enemy->s.origin[1] - actor->s.origin[1];
681 	if (deltax>10)
682 		d[1]= 0;
683 	else if (deltax<-10)
684 		d[1]= 180;
685 	else
686 		d[1]= DI_NODIR;
687 	if (deltay<-10)
688 		d[2]= 270;
689 	else if (deltay>10)
690 		d[2]= 90;
691 	else
692 		d[2]= DI_NODIR;
693 
694 // try direct route
695 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
696 	{
697 		if (d[1] == 0)
698 			tdir = d[2] == 90 ? 45 : 315;
699 		else
700 			tdir = d[2] == 90 ? 135 : 215;
701 
702 		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
703 			return;
704 	}
705 
706 // try other directions
707 	if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
708 	{
709 		tdir=d[1];
710 		d[1]=d[2];
711 		d[2]=tdir;
712 	}
713 
714 	if (d[1]!=DI_NODIR && d[1]!=turnaround
715 	&& SV_StepDirection(actor, d[1], dist))
716 			return;
717 
718 	if (d[2]!=DI_NODIR && d[2]!=turnaround
719 	&& SV_StepDirection(actor, d[2], dist))
720 			return;
721 
722 //ROGUE
723 	if(actor->monsterinfo.blocked)
724 	{
725 		if ((actor->inuse) && (actor->health > 0))
726 		{
727 			if((actor->monsterinfo.blocked)(actor, dist))
728 				return;
729 		}
730 	}
731 //ROGUE
732 
733 /* there is no direct path to the player, so pick another direction */
734 
735 	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
736 			return;
737 
738 	if (rand()&1) 	/*randomly determine direction of search*/
739 	{
740 		for (tdir=0 ; tdir<=315 ; tdir += 45)
741 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
742 					return;
743 	}
744 	else
745 	{
746 		for (tdir=315 ; tdir >=0 ; tdir -= 45)
747 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
748 					return;
749 	}
750 
751 	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
752 			return;
753 
754 	actor->ideal_yaw = olddir;		// can't move
755 
756 // if a bridge was pulled out from underneath a monster, it may not have
757 // a valid standing position at all
758 
759 	if (!M_CheckBottom (actor))
760 		SV_FixCheckBottom (actor);
761 }
762 
763 /*
764 ======================
765 SV_CloseEnough
766 
767 ======================
768 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)769 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
770 {
771 	int		i;
772 
773 	for (i=0 ; i<3 ; i++)
774 	{
775 		if (goal->absmin[i] > ent->absmax[i] + dist)
776 			return false;
777 		if (goal->absmax[i] < ent->absmin[i] - dist)
778 			return false;
779 	}
780 	return true;
781 }
782 
783 /*
784 ======================
785 M_MoveToGoal
786 ======================
787 */
M_MoveToGoal(edict_t * ent,float dist)788 void M_MoveToGoal (edict_t *ent, float dist)
789 {
790 	edict_t		*goal;
791 
792 	goal = ent->goalentity;
793 
794 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
795 		return;
796 
797 // if the next step hits the enemy, return immediately
798 	if (ent->enemy &&  SV_CloseEnough (ent, ent->enemy, dist) )
799 		return;
800 
801 // bump around...
802 //	if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
803 // PMM - charging monsters (AI_CHARGING) don't deflect unless they have to
804 	if ( (((rand()&3)==1) && !(ent->monsterinfo.aiflags & AI_CHARGING)) || !SV_StepDirection (ent, ent->ideal_yaw, dist))
805 	{
806 		if (ent->monsterinfo.aiflags & AI_BLOCKED)
807 		{
808 //			if ((g_showlogic) && (g_showlogic->value))
809 //				gi.dprintf ("tesla attack detected, not changing direction!\n");
810 			ent->monsterinfo.aiflags &= ~AI_BLOCKED;
811 			return;
812 		}
813 		if (ent->inuse)
814 			SV_NewChaseDir (ent, goal, dist);
815 	}
816 }
817 
818 
819 /*
820 ===============
821 M_walkmove
822 ===============
823 */
M_walkmove(edict_t * ent,float yaw,float dist)824 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
825 {
826 	vec3_t	move;
827 	// PMM
828 	qboolean	retval;
829 
830 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
831 		return false;
832 
833 	yaw = yaw*M_PI*2 / 360;
834 
835 	move[0] = cos(yaw)*dist;
836 	move[1] = sin(yaw)*dist;
837 	move[2] = 0;
838 
839 	// PMM
840 	retval = SV_movestep(ent, move, true);
841 	ent->monsterinfo.aiflags &= ~AI_BLOCKED;
842 	return retval;
843 	// pmm
844 	//return SV_movestep(ent, move, true);
845 }
846