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