1 // m_move.c -- monster movement
2
3 #include "g_local.h"
4
5 #define STEPSIZE 18
6
7 /*
8 =============
9 M_CheckBottom
10
11 Returns false if any part of the bottom of the entity is off an edge that
12 is not a staircase.
13
14 =============
15 */
16 int c_yes, c_no;
17
M_CheckBottom(edict_t * ent)18 qboolean M_CheckBottom (edict_t *ent)
19 {
20 vec3_t mins, maxs, start, stop;
21 trace_t trace;
22 int x, y;
23 float mid, bottom;
24
25 VectorAdd (ent->s.origin, ent->mins, mins);
26 VectorAdd (ent->s.origin, ent->maxs, maxs);
27
28 // if all of the points under the corners are solid world, don't bother
29 // with the tougher checks
30 // the corners must be within 16 of the midpoint
31 start[2] = mins[2] - 1;
32 for (x=0 ; x<=1 ; x++)
33 for (y=0 ; y<=1 ; y++)
34 {
35 start[0] = x ? maxs[0] : mins[0];
36 start[1] = y ? maxs[1] : mins[1];
37 if (gi.pointcontents (start) != CONTENTS_SOLID)
38 goto realcheck;
39 }
40
41 c_yes++;
42 return true; // we got out easy
43
44 realcheck:
45 c_no++;
46 //
47 // check it for real...
48 //
49 start[2] = mins[2];
50
51 // the midpoint must be within 16 of the bottom
52 start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
53 start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
54 stop[2] = start[2] - 2*STEPSIZE;
55 trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
56
57 if (trace.fraction == 1.0)
58 return false;
59 mid = bottom = trace.endpos[2];
60
61 // the corners must be within 16 of the midpoint
62 for (x=0 ; x<=1 ; x++)
63 for (y=0 ; y<=1 ; y++)
64 {
65 start[0] = stop[0] = x ? maxs[0] : mins[0];
66 start[1] = stop[1] = y ? maxs[1] : mins[1];
67
68 trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
69
70 if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
71 bottom = trace.endpos[2];
72 if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
73 return false;
74 }
75
76 c_yes++;
77 return true;
78 }
79
80
81 /*
82 =============
83 SV_movestep
84
85 Called by monster program code.
86 The move will be adjusted for slopes and stairs, but if the move isn't
87 possible, no move is done, false is returned, and
88 pr_global_struct->trace_normal is set to the normal of the blocking wall
89 =============
90 */
91 //FIXME since we need to test end position contents here, can we avoid doing
92 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)93 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
94 {
95 float dz;
96 vec3_t oldorg, neworg, end;
97 trace_t trace;
98 int i;
99 float stepsize;
100 vec3_t test;
101 int contents;
102
103 // try the move
104 VectorCopy (ent->s.origin, oldorg);
105 VectorAdd (ent->s.origin, move, neworg);
106
107 // flying monsters don't step up
108 if ( ent->flags & (FL_SWIM | FL_FLY) )
109 {
110 // try one move with vertical motion, then one without
111 for (i=0 ; i<2 ; i++)
112 {
113 VectorAdd (ent->s.origin, move, neworg);
114 if (i == 0 && ent->enemy)
115 {
116 if (!ent->goalentity)
117 ent->goalentity = ent->enemy;
118 dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
119 if (ent->goalentity->client)
120 {
121 if (dz > 40)
122 neworg[2] -= 8;
123 if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
124 if (dz < 30)
125 neworg[2] += 8;
126 }
127 else
128 {
129 if (dz > 8)
130 neworg[2] -= 8;
131 else if (dz > 0)
132 neworg[2] -= dz;
133 else if (dz < -8)
134 neworg[2] += 8;
135 else
136 neworg[2] += dz;
137 }
138 }
139 trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
140
141 // fly monsters don't enter water voluntarily
142 if (ent->flags & FL_FLY)
143 {
144 if (!ent->waterlevel)
145 {
146 test[0] = trace.endpos[0];
147 test[1] = trace.endpos[1];
148 test[2] = trace.endpos[2] + ent->mins[2] + 1;
149 contents = gi.pointcontents(test);
150 if (contents & MASK_WATER)
151 return false;
152 }
153 }
154
155 // swim monsters don't exit water voluntarily
156 if (ent->flags & FL_SWIM)
157 {
158 if (ent->waterlevel < 2)
159 {
160 test[0] = trace.endpos[0];
161 test[1] = trace.endpos[1];
162 test[2] = trace.endpos[2] + ent->mins[2] + 1;
163 contents = gi.pointcontents(test);
164 if (!(contents & MASK_WATER))
165 return false;
166 }
167 }
168
169 if (trace.fraction == 1)
170 {
171 VectorCopy (trace.endpos, ent->s.origin);
172 if (relink)
173 {
174 gi.linkentity (ent);
175 G_TouchTriggers (ent);
176 }
177 return true;
178 }
179
180 if (!ent->enemy)
181 break;
182 }
183
184 return false;
185 }
186
187 // push down from a step height above the wished position
188 if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
189 stepsize = STEPSIZE;
190 else
191 stepsize = 1;
192
193 neworg[2] += stepsize;
194 VectorCopy (neworg, end);
195 end[2] -= stepsize*2;
196
197 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
198
199 if (trace.allsolid)
200 return false;
201
202 if (trace.startsolid)
203 {
204 neworg[2] -= stepsize;
205 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
206 if (trace.allsolid || trace.startsolid)
207 return false;
208 }
209
210
211 // don't go in to water
212 if (ent->waterlevel == 0)
213 {
214 test[0] = trace.endpos[0];
215 test[1] = trace.endpos[1];
216 test[2] = trace.endpos[2] + ent->mins[2] + 1;
217 contents = gi.pointcontents(test);
218
219 if (contents & MASK_WATER && ent->movetype != MOVETYPE_FALLFLOAT)
220 return false;
221 }
222
223 if (trace.fraction == 1)
224 {
225 // if monster had the ground pulled out, go ahead and fall
226 if ( ent->flags & FL_PARTIALGROUND )
227 {
228 VectorAdd (ent->s.origin, move, ent->s.origin);
229 if (relink)
230 {
231 gi.linkentity (ent);
232 G_TouchTriggers (ent);
233 }
234 ent->groundentity = NULL;
235 return true;
236 }
237 else if (ent->movetype == MOVETYPE_FALLFLOAT)
238 {
239 // can fall over the edge
240 VectorAdd (ent->s.origin, move, ent->s.origin);
241 if (relink)
242 {
243 gi.linkentity (ent);
244 G_TouchTriggers (ent);
245 }
246 ent->groundentity = NULL;
247 return true;
248 }
249
250 return false; // walked off an edge
251 }
252
253 // check point traces down for dangling corners
254 VectorCopy (trace.endpos, ent->s.origin);
255
256 if (!M_CheckBottom (ent))
257 {
258 if ( ent->flags & FL_PARTIALGROUND )
259 { // entity had floor mostly pulled out from underneath it
260 // and is trying to correct
261 if (relink)
262 {
263 gi.linkentity (ent);
264 G_TouchTriggers (ent);
265 }
266 return true;
267 }
268 else if (ent->movetype == MOVETYPE_FALLFLOAT)
269 {
270 // can fall over the edge
271 if (relink)
272 {
273 gi.linkentity (ent);
274 G_TouchTriggers (ent);
275 }
276 return true;
277 }
278 VectorCopy (oldorg, ent->s.origin);
279 return false;
280 }
281
282 if ( ent->flags & FL_PARTIALGROUND )
283 {
284 ent->flags &= ~FL_PARTIALGROUND;
285 }
286 ent->groundentity = trace.ent;
287 ent->groundentity_linkcount = trace.ent->linkcount;
288
289 // the move is ok
290 if (relink)
291 {
292 gi.linkentity (ent);
293 G_TouchTriggers (ent);
294 }
295 return true;
296 }
297
298
299 //============================================================================
300
301 /*
302 ===============
303 M_ChangeYaw
304
305 ===============
306 */
M_ChangeYaw(edict_t * ent)307 void M_ChangeYaw (edict_t *ent)
308 {
309 float ideal;
310 float current;
311 float move;
312 float speed;
313
314 current = anglemod(ent->s.angles[YAW]);
315 ideal = ent->ideal_yaw;
316
317 if (current == ideal)
318 return;
319
320 move = ideal - current;
321 speed = ent->yaw_speed;
322 if (ideal > current)
323 {
324 while (move >= 180)
325 move = move - 360;
326 }
327 else
328 {
329 while (move <= -180)
330 move = move + 360;
331 }
332 if (move > 0)
333 {
334 if (move > speed)
335 move = speed;
336 }
337 else
338 {
339 if (move < -speed)
340 move = -speed;
341 }
342
343 ent->s.angles[YAW] = anglemod (current + move);
344 }
345
346
347 /*
348 ======================
349 SV_StepDirection
350
351 Turns to the movement direction, and walks the current distance if
352 facing it.
353
354 ======================
355 */
SV_StepDirection(edict_t * ent,float yaw,float dist)356 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
357 {
358 vec3_t move, oldorigin;
359 float delta;
360
361 ent->ideal_yaw = yaw;
362 M_ChangeYaw (ent);
363
364 yaw = yaw*M_PI*2 / 360;
365 move[0] = cos(yaw)*dist;
366 move[1] = sin(yaw)*dist;
367 move[2] = 0;
368
369 VectorCopy (ent->s.origin, oldorigin);
370 if (SV_movestep (ent, move, false))
371 {
372 delta = ent->s.angles[YAW] - ent->ideal_yaw;
373 if (delta > 45 && delta < 315)
374 { // not turned far enough, so don't take the step
375 VectorCopy (oldorigin, ent->s.origin);
376 }
377 gi.linkentity (ent);
378 G_TouchTriggers (ent);
379 return true;
380 }
381 gi.linkentity (ent);
382 G_TouchTriggers (ent);
383 return false;
384 }
385
386 /*
387 ======================
388 SV_FixCheckBottom
389
390 ======================
391 */
SV_FixCheckBottom(edict_t * ent)392 void SV_FixCheckBottom (edict_t *ent)
393 {
394 ent->flags |= FL_PARTIALGROUND;
395 }
396
397
398
399 /*
400 ================
401 SV_NewChaseDir
402
403 ================
404 */
405 #define DI_NODIR -1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)406 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
407 {
408 float deltax,deltay;
409 float d[3];
410 float tdir, olddir, turnaround;
411
412 // how did we get here without an enemy?
413 if (!enemy)
414 return;
415
416 olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
417 turnaround = anglemod(olddir - 180);
418
419 deltax = enemy->s.origin[0] - actor->s.origin[0];
420 deltay = enemy->s.origin[1] - actor->s.origin[1];
421 if (deltax>10)
422 d[1]= 0;
423 else if (deltax<-10)
424 d[1]= 180;
425 else
426 d[1]= DI_NODIR;
427 if (deltay<-10)
428 d[2]= 270;
429 else if (deltay>10)
430 d[2]= 90;
431 else
432 d[2]= DI_NODIR;
433
434 // try direct route
435 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
436 {
437 if (d[1] == 0)
438 tdir = d[2] == 90 ? 45 : 315;
439 else
440 tdir = d[2] == 90 ? 135 : 215;
441
442 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
443 return;
444 }
445
446 // try other directions
447 if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax))
448 {
449 tdir=d[1];
450 d[1]=d[2];
451 d[2]=tdir;
452 }
453
454 if (d[1]!=DI_NODIR && d[1]!=turnaround
455 && SV_StepDirection(actor, d[1], dist))
456 return;
457
458 if (d[2]!=DI_NODIR && d[2]!=turnaround
459 && SV_StepDirection(actor, d[2], dist))
460 return;
461
462 /* there is no direct path to the player, so pick another direction */
463
464 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
465 return;
466
467 if (rand()&1) /*randomly determine direction of search*/
468 {
469 for (tdir=0 ; tdir<=315 ; tdir += 45)
470 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
471 return;
472 }
473 else
474 {
475 for (tdir=315 ; tdir >=0 ; tdir -= 45)
476 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
477 return;
478 }
479
480 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
481 return;
482
483 actor->ideal_yaw = olddir; // can't move
484
485 // if a bridge was pulled out from underneath a monster, it may not have
486 // a valid standing position at all
487
488 if (!M_CheckBottom (actor))
489 SV_FixCheckBottom (actor);
490 }
491
492 /*
493 ======================
494 SV_CloseEnough
495
496 ======================
497 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)498 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
499 {
500 int i;
501
502 for (i=0 ; i<3 ; i++)
503 {
504 if (goal->absmin[i] > ent->absmax[i] + dist)
505 return false;
506 if (goal->absmax[i] < ent->absmin[i] - dist)
507 return false;
508 }
509 return true;
510 }
511
512 /*
513 ======================
514 M_MoveToGoal
515 ======================
516 */
M_MoveToGoal(edict_t * ent,float dist)517 void M_MoveToGoal (edict_t *ent, float dist)
518 {
519 edict_t *goal;
520
521 goal = ent->goalentity;
522
523 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
524 return;
525
526 // if the next step hits the enemy, return immediately
527 if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) )
528 return;
529
530 // bump around...
531 if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
532 {
533 if (ent->inuse)
534 SV_NewChaseDir (ent, goal, dist);
535 }
536 }
537
538
539 /*
540 ===============
541 M_walkmove
542 ===============
543 */
M_walkmove(edict_t * ent,float yaw,float dist)544 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
545 {
546 vec3_t move;
547
548 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
549 return false;
550
551 yaw = yaw*M_PI*2 / 360;
552
553 move[0] = cos(yaw)*dist;
554 move[1] = sin(yaw)*dist;
555 move[2] = 0;
556
557 return SV_movestep(ent, move, true);
558 }
559
560 qboolean ai_checkattack (edict_t *self, float dist);
561 /*
562 ====================
563 M_MoveAwayFromFlare
564 ====================
565 */
M_MoveAwayFromFlare(edict_t * self,float dist)566 qboolean M_MoveAwayFromFlare(edict_t *self, float dist)
567 {
568 edict_t *e = NULL;
569 edict_t *goal = NULL;
570 vec3_t delta;
571 vec3_t forward;
572
573 // find the closest flare
574 while(1)
575 {
576 e = findradius(e, self->s.origin, 256);
577 if (e == NULL)
578
579
580
581 break;
582
583 if (Q_stricmp(e->classname, "flare") == 0)
584 break;
585 }
586
587 goal = G_Spawn();
588 self->goalentity = goal;
589 if (e == NULL)
590 {
591 // just move forward
592 AngleVectors(self->s.angles, forward, NULL, NULL);
593 VectorMA(self->s.origin, 128, forward, goal->s.origin);
594 }
595 else /* e != NULL */
596 {
597 VectorSubtract(self->s.origin, e->s.origin, delta);
598 VectorNormalize(delta);
599 VectorMA(self->s.origin, 128, delta, goal->s.origin);
600 }
601
602 if (rand() & 7 == 1)
603 {
604 // set the ideal_yaw
605 VectorSubtract(goal->s.origin, self->s.origin, delta);
606 self->ideal_yaw = vectoyaw(delta);
607 }
608
609 if ( (rand()&3)==1 || !SV_StepDirection (self, self->ideal_yaw, dist))
610 {
611 SV_NewChaseDir (self, goal, dist);
612 }
613
614 self->goalentity = NULL;
615 G_FreeEdict(goal);
616
617 return true;
618 }
619