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