]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_move.c
WritePicture: also support png input images :P
[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         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
220
221 // the move is ok
222         if (relink)
223         {
224                 SV_LinkEdict(ent);
225                 SV_LinkEdict_TouchAreaGrid(ent);
226         }
227         return true;
228 }
229
230
231 //============================================================================
232
233 /*
234 ======================
235 SV_StepDirection
236
237 Turns to the movement direction, and walks the current distance if
238 facing it.
239
240 ======================
241 */
242 void VM_changeyaw (void);
243 qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
244 {
245         vec3_t          move, oldorigin;
246         float           delta;
247
248         ent->fields.server->ideal_yaw = yaw;
249         VM_changeyaw();
250
251         yaw = yaw*M_PI*2 / 360;
252         move[0] = cos(yaw)*dist;
253         move[1] = sin(yaw)*dist;
254         move[2] = 0;
255
256         VectorCopy (ent->fields.server->origin, oldorigin);
257         if (SV_movestep (ent, move, false, false, false))
258         {
259                 delta = ent->fields.server->angles[YAW] - ent->fields.server->ideal_yaw;
260                 if (delta > 45 && delta < 315)
261                 {               // not turned far enough, so don't take the step
262                         VectorCopy (oldorigin, ent->fields.server->origin);
263                 }
264                 SV_LinkEdict(ent);
265                 SV_LinkEdict_TouchAreaGrid(ent);
266                 return true;
267         }
268         SV_LinkEdict(ent);
269         SV_LinkEdict_TouchAreaGrid(ent);
270
271         return false;
272 }
273
274 /*
275 ======================
276 SV_FixCheckBottom
277
278 ======================
279 */
280 void SV_FixCheckBottom (prvm_edict_t *ent)
281 {
282         ent->fields.server->flags = (int)ent->fields.server->flags | FL_PARTIALGROUND;
283 }
284
285
286
287 /*
288 ================
289 SV_NewChaseDir
290
291 ================
292 */
293 #define DI_NODIR        -1
294 void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
295 {
296         float           deltax,deltay;
297         float                   d[3];
298         float           tdir, olddir, turnaround;
299
300         olddir = ANGLEMOD((int)(actor->fields.server->ideal_yaw/45)*45);
301         turnaround = ANGLEMOD(olddir - 180);
302
303         deltax = enemy->fields.server->origin[0] - actor->fields.server->origin[0];
304         deltay = enemy->fields.server->origin[1] - actor->fields.server->origin[1];
305         if (deltax>10)
306                 d[1]= 0;
307         else if (deltax<-10)
308                 d[1]= 180;
309         else
310                 d[1]= DI_NODIR;
311         if (deltay<-10)
312                 d[2]= 270;
313         else if (deltay>10)
314                 d[2]= 90;
315         else
316                 d[2]= DI_NODIR;
317
318 // try direct route
319         if (d[1] != DI_NODIR && d[2] != DI_NODIR)
320         {
321                 if (d[1] == 0)
322                         tdir = d[2] == 90 ? 45 : 315;
323                 else
324                         tdir = d[2] == 90 ? 135 : 215;
325
326                 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
327                         return;
328         }
329
330 // try other directions
331         if ( ((rand()&3) & 1) ||  fabs(deltay)>fabs(deltax))
332         {
333                 tdir=d[1];
334                 d[1]=d[2];
335                 d[2]=tdir;
336         }
337
338         if (d[1]!=DI_NODIR && d[1]!=turnaround
339         && SV_StepDirection(actor, d[1], dist))
340                         return;
341
342         if (d[2]!=DI_NODIR && d[2]!=turnaround
343         && SV_StepDirection(actor, d[2], dist))
344                         return;
345
346 /* there is no direct path to the player, so pick another direction */
347
348         if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
349                         return;
350
351         if (rand()&1)   /*randomly determine direction of search*/
352         {
353                 for (tdir=0 ; tdir<=315 ; tdir += 45)
354                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
355                                         return;
356         }
357         else
358         {
359                 for (tdir=315 ; tdir >=0 ; tdir -= 45)
360                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
361                                         return;
362         }
363
364         if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
365                         return;
366
367         actor->fields.server->ideal_yaw = olddir;               // can't move
368
369 // if a bridge was pulled out from underneath a monster, it may not have
370 // a valid standing position at all
371
372         if (!SV_CheckBottom (actor))
373                 SV_FixCheckBottom (actor);
374
375 }
376
377 /*
378 ======================
379 SV_CloseEnough
380
381 ======================
382 */
383 qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
384 {
385         int             i;
386
387         for (i=0 ; i<3 ; i++)
388         {
389                 if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
390                         return false;
391                 if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
392                         return false;
393         }
394         return true;
395 }
396
397 /*
398 ======================
399 SV_MoveToGoal
400
401 ======================
402 */
403 void SV_MoveToGoal (void)
404 {
405         prvm_edict_t            *ent, *goal;
406         float           dist;
407
408         VM_SAFEPARMCOUNT(1, SV_MoveToGoal);
409
410         ent = PRVM_PROG_TO_EDICT(prog->globals.server->self);
411         goal = PRVM_PROG_TO_EDICT(ent->fields.server->goalentity);
412         dist = PRVM_G_FLOAT(OFS_PARM0);
413
414         if ( !( (int)ent->fields.server->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
415         {
416                 PRVM_G_FLOAT(OFS_RETURN) = 0;
417                 return;
418         }
419
420 // if the next step hits the enemy, return immediately
421         if ( PRVM_PROG_TO_EDICT(ent->fields.server->enemy) != prog->edicts &&  SV_CloseEnough (ent, goal, dist) )
422                 return;
423
424 // bump around...
425         if ( (rand()&3)==1 ||
426         !SV_StepDirection (ent, ent->fields.server->ideal_yaw, dist))
427         {
428                 SV_NewChaseDir (ent, goal, dist);
429         }
430 }
431