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