added CVAR_SAVE and CVAR_NOTIFY flags to cvar_t structure (at the beginning), updated...
[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 #define STEPSIZE        18
25
26 /*
27 =============
28 SV_CheckBottom
29
30 Returns false if any part of the bottom of the entity is off an edge that
31 is not a staircase.
32
33 =============
34 */
35 int c_yes, c_no;
36
37 qboolean SV_CheckBottom (edict_t *ent)
38 {
39         vec3_t  mins, maxs, start, stop;
40         trace_t trace;
41         int             x, y;
42         float   mid, bottom;
43         
44         VectorAdd (ent->v.origin, ent->v.mins, mins);
45         VectorAdd (ent->v.origin, ent->v.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_PointContents (start) != CONTENTS_SOLID)
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*STEPSIZE;
74         trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, 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_Move (start, vec3_origin, vec3_origin, stop, true, 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] > STEPSIZE)
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, false is returned, and
107 pr_global_struct->trace_normal is set to the normal of the blocking wall
108 =============
109 */
110 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
111 {
112         float           dz;
113         vec3_t          oldorg, neworg, end;
114         trace_t         trace;
115         int                     i;
116         edict_t         *enemy;
117
118 // try the move 
119         VectorCopy (ent->v.origin, oldorg);
120         VectorAdd (ent->v.origin, move, neworg);
121
122 // flying monsters don't step up
123         if ( (int)ent->v.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 (ent->v.origin, move, neworg);
129                         enemy = PROG_TO_EDICT(ent->v.enemy);
130                         if (i == 0 && enemy != sv.edicts)
131                         {
132                                 dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
133                                 if (dz > 40)
134                                         neworg[2] -= 8;
135                                 if (dz < 30)
136                                         neworg[2] += 8;
137                         }
138                         trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent);
139         
140                         if (trace.fraction == 1)
141                         {
142                                 if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY )
143                                         return false;   // swim monster left water
144         
145                                 VectorCopy (trace.endpos, ent->v.origin);
146                                 if (relink)
147                                         SV_LinkEdict (ent, true);
148                                 return true;
149                         }
150                         
151                         if (enemy == sv.edicts)
152                                 break;
153                 }
154                 
155                 return false;
156         }
157
158 // push down from a step height above the wished position
159         neworg[2] += STEPSIZE;
160         VectorCopy (neworg, end);
161         end[2] -= STEPSIZE*2;
162
163         trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
164
165         if (trace.allsolid)
166                 return false;
167
168         if (trace.startsolid)
169         {
170                 neworg[2] -= STEPSIZE;
171                 trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
172                 if (trace.allsolid || trace.startsolid)
173                         return false;
174         }
175         if (trace.fraction == 1)
176         {
177         // if monster had the ground pulled out, go ahead and fall
178                 if ( (int)ent->v.flags & FL_PARTIALGROUND )
179                 {
180                         VectorAdd (ent->v.origin, move, ent->v.origin);
181                         if (relink)
182                                 SV_LinkEdict (ent, true);
183                         ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
184 //      Con_Printf ("fall down\n"); 
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->v.origin);
193         
194         if (!SV_CheckBottom (ent))
195         {
196                 if ( (int)ent->v.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->v.origin);
204                 return false;
205         }
206
207         if ( (int)ent->v.flags & FL_PARTIALGROUND )
208         {
209 //              Con_Printf ("back on ground\n"); 
210                 ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND;
211         }
212         ent->v.groundentity = EDICT_TO_PROG(trace.ent);
213
214 // the move is ok
215         if (relink)
216                 SV_LinkEdict (ent, true);
217         return true;
218 }
219
220
221 //============================================================================
222
223 /*
224 ======================
225 SV_StepDirection
226
227 Turns to the movement direction, and walks the current distance if
228 facing it.
229
230 ======================
231 */
232 void PF_changeyaw (void);
233 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
234 {
235         vec3_t          move, oldorigin;
236         float           delta;
237         
238         ent->v.ideal_yaw = yaw;
239         PF_changeyaw();
240         
241         yaw = yaw*M_PI*2 / 360;
242         move[0] = cos(yaw)*dist;
243         move[1] = sin(yaw)*dist;
244         move[2] = 0;
245
246         VectorCopy (ent->v.origin, oldorigin);
247         if (SV_movestep (ent, move, false))
248         {
249                 delta = ent->v.angles[YAW] - ent->v.ideal_yaw;
250                 if (delta > 45 && delta < 315)
251                 {               // not turned far enough, so don't take the step
252                         VectorCopy (oldorigin, ent->v.origin);
253                 }
254                 SV_LinkEdict (ent, true);
255                 return true;
256         }
257         SV_LinkEdict (ent, true);
258                 
259         return false;
260 }
261
262 /*
263 ======================
264 SV_FixCheckBottom
265
266 ======================
267 */
268 void SV_FixCheckBottom (edict_t *ent)
269 {
270 //      Con_Printf ("SV_FixCheckBottom\n");
271         
272         ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND;
273 }
274
275
276
277 /*
278 ================
279 SV_NewChaseDir
280
281 ================
282 */
283 #define DI_NODIR        -1
284 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
285 {
286         float           deltax,deltay;
287         float                   d[3];
288         float           tdir, olddir, turnaround;
289
290         olddir = ANGLEMOD((int)(actor->v.ideal_yaw/45)*45);
291         turnaround = ANGLEMOD(olddir - 180);
292
293         deltax = enemy->v.origin[0] - actor->v.origin[0];
294         deltay = enemy->v.origin[1] - actor->v.origin[1];
295         if (deltax>10)
296                 d[1]= 0;
297         else if (deltax<-10)
298                 d[1]= 180;
299         else
300                 d[1]= DI_NODIR;
301         if (deltay<-10)
302                 d[2]= 270;
303         else if (deltay>10)
304                 d[2]= 90;
305         else
306                 d[2]= DI_NODIR;
307
308 // try direct route
309         if (d[1] != DI_NODIR && d[2] != DI_NODIR)
310         {
311                 if (d[1] == 0)
312                         tdir = d[2] == 90 ? 45 : 315;
313                 else
314                         tdir = d[2] == 90 ? 135 : 215;
315                         
316                 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
317                         return;
318         }
319
320 // try other directions
321         if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
322         {
323                 tdir=d[1];
324                 d[1]=d[2];
325                 d[2]=tdir;
326         }
327
328         if (d[1]!=DI_NODIR && d[1]!=turnaround 
329         && SV_StepDirection(actor, d[1], dist))
330                         return;
331
332         if (d[2]!=DI_NODIR && d[2]!=turnaround
333         && SV_StepDirection(actor, d[2], dist))
334                         return;
335
336 /* there is no direct path to the player, so pick another direction */
337
338         if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
339                         return;
340
341         if (rand()&1)   /*randomly determine direction of search*/
342         {
343                 for (tdir=0 ; tdir<=315 ; tdir += 45)
344                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
345                                         return;
346         }
347         else
348         {
349                 for (tdir=315 ; tdir >=0 ; tdir -= 45)
350                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
351                                         return;
352         }
353
354         if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
355                         return;
356
357         actor->v.ideal_yaw = olddir;            // can't move
358
359 // if a bridge was pulled out from underneath a monster, it may not have
360 // a valid standing position at all
361
362         if (!SV_CheckBottom (actor))
363                 SV_FixCheckBottom (actor);
364
365 }
366
367 /*
368 ======================
369 SV_CloseEnough
370
371 ======================
372 */
373 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
374 {
375         int             i;
376         
377         for (i=0 ; i<3 ; i++)
378         {
379                 if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
380                         return false;
381                 if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
382                         return false;
383         }
384         return true;
385 }
386
387 /*
388 ======================
389 SV_MoveToGoal
390
391 ======================
392 */
393 void SV_MoveToGoal (void)
394 {
395         edict_t         *ent, *goal;
396         float           dist;
397
398         ent = PROG_TO_EDICT(pr_global_struct->self);
399         goal = PROG_TO_EDICT(ent->v.goalentity);
400         dist = G_FLOAT(OFS_PARM0);
401
402         if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
403         {
404                 G_FLOAT(OFS_RETURN) = 0;
405                 return;
406         }
407
408 // if the next step hits the enemy, return immediately
409         if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts &&  SV_CloseEnough (ent, goal, dist) )
410                 return;
411
412 // bump around...
413         if ( (rand()&3)==1 ||
414         !SV_StepDirection (ent, ent->v.ideal_yaw, dist))
415         {
416                 SV_NewChaseDir (ent, goal, dist);
417         }
418 }
419