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