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