]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_move.c
cmd: Use reentrant mutex for cbufs. Fixes deadlock when expanding aliases in some...
[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 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 */
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 */
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 */
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
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 */
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 VM_SV_MoveToGoal
417
418 ======================
419 */
420 void VM_SV_MoveToGoal(prvm_prog_t *prog)
421 {
422         prvm_edict_t            *ent, *goal;
423         float           dist;
424
425         VM_SAFEPARMCOUNT(1, VM_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