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 /*
134 =============
135 SV_movestep
136 
137 Called by monster program code.
138 The move will be adjusted for slopes and stairs, but if the move isn't
139 possible, no move is done, false is returned, and
140 pr_global_struct->trace_normal is set to the normal of the blocking wall
141 =============
142 */
143 //FIXME since we need to test end position contents here, can we avoid doing
144 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)145 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
146 {
147 	float		dz;
148 	vec3_t		oldorg, neworg, end;
149 	trace_t		trace;
150 	int			i;
151 	float		stepsize;
152 	vec3_t		test;
153 	int			contents;
154 	edict_t		*current_bad;		// PGM
155 	float		minheight;			// pmm
156 
157 //======
158 //PGM
159 	current_bad = CheckForBadArea(ent);
160 	if(current_bad)
161 	{
162 //		gi.dprintf("in bad area\n");
163 		ent->bad_area = current_bad;
164 
165 		if(ent->enemy && !strcmp(ent->enemy->classname, "tesla"))
166 		{
167 //			gi.dprintf("%s  -->>  ", vtos(move));
168 			VectorScale(move, -1, move);
169 //			gi.dprintf("%s\n", vtos(move));
170 		}
171 	}
172 	else if(ent->bad_area)
173 	{
174 		// if we're no longer in a bad area, get back to business.
175 		ent->bad_area = NULL;
176 		if(ent->oldenemy)// && ent->bad_area->owner == ent->enemy)
177 		{
178 //			gi.dprintf("resuming being pissed at %s\n", ent->oldenemy->classname);
179 			ent->enemy = ent->oldenemy;
180 			ent->goalentity = ent->oldenemy;
181 			FoundTarget(ent);
182 			return true;
183 		}
184 	}
185 //PGM
186 //======
187 
188 // try the move
189 	VectorCopy (ent->s.origin, oldorg);
190 	VectorAdd (ent->s.origin, move, neworg);
191 
192 // flying monsters don't step up
193 	if ( ent->flags & (FL_SWIM | FL_FLY) )
194 	{
195 	// try one move with vertical motion, then one without
196 		for (i=0 ; i<2 ; i++)
197 		{
198 			VectorAdd (ent->s.origin, move, neworg);
199 			if (i == 0 && ent->enemy)
200 			{
201 				if (!ent->goalentity)
202 					ent->goalentity = ent->enemy;
203 				dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
204 				if (ent->goalentity->client)
205 				{
206 					// we want the carrier to stay a certain distance off the ground, to help prevent him
207 					// from shooting his fliers, who spawn in below him
208 					//
209 					if (!strcmp(ent->classname, "monster_carrier"))
210 						minheight = 104;
211 					else
212 						minheight = 40;
213 //					if (dz > 40)
214 					if (dz > minheight)
215 //	pmm
216 						neworg[2] -= 8;
217 					if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
218 						if (dz < (minheight - 10))
219 							neworg[2] += 8;
220 				}
221 				else
222 				{
223 					if (dz > 8)
224 						neworg[2] -= 8;
225 					else if (dz > 0)
226 						neworg[2] -= dz;
227 					else if (dz < -8)
228 						neworg[2] += 8;
229 					else
230 						neworg[2] += dz;
231 				}
232 			}
233 
234 			trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
235 
236 			// fly monsters don't enter water voluntarily
237 			if (ent->flags & FL_FLY)
238 			{
239 				if (!ent->waterlevel)
240 				{
241 					test[0] = trace.endpos[0];
242 					test[1] = trace.endpos[1];
243 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
244 					contents = gi.pointcontents(test);
245 					if (contents & MASK_WATER)
246 						return false;
247 				}
248 			}
249 
250 			// swim monsters don't exit water voluntarily
251 			if (ent->flags & FL_SWIM)
252 			{
253 				if (ent->waterlevel < 2)
254 				{
255 					test[0] = trace.endpos[0];
256 					test[1] = trace.endpos[1];
257 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
258 					contents = gi.pointcontents(test);
259 					if (!(contents & MASK_WATER))
260 						return false;
261 				}
262 			}
263 
264 //			if (trace.fraction == 1)
265 
266 			// PMM - changed above to this
267 			if ((trace.fraction == 1) && (!trace.allsolid) && (!trace.startsolid))
268 			{
269 				VectorCopy (trace.endpos, ent->s.origin);
270 //=====
271 //PGM
272 				if(!current_bad && CheckForBadArea(ent))
273 				{
274 //						gi.dprintf("Oooh! Bad Area!\n");
275 					VectorCopy (oldorg, ent->s.origin);
276 				}
277 				else
278 				{
279 					if (relink)
280 					{
281 						gi.linkentity (ent);
282 						G_TouchTriggers (ent);
283 					}
284 					return true;
285 				}
286 //PGM
287 //=====
288 			}
289 
290 			if (!ent->enemy)
291 				break;
292 		}
293 
294 		return false;
295 	}
296 
297 // push down from a step height above the wished position
298 	if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
299 		stepsize = STEPSIZE;
300 	else
301 		stepsize = 1;
302 
303 //PGM
304 #ifdef ROGUE_GRAVITY
305 	// trace from 1 stepsize gravityUp to 2 stepsize gravityDown.
306 	VectorMA(neworg, -1 * stepsize, ent->gravityVector, neworg);
307 	VectorMA(neworg, 2 * stepsize, ent->gravityVector, end);
308 #else
309 	neworg[2] += stepsize;
310 	VectorCopy (neworg, end);
311 	end[2] -= stepsize*2;
312 #endif
313 //PGM
314 
315 	trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
316 
317 	if (trace.allsolid)
318 		return false;
319 
320 	if (trace.startsolid)
321 	{
322 		neworg[2] -= stepsize;
323 		trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
324 		if (trace.allsolid || trace.startsolid)
325 			return false;
326 	}
327 
328 
329 	// don't go in to water
330 	if (ent->waterlevel == 0)
331 	{
332 //PGM
333 #ifdef ROGUE_GRAVITY
334 		test[0] = trace.endpos[0];
335 		test[1] = trace.endpos[1];
336 		if(ent->gravityVector[2] > 0)
337 			test[2] = trace.endpos[2] + ent->maxs[2] - 1;
338 		else
339 			test[2] = trace.endpos[2] + ent->mins[2] + 1;
340 #else
341 		test[0] = trace.endpos[0];
342 		test[1] = trace.endpos[1];
343 		test[2] = trace.endpos[2] + ent->mins[2] + 1;
344 #endif
345 //PGM
346 
347 		contents = gi.pointcontents(test);
348 
349 		if (contents & MASK_WATER)
350 			return false;
351 	}
352 
353 	if (trace.fraction == 1)
354 	{
355 	// if monster had the ground pulled out, go ahead and fall
356 		if ( ent->flags & FL_PARTIALGROUND )
357 		{
358 			VectorAdd (ent->s.origin, move, ent->s.origin);
359 			if (relink)
360 			{
361 				gi.linkentity (ent);
362 				G_TouchTriggers (ent);
363 			}
364 			ent->groundentity = NULL;
365 			return true;
366 		}
367 
368 		return false;		// walked off an edge
369 	}
370 
371 // check point traces down for dangling corners
372 	VectorCopy (trace.endpos, ent->s.origin);
373 
374 //PGM
375 	new_bad = CheckForBadArea(ent);
376 	if(!current_bad && new_bad)
377 	{
378 		if (new_bad->owner)
379 		{
380 			if ((g_showlogic) && (g_showlogic->value))
381 				gi.dprintf("Blocked -");
382 			if (!strcmp(new_bad->owner->classname, "tesla"))
383 			{
384 				if ((g_showlogic) && (g_showlogic->value))
385 					gi.dprintf ("it's a tesla -");
386 				if ((!(ent->enemy)) || (!(ent->enemy->inuse)))
387 				{
388 					if ((g_showlogic) && (g_showlogic->value))
389 						gi.dprintf ("I don't have a valid enemy!\n");
390 				}
391 				else if (!strcmp(ent->enemy->classname, "telsa"))
392 				{
393 					if ((g_showlogic) && (g_showlogic->value))
394 						gi.dprintf ("but we're already mad at a tesla\n");
395 				}
396 				else if ((ent->enemy) && (ent->enemy->client))
397 				{
398 					if ((g_showlogic) && (g_showlogic->value))
399 						gi.dprintf ("we have a player enemy -");
400 					if (visible(ent, ent->enemy))
401 					{
402 						if ((g_showlogic) && (g_showlogic->value))
403 							gi.dprintf ("we can see him -");
404 					}
405 					else
406 					{
407 						if ((g_showlogic) && (g_showlogic->value))
408 							gi.dprintf ("can't see him, kill the tesla! -");
409 					}
410 				}
411 				else
412 				{
413 					if ((g_showlogic) && (g_showlogic->value))
414 						gi.dprintf ("the enemy isn't a player -");
415 				}
416 			}
417 		}
418 		gi.dprintf ("\n");
419 
420 
421 		VectorCopy (oldorg, ent->s.origin);
422 		return false;
423 	}
424 //PGM
425 
426 	if (!M_CheckBottom (ent))
427 	{
428 		if ( ent->flags & FL_PARTIALGROUND )
429 		{	// entity had floor mostly pulled out from underneath it
430 			// and is trying to correct
431 			if (relink)
432 			{
433 				gi.linkentity (ent);
434 				G_TouchTriggers (ent);
435 			}
436 			return true;
437 		}
438 		VectorCopy (oldorg, ent->s.origin);
439 		return false;
440 	}
441 
442 	if ( ent->flags & FL_PARTIALGROUND )
443 	{
444 		ent->flags &= ~FL_PARTIALGROUND;
445 	}
446 	ent->groundentity = trace.ent;
447 	ent->groundentity_linkcount = trace.ent->linkcount;
448 
449 // the move is ok
450 	if (relink)
451 	{
452 		gi.linkentity (ent);
453 		G_TouchTriggers (ent);
454 	}
455 	return true;
456 }
457 
458 
459 //============================================================================
460 
461 /*
462 ===============
463 M_ChangeYaw
464 
465 ===============
466 */
M_ChangeYaw(edict_t * ent)467 void M_ChangeYaw (edict_t *ent)
468 {
469 	float	ideal;
470 	float	current;
471 	float	move;
472 	float	speed;
473 
474 	current = anglemod(ent->s.angles[YAW]);
475 	ideal = ent->ideal_yaw;
476 
477 	if (current == ideal)
478 		return;
479 
480 	move = ideal - current;
481 	speed = ent->yaw_speed;
482 	if (ideal > current)
483 	{
484 		if (move >= 180)
485 			move = move - 360;
486 	}
487 	else
488 	{
489 		if (move <= -180)
490 			move = move + 360;
491 	}
492 	if (move > 0)
493 	{
494 		if (move > speed)
495 			move = speed;
496 	}
497 	else
498 	{
499 		if (move < -speed)
500 			move = -speed;
501 	}
502 
503 	ent->s.angles[YAW] = anglemod (current + move);
504 }
505 
506 
507 /*
508 ======================
509 SV_StepDirection
510 
511 Turns to the movement direction, and walks the current distance if
512 facing it.
513 
514 ======================
515 */
SV_StepDirection(edict_t * ent,float yaw,float dist)516 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
517 {
518 	vec3_t		move, oldorigin;
519 	float		delta;
520 
521 	if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem
522 
523 	ent->ideal_yaw = yaw;
524 	M_ChangeYaw (ent);
525 
526 	yaw = yaw*M_PI*2 / 360;
527 	move[0] = cos(yaw)*dist;
528 	move[1] = sin(yaw)*dist;
529 	move[2] = 0;
530 
531 	VectorCopy (ent->s.origin, oldorigin);
532 	if (SV_movestep (ent, move, false))
533 	{
534 		if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem
535 
536 		delta = ent->s.angles[YAW] - ent->ideal_yaw;
537 		if (strncmp(ent->classname, "monster_widow", 13))
538 		{
539 			if (delta > 45 && delta < 315)
540 			{		// not turned far enough, so don't take the step
541 				VectorCopy (oldorigin, ent->s.origin);
542 			}
543 		}
544 		gi.linkentity (ent);
545 		G_TouchTriggers (ent);
546 		return true;
547 	}
548 	gi.linkentity (ent);
549 	G_TouchTriggers (ent);
550 	return false;
551 }
552 
553 /*
554 ======================
555 SV_FixCheckBottom
556 
557 ======================
558 */
SV_FixCheckBottom(edict_t * ent)559 void SV_FixCheckBottom (edict_t *ent)
560 {
561 	ent->flags |= FL_PARTIALGROUND;
562 }
563 
564 
565 
566 /*
567 ================
568 SV_NewChaseDir
569 
570 ================
571 */
572 #define	DI_NODIR	-1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)573 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
574 {
575 	float	deltax,deltay;
576 	float	d[3];
577 	float	tdir, olddir, turnaround;
578 
579 	//FIXME: how did we get here with no enemy
580 	if (!enemy)
581 		return;
582 
583 	olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
584 	turnaround = anglemod(olddir - 180);
585 
586 	deltax = enemy->s.origin[0] - actor->s.origin[0];
587 	deltay = enemy->s.origin[1] - actor->s.origin[1];
588 	if (deltax>10)
589 		d[1]= 0;
590 	else if (deltax<-10)
591 		d[1]= 180;
592 	else
593 		d[1]= DI_NODIR;
594 	if (deltay<-10)
595 		d[2]= 270;
596 	else if (deltay>10)
597 		d[2]= 90;
598 	else
599 		d[2]= DI_NODIR;
600 
601 // try direct route
602 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
603 	{
604 		if (d[1] == 0)
605 			tdir = d[2] == 90 ? 45 : 315;
606 		else
607 			tdir = d[2] == 90 ? 135 : 215;
608 
609 		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
610 			return;
611 	}
612 
613 // try other directions
614 	if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
615 	{
616 		tdir=d[1];
617 		d[1]=d[2];
618 		d[2]=tdir;
619 	}
620 
621 	if (d[1]!=DI_NODIR && d[1]!=turnaround
622 	&& SV_StepDirection(actor, d[1], dist))
623 			return;
624 
625 	if (d[2]!=DI_NODIR && d[2]!=turnaround
626 	&& SV_StepDirection(actor, d[2], dist))
627 			return;
628 
629 //ROGUE
630 	if(actor->monsterinfo.blocked)
631 	{
632 		if((actor->monsterinfo.blocked)(actor, dist))
633 			return;
634 	}
635 //ROGUE
636 
637 /* there is no direct path to the player, so pick another direction */
638 
639 	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
640 			return;
641 
642 	if (rand()&1) 	/*randomly determine direction of search*/
643 	{
644 		for (tdir=0 ; tdir<=315 ; tdir += 45)
645 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
646 					return;
647 	}
648 	else
649 	{
650 		for (tdir=315 ; tdir >=0 ; tdir -= 45)
651 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
652 					return;
653 	}
654 
655 	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
656 			return;
657 
658 	actor->ideal_yaw = olddir;		// can't move
659 
660 // if a bridge was pulled out from underneath a monster, it may not have
661 // a valid standing position at all
662 
663 	if (!M_CheckBottom (actor))
664 		SV_FixCheckBottom (actor);
665 }
666 
667 /*
668 ======================
669 SV_CloseEnough
670 
671 ======================
672 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)673 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
674 {
675 	int		i;
676 
677 	for (i=0 ; i<3 ; i++)
678 	{
679 		if (goal->absmin[i] > ent->absmax[i] + dist)
680 			return false;
681 		if (goal->absmax[i] < ent->absmin[i] - dist)
682 			return false;
683 	}
684 	return true;
685 }
686 
687 /*
688 ======================
689 M_MoveToGoal
690 ======================
691 */
M_MoveToGoal(edict_t * ent,float dist)692 void M_MoveToGoal (edict_t *ent, float dist)
693 {
694 	edict_t		*goal;
695 
696 	goal = ent->goalentity;
697 
698 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
699 		return;
700 
701 // if the next step hits the enemy, return immediately
702 	if (ent->enemy &&  SV_CloseEnough (ent, ent->enemy, dist) )
703 		return;
704 
705 // bump around...
706 //	if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
707 // PMM - charging monsters (AI_CHARGING) don't deflect unless they have to
708 	if ( (((rand()&3)==1) && !(ent->monsterinfo.aiflags & AI_CHARGING)) || !SV_StepDirection (ent, ent->ideal_yaw, dist))
709 	{
710 		if (ent->inuse)
711 			SV_NewChaseDir (ent, goal, dist);
712 	}
713 }
714 
715 
716 /*
717 ===============
718 M_walkmove
719 ===============
720 */
M_walkmove(edict_t * ent,float yaw,float dist)721 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
722 {
723 	vec3_t	move;
724 
725 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
726 		return false;
727 
728 	yaw = yaw*M_PI*2 / 360;
729 
730 	move[0] = cos(yaw)*dist;
731 	move[1] = sin(yaw)*dist;
732 	move[2] = 0;
733 
734 	return SV_movestep(ent, move, true);
735 }
736