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 qFalse 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 qBool 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 	Vec3Add (ent->s.origin, ent->mins, mins);
45 	Vec3Add (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 qTrue;		// 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, vec3Origin, vec3Origin, stop, ent, MASK_MONSTERSOLID);
75 
76 	if (trace.fraction == 1.0)
77 		return qFalse;
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, vec3Origin, vec3Origin, 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 qFalse;
93 		}
94 
95 	c_yes++;
96 	return qTrue;
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, qFalse 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,qBool relink)112 qBool SV_movestep (edict_t *ent, vec3_t move, qBool 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 	Vec3Copy (ent->s.origin, oldorg);
124 	Vec3Add (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 			Vec3Add (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 qFalse;
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 qFalse;
185 				}
186 			}
187 
188 			if (trace.fraction == 1)
189 			{
190 				Vec3Copy (trace.endPos, ent->s.origin);
191 				if (relink)
192 				{
193 					gi.linkentity (ent);
194 					G_TouchTriggers (ent);
195 				}
196 				return qTrue;
197 			}
198 
199 			if (!ent->enemy)
200 				break;
201 		}
202 
203 		return qFalse;
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 	Vec3Copy (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 qFalse;
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 qFalse;
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 qFalse;
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 			Vec3Add (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 qTrue;
255 		}
256 
257 		return qFalse;		// walked off an edge
258 	}
259 
260 // check point traces down for dangling corners
261 	Vec3Copy (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 qTrue;
274 		}
275 		Vec3Copy (oldorg, ent->s.origin);
276 		return qFalse;
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 qTrue;
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 = AngleModf (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] = AngleModf (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 qBool 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 	Vec3Copy (ent->s.origin, oldorigin);
367 	if (SV_movestep (ent, move, qFalse))
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 			Vec3Copy (oldorigin, ent->s.origin);
373 		}
374 		gi.linkentity (ent);
375 		G_TouchTriggers (ent);
376 		return qTrue;
377 	}
378 	gi.linkentity (ent);
379 	G_TouchTriggers (ent);
380 	return qFalse;
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 = AngleModf ((int)(actor->ideal_yaw/45)*45);
414 	turnaround = AngleModf (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 qBool 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 qFalse;
503 		if (goal->absMax[i] < ent->absMin[i] - dist)
504 			return qFalse;
505 	}
506 	return qTrue;
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 qBool 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 qFalse;
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, qTrue);
556 }
557