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