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