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