1 /*
2 Copyright (C) 1996-1997 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 // sv_move.c -- monster movement
21 
22 #include "quakedef.h"
23 #include "prvm_cmds.h"
24 
25 /*
26 =============
27 SV_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 */
34 int c_yes, c_no;
35 
SV_CheckBottom(prvm_edict_t * ent)36 qboolean SV_CheckBottom (prvm_edict_t *ent)
37 {
38 	prvm_prog_t *prog = SVVM_prog;
39 	vec3_t	mins, maxs, start, stop;
40 	trace_t	trace;
41 	int		x, y;
42 	float	mid, bottom;
43 
44 	VectorAdd (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
45 	VectorAdd (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(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 (!(SV_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
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*sv_stepheight.value;
74 	trace = SV_TraceLine(start, stop, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
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 = SV_TraceLine(start, stop, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
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] > sv_stepheight.value)
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 and false is returned
107 =============
108 */
SV_movestep(prvm_edict_t * ent,vec3_t move,qboolean relink,qboolean noenemy,qboolean settrace)109 qboolean SV_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
110 {
111 	prvm_prog_t *prog = SVVM_prog;
112 	float		dz;
113 	vec3_t		oldorg, neworg, end, traceendpos, entorigin, entmins, entmaxs;
114 	trace_t		trace;
115 	int			i;
116 	prvm_edict_t		*enemy;
117 
118 // try the move
119 	VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
120 	VectorAdd (PRVM_serveredictvector(ent, origin), move, neworg);
121 	VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
122 	VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
123 
124 // flying monsters don't step up
125 	if ( (int)PRVM_serveredictfloat(ent, flags) & (FL_SWIM | FL_FLY) )
126 	{
127 	// try one move with vertical motion, then one without
128 		for (i=0 ; i<2 ; i++)
129 		{
130 			VectorAdd (PRVM_serveredictvector(ent, origin), move, neworg);
131 			if (noenemy)
132 				enemy = prog->edicts;
133 			else
134 			{
135 				enemy = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, enemy));
136 				if (i == 0 && enemy != prog->edicts)
137 				{
138 					dz = PRVM_serveredictvector(ent, origin)[2] - PRVM_serveredictvector(enemy, origin)[2];
139 					if (dz > 40)
140 						neworg[2] -= 8;
141 					if (dz < 30)
142 						neworg[2] += 8;
143 				}
144 			}
145 			VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
146 			trace = SV_TraceBox(entorigin, entmins, entmaxs, neworg, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
147 
148 			if (trace.fraction == 1)
149 			{
150 				VectorCopy(trace.endpos, traceendpos);
151 				if (((int)PRVM_serveredictfloat(ent, flags) & FL_SWIM) && !(SV_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
152 					return false;	// swim monster left water
153 
154 				VectorCopy (traceendpos, PRVM_serveredictvector(ent, origin));
155 				if (relink)
156 				{
157 					SV_LinkEdict(ent);
158 					SV_LinkEdict_TouchAreaGrid(ent);
159 				}
160 				return true;
161 			}
162 
163 			if (enemy == prog->edicts)
164 				break;
165 		}
166 
167 		return false;
168 	}
169 
170 // push down from a step height above the wished position
171 	neworg[2] += sv_stepheight.value;
172 	VectorCopy (neworg, end);
173 	end[2] -= sv_stepheight.value*2;
174 
175 	trace = SV_TraceBox(neworg, entmins, entmaxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
176 
177 	if (trace.startsolid)
178 	{
179 		neworg[2] -= sv_stepheight.value;
180 		trace = SV_TraceBox(neworg, entmins, entmaxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
181 		if (trace.startsolid)
182 			return false;
183 	}
184 	if (trace.fraction == 1)
185 	{
186 	// if monster had the ground pulled out, go ahead and fall
187 		if ( (int)PRVM_serveredictfloat(ent, flags) & FL_PARTIALGROUND )
188 		{
189 			VectorAdd (PRVM_serveredictvector(ent, origin), move, PRVM_serveredictvector(ent, origin));
190 			if (relink)
191 			{
192 				SV_LinkEdict(ent);
193 				SV_LinkEdict_TouchAreaGrid(ent);
194 			}
195 			PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
196 			return true;
197 		}
198 
199 		return false;		// walked off an edge
200 	}
201 
202 // check point traces down for dangling corners
203 	VectorCopy (trace.endpos, PRVM_serveredictvector(ent, origin));
204 
205 	if (!SV_CheckBottom (ent))
206 	{
207 		if ( (int)PRVM_serveredictfloat(ent, flags) & FL_PARTIALGROUND )
208 		{	// entity had floor mostly pulled out from underneath it
209 			// and is trying to correct
210 			if (relink)
211 			{
212 				SV_LinkEdict(ent);
213 				SV_LinkEdict_TouchAreaGrid(ent);
214 			}
215 			return true;
216 		}
217 		VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
218 		return false;
219 	}
220 
221 	if ( (int)PRVM_serveredictfloat(ent, flags) & FL_PARTIALGROUND )
222 		PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_PARTIALGROUND;
223 
224 // gameplayfix: check if reached pretty steep plane and bail
225 	if ( ! ( (int)PRVM_serveredictfloat(ent, flags) & (FL_SWIM | FL_FLY) ) && sv_gameplayfix_nostepmoveonsteepslopes.integer )
226 	{
227 		if (trace.plane.normal[ 2 ] < 0.5)
228 		{
229 			VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
230 			return false;
231 		}
232 	}
233 
234 	PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
235 
236 // the move is ok
237 	if (relink)
238 	{
239 		SV_LinkEdict(ent);
240 		SV_LinkEdict_TouchAreaGrid(ent);
241 	}
242 	return true;
243 }
244 
245 
246 //============================================================================
247 
248 /*
249 ======================
250 SV_StepDirection
251 
252 Turns to the movement direction, and walks the current distance if
253 facing it.
254 
255 ======================
256 */
SV_StepDirection(prvm_edict_t * ent,float yaw,float dist)257 static qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
258 {
259 	prvm_prog_t *prog = SVVM_prog;
260 	vec3_t		move, oldorigin;
261 	float		delta;
262 
263 	PRVM_serveredictfloat(ent, ideal_yaw) = yaw;
264 	VM_changeyaw(prog);
265 
266 	yaw = yaw*M_PI*2 / 360;
267 	move[0] = cos(yaw)*dist;
268 	move[1] = sin(yaw)*dist;
269 	move[2] = 0;
270 
271 	VectorCopy (PRVM_serveredictvector(ent, origin), oldorigin);
272 	if (SV_movestep (ent, move, false, false, false))
273 	{
274 		delta = PRVM_serveredictvector(ent, angles)[YAW] - PRVM_serveredictfloat(ent, ideal_yaw);
275 		if (delta > 45 && delta < 315)
276 		{		// not turned far enough, so don't take the step
277 			VectorCopy (oldorigin, PRVM_serveredictvector(ent, origin));
278 		}
279 		SV_LinkEdict(ent);
280 		SV_LinkEdict_TouchAreaGrid(ent);
281 		return true;
282 	}
283 	SV_LinkEdict(ent);
284 	SV_LinkEdict_TouchAreaGrid(ent);
285 
286 	return false;
287 }
288 
289 /*
290 ======================
291 SV_FixCheckBottom
292 
293 ======================
294 */
SV_FixCheckBottom(prvm_edict_t * ent)295 static void SV_FixCheckBottom (prvm_edict_t *ent)
296 {
297 	prvm_prog_t *prog = SVVM_prog;
298 	PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_PARTIALGROUND;
299 }
300 
301 
302 
303 /*
304 ================
305 SV_NewChaseDir
306 
307 ================
308 */
309 #define	DI_NODIR	-1
SV_NewChaseDir(prvm_edict_t * actor,prvm_edict_t * enemy,float dist)310 static void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
311 {
312 	prvm_prog_t *prog = SVVM_prog;
313 	float		deltax,deltay;
314 	float			d[3];
315 	float		tdir, olddir, turnaround;
316 
317 	olddir = ANGLEMOD((int)(PRVM_serveredictfloat(actor, ideal_yaw)/45)*45);
318 	turnaround = ANGLEMOD(olddir - 180);
319 
320 	deltax = PRVM_serveredictvector(enemy, origin)[0] - PRVM_serveredictvector(actor, origin)[0];
321 	deltay = PRVM_serveredictvector(enemy, origin)[1] - PRVM_serveredictvector(actor, origin)[1];
322 	if (deltax>10)
323 		d[1]= 0;
324 	else if (deltax<-10)
325 		d[1]= 180;
326 	else
327 		d[1]= DI_NODIR;
328 	if (deltay<-10)
329 		d[2]= 270;
330 	else if (deltay>10)
331 		d[2]= 90;
332 	else
333 		d[2]= DI_NODIR;
334 
335 // try direct route
336 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
337 	{
338 		if (d[1] == 0)
339 			tdir = d[2] == 90 ? 45 : 315;
340 		else
341 			tdir = d[2] == 90 ? 135 : 215;
342 
343 		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
344 			return;
345 	}
346 
347 // try other directions
348 	if ( ((rand()&3) & 1) ||  fabs(deltay)>fabs(deltax))
349 	{
350 		tdir=d[1];
351 		d[1]=d[2];
352 		d[2]=tdir;
353 	}
354 
355 	if (d[1]!=DI_NODIR && d[1]!=turnaround
356 	&& SV_StepDirection(actor, d[1], dist))
357 			return;
358 
359 	if (d[2]!=DI_NODIR && d[2]!=turnaround
360 	&& SV_StepDirection(actor, d[2], dist))
361 			return;
362 
363 /* there is no direct path to the player, so pick another direction */
364 
365 	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
366 			return;
367 
368 	if (rand()&1) 	/*randomly determine direction of search*/
369 	{
370 		for (tdir=0 ; tdir<=315 ; tdir += 45)
371 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
372 					return;
373 	}
374 	else
375 	{
376 		for (tdir=315 ; tdir >=0 ; tdir -= 45)
377 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
378 					return;
379 	}
380 
381 	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
382 			return;
383 
384 	PRVM_serveredictfloat(actor, ideal_yaw) = olddir;		// can't move
385 
386 // if a bridge was pulled out from underneath a monster, it may not have
387 // a valid standing position at all
388 
389 	if (!SV_CheckBottom (actor))
390 		SV_FixCheckBottom (actor);
391 
392 }
393 
394 /*
395 ======================
396 SV_CloseEnough
397 
398 ======================
399 */
SV_CloseEnough(prvm_edict_t * ent,prvm_edict_t * goal,float dist)400 static qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
401 {
402 	int		i;
403 
404 	for (i=0 ; i<3 ; i++)
405 	{
406 		if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
407 			return false;
408 		if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
409 			return false;
410 	}
411 	return true;
412 }
413 
414 /*
415 ======================
416 SV_MoveToGoal
417 
418 ======================
419 */
VM_SV_MoveToGoal(prvm_prog_t * prog)420 void VM_SV_MoveToGoal(prvm_prog_t *prog)
421 {
422 	prvm_edict_t		*ent, *goal;
423 	float		dist;
424 
425 	VM_SAFEPARMCOUNT(1, SV_MoveToGoal);
426 
427 	ent = PRVM_PROG_TO_EDICT(PRVM_serverglobaledict(self));
428 	goal = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, goalentity));
429 	dist = PRVM_G_FLOAT(OFS_PARM0);
430 
431 	if ( !( (int)PRVM_serveredictfloat(ent, flags) & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
432 	{
433 		PRVM_G_FLOAT(OFS_RETURN) = 0;
434 		return;
435 	}
436 
437 // if the next step hits the enemy, return immediately
438 	if ( PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, enemy)) != prog->edicts &&  SV_CloseEnough (ent, goal, dist) )
439 		return;
440 
441 // bump around...
442 	if ( (rand()&3)==1 ||
443 	!SV_StepDirection (ent, PRVM_serveredictfloat(ent, ideal_yaw), dist))
444 	{
445 		SV_NewChaseDir (ent, goal, dist);
446 	}
447 }
448 
449