1 // m_move.c -- monster movement
2 
3 #include "g_local.h"
4 
5 #define	STEPSIZE	18
6 
7 /*
8 =============
9 M_CheckBottom
10 
11 Returns false if any part of the bottom of the entity is off an edge that
12 is not a staircase.
13 
14 =============
15 */
16 int c_yes, c_no;
17 
M_CheckBottom(edict_t * ent)18 qboolean M_CheckBottom (edict_t *ent)
19 {
20 	vec3_t	mins, maxs, start, stop;
21 	trace_t	trace;
22 	int		x, y;
23 	float	mid, bottom;
24 
25 	VectorAdd (ent->s.origin, ent->mins, mins);
26 	VectorAdd (ent->s.origin, ent->maxs, maxs);
27 
28 // if all of the points under the corners are solid world, don't bother
29 // with the tougher checks
30 // the corners must be within 16 of the midpoint
31 	start[2] = mins[2] - 1;
32 	for	(x=0 ; x<=1 ; x++)
33 		for	(y=0 ; y<=1 ; y++)
34 		{
35 			start[0] = x ? maxs[0] : mins[0];
36 			start[1] = y ? maxs[1] : mins[1];
37 			if (gi.pointcontents (start) != CONTENTS_SOLID)
38 				goto realcheck;
39 		}
40 
41 	c_yes++;
42 	return true;		// we got out easy
43 
44 realcheck:
45 	c_no++;
46 //
47 // check it for real...
48 //
49 	start[2] = mins[2];
50 
51 // the midpoint must be within 16 of the bottom
52 	start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
53 	start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
54 	stop[2] = start[2] - 2*STEPSIZE;
55 	trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
56 
57 	if (trace.fraction == 1.0)
58 		return false;
59 	mid = bottom = trace.endpos[2];
60 
61 // the corners must be within 16 of the midpoint
62 	for	(x=0 ; x<=1 ; x++)
63 		for	(y=0 ; y<=1 ; y++)
64 		{
65 			start[0] = stop[0] = x ? maxs[0] : mins[0];
66 			start[1] = stop[1] = y ? maxs[1] : mins[1];
67 
68 			trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
69 
70 			if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
71 				bottom = trace.endpos[2];
72 			if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
73 				return false;
74 		}
75 
76 	c_yes++;
77 	return true;
78 }
79 
80 
81 /*
82 =============
83 SV_movestep
84 
85 Called by monster program code.
86 The move will be adjusted for slopes and stairs, but if the move isn't
87 possible, no move is done, false is returned, and
88 pr_global_struct->trace_normal is set to the normal of the blocking wall
89 =============
90 */
91 //FIXME since we need to test end position contents here, can we avoid doing
92 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)93 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
94 {
95 	float		dz;
96 	vec3_t		oldorg, neworg, end;
97 	trace_t		trace;
98 	int			i;
99 	float		stepsize;
100 	vec3_t		test;
101 	int			contents;
102 
103 // try the move
104 	VectorCopy (ent->s.origin, oldorg);
105 	VectorAdd (ent->s.origin, move, neworg);
106 
107 // flying monsters don't step up
108 	if ( ent->flags & (FL_SWIM | FL_FLY) )
109 	{
110 	// try one move with vertical motion, then one without
111 		for (i=0 ; i<2 ; i++)
112 		{
113 			VectorAdd (ent->s.origin, move, neworg);
114 			if (i == 0 && ent->enemy)
115 			{
116 				if (!ent->goalentity)
117 					ent->goalentity = ent->enemy;
118 				dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
119 				if (ent->goalentity->client)
120 				{
121 					if (dz > 40)
122 						neworg[2] -= 8;
123 					if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
124 						if (dz < 30)
125 							neworg[2] += 8;
126 				}
127 				else
128 				{
129 					if (dz > 8)
130 						neworg[2] -= 8;
131 					else if (dz > 0)
132 						neworg[2] -= dz;
133 					else if (dz < -8)
134 						neworg[2] += 8;
135 					else
136 						neworg[2] += dz;
137 				}
138 			}
139 			trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
140 
141 			// fly monsters don't enter water voluntarily
142 			if (ent->flags & FL_FLY)
143 			{
144 				if (!ent->waterlevel)
145 				{
146 					test[0] = trace.endpos[0];
147 					test[1] = trace.endpos[1];
148 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
149 					contents = gi.pointcontents(test);
150 					if (contents & MASK_WATER)
151 						return false;
152 				}
153 			}
154 
155 			// swim monsters don't exit water voluntarily
156 			if (ent->flags & FL_SWIM)
157 			{
158 				if (ent->waterlevel < 2)
159 				{
160 					test[0] = trace.endpos[0];
161 					test[1] = trace.endpos[1];
162 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
163 					contents = gi.pointcontents(test);
164 					if (!(contents & MASK_WATER))
165 						return false;
166 				}
167 			}
168 
169 			if (trace.fraction == 1)
170 			{
171 				VectorCopy (trace.endpos, ent->s.origin);
172 				if (relink)
173 				{
174 					gi.linkentity (ent);
175 					G_TouchTriggers (ent);
176 				}
177 				return true;
178 			}
179 
180 			if (!ent->enemy)
181 				break;
182 		}
183 
184 		return false;
185 	}
186 
187 // push down from a step height above the wished position
188 	if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
189 		stepsize = STEPSIZE;
190 	else
191 		stepsize = 1;
192 
193 	neworg[2] += stepsize;
194 	VectorCopy (neworg, end);
195 	end[2] -= stepsize*2;
196 
197 	trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
198 
199 	if (trace.allsolid)
200 		return false;
201 
202 	if (trace.startsolid)
203 	{
204 		neworg[2] -= stepsize;
205 		trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
206 		if (trace.allsolid || trace.startsolid)
207 			return false;
208 	}
209 
210 
211 	// don't go in to water
212 	if (ent->waterlevel == 0)
213 	{
214 		test[0] = trace.endpos[0];
215 		test[1] = trace.endpos[1];
216 		test[2] = trace.endpos[2] + ent->mins[2] + 1;
217 		contents = gi.pointcontents(test);
218 
219 		if (contents & MASK_WATER && ent->movetype != MOVETYPE_FALLFLOAT)
220 			return false;
221 	}
222 
223 	if (trace.fraction == 1)
224 	{
225 	// if monster had the ground pulled out, go ahead and fall
226 		if ( ent->flags & FL_PARTIALGROUND )
227 		{
228 			VectorAdd (ent->s.origin, move, ent->s.origin);
229 			if (relink)
230 			{
231 				gi.linkentity (ent);
232 				G_TouchTriggers (ent);
233 			}
234 			ent->groundentity = NULL;
235 			return true;
236 		}
237 		else if (ent->movetype == MOVETYPE_FALLFLOAT)
238 		{
239 			// can fall over the edge
240 			VectorAdd (ent->s.origin, move, ent->s.origin);
241 			if (relink)
242 			{
243 				gi.linkentity (ent);
244 				G_TouchTriggers (ent);
245 			}
246 			ent->groundentity = NULL;
247 			return true;
248 		}
249 
250 		return false;		// walked off an edge
251 	}
252 
253 // check point traces down for dangling corners
254 	VectorCopy (trace.endpos, ent->s.origin);
255 
256 	if (!M_CheckBottom (ent))
257 	{
258 		if ( ent->flags & FL_PARTIALGROUND )
259 		{	// entity had floor mostly pulled out from underneath it
260 			// and is trying to correct
261 			if (relink)
262 			{
263 				gi.linkentity (ent);
264 				G_TouchTriggers (ent);
265 			}
266 			return true;
267 		}
268 		else if (ent->movetype == MOVETYPE_FALLFLOAT)
269 		{
270 			// can fall over the edge
271 			if (relink)
272 			{
273 				gi.linkentity (ent);
274 				G_TouchTriggers (ent);
275 			}
276 			return true;
277 		}
278 		VectorCopy (oldorg, ent->s.origin);
279 		return false;
280 	}
281 
282 	if ( ent->flags & FL_PARTIALGROUND )
283 	{
284 		ent->flags &= ~FL_PARTIALGROUND;
285 	}
286 	ent->groundentity = trace.ent;
287 	ent->groundentity_linkcount = trace.ent->linkcount;
288 
289 // the move is ok
290 	if (relink)
291 	{
292 		gi.linkentity (ent);
293 		G_TouchTriggers (ent);
294 	}
295 	return true;
296 }
297 
298 
299 //============================================================================
300 
301 /*
302 ===============
303 M_ChangeYaw
304 
305 ===============
306 */
M_ChangeYaw(edict_t * ent)307 void M_ChangeYaw (edict_t *ent)
308 {
309 	float	ideal;
310 	float	current;
311 	float	move;
312 	float	speed;
313 
314 	current = anglemod(ent->s.angles[YAW]);
315 	ideal = ent->ideal_yaw;
316 
317 	if (current == ideal)
318 		return;
319 
320 	move = ideal - current;
321 	speed = ent->yaw_speed;
322 	if (ideal > current)
323 	{
324 		while (move >= 180)
325 			move = move - 360;
326 	}
327 	else
328 	{
329 		while (move <= -180)
330 			move = move + 360;
331 	}
332 	if (move > 0)
333 	{
334 		if (move > speed)
335 			move = speed;
336 	}
337 	else
338 	{
339 		if (move < -speed)
340 			move = -speed;
341 	}
342 
343 	ent->s.angles[YAW] = anglemod (current + move);
344 }
345 
346 
347 /*
348 ======================
349 SV_StepDirection
350 
351 Turns to the movement direction, and walks the current distance if
352 facing it.
353 
354 ======================
355 */
SV_StepDirection(edict_t * ent,float yaw,float dist)356 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
357 {
358 	vec3_t		move, oldorigin;
359 	float		delta;
360 
361 	ent->ideal_yaw = yaw;
362 	M_ChangeYaw (ent);
363 
364 	yaw = yaw*M_PI*2 / 360;
365 	move[0] = cos(yaw)*dist;
366 	move[1] = sin(yaw)*dist;
367 	move[2] = 0;
368 
369 	VectorCopy (ent->s.origin, oldorigin);
370 	if (SV_movestep (ent, move, false))
371 	{
372 		delta = ent->s.angles[YAW] - ent->ideal_yaw;
373 		if (delta > 45 && delta < 315)
374 		{		// not turned far enough, so don't take the step
375 			VectorCopy (oldorigin, ent->s.origin);
376 		}
377 		gi.linkentity (ent);
378 		G_TouchTriggers (ent);
379 		return true;
380 	}
381 	gi.linkentity (ent);
382 	G_TouchTriggers (ent);
383 	return false;
384 }
385 
386 /*
387 ======================
388 SV_FixCheckBottom
389 
390 ======================
391 */
SV_FixCheckBottom(edict_t * ent)392 void SV_FixCheckBottom (edict_t *ent)
393 {
394 	ent->flags |= FL_PARTIALGROUND;
395 }
396 
397 
398 
399 /*
400 ================
401 SV_NewChaseDir
402 
403 ================
404 */
405 #define	DI_NODIR	-1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)406 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
407 {
408 	float	deltax,deltay;
409 	float	d[3];
410 	float	tdir, olddir, turnaround;
411 
412 	// how did we get here without an enemy?
413 	if (!enemy)
414 		return;
415 
416 	olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
417 	turnaround = anglemod(olddir - 180);
418 
419 	deltax = enemy->s.origin[0] - actor->s.origin[0];
420 	deltay = enemy->s.origin[1] - actor->s.origin[1];
421 	if (deltax>10)
422 		d[1]= 0;
423 	else if (deltax<-10)
424 		d[1]= 180;
425 	else
426 		d[1]= DI_NODIR;
427 	if (deltay<-10)
428 		d[2]= 270;
429 	else if (deltay>10)
430 		d[2]= 90;
431 	else
432 		d[2]= DI_NODIR;
433 
434 // try direct route
435 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
436 	{
437 		if (d[1] == 0)
438 			tdir = d[2] == 90 ? 45 : 315;
439 		else
440 			tdir = d[2] == 90 ? 135 : 215;
441 
442 		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
443 			return;
444 	}
445 
446 // try other directions
447 	if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
448 	{
449 		tdir=d[1];
450 		d[1]=d[2];
451 		d[2]=tdir;
452 	}
453 
454 	if (d[1]!=DI_NODIR && d[1]!=turnaround
455 	&& SV_StepDirection(actor, d[1], dist))
456 			return;
457 
458 	if (d[2]!=DI_NODIR && d[2]!=turnaround
459 	&& SV_StepDirection(actor, d[2], dist))
460 			return;
461 
462 /* there is no direct path to the player, so pick another direction */
463 
464 	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
465 			return;
466 
467 	if (rand()&1) 	/*randomly determine direction of search*/
468 	{
469 		for (tdir=0 ; tdir<=315 ; tdir += 45)
470 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
471 					return;
472 	}
473 	else
474 	{
475 		for (tdir=315 ; tdir >=0 ; tdir -= 45)
476 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
477 					return;
478 	}
479 
480 	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
481 			return;
482 
483 	actor->ideal_yaw = olddir;		// can't move
484 
485 // if a bridge was pulled out from underneath a monster, it may not have
486 // a valid standing position at all
487 
488 	if (!M_CheckBottom (actor))
489 		SV_FixCheckBottom (actor);
490 }
491 
492 /*
493 ======================
494 SV_CloseEnough
495 
496 ======================
497 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)498 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
499 {
500 	int		i;
501 
502 	for (i=0 ; i<3 ; i++)
503 	{
504 		if (goal->absmin[i] > ent->absmax[i] + dist)
505 			return false;
506 		if (goal->absmax[i] < ent->absmin[i] - dist)
507 			return false;
508 	}
509 	return true;
510 }
511 
512 /*
513 ======================
514 M_MoveToGoal
515 ======================
516 */
M_MoveToGoal(edict_t * ent,float dist)517 void M_MoveToGoal (edict_t *ent, float dist)
518 {
519 	edict_t		*goal;
520 
521 	goal = ent->goalentity;
522 
523 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
524 		return;
525 
526 // if the next step hits the enemy, return immediately
527 	if (ent->enemy &&  SV_CloseEnough (ent, ent->enemy, dist) )
528 		return;
529 
530 // bump around...
531 	if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
532 	{
533 		if (ent->inuse)
534 			SV_NewChaseDir (ent, goal, dist);
535 	}
536 }
537 
538 
539 /*
540 ===============
541 M_walkmove
542 ===============
543 */
M_walkmove(edict_t * ent,float yaw,float dist)544 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
545 {
546 	vec3_t	move;
547 
548 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
549 		return false;
550 
551 	yaw = yaw*M_PI*2 / 360;
552 
553 	move[0] = cos(yaw)*dist;
554 	move[1] = sin(yaw)*dist;
555 	move[2] = 0;
556 
557 	return SV_movestep(ent, move, true);
558 }
559 
560 qboolean ai_checkattack (edict_t *self, float dist);
561 /*
562 ====================
563 M_MoveAwayFromFlare
564 ====================
565 */
M_MoveAwayFromFlare(edict_t * self,float dist)566 qboolean M_MoveAwayFromFlare(edict_t *self, float dist)
567 {
568 	edict_t *e = NULL;
569 	edict_t *goal = NULL;
570 	vec3_t delta;
571 	vec3_t forward;
572 
573 	// find the closest flare
574 	while(1)
575 	{
576 		e = findradius(e, self->s.origin, 256);
577 		if (e == NULL)
578 
579 
580 
581 			break;
582 
583 		if (Q_stricmp(e->classname, "flare") == 0)
584 			break;
585 	}
586 
587 	goal = G_Spawn();
588 	self->goalentity = goal;
589 	if (e == NULL)
590 	{
591 		// just move forward
592 		AngleVectors(self->s.angles, forward, NULL, NULL);
593 		VectorMA(self->s.origin, 128, forward, goal->s.origin);
594 	}
595 	else /* e != NULL */
596 	{
597 		VectorSubtract(self->s.origin, e->s.origin, delta);
598 		VectorNormalize(delta);
599 		VectorMA(self->s.origin, 128, delta, goal->s.origin);
600 	}
601 
602 	if (rand() & 7 == 1)
603 	{
604 		// set the ideal_yaw
605 		VectorSubtract(goal->s.origin, self->s.origin, delta);
606 		self->ideal_yaw = vectoyaw(delta);
607 	}
608 
609 	if ( (rand()&3)==1 || !SV_StepDirection (self, self->ideal_yaw, dist))
610 	{
611 		SV_NewChaseDir (self, goal, dist);
612 	}
613 
614 	self->goalentity = NULL;
615 	G_FreeEdict(goal);
616 
617 	return true;
618 }
619