1 /*
2 Copyright (C) 1997-2001 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 // m_move.c -- monster movement
21
22 #include "g_local.h"
23
24 #define STEPSIZE 18
25
26 /*
27 =============
28 M_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
M_CheckBottom(edict_t * ent)37 qboolean M_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->s.origin, ent->mins, mins);
45 VectorAdd (ent->s.origin, ent->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 (gi.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 = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
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 = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
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 //FIXME since we need to test end position contents here, can we avoid doing
111 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)112 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
113 {
114 float dz;
115 vec3_t oldorg, neworg, end;
116 trace_t trace;
117 int i;
118 float stepsize;
119 vec3_t test;
120 int contents;
121
122 // try the move
123 VectorCopy (ent->s.origin, oldorg);
124 VectorAdd (ent->s.origin, move, neworg);
125
126 // flying monsters don't step up
127 if ( ent->flags & (FL_SWIM | FL_FLY) )
128 {
129 // try one move with vertical motion, then one without
130 for (i=0 ; i<2 ; i++)
131 {
132 VectorAdd (ent->s.origin, move, neworg);
133 if (i == 0 && ent->enemy)
134 {
135 if (!ent->goalentity)
136 ent->goalentity = ent->enemy;
137 dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
138 if (ent->goalentity->client)
139 {
140 if (dz > 40)
141 neworg[2] -= 8;
142 if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
143 if (dz < 30)
144 neworg[2] += 8;
145 }
146 else
147 {
148 if (dz > 8)
149 neworg[2] -= 8;
150 else if (dz > 0)
151 neworg[2] -= dz;
152 else if (dz < -8)
153 neworg[2] += 8;
154 else
155 neworg[2] += dz;
156 }
157 }
158 trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
159
160 // fly monsters don't enter water voluntarily
161 if (ent->flags & FL_FLY)
162 {
163 if (!ent->waterlevel)
164 {
165 test[0] = trace.endpos[0];
166 test[1] = trace.endpos[1];
167 test[2] = trace.endpos[2] + ent->mins[2] + 1;
168 contents = gi.pointcontents(test);
169 if (contents & MASK_WATER)
170 return false;
171 }
172 }
173
174 // swim monsters don't exit water voluntarily
175 if (ent->flags & FL_SWIM)
176 {
177 if (ent->waterlevel < 2)
178 {
179 test[0] = trace.endpos[0];
180 test[1] = trace.endpos[1];
181 test[2] = trace.endpos[2] + ent->mins[2] + 1;
182 contents = gi.pointcontents(test);
183 if (!(contents & MASK_WATER))
184 return false;
185 }
186 }
187
188 if (trace.fraction == 1)
189 {
190 VectorCopy (trace.endpos, ent->s.origin);
191 if (relink)
192 {
193 gi.linkentity (ent);
194 G_TouchTriggers (ent);
195 }
196 return true;
197 }
198
199 if (!ent->enemy)
200 break;
201 }
202
203 return false;
204 }
205
206 // push down from a step height above the wished position
207 if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
208 stepsize = STEPSIZE;
209 else
210 stepsize = 1;
211
212 neworg[2] += stepsize;
213 VectorCopy (neworg, end);
214 end[2] -= stepsize*2;
215
216 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
217
218 if (trace.allsolid)
219 return false;
220
221 if (trace.startsolid)
222 {
223 neworg[2] -= stepsize;
224 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
225 if (trace.allsolid || trace.startsolid)
226 return false;
227 }
228
229
230 // don't go in to water
231 if (ent->waterlevel == 0)
232 {
233 test[0] = trace.endpos[0];
234 test[1] = trace.endpos[1];
235 test[2] = trace.endpos[2] + ent->mins[2] + 1;
236 contents = gi.pointcontents(test);
237
238 if (contents & MASK_WATER)
239 return false;
240 }
241
242 if (trace.fraction == 1)
243 {
244 // if monster had the ground pulled out, go ahead and fall
245 if ( ent->flags & FL_PARTIALGROUND )
246 {
247 VectorAdd (ent->s.origin, move, ent->s.origin);
248 if (relink)
249 {
250 gi.linkentity (ent);
251 G_TouchTriggers (ent);
252 }
253 ent->groundentity = NULL;
254 return true;
255 }
256
257 return false; // walked off an edge
258 }
259
260 // check point traces down for dangling corners
261 VectorCopy (trace.endpos, ent->s.origin);
262
263 if (!M_CheckBottom (ent))
264 {
265 if ( ent->flags & FL_PARTIALGROUND )
266 { // entity had floor mostly pulled out from underneath it
267 // and is trying to correct
268 if (relink)
269 {
270 gi.linkentity (ent);
271 G_TouchTriggers (ent);
272 }
273 return true;
274 }
275 VectorCopy (oldorg, ent->s.origin);
276 return false;
277 }
278
279 if ( ent->flags & FL_PARTIALGROUND )
280 {
281 ent->flags &= ~FL_PARTIALGROUND;
282 }
283 ent->groundentity = trace.ent;
284 ent->groundentity_linkcount = trace.ent->linkcount;
285
286 // the move is ok
287 if (relink)
288 {
289 gi.linkentity (ent);
290 G_TouchTriggers (ent);
291 }
292 return true;
293 }
294
295
296 //============================================================================
297
298 /*
299 ===============
300 M_ChangeYaw
301
302 ===============
303 */
M_ChangeYaw(edict_t * ent)304 void M_ChangeYaw (edict_t *ent)
305 {
306 float ideal;
307 float current;
308 float move;
309 float speed;
310
311 current = anglemod(ent->s.angles[YAW]);
312 ideal = ent->ideal_yaw;
313
314 if (current == ideal)
315 return;
316
317 move = ideal - current;
318 speed = ent->yaw_speed;
319 if (ideal > current)
320 {
321 if (move >= 180)
322 move = move - 360;
323 }
324 else
325 {
326 if (move <= -180)
327 move = move + 360;
328 }
329 if (move > 0)
330 {
331 if (move > speed)
332 move = speed;
333 }
334 else
335 {
336 if (move < -speed)
337 move = -speed;
338 }
339
340 ent->s.angles[YAW] = anglemod (current + move);
341 }
342
343
344 /*
345 ======================
346 SV_StepDirection
347
348 Turns to the movement direction, and walks the current distance if
349 facing it.
350
351 ======================
352 */
SV_StepDirection(edict_t * ent,float yaw,float dist)353 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
354 {
355 vec3_t move, oldorigin;
356 float delta;
357
358 ent->ideal_yaw = yaw;
359 M_ChangeYaw (ent);
360
361 yaw = yaw*M_PI*2 / 360;
362 move[0] = cos(yaw)*dist;
363 move[1] = sin(yaw)*dist;
364 move[2] = 0;
365
366 VectorCopy (ent->s.origin, oldorigin);
367 if (SV_movestep (ent, move, false))
368 {
369 delta = ent->s.angles[YAW] - ent->ideal_yaw;
370 if (delta > 45 && delta < 315)
371 { // not turned far enough, so don't take the step
372 VectorCopy (oldorigin, ent->s.origin);
373 }
374 gi.linkentity (ent);
375 G_TouchTriggers (ent);
376 return true;
377 }
378 gi.linkentity (ent);
379 G_TouchTriggers (ent);
380 return false;
381 }
382
383 /*
384 ======================
385 SV_FixCheckBottom
386
387 ======================
388 */
SV_FixCheckBottom(edict_t * ent)389 void SV_FixCheckBottom (edict_t *ent)
390 {
391 ent->flags |= FL_PARTIALGROUND;
392 }
393
394
395
396 /*
397 ================
398 SV_NewChaseDir
399
400 ================
401 */
402 #define DI_NODIR -1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)403 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
404 {
405 float deltax,deltay;
406 float d[3];
407 float tdir, olddir, turnaround;
408
409 //FIXME: how did we get here with no enemy
410 if (!enemy)
411 return;
412
413 olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
414 turnaround = anglemod(olddir - 180);
415
416 deltax = enemy->s.origin[0] - actor->s.origin[0];
417 deltay = enemy->s.origin[1] - actor->s.origin[1];
418 if (deltax>10)
419 d[1]= 0;
420 else if (deltax<-10)
421 d[1]= 180;
422 else
423 d[1]= DI_NODIR;
424 if (deltay<-10)
425 d[2]= 270;
426 else if (deltay>10)
427 d[2]= 90;
428 else
429 d[2]= DI_NODIR;
430
431 // try direct route
432 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
433 {
434 if (d[1] == 0)
435 tdir = d[2] == 90 ? 45 : 315;
436 else
437 tdir = d[2] == 90 ? 135 : 215;
438
439 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
440 return;
441 }
442
443 // try other directions
444 if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax))
445 {
446 tdir=d[1];
447 d[1]=d[2];
448 d[2]=tdir;
449 }
450
451 if (d[1]!=DI_NODIR && d[1]!=turnaround
452 && SV_StepDirection(actor, d[1], dist))
453 return;
454
455 if (d[2]!=DI_NODIR && d[2]!=turnaround
456 && SV_StepDirection(actor, d[2], dist))
457 return;
458
459 /* there is no direct path to the player, so pick another direction */
460
461 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
462 return;
463
464 if (rand()&1) /*randomly determine direction of search*/
465 {
466 for (tdir=0 ; tdir<=315 ; tdir += 45)
467 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
468 return;
469 }
470 else
471 {
472 for (tdir=315 ; tdir >=0 ; tdir -= 45)
473 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
474 return;
475 }
476
477 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
478 return;
479
480 actor->ideal_yaw = olddir; // can't move
481
482 // if a bridge was pulled out from underneath a monster, it may not have
483 // a valid standing position at all
484
485 if (!M_CheckBottom (actor))
486 SV_FixCheckBottom (actor);
487 }
488
489 /*
490 ======================
491 SV_CloseEnough
492
493 ======================
494 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)495 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
496 {
497 int i;
498
499 for (i=0 ; i<3 ; i++)
500 {
501 if (goal->absmin[i] > ent->absmax[i] + dist)
502 return false;
503 if (goal->absmax[i] < ent->absmin[i] - dist)
504 return false;
505 }
506 return true;
507 }
508
509
510 /*
511 ======================
512 M_MoveToGoal
513 ======================
514 */
M_MoveToGoal(edict_t * ent,float dist)515 void M_MoveToGoal (edict_t *ent, float dist)
516 {
517 edict_t *goal;
518
519 goal = ent->goalentity;
520
521 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
522 return;
523
524 // if the next step hits the enemy, return immediately
525 if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) )
526 return;
527
528 // bump around...
529 if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
530 {
531 if (ent->inuse)
532 SV_NewChaseDir (ent, goal, dist);
533 }
534 }
535
536
537 /*
538 ===============
539 M_walkmove
540 ===============
541 */
M_walkmove(edict_t * ent,float yaw,float dist)542 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
543 {
544 vec3_t move;
545
546 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
547 return false;
548
549 yaw = yaw*M_PI*2 / 360;
550
551 move[0] = cos(yaw)*dist;
552 move[1] = sin(yaw)*dist;
553 move[2] = 0;
554
555 return SV_movestep(ent, move, true);
556 }
557