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