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