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