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 // ROGUE
IsBadAhead(edict_t * self,edict_t * bad,vec3_t move)134 qboolean IsBadAhead (edict_t *self, edict_t *bad, vec3_t move)
135 {
136 vec3_t dir;
137 vec3_t forward;
138 float dp_bad, dp_move;
139 vec3_t move_copy;
140
141 VectorCopy (move, move_copy);
142
143 VectorSubtract (bad->s.origin, self->s.origin, dir);
144 VectorNormalize (dir);
145 AngleVectors (self->s.angles, forward, NULL, NULL);
146 dp_bad = DotProduct (forward, dir);
147
148 VectorNormalize (move_copy);
149 AngleVectors (self->s.angles, forward, NULL, NULL);
150 dp_move = DotProduct (forward, move_copy);
151
152 if ((dp_bad < 0) && (dp_move < 0))
153 return true;
154 if ((dp_bad > 0) && (dp_move > 0))
155 return true;
156
157 return false;
158 /*
159 if(DotProduct(forward, dir) > 0)
160 {
161 // gi.dprintf ("bad ahead...\n");
162 return true;
163 }
164
165 // gi.dprintf ("bad behind...\n");
166 return false;
167 */
168 }
169 // ROGUE
170 //============
171
172 /*
173 =============
174 SV_movestep
175
176 Called by monster program code.
177 The move will be adjusted for slopes and stairs, but if the move isn't
178 possible, no move is done, false is returned, and
179 pr_global_struct->trace_normal is set to the normal of the blocking wall
180 =============
181 */
182 //FIXME since we need to test end position contents here, can we avoid doing
183 //it again later in catagorize position?
SV_movestep(edict_t * ent,vec3_t move,qboolean relink)184 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
185 {
186 float dz;
187 vec3_t oldorg, neworg, end;
188 trace_t trace;
189 int i;
190 float stepsize;
191 vec3_t test;
192 int contents;
193 edict_t *current_bad; // PGM
194 float minheight; // pmm
195
196 //======
197 //PGM
198
199 // PMM - who cares about bad areas if you're dead?
200 if (ent->health > 0)
201 {
202 current_bad = CheckForBadArea(ent);
203 if(current_bad)
204 {
205 ent->bad_area = current_bad;
206
207 if(ent->enemy && !strcmp(ent->enemy->classname, "tesla"))
208 {
209 // if the tesla is in front of us, back up...
210 if (IsBadAhead (ent, current_bad, move))
211 VectorScale(move, -1, move);
212 }
213 }
214 else if(ent->bad_area)
215 {
216 // if we're no longer in a bad area, get back to business.
217 ent->bad_area = NULL;
218 if(ent->oldenemy)// && ent->bad_area->owner == ent->enemy)
219 {
220 // gi.dprintf("resuming being pissed at %s\n", ent->oldenemy->classname);
221 ent->enemy = ent->oldenemy;
222 ent->goalentity = ent->oldenemy;
223 FoundTarget(ent);
224 // FIXME - remove this when ready!!!
225 // if (ent->lastMoveTime == level.time)
226 // if ((g_showlogic) && (g_showlogic->value))
227 // gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
228 // ent->lastMoveTime = level.time;
229 // FIXME
230
231 return true;
232 }
233 }
234 }
235 //PGM
236 //======
237
238 // try the move
239 VectorCopy (ent->s.origin, oldorg);
240 VectorAdd (ent->s.origin, move, neworg);
241
242 // flying monsters don't step up
243 if ( ent->flags & (FL_SWIM | FL_FLY) )
244 {
245 // try one move with vertical motion, then one without
246 for (i=0 ; i<2 ; i++)
247 {
248 VectorAdd (ent->s.origin, move, neworg);
249 if (i == 0 && ent->enemy)
250 {
251 if (!ent->goalentity)
252 ent->goalentity = ent->enemy;
253 dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
254 if (ent->goalentity->client)
255 {
256 // we want the carrier to stay a certain distance off the ground, to help prevent him
257 // from shooting his fliers, who spawn in below him
258 //
259 if (!strcmp(ent->classname, "monster_carrier"))
260 minheight = 104;
261 else
262 minheight = 40;
263 // if (dz > 40)
264 if (dz > minheight)
265 // pmm
266 neworg[2] -= 8;
267 if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
268 if (dz < (minheight - 10))
269 neworg[2] += 8;
270 }
271 else
272 {
273 if (dz > 8)
274 neworg[2] -= 8;
275 else if (dz > 0)
276 neworg[2] -= dz;
277 else if (dz < -8)
278 neworg[2] += 8;
279 else
280 neworg[2] += dz;
281 }
282 }
283
284 trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
285
286 // fly monsters don't enter water voluntarily
287 if (ent->flags & FL_FLY)
288 {
289 if (!ent->waterlevel)
290 {
291 test[0] = trace.endpos[0];
292 test[1] = trace.endpos[1];
293 test[2] = trace.endpos[2] + ent->mins[2] + 1;
294 contents = gi.pointcontents(test);
295 if (contents & MASK_WATER)
296 return false;
297 }
298 }
299
300 // swim monsters don't exit water voluntarily
301 if (ent->flags & FL_SWIM)
302 {
303 if (ent->waterlevel < 2)
304 {
305 test[0] = trace.endpos[0];
306 test[1] = trace.endpos[1];
307 test[2] = trace.endpos[2] + ent->mins[2] + 1;
308 contents = gi.pointcontents(test);
309 if (!(contents & MASK_WATER))
310 return false;
311 }
312 }
313
314 // if (trace.fraction == 1)
315
316 // PMM - changed above to this
317 if ((trace.fraction == 1) && (!trace.allsolid) && (!trace.startsolid))
318 {
319 VectorCopy (trace.endpos, ent->s.origin);
320 //=====
321 //PGM
322 if(!current_bad && CheckForBadArea(ent))
323 {
324 // gi.dprintf("Oooh! Bad Area!\n");
325 VectorCopy (oldorg, ent->s.origin);
326 }
327 else
328 {
329 if (relink)
330 {
331 gi.linkentity (ent);
332 G_TouchTriggers (ent);
333 }
334 // FIXME - remove this when ready!!!
335 // if (ent->lastMoveTime == level.time)
336 // if ((g_showlogic) && (g_showlogic->value))
337 // gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
338 // ent->lastMoveTime = level.time;
339 // FIXME
340
341 return true;
342 }
343 //PGM
344 //=====
345 }
346
347 if (!ent->enemy)
348 break;
349 }
350
351 return false;
352 }
353
354 // push down from a step height above the wished position
355 if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
356 stepsize = STEPSIZE;
357 else
358 stepsize = 1;
359
360 //PGM
361 #ifdef ROGUE_GRAVITY
362 // trace from 1 stepsize gravityUp to 2 stepsize gravityDown.
363 VectorMA(neworg, -1 * stepsize, ent->gravityVector, neworg);
364 VectorMA(neworg, 2 * stepsize, ent->gravityVector, end);
365 #else
366 neworg[2] += stepsize;
367 VectorCopy (neworg, end);
368 end[2] -= stepsize*2;
369 #endif
370 //PGM
371
372 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
373
374 if (trace.allsolid)
375 return false;
376
377 if (trace.startsolid)
378 {
379 neworg[2] -= stepsize;
380 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
381 if (trace.allsolid || trace.startsolid)
382 return false;
383 }
384
385
386 // don't go in to water
387 if (ent->waterlevel == 0)
388 {
389 //PGM
390 #ifdef ROGUE_GRAVITY
391 test[0] = trace.endpos[0];
392 test[1] = trace.endpos[1];
393 if(ent->gravityVector[2] > 0)
394 test[2] = trace.endpos[2] + ent->maxs[2] - 1;
395 else
396 test[2] = trace.endpos[2] + ent->mins[2] + 1;
397 #else
398 test[0] = trace.endpos[0];
399 test[1] = trace.endpos[1];
400 test[2] = trace.endpos[2] + ent->mins[2] + 1;
401 #endif
402 //PGM
403
404 contents = gi.pointcontents(test);
405
406 if (contents & MASK_WATER)
407 return false;
408 }
409
410 if (trace.fraction == 1)
411 {
412 // if monster had the ground pulled out, go ahead and fall
413 if ( ent->flags & FL_PARTIALGROUND )
414 {
415 VectorAdd (ent->s.origin, move, ent->s.origin);
416 if (relink)
417 {
418 gi.linkentity (ent);
419 G_TouchTriggers (ent);
420 }
421 ent->groundentity = NULL;
422 // FIXME - remove this when ready!!!
423 // if (ent->lastMoveTime == level.time)
424 // if ((g_showlogic) && (g_showlogic->value))
425 // gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
426 // ent->lastMoveTime = level.time;
427 // FIXME
428
429 return true;
430 }
431
432 return false; // walked off an edge
433 }
434
435 // check point traces down for dangling corners
436 VectorCopy (trace.endpos, ent->s.origin);
437
438 //PGM
439 // PMM - don't bother with bad areas if we're dead
440 if (ent->health > 0)
441 {
442 // use AI_BLOCKED to tell the calling layer that we're now mad at a tesla
443 new_bad = CheckForBadArea(ent);
444 if(!current_bad && new_bad)
445 {
446 if (new_bad->owner)
447 {
448 // if ((g_showlogic) && (g_showlogic->value))
449 // gi.dprintf("Blocked -");
450 if (!strcmp(new_bad->owner->classname, "tesla"))
451 {
452 // if ((g_showlogic) && (g_showlogic->value))
453 // gi.dprintf ("it's a tesla -");
454 if ((!(ent->enemy)) || (!(ent->enemy->inuse)))
455 {
456 // if ((g_showlogic) && (g_showlogic->value))
457 // gi.dprintf ("I don't have a valid enemy, attacking tesla!\n");
458 TargetTesla (ent, new_bad->owner);
459 ent->monsterinfo.aiflags |= AI_BLOCKED;
460 }
461 else if (!strcmp(ent->enemy->classname, "telsa"))
462 {
463 // if ((g_showlogic) && (g_showlogic->value))
464 // gi.dprintf ("but we're already mad at a tesla\n");
465 }
466 else if ((ent->enemy) && (ent->enemy->client))
467 {
468 // if ((g_showlogic) && (g_showlogic->value))
469 // gi.dprintf ("we have a player enemy -");
470 if (visible(ent, ent->enemy))
471 {
472 // if ((g_showlogic) && (g_showlogic->value))
473 // gi.dprintf ("we can see him -");
474 }
475 else
476 {
477 // if ((g_showlogic) && (g_showlogic->value))
478 // gi.dprintf ("can't see him, kill the tesla! -");
479 TargetTesla (ent, new_bad->owner);
480 ent->monsterinfo.aiflags |= AI_BLOCKED;
481 }
482 }
483 else
484 {
485 // if ((g_showlogic) && (g_showlogic->value))
486 // gi.dprintf ("the enemy isn't a player, killing tesla -");
487 TargetTesla (ent, new_bad->owner);
488 ent->monsterinfo.aiflags |= AI_BLOCKED;
489 }
490 }
491 // else if ((g_showlogic) && (g_showlogic->value))
492 // {
493 // gi.dprintf(" by non-tesla bad area!");
494 // }
495 }
496 // gi.dprintf ("\n");
497
498 VectorCopy (oldorg, ent->s.origin);
499 return false;
500 }
501 }
502 //PGM
503
504 if (!M_CheckBottom (ent))
505 {
506 if ( ent->flags & FL_PARTIALGROUND )
507 { // entity had floor mostly pulled out from underneath it
508 // and is trying to correct
509 if (relink)
510 {
511 gi.linkentity (ent);
512 G_TouchTriggers (ent);
513 }
514 // FIXME - remove this when ready!!!
515 // if (ent->lastMoveTime == level.time)
516 // if ((g_showlogic) && (g_showlogic->value))
517 // gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
518 // ent->lastMoveTime = level.time;
519 // FIXME
520
521 return true;
522 }
523 VectorCopy (oldorg, ent->s.origin);
524 return false;
525 }
526
527 if ( ent->flags & FL_PARTIALGROUND )
528 {
529 ent->flags &= ~FL_PARTIALGROUND;
530 }
531 ent->groundentity = trace.ent;
532 ent->groundentity_linkcount = trace.ent->linkcount;
533
534 // the move is ok
535 if (relink)
536 {
537 gi.linkentity (ent);
538 G_TouchTriggers (ent);
539 }
540 // FIXME - remove this when ready!!!
541 // if (ent->lastMoveTime == level.time)
542 // if ((g_showlogic) && (g_showlogic->value))
543 // gi.dprintf ("Duplicate move detected for %s, please tell programmers!\n", ent->classname);
544 // ent->lastMoveTime = level.time;
545 // FIXME
546
547 return true;
548 }
549
550
551 //============================================================================
552
553 /*
554 ===============
555 M_ChangeYaw
556
557 ===============
558 */
M_ChangeYaw(edict_t * ent)559 void M_ChangeYaw (edict_t *ent)
560 {
561 float ideal;
562 float current;
563 float move;
564 float speed;
565
566 current = anglemod(ent->s.angles[YAW]);
567 ideal = ent->ideal_yaw;
568
569 if (current == ideal)
570 return;
571
572 move = ideal - current;
573 speed = ent->yaw_speed;
574 if (ideal > current)
575 {
576 if (move >= 180)
577 move = move - 360;
578 }
579 else
580 {
581 if (move <= -180)
582 move = move + 360;
583 }
584 if (move > 0)
585 {
586 if (move > speed)
587 move = speed;
588 }
589 else
590 {
591 if (move < -speed)
592 move = -speed;
593 }
594
595 ent->s.angles[YAW] = anglemod (current + move);
596 }
597
598
599 /*
600 ======================
601 SV_StepDirection
602
603 Turns to the movement direction, and walks the current distance if
604 facing it.
605
606 ======================
607 */
SV_StepDirection(edict_t * ent,float yaw,float dist)608 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
609 {
610 vec3_t move, oldorigin;
611 float delta;
612
613 if(!ent->inuse) return true; // PGM g_touchtrigger free problem
614
615 ent->ideal_yaw = yaw;
616 M_ChangeYaw (ent);
617
618 yaw = yaw*M_PI*2 / 360;
619 move[0] = cos(yaw)*dist;
620 move[1] = sin(yaw)*dist;
621 move[2] = 0;
622
623 VectorCopy (ent->s.origin, oldorigin);
624 if (SV_movestep (ent, move, false))
625 {
626 ent->monsterinfo.aiflags &= ~AI_BLOCKED;
627 if(!ent->inuse) return true; // PGM g_touchtrigger free problem
628
629 delta = ent->s.angles[YAW] - ent->ideal_yaw;
630 if (strncmp(ent->classname, "monster_widow", 13))
631 {
632 if (delta > 45 && delta < 315)
633 { // not turned far enough, so don't take the step
634 VectorCopy (oldorigin, ent->s.origin);
635 }
636 }
637 gi.linkentity (ent);
638 G_TouchTriggers (ent);
639 return true;
640 }
641 gi.linkentity (ent);
642 G_TouchTriggers (ent);
643 return false;
644 }
645
646 /*
647 ======================
648 SV_FixCheckBottom
649
650 ======================
651 */
SV_FixCheckBottom(edict_t * ent)652 void SV_FixCheckBottom (edict_t *ent)
653 {
654 ent->flags |= FL_PARTIALGROUND;
655 }
656
657
658
659 /*
660 ================
661 SV_NewChaseDir
662
663 ================
664 */
665 #define DI_NODIR -1
SV_NewChaseDir(edict_t * actor,edict_t * enemy,float dist)666 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
667 {
668 float deltax,deltay;
669 float d[3];
670 float tdir, olddir, turnaround;
671
672 //FIXME: how did we get here with no enemy
673 if (!enemy)
674 return;
675
676 olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
677 turnaround = anglemod(olddir - 180);
678
679 deltax = enemy->s.origin[0] - actor->s.origin[0];
680 deltay = enemy->s.origin[1] - actor->s.origin[1];
681 if (deltax>10)
682 d[1]= 0;
683 else if (deltax<-10)
684 d[1]= 180;
685 else
686 d[1]= DI_NODIR;
687 if (deltay<-10)
688 d[2]= 270;
689 else if (deltay>10)
690 d[2]= 90;
691 else
692 d[2]= DI_NODIR;
693
694 // try direct route
695 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
696 {
697 if (d[1] == 0)
698 tdir = d[2] == 90 ? 45 : 315;
699 else
700 tdir = d[2] == 90 ? 135 : 215;
701
702 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
703 return;
704 }
705
706 // try other directions
707 if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax))
708 {
709 tdir=d[1];
710 d[1]=d[2];
711 d[2]=tdir;
712 }
713
714 if (d[1]!=DI_NODIR && d[1]!=turnaround
715 && SV_StepDirection(actor, d[1], dist))
716 return;
717
718 if (d[2]!=DI_NODIR && d[2]!=turnaround
719 && SV_StepDirection(actor, d[2], dist))
720 return;
721
722 //ROGUE
723 if(actor->monsterinfo.blocked)
724 {
725 if ((actor->inuse) && (actor->health > 0))
726 {
727 if((actor->monsterinfo.blocked)(actor, dist))
728 return;
729 }
730 }
731 //ROGUE
732
733 /* there is no direct path to the player, so pick another direction */
734
735 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
736 return;
737
738 if (rand()&1) /*randomly determine direction of search*/
739 {
740 for (tdir=0 ; tdir<=315 ; tdir += 45)
741 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
742 return;
743 }
744 else
745 {
746 for (tdir=315 ; tdir >=0 ; tdir -= 45)
747 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
748 return;
749 }
750
751 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
752 return;
753
754 actor->ideal_yaw = olddir; // can't move
755
756 // if a bridge was pulled out from underneath a monster, it may not have
757 // a valid standing position at all
758
759 if (!M_CheckBottom (actor))
760 SV_FixCheckBottom (actor);
761 }
762
763 /*
764 ======================
765 SV_CloseEnough
766
767 ======================
768 */
SV_CloseEnough(edict_t * ent,edict_t * goal,float dist)769 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
770 {
771 int i;
772
773 for (i=0 ; i<3 ; i++)
774 {
775 if (goal->absmin[i] > ent->absmax[i] + dist)
776 return false;
777 if (goal->absmax[i] < ent->absmin[i] - dist)
778 return false;
779 }
780 return true;
781 }
782
783 /*
784 ======================
785 M_MoveToGoal
786 ======================
787 */
M_MoveToGoal(edict_t * ent,float dist)788 void M_MoveToGoal (edict_t *ent, float dist)
789 {
790 edict_t *goal;
791
792 goal = ent->goalentity;
793
794 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
795 return;
796
797 // if the next step hits the enemy, return immediately
798 if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) )
799 return;
800
801 // bump around...
802 // if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
803 // PMM - charging monsters (AI_CHARGING) don't deflect unless they have to
804 if ( (((rand()&3)==1) && !(ent->monsterinfo.aiflags & AI_CHARGING)) || !SV_StepDirection (ent, ent->ideal_yaw, dist))
805 {
806 if (ent->monsterinfo.aiflags & AI_BLOCKED)
807 {
808 // if ((g_showlogic) && (g_showlogic->value))
809 // gi.dprintf ("tesla attack detected, not changing direction!\n");
810 ent->monsterinfo.aiflags &= ~AI_BLOCKED;
811 return;
812 }
813 if (ent->inuse)
814 SV_NewChaseDir (ent, goal, dist);
815 }
816 }
817
818
819 /*
820 ===============
821 M_walkmove
822 ===============
823 */
M_walkmove(edict_t * ent,float yaw,float dist)824 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
825 {
826 vec3_t move;
827 // PMM
828 qboolean retval;
829
830 if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
831 return false;
832
833 yaw = yaw*M_PI*2 / 360;
834
835 move[0] = cos(yaw)*dist;
836 move[1] = sin(yaw)*dist;
837 move[2] = 0;
838
839 // PMM
840 retval = SV_movestep(ent, move, true);
841 ent->monsterinfo.aiflags &= ~AI_BLOCKED;
842 return retval;
843 // pmm
844 //return SV_movestep(ent, move, true);
845 }
846