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