1
2 #include "g_local.h"
3
4 //===============================
5 // BLOCKED Logic
6 //===============================
7
8 /*
9 gi.WriteByte (svc_temp_entity);
10 gi.WriteByte (TE_DEBUGTRAIL);
11 gi.WritePosition (pt1);
12 gi.WritePosition (pt2);
13 gi.multicast (pt1, MULTICAST_PVS);
14
15 self->nextthink = level.time + 10;
16 */
17
18 // plat states, copied from g_func.c
19
20 #define STATE_TOP 0
21 #define STATE_BOTTOM 1
22 #define STATE_UP 2
23 #define STATE_DOWN 3
24
25 qboolean face_wall (edict_t *self);
26 void HuntTarget (edict_t *self);
27
28 // PMM
29 qboolean parasite_drain_attack_ok (vec3_t start, vec3_t end);
30
31
32 // blocked_checkshot
33 // shotchance: 0-1, chance they'll take the shot if it's clear.
blocked_checkshot(edict_t * self,float shotChance)34 qboolean blocked_checkshot (edict_t *self, float shotChance)
35 {
36 qboolean playerVisible;
37
38 if(!self->enemy)
39 return false;
40
41 // blocked checkshot is only against players. this will
42 // filter out player sounds and other shit they should
43 // not be firing at.
44 if(!(self->enemy->client))
45 return false;
46
47 if (random() < shotChance)
48 return false;
49
50 // PMM - special handling for the parasite
51 if (!strcmp(self->classname, "monster_parasite"))
52 {
53 vec3_t f, r, offset, start, end;
54 trace_t tr;
55 AngleVectors (self->s.angles, f, r, NULL);
56 VectorSet (offset, 24, 0, 6);
57 G_ProjectSource (self->s.origin, offset, f, r, start);
58
59 VectorCopy (self->enemy->s.origin, end);
60 if (!parasite_drain_attack_ok(start, end))
61 {
62 end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8;
63 if (!parasite_drain_attack_ok(start, end))
64 {
65 end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8;
66 if (!parasite_drain_attack_ok(start, end))
67 return false;
68 }
69 }
70 VectorCopy (self->enemy->s.origin, end);
71
72 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
73 if (tr.ent != self->enemy)
74 {
75 self->monsterinfo.aiflags |= AI_BLOCKED;
76
77 if(self->monsterinfo.attack)
78 self->monsterinfo.attack(self);
79
80 self->monsterinfo.aiflags &= ~AI_BLOCKED;
81 return true;
82 }
83 }
84
85 playerVisible = visible (self, self->enemy);
86 // always shoot at teslas
87 if(playerVisible)
88 {
89 if (!strcmp(self->enemy->classname, "tesla"))
90 {
91 // if(g_showlogic && g_showlogic->value)
92 // gi.dprintf("blocked: taking a shot\n");
93
94 // turn on AI_BLOCKED to let the monster know the attack is being called
95 // by the blocked functions...
96 self->monsterinfo.aiflags |= AI_BLOCKED;
97
98 if(self->monsterinfo.attack)
99 self->monsterinfo.attack(self);
100
101 self->monsterinfo.aiflags &= ~AI_BLOCKED;
102 return true;
103 }
104 }
105
106 return false;
107 }
108
109 // blocked_checkplat
110 // dist: how far they are trying to walk.
blocked_checkplat(edict_t * self,float dist)111 qboolean blocked_checkplat (edict_t *self, float dist)
112 {
113 int playerPosition;
114 trace_t trace;
115 vec3_t pt1, pt2;
116 vec3_t forward;
117 edict_t *plat;
118
119 if(!self->enemy)
120 return false;
121
122 // check player's relative altitude
123 if(self->enemy->absmin[2] >= self->absmax[2])
124 playerPosition = 1;
125 else if(self->enemy->absmax[2] <= self->absmin[2])
126 playerPosition = -1;
127 else
128 playerPosition = 0;
129
130 // if we're close to the same position, don't bother trying plats.
131 if(playerPosition == 0)
132 return false;
133
134 plat = NULL;
135
136 // see if we're already standing on a plat.
137 if(self->groundentity && self->groundentity != world)
138 {
139 if(!strncmp(self->groundentity->classname, "func_plat", 8))
140 plat = self->groundentity;
141 }
142
143 // if we're not, check to see if we'll step onto one with this move
144 if(!plat)
145 {
146 AngleVectors (self->s.angles, forward, NULL, NULL);
147 VectorMA(self->s.origin, dist, forward, pt1);
148 VectorCopy (pt1, pt2);
149 pt2[2] -= 384;
150
151 trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID);
152 if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
153 {
154 if(!strncmp(trace.ent->classname, "func_plat", 8))
155 {
156 plat = trace.ent;
157 }
158 }
159 }
160
161 // if we've found a plat, trigger it.
162 if(plat && plat->use)
163 {
164 if (playerPosition == 1)
165 {
166 if((self->groundentity == plat && plat->moveinfo.state == STATE_BOTTOM) ||
167 (self->groundentity != plat && plat->moveinfo.state == STATE_TOP))
168 {
169 // if(g_showlogic && g_showlogic->value)
170 // gi.dprintf("player above, and plat will raise. using!\n");
171 plat->use (plat, self, self);
172 return true;
173 }
174 }
175 else if(playerPosition == -1)
176 {
177 if((self->groundentity == plat && plat->moveinfo.state == STATE_TOP) ||
178 (self->groundentity != plat && plat->moveinfo.state == STATE_BOTTOM))
179 {
180 // if(g_showlogic && g_showlogic->value)
181 // gi.dprintf("player below, and plat will lower. using!\n");
182 plat->use (plat, self, self);
183 return true;
184 }
185 }
186 // if(g_showlogic && g_showlogic->value)
187 // gi.dprintf("hit a plat, not using. ppos: %d plat: %d\n", playerPosition, plat->moveinfo.state);
188 }
189
190 return false;
191 }
192
193 // blocked_checkjump
194 // dist: how far they are trying to walk.
195 // maxDown/maxUp: how far they'll ok a jump for. set to 0 to disable that direction.
blocked_checkjump(edict_t * self,float dist,float maxDown,float maxUp)196 qboolean blocked_checkjump (edict_t *self, float dist, float maxDown, float maxUp)
197 {
198 int playerPosition;
199 trace_t trace;
200 vec3_t pt1, pt2;
201 vec3_t forward, up;
202
203 if(!self->enemy)
204 return false;
205
206 AngleVectors (self->s.angles, forward, NULL, up);
207
208 if(self->enemy->absmin[2] > (self->absmin[2] + 16))
209 playerPosition = 1;
210 else if(self->enemy->absmin[2] < (self->absmin[2] - 16))
211 playerPosition = -1;
212 else
213 playerPosition = 0;
214
215 if(playerPosition == -1 && maxDown)
216 {
217 // check to make sure we can even get to the spot we're going to "fall" from
218 VectorMA(self->s.origin, 48, forward, pt1);
219 trace = gi.trace(self->s.origin, self->mins, self->maxs, pt1, self, MASK_MONSTERSOLID);
220 if(trace.fraction < 1)
221 {
222 // gi.dprintf("can't get thar from hear...\n");
223 return false;
224 }
225
226 VectorCopy (pt1, pt2);
227 pt2[2] = self->mins[2] - maxDown - 1;
228
229 trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
230 if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
231 {
232 if((self->absmin[2] - trace.endpos[2]) >= 24 && trace.contents & MASK_SOLID)
233 {
234 if( (self->enemy->absmin[2] - trace.endpos[2]) > 32)
235 {
236 // if(g_showlogic && g_showlogic->value)
237 // gi.dprintf("That'll take me too far down...%0.1f\n", (self->enemy->absmin[2] - trace.endpos[2]));
238 return false;
239 }
240
241 if(trace.plane.normal[2] < 0.9)
242 {
243 // gi.dprintf("Floor angle too much! %s\n", vtos(trace.plane.normal));
244 return false;
245 }
246 // if(g_showlogic && g_showlogic->value)
247 // gi.dprintf("Geronimo! %0.1f\n", (self->absmin[2] - trace.endpos[2]));
248 return true;
249 }
250 // else if(g_showlogic && g_showlogic->value)
251 // {
252 // if(!(trace.contents & MASK_SOLID))
253 // gi.dprintf("Ooooh... Bad stuff down there...\n");
254 // else
255 // gi.dprintf("Too far to fall\n");
256 // }
257 }
258 // else if(g_showlogic && g_showlogic->value)
259 // gi.dprintf("Ooooh... Too far to fall...\n");
260 }
261 else if(playerPosition == 1 && maxUp)
262 {
263 VectorMA(self->s.origin, 48, forward, pt1);
264 VectorCopy(pt1, pt2);
265 pt1[2] = self->absmax[2] + maxUp;
266
267 trace = gi.trace(pt1, vec3_origin, vec3_origin, pt2, self, MASK_MONSTERSOLID | MASK_WATER);
268 if(trace.fraction < 1 && !trace.allsolid && !trace.startsolid)
269 {
270 if((trace.endpos[2] - self->absmin[2]) <= maxUp && trace.contents & MASK_SOLID)
271 {
272 // if(g_showlogic && g_showlogic->value)
273 // gi.dprintf("Jumping Up! %0.1f\n", (trace.endpos[2] - self->absmin[2]));
274
275 face_wall(self);
276 return true;
277 }
278 // else if(g_showlogic && g_showlogic->value)
279 // gi.dprintf("Too high to jump %0.1f\n", (trace.endpos[2] - self->absmin[2]));
280 }
281 // else if(g_showlogic && g_showlogic->value)
282 // gi.dprintf("Not something I could jump onto\n");
283 }
284 // else if(g_showlogic && g_showlogic->value)
285 // gi.dprintf("Player at similar level. No need to jump up?\n");
286
287 return false;
288 }
289
290 // checks to see if another coop player is nearby, and will switch.
blocked_checknewenemy(edict_t * self)291 qboolean blocked_checknewenemy (edict_t *self)
292 {
293 /*
294 int player;
295 edict_t *ent;
296
297 if (!(coop->value))
298 return false;
299
300 for (player = 1; player <= game.maxclients; player++)
301 {
302 ent = &g_edicts[player];
303 if (!ent->inuse)
304 continue;
305 if (!ent->client)
306 continue;
307 if (ent == self->enemy)
308 continue;
309
310 if (visible (self, ent))
311 {
312 if (g_showlogic && g_showlogic->value)
313 gi.dprintf ("B_CNE: %s acquired new enemy %s\n", self->classname, ent->client->pers.netname);
314
315 self->enemy = ent;
316 FoundTarget (self);
317 return true;
318 }
319 }
320
321 return false;
322 */
323 return false;
324 }
325
326 // *************************
327 // HINT PATHS
328 // *************************
329
330 #define HINT_ENDPOINT 0x0001
331 #define MAX_HINT_CHAINS 100
332
333 int hint_paths_present;
334 edict_t *hint_path_start[MAX_HINT_CHAINS];
335 int num_hint_paths;
336
337 //
338 // AI code
339 //
340
341 // =============
342 // hintpath_findstart - given any hintpath node, finds the start node
343 // =============
hintpath_findstart(edict_t * ent)344 edict_t *hintpath_findstart(edict_t *ent)
345 {
346 edict_t *e;
347 edict_t *last;
348 int field;
349
350 if(ent->target) // starting point
351 {
352 last = world;
353 field = FOFS(targetname);
354 e = G_Find(NULL, field, ent->target);
355 while(e)
356 {
357 last = e;
358 if(!e->target)
359 break;
360 e = G_Find(NULL, field, e->target);
361 }
362 }
363 else // end point
364 {
365 last = world;
366 field = FOFS(target);
367 e = G_Find(NULL, field, ent->targetname);
368 while(e)
369 {
370 last = e;
371 if(!e->targetname)
372 break;
373 e = G_Find(NULL, field, e->targetname);
374 }
375 }
376
377 if(!(last->spawnflags & HINT_ENDPOINT))
378 {
379 // gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
380 return NULL;
381 }
382
383 if(last == world)
384 last = NULL;
385 return last;
386 }
387
388 // =============
389 // hintpath_other_end - given one endpoint of a hintpath, returns the other end.
390 // =============
hintpath_other_end(edict_t * ent)391 edict_t *hintpath_other_end(edict_t *ent)
392 {
393 edict_t *e;
394 edict_t *last;
395 int field;
396
397 if(ent->target) // starting point
398 {
399 last = world;
400 field = FOFS(targetname);
401 e = G_Find(NULL, field, ent->target);
402 while(e)
403 {
404 last = e;
405 if(!e->target)
406 break;
407 e = G_Find(NULL, field, e->target);
408 }
409 }
410 else // end point
411 {
412 last = world;
413 field = FOFS(target);
414 e = G_Find(NULL, field, ent->targetname);
415 while(e)
416 {
417 last = e;
418 if(!e->targetname)
419 break;
420 e = G_Find(NULL, field, e->targetname);
421 }
422 }
423
424 if(!(last->spawnflags & HINT_ENDPOINT))
425 {
426 // gi.dprintf ("end of chain is not HINT_ENDPOINT\n");
427 return NULL;
428 }
429
430 if(last == world)
431 last = NULL;
432 return last;
433 }
434
435 // =============
436 // hintpath_go - starts a monster (self) moving towards the hintpath (point)
437 // disables all contrary AI flags.
438 // =============
hintpath_go(edict_t * self,edict_t * point)439 void hintpath_go (edict_t *self, edict_t *point)
440 {
441 vec3_t dir;
442 vec3_t angles;
443
444 VectorSubtract(point->s.origin, self->s.origin, dir);
445 vectoangles2(dir, angles);
446
447 self->ideal_yaw = angles[YAW];
448 self->goalentity = self->movetarget = point;
449 self->monsterinfo.pausetime = 0;
450 self->monsterinfo.aiflags |= AI_HINT_PATH;
451 self->monsterinfo.aiflags &= ~(AI_SOUND_TARGET | AI_PURSUIT_LAST_SEEN | AI_PURSUE_NEXT | AI_PURSUE_TEMP);
452 // run for it
453 self->monsterinfo.search_time = level.time;
454 self->monsterinfo.run (self);
455 }
456
457 // =============
458 // hintpath_stop - bails a monster out of following hint paths
459 // =============
hintpath_stop(edict_t * self)460 void hintpath_stop (edict_t *self)
461 {
462 self->goalentity = NULL;
463 self->movetarget = NULL;
464 // self->monsterinfo.last_hint = NULL;
465 self->monsterinfo.last_hint_time = level.time;
466 self->monsterinfo.goal_hint = NULL;
467 self->monsterinfo.aiflags &= ~AI_HINT_PATH;
468 if (has_valid_enemy(self))
469 {
470 // if we can see our target, go nuts
471 if (visible(self, self->enemy))
472 {
473 FoundTarget (self);
474 return;
475 }
476 // otherwise, keep chasing
477 HuntTarget (self);
478 return;
479 }
480 // if our enemy is no longer valid, forget about our enemy and go into stand
481 self->enemy = NULL;
482 // we need the pausetime otherwise the stand code
483 // will just revert to walking with no target and
484 // the monsters will wonder around aimlessly trying
485 // to hunt the world entity
486 self->monsterinfo.pausetime = level.time + 100000000;
487 self->monsterinfo.stand (self);
488 }
489
490 // =============
491 // monsterlost_checkhint - the monster (self) will check around for valid hintpaths.
492 // a valid hintpath is one where the two endpoints can see both the monster
493 // and the monster's enemy. if only one person is visible from the endpoints,
494 // it will not go for it.
495 // =============
496 qboolean monsterlost_checkhint2 (edict_t *self);
497
monsterlost_checkhint(edict_t * self)498 qboolean monsterlost_checkhint (edict_t *self)
499 {
500 edict_t *e, *monster_pathchain, *target_pathchain, *checkpoint;
501 edict_t *closest;
502 float closest_range = 1000000;
503 edict_t *start, *destination;
504 int field;
505 int count1=0, count2=0, count3=0, count4=0, count5=0;
506 float r;
507 int i;
508 qboolean hint_path_represented[MAX_HINT_CHAINS];
509
510 // if there are no hint paths on this map, exit immediately.
511 if(!hint_paths_present)
512 return false;
513
514 if(!self->enemy)
515 return false;
516
517 if (self->monsterinfo.aiflags & AI_STAND_GROUND)
518 return false;
519
520 if (!strcmp(self->classname, "monster_turret"))
521 return false;
522
523 monster_pathchain = NULL;
524
525 field = FOFS(classname);
526
527 // find all the hint_paths.
528 // FIXME - can we not do this every time?
529 for (i=0; i < num_hint_paths; i++)
530 {
531 e = hint_path_start[i];
532 while(e)
533 {
534 count1++;
535 if (e->monster_hint_chain)
536 {
537 // gi.dprintf ("uh, oh, I didn't clean up after myself\n");
538 e->monster_hint_chain = NULL;
539 }
540 if (monster_pathchain)
541 {
542 checkpoint->monster_hint_chain = e;
543 checkpoint = e;
544 }
545 else
546 {
547 monster_pathchain = e;
548 checkpoint = e;
549 }
550 e = e->hint_chain;
551 }
552 }
553
554 // filter them by distance and visibility to the monster
555 e = monster_pathchain;
556 checkpoint = NULL;
557 while (e)
558 {
559 r = realrange (self, e);
560
561 // if (r > 512)
562 // count3++;
563
564 if (r > 512)
565 {
566 count2++;
567 // if (g_showlogic && g_showlogic->value)
568 // {
569 // gi.dprintf ("MONSTER (%s) DISTANCE: ", self->classname);
570 // if (e->targetname)
571 // gi.dprintf ("targetname %s\n", e->targetname);
572 // else
573 // gi.dprintf ("start -> %s\n", e->target);
574 // }
575 if (checkpoint)
576 {
577 checkpoint->monster_hint_chain = e->monster_hint_chain;
578 e->monster_hint_chain = NULL;
579 e = checkpoint->monster_hint_chain;
580 continue;
581 }
582 else
583 {
584 // use checkpoint as temp pointer
585 checkpoint = e;
586 e = e->monster_hint_chain;
587 checkpoint->monster_hint_chain = NULL;
588 // and clear it again
589 checkpoint = NULL;
590 // since we have yet to find a valid one (or else checkpoint would be set) move the
591 // start of monster_pathchain
592 monster_pathchain = e;
593 continue;
594 }
595 }
596 if (!visible(self, e))
597 {
598 count4++;
599 // if (g_showlogic && g_showlogic->value)
600 // {
601 // gi.dprintf ("MONSTER (%s) VISIBILITY: ", self->classname);
602 // if (e->targetname)
603 // gi.dprintf ("targetname %s\n", e->targetname);
604 // else
605 // gi.dprintf ("start -> %s\n", e->target);
606 // }
607 if (checkpoint)
608 {
609 checkpoint->monster_hint_chain = e->monster_hint_chain;
610 e->monster_hint_chain = NULL;
611 e = checkpoint->monster_hint_chain;
612 continue;
613 }
614 else
615 {
616 // use checkpoint as temp pointer
617 checkpoint = e;
618 e = e->monster_hint_chain;
619 checkpoint->monster_hint_chain = NULL;
620 // and clear it again
621 checkpoint = NULL;
622 // since we have yet to find a valid one (or else checkpoint would be set) move the
623 // start of monster_pathchain
624 monster_pathchain = e;
625 continue;
626 }
627 }
628 // if it passes all the tests, it's a keeper
629 // if (g_showlogic && g_showlogic->value)
630 // {
631 // gi.dprintf ("MONSTER (%s) ACCEPT: ", self->classname);
632 // if (e->targetname)
633 // gi.dprintf ("targetname %s\n", e->targetname);
634 // else
635 // gi.dprintf ("start -> %s\n", e->target);
636 // }
637 count5++;
638 checkpoint = e;
639 e = e->monster_hint_chain;
640 }
641
642 // at this point, we have a list of all of the eligible hint nodes for the monster
643 // we now take them, figure out what hint chains they're on, and traverse down those chains,
644 // seeing whether any can see the player
645 //
646 // first, we figure out which hint chains we have represented in monster_pathchain
647 if (count5 == 0)
648 {
649 // if ((g_showlogic) && (g_showlogic->value))
650 // gi.dprintf ("No eligible hint paths found.\n");
651 return false;
652 }
653
654 for (i=0; i < num_hint_paths; i++)
655 {
656 hint_path_represented[i] = false;
657 }
658 e = monster_pathchain;
659 checkpoint = NULL;
660 while (e)
661 {
662 if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
663 {
664 // if (g_showlogic && g_showlogic->value)
665 // gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
666 return false;
667 }
668 hint_path_represented[e->hint_chain_id] = true;
669 e = e->monster_hint_chain;
670 }
671
672 count1 = 0;
673 count2 = 0;
674 count3 = 0;
675 count4 = 0;
676 count5 = 0;
677
678 // now, build the target_pathchain which contains all of the hint_path nodes we need to check for
679 // validity (within range, visibility)
680 target_pathchain = NULL;
681 checkpoint = NULL;
682 for (i=0; i < num_hint_paths; i++)
683 {
684 // if this hint chain is represented in the monster_hint_chain, add all of it's nodes to the target_pathchain
685 // for validity checking
686 if (hint_path_represented[i])
687 {
688 e = hint_path_start[i];
689 while (e)
690 {
691 if (target_pathchain)
692 {
693 checkpoint->target_hint_chain = e;
694 checkpoint = e;
695 }
696 else
697 {
698 target_pathchain = e;
699 checkpoint = e;
700 }
701 e = e->hint_chain;
702 }
703 }
704 }
705
706 // target_pathchain is a list of all of the hint_path nodes we need to check for validity relative to the target
707 e = target_pathchain;
708 checkpoint = NULL;
709 while (e)
710 {
711 r = realrange (self->enemy, e);
712
713 // if (r > 512)
714 // count3++;
715
716 if (r > 512)
717 {
718 count2++;
719 // if (g_showlogic && g_showlogic->value)
720 // {
721 // gi.dprintf ("TARGET RANGE: ");
722 // if (e->targetname)
723 // gi.dprintf ("targetname %s\n", e->targetname);
724 // else
725 // gi.dprintf ("start -> %s\n", e->target);
726 // }
727 if (checkpoint)
728 {
729 checkpoint->target_hint_chain = e->target_hint_chain;
730 e->target_hint_chain = NULL;
731 e = checkpoint->target_hint_chain;
732 continue;
733 }
734 else
735 {
736 // use checkpoint as temp pointer
737 checkpoint = e;
738 e = e->target_hint_chain;
739 checkpoint->target_hint_chain = NULL;
740 // and clear it again
741 checkpoint = NULL;
742 target_pathchain = e;
743 continue;
744 }
745 }
746 if (!visible(self->enemy, e))
747 {
748 count4++;
749 // if (g_showlogic && g_showlogic->value)
750 // {
751 // gi.dprintf ("TARGET VISIBILITY: ");
752 // if (e->targetname)
753 // gi.dprintf ("targetname %s\n", e->targetname);
754 // else
755 // gi.dprintf ("start -> %s\n", e->target);
756 // }
757 if (checkpoint)
758 {
759 checkpoint->target_hint_chain = e->target_hint_chain;
760 e->target_hint_chain = NULL;
761 e = checkpoint->target_hint_chain;
762 continue;
763 }
764 else
765 {
766 // use checkpoint as temp pointer
767 checkpoint = e;
768 e = e->target_hint_chain;
769 checkpoint->target_hint_chain = NULL;
770 // and clear it again
771 checkpoint = NULL;
772 target_pathchain = e;
773 continue;
774 }
775 }
776 // if it passes all the tests, it's a keeper
777 // if (g_showlogic && g_showlogic->value)
778 // {
779 // gi.dprintf ("TARGET ACCEPT: ");
780 // if (e->targetname)
781 // gi.dprintf ("targetname %s\n", e->targetname);
782 // else
783 // gi.dprintf ("start -> %s\n", e->target);
784 // }
785 count5++;
786 checkpoint = e;
787 e = e->target_hint_chain;
788 }
789
790 // at this point we should have:
791 // monster_pathchain - a list of "monster valid" hint_path nodes linked together by monster_hint_chain
792 // target_pathcain - a list of "target valid" hint_path nodes linked together by target_hint_chain. these
793 // are filtered such that only nodes which are on the same chain as "monster valid" nodes
794 //
795 // Now, we figure out which "monster valid" node we want to use
796 //
797 // To do this, we first off make sure we have some target nodes. If we don't, there are no valid hint_path nodes
798 // for us to take
799 //
800 // If we have some, we filter all of our "monster valid" nodes by which ones have "target valid" nodes on them
801 //
802 // Once this filter is finished, we select the closest "monster valid" node, and go to it.
803
804 if (count5 == 0)
805 {
806 // if ((g_showlogic) && (g_showlogic->value))
807 // gi.dprintf ("No valid target nodes found\n");
808 return false;
809 }
810
811 // reuse the hint_chain_represented array, this time to see which chains are represented by the target
812 for (i=0; i < num_hint_paths; i++)
813 {
814 hint_path_represented[i] = false;
815 }
816
817 e = target_pathchain;
818 checkpoint = NULL;
819 while (e)
820 {
821 if ((e->hint_chain_id < 0) || (e->hint_chain_id > num_hint_paths))
822 {
823 // gi.dprintf ("bad hint_chain_id! %d\n", e->hint_chain_id);
824 return false;
825 }
826 hint_path_represented[e->hint_chain_id] = true;
827 e = e->target_hint_chain;
828 }
829
830 // traverse the monster_pathchain - if the hint_node isn't represented in the "target valid" chain list,
831 // remove it
832 // if it is on the list, check it for range from the monster. If the range is the closest, keep it
833 //
834 closest = NULL;
835 e = monster_pathchain;
836 while (e)
837 {
838 if (!(hint_path_represented[e->hint_chain_id]))
839 {
840 checkpoint = e->monster_hint_chain;
841 e->monster_hint_chain = NULL;
842 e = checkpoint;
843 continue;
844 }
845 r = realrange(self, e);
846 if (r < closest_range)
847 closest = e;
848 e = e->monster_hint_chain;
849 }
850
851 if (!closest)
852 {
853 // if ((g_showlogic) && (g_showlogic->value))
854 // gi.dprintf ("Failed to find closest node for monster. Shouldn't happen.\n");
855 return false;
856 }
857
858 start = closest;
859 // now we know which one is the closest to the monster .. this is the one the monster will go to
860 // we need to finally determine what the DESTINATION node is for the monster .. walk down the hint_chain,
861 // and find the closest one to the player
862
863 closest = NULL;
864 closest_range = 10000000;
865 e = target_pathchain;
866 while (e)
867 {
868 if (start->hint_chain_id == e->hint_chain_id)
869 {
870 r = realrange(self, e);
871 if (r < closest_range)
872 closest = e;
873 }
874 e = e->target_hint_chain;
875 }
876
877 if (!closest)
878 {
879 // if ((g_showlogic) && (g_showlogic->value))
880 // gi.dprintf ("Failed to find closest node for target. Shouldn't happen.\n");
881 return false;
882 }
883
884 destination = closest;
885
886 self->monsterinfo.goal_hint = destination;
887 // self->monsterinfo.last_hint = NULL;
888 hintpath_go(self, start);
889
890 // if(g_showlogic && g_showlogic->value)
891 // {
892 // gi.dprintf ("found path. proceed to ");
893 // if (start->targetname)
894 // gi.dprintf ("%s to get to ", start->targetname);
895 // else
896 // gi.dprintf ("start (->%s) to get to ", start->target);
897 // if (destination->targetname)
898 // gi.dprintf ("%s.", destination->targetname);
899 // else
900 // gi.dprintf ("start (->%s)", destination->target);
901 // }
902 // gi.dprintf("found path. proceed to %s to get to %s\n", vtos(start->s.origin), vtos(destination->s.origin));
903
904 return true;
905 }
906 /*
907 qboolean monsterlost_checkhint2 (edict_t *self)
908 {
909 edict_t *e, *e2, *goPoint;
910 int field;
911 int playerVisible, selfVisible;
912
913 // if there are no hint paths on this map, exit immediately.
914 if(!hint_paths_present)
915 return false;
916
917 if(!self->enemy)
918 return false;
919
920 goPoint = NULL;
921 field = FOFS(classname);
922
923 // check all the hint_paths.
924 e = G_Find(NULL, field, "hint_path");
925 while(e)
926 {
927 // if it's an endpoint, check for "validity"
928 if(e->spawnflags & HINT_ENDPOINT)
929 {
930 // check visibility from this spot
931 selfVisible = visible(e, self);
932 playerVisible = visible(e, self->enemy);
933 // gi.dprintf("checking endpoint at %s %d %d\n", vtos(e->s.origin),selfVisible,playerVisible);
934
935 // at least one of us is visible from this endpoint.
936 // now check the other one if needed.
937 if(selfVisible || playerVisible)
938 {
939 // if endpoint 1 saw me, set my destination to it.
940 if(selfVisible)
941 goPoint = e;
942
943 // if both aren't visible, try the other endpoint
944 if(!selfVisible || !playerVisible)
945 {
946 e2 = hintpath_other_end(e);
947 if(!e2) // could not connect to the other endpoint
948 {
949 gi.dprintf("Unlinked hint paths!\n");
950 return false;
951 }
952
953 // if endpoint 1 saw the enemy, see if endpoint 2 sees me
954 if(!selfVisible)
955 selfVisible = visible(e2, self);
956 // if endpoint 1 saw me, see if endpoint 2 sees the enemy
957 else if(!playerVisible)
958 playerVisible = visible(e2, self->enemy);
959
960 // if endpoint 2 saw me, set my destination to it.
961 if(!goPoint && selfVisible)
962 goPoint = e2;
963
964 // gi.dprintf("checking other endpoint at %s %d %d\n", vtos(e2->s.origin),selfVisible,playerVisible);
965 }
966
967 // if both are visible from at least one endpoint,
968 // go for it.
969 if(selfVisible && playerVisible)
970 {
971 // set me to go to goPoint
972 if(g_showlogic && g_showlogic->value)
973 gi.dprintf("found path. proceed to %s\n", vtos(goPoint->s.origin));
974
975 // since this is a new hint path trip, set last_hint to NULL
976 self->monsterinfo.last_hint = NULL;
977 hintpath_go(self, goPoint);
978 return true;
979 }
980 }
981 }
982 e = G_Find(e, field, "hint_path");
983 }
984
985 // if we got here, we didn't find a valid path
986 if(g_showlogic && g_showlogic->value)
987 gi.dprintf("blocked_checkhint: found no paths\n");
988 return false;
989 }
990 */
991 //
992 // Path code
993 //
994
995 // =============
996 // hint_path_touch - someone's touched the hint_path
997 // =============
hint_path_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)998 void hint_path_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
999 {
1000 edict_t *e, *goal, *next;
1001 // int chain; // direction - (-1) = upstream, (1) = downstream, (0) = done
1002 qboolean goalFound = false;
1003
1004 // make sure we're the target of it's obsession
1005 if(other->movetarget == self)
1006 {
1007 goal = other->monsterinfo.goal_hint;
1008
1009 // if the monster is where he wants to be
1010 if (goal == self)
1011 {
1012 // if(g_showlogic && g_showlogic->value)
1013 // gi.dprintf("Got to goal, detatching\n");
1014 hintpath_stop (other);
1015 return;
1016 }
1017 else
1018 {
1019 // if we aren't, figure out which way we want to go
1020 e = hint_path_start[self->hint_chain_id];
1021 while (e)
1022 {
1023 // if we get up to ourselves on the hint chain, we're going down it
1024 if (e == self)
1025 {
1026 next = e->hint_chain;
1027 break;
1028 }
1029 if (e == goal)
1030 goalFound = true;
1031 // if we get to where the next link on the chain is this hint_path and have found the goal on the way
1032 // we're going upstream, so remember who the previous link is
1033 if ((e->hint_chain == self) && goalFound)
1034 {
1035 next = e;
1036 break;
1037 }
1038 e = e->hint_chain;
1039 }
1040 }
1041
1042 // if we couldn't find it, have the monster go back to normal hunting.
1043 if(!next)
1044 {
1045 // if(g_showlogic && g_showlogic->value)
1046 // gi.dprintf("couldn't figure out next node, dropping hint path\n");
1047 hintpath_stop(other);
1048 return;
1049 }
1050
1051 // set the last_hint entry to this hint_path, and
1052 // send him on his way
1053 // other->monsterinfo.last_hint = self;
1054 // if(g_showlogic && g_showlogic->value)
1055 // {
1056 // gi.dprintf("moving to next point, ");
1057 // if (next->targetname)
1058 // gi.dprintf ("targetname %s\n", next->targetname);
1059 // else
1060 // gi.dprintf ("start -> %s\n", next->target);
1061 // }
1062 hintpath_go(other, next);
1063
1064 // have the monster freeze if the hint path we just touched has a wait time
1065 // on it, for example, when riding a plat.
1066 if(self->wait)
1067 {
1068 // if(g_showlogic && g_showlogic->value)
1069 // gi.dprintf("monster waiting %0.1f\n", self->wait);
1070 other->nextthink = level.time + self->wait;
1071 }
1072 }
1073 }
1074 /*
1075 void hint_path_touch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1076 {
1077 edict_t *next, *last;
1078 int chain;
1079
1080 // make sure we're the target of it's obsession
1081 if(other->movetarget == self)
1082 {
1083 chain = 0; // direction the monster is going in the chain
1084 next = NULL; // next hint_path
1085
1086 // gi.dprintf("hint_path %s\n", vtos(self->s.origin));
1087 // is this the first hintpath targeted? if so, we can do this easily.
1088 if(other->monsterinfo.last_hint == NULL)
1089 {
1090 if(self->target) // forward chaining
1091 chain = 1;
1092 else // backward chaining
1093 chain = -1;
1094 }
1095 else
1096 {
1097 // shortcut to last_hint
1098 last = other->monsterinfo.last_hint;
1099
1100 // make sure it's valid...
1101 if ( (last < g_edicts) || (last >= &g_edicts[globals.num_edicts]))
1102 {
1103 if(g_showlogic && g_showlogic->value)
1104 {
1105 gi.dprintf("bogus last_hint encountered.\n");
1106 gi.dprintf("detaching from hint path %d\n", chain);
1107 }
1108 hintpath_stop (other);
1109 return;
1110 }
1111
1112 // if we're an endpoint, then the monster is done moving.
1113 if(self->spawnflags & HINT_ENDPOINT)
1114 {
1115 chain = 0;
1116 }
1117 // if last hint's target is our targetname, it's forward chaining.
1118 else if(last->target && self->targetname && !strcmp(last->target, self->targetname))
1119 {
1120 chain = 1;
1121 }
1122 // if last hint's targetname is our target, it's backward chaining.
1123 // FIXME - last->targetname was 1, not NULL ???? was a screwed up hintpath
1124 else if(self->target && last->targetname && !strcmp(last->targetname, self->target))
1125 {
1126 chain = -1;
1127 }
1128 else // if it gets here, i'm not sure how
1129 {
1130 gi.dprintf("hit an uncovered possibility in hint_path_touch\n");
1131 chain = 0;
1132 }
1133 }
1134
1135 // find the "next" hint_path
1136 if(chain == 1 && self->target) // forward chaining
1137 next = G_Find(NULL, FOFS(targetname), self->target);
1138 else if(chain == -1 && self->targetname) // backward chaining
1139 next = G_Find(NULL, FOFS(target), self->targetname);
1140
1141 // if we couldn't find it, have the monster go back to normal hunting.
1142 if(!next)
1143 {
1144 if(g_showlogic && g_showlogic->value)
1145 gi.dprintf("detaching from hint path %d\n", chain);
1146 hintpath_stop(other);
1147 return;
1148 }
1149
1150 // set the last_hint entry to this hint_path, and
1151 // send him on his way
1152 other->monsterinfo.last_hint = self;
1153 if(g_showlogic && g_showlogic->value)
1154 gi.dprintf("moving to next point, %s\n", vtos(next->s.origin));
1155 hintpath_go(other, next);
1156
1157 // have the monster freeze if the hint path we just touched has a wait time
1158 // on it, for example, when riding a plat.
1159 if(self->wait)
1160 {
1161 if(g_showlogic && g_showlogic->value)
1162 gi.dprintf("monster waiting %0.1f\n", self->wait);
1163 other->nextthink = level.time + self->wait;
1164 }
1165 }
1166 }
1167 */
1168
1169 /*QUAKED hint_path (.5 .3 0) (-8 -8 -8) (8 8 8) END
1170 Target: next hint path
1171
1172 END - set this flag on the endpoints of each hintpath.
1173
1174 "wait" - set this if you want the monster to freeze when they touch this hintpath
1175 */
SP_hint_path(edict_t * self)1176 void SP_hint_path (edict_t *self)
1177 {
1178 if (deathmatch->value)
1179 {
1180 G_FreeEdict(self);
1181 return;
1182 }
1183
1184 if (!self->targetname && !self->target)
1185 {
1186 gi.dprintf ("unlinked hint_path at %s\n", vtos(self->s.origin));
1187 G_FreeEdict (self);
1188 return;
1189 }
1190
1191 self->solid = SOLID_TRIGGER;
1192 self->touch = hint_path_touch;
1193 VectorSet (self->mins, -8, -8, -8);
1194 VectorSet (self->maxs, 8, 8, 8);
1195 self->svflags |= SVF_NOCLIENT;
1196 gi.linkentity (self);
1197 }
1198
1199 //int hint_paths_present;
1200 //edict_t *hint_path_start[100];
1201 //int num_hint_paths;
1202
1203 // ============
1204 // InitHintPaths - Called by InitGame (g_save) to enable quick exits if valid
1205 // ============
InitHintPaths(void)1206 void InitHintPaths (void)
1207 {
1208 edict_t *e, *current;
1209 int field, i, count2;
1210 qboolean errors = false;
1211
1212 hint_paths_present = 0;
1213
1214 // check all the hint_paths.
1215 field = FOFS(classname);
1216 e = G_Find(NULL, field, "hint_path");
1217 if(e)
1218 {
1219 // gi.dprintf("hint paths present on map\n");
1220 hint_paths_present = 1;
1221 }
1222 else
1223 {
1224 // if ((g_showlogic) && (g_showlogic->value))
1225 // gi.dprintf ("hint paths not present on map\n");
1226 return;
1227 }
1228
1229 memset (hint_path_start, 0, MAX_HINT_CHAINS*sizeof (edict_t *));
1230 num_hint_paths = 0;
1231 while(e)
1232 {
1233 if(e->spawnflags & HINT_ENDPOINT)
1234 {
1235 if (e->target) // start point
1236 {
1237 if (e->targetname) // this is a bad end, ignore it
1238 {
1239 gi.dprintf ("Hint path at %s marked as endpoint with both target (%s) and targetname (%s)\n",
1240 vtos (e->s.origin), e->target, e->targetname);
1241 errors = true;
1242 }
1243 else
1244 {
1245 if (num_hint_paths >= MAX_HINT_CHAINS)
1246 {
1247 // gi.dprintf ("Only %d hint chains allowed. Connect some together!\n", MAX_HINT_CHAINS);
1248 break;
1249 }
1250 hint_path_start[num_hint_paths++] = e;
1251 }
1252 }
1253 }
1254 e = G_Find(e, field, "hint_path");
1255 }
1256
1257 field = FOFS(targetname);
1258 for (i=0; i< num_hint_paths; i++)
1259 {
1260 count2 = 1;
1261 current = hint_path_start[i];
1262 current->hint_chain_id = i;
1263 // gi.dprintf ("start ");
1264 e = G_Find(NULL, field, current->target);
1265 if (G_Find(e, field, current->target))
1266 {
1267 gi.dprintf ("\nForked hint path at %s detected for chain %d, target %s\n",
1268 vtos (current->s.origin), num_hint_paths, current->target);
1269 hint_path_start[i]->hint_chain = NULL;
1270 count2 = 0;
1271 errors = true;
1272 continue;
1273 }
1274 while (e)
1275 {
1276 if (e->hint_chain)
1277 {
1278 gi.dprintf ("\nCircular hint path at %s detected for chain %d, targetname %s\n",
1279 vtos (e->s.origin), num_hint_paths, e->targetname);
1280 hint_path_start[i]->hint_chain = NULL;
1281 count2 = 0;
1282 errors = true;
1283 break;
1284 }
1285 count2++;
1286 current->hint_chain = e;
1287 current = e;
1288 current->hint_chain_id = i;
1289 // gi.dprintf ("-> %s ", current->targetname);
1290 if (!current->target)
1291 break;
1292 e = G_Find(NULL, field, current->target);
1293 if (G_Find(e, field, current->target))
1294 {
1295 gi.dprintf ("\nForked hint path at %s detected for chain %d, target %s\n",
1296 vtos (current->s.origin), num_hint_paths, current->target);
1297 hint_path_start[i]->hint_chain = NULL;
1298 count2 = 0;
1299 break;
1300 }
1301 }
1302 // if ((g_showlogic) && (g_showlogic->value))
1303 // if (count2)
1304 // {
1305 // goodcount++;
1306 // gi.dprintf ("\nhint_path #%d, %d elements\n\n", i, count2);
1307 // }
1308 // else
1309 // gi.dprintf ("\nhint_path #%d invalid\n\n", i);
1310 }
1311 // if (errors)
1312 // gi.error ("hint_path processing failed, fix errors\n");
1313 // if ((g_showlogic) && (g_showlogic->value))
1314 // gi.dprintf ("hint_path processing done, %d hint paths linked\n", num_hint_paths);
1315 }
1316
1317 // *****************************
1318 // MISCELLANEOUS STUFF
1319 // *****************************
1320
1321 // PMM - inback
1322 // use to see if opponent is behind you (not to side)
1323 // if it looks a lot like infront, well, there's a reason
1324
inback(edict_t * self,edict_t * other)1325 qboolean inback (edict_t *self, edict_t *other)
1326 {
1327 vec3_t vec;
1328 float dot;
1329 vec3_t forward;
1330
1331 AngleVectors (self->s.angles, forward, NULL, NULL);
1332 VectorSubtract (other->s.origin, self->s.origin, vec);
1333 VectorNormalize (vec);
1334 dot = DotProduct (vec, forward);
1335
1336 if (dot < -0.3)
1337 return true;
1338 return false;
1339 }
1340
realrange(edict_t * self,edict_t * other)1341 float realrange (edict_t *self, edict_t *other)
1342 {
1343 vec3_t dir;
1344
1345 VectorSubtract (self->s.origin, other->s.origin, dir);
1346
1347 return VectorLength(dir);
1348 }
1349
face_wall(edict_t * self)1350 qboolean face_wall (edict_t *self)
1351 {
1352 vec3_t pt;
1353 vec3_t forward;
1354 vec3_t ang;
1355 trace_t tr;
1356
1357 AngleVectors (self->s.angles, forward, NULL, NULL);
1358 VectorMA(self->s.origin, 64, forward, pt);
1359 tr = gi.trace(self->s.origin, vec3_origin, vec3_origin, pt, self, MASK_MONSTERSOLID);
1360 if(tr.fraction < 1 && !tr.allsolid && !tr.startsolid)
1361 {
1362 vectoangles2(tr.plane.normal, ang);
1363 self->ideal_yaw = ang[YAW] + 180;
1364 if(self->ideal_yaw > 360)
1365 self->ideal_yaw -= 360;
1366
1367 // if(g_showlogic && g_showlogic->value)
1368 // gi.dprintf("facing wall, dir %0.1f/%0.1f\n", ang[YAW], self->ideal_yaw);
1369 M_ChangeYaw(self);
1370 return true;
1371 }
1372
1373 return false;
1374 }
1375
1376 //
1377 // Monster "Bad" Areas
1378 //
1379
badarea_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)1380 void badarea_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
1381 {
1382 // drawbbox(ent);
1383 }
1384
SpawnBadArea(vec3_t mins,vec3_t maxs,float lifespan,edict_t * owner)1385 edict_t *SpawnBadArea(vec3_t mins, vec3_t maxs, float lifespan, edict_t *owner)
1386 {
1387 edict_t *badarea;
1388 vec3_t origin;
1389
1390 VectorAdd(mins, maxs, origin);
1391 VectorScale(origin, 0.5, origin);
1392
1393 VectorSubtract(maxs, origin, maxs);
1394 VectorSubtract(mins, origin, mins);
1395
1396 badarea = G_Spawn();
1397 VectorCopy(origin, badarea->s.origin);
1398 VectorCopy(maxs, badarea->maxs);
1399 VectorCopy(mins, badarea->mins);
1400 badarea->touch = badarea_touch;
1401 badarea->movetype = MOVETYPE_NONE;
1402 badarea->solid = SOLID_TRIGGER;
1403 badarea->classname = "bad_area";
1404 gi.linkentity (badarea);
1405
1406 // gi.dprintf("(%s)-(%s)\n", vtos(badarea->absmin), vtos(badarea->absmax));
1407
1408 if(lifespan)
1409 {
1410 badarea->think = G_FreeEdict;
1411 badarea->nextthink = level.time + lifespan;
1412 }
1413 if(owner)
1414 {
1415 badarea->owner = owner;
1416 }
1417
1418 // drawbbox(badarea);
1419 return badarea;
1420 }
1421
1422 // CheckForBadArea
1423 // This is a customized version of G_TouchTriggers that will check
1424 // for bad area triggers and return them if they're touched.
CheckForBadArea(edict_t * ent)1425 edict_t *CheckForBadArea(edict_t *ent)
1426 {
1427 int i, num;
1428 edict_t *touch[MAX_EDICTS], *hit;
1429 vec3_t mins, maxs;
1430
1431 VectorAdd(ent->s.origin, ent->mins, mins);
1432 VectorAdd(ent->s.origin, ent->maxs, maxs);
1433
1434 num = gi.BoxEdicts (mins, maxs, touch, MAX_EDICTS, AREA_TRIGGERS);
1435
1436 // drawbbox(ent);
1437
1438 // be careful, it is possible to have an entity in this
1439 // list removed before we get to it (killtriggered)
1440 for (i=0 ; i<num ; i++)
1441 {
1442 hit = touch[i];
1443 if (!hit->inuse)
1444 continue;
1445 if (hit->touch == badarea_touch)
1446 {
1447 return hit;
1448 }
1449 }
1450
1451 return NULL;
1452 }
1453
1454 #define TESLA_DAMAGE_RADIUS 128
1455
MarkTeslaArea(edict_t * self,edict_t * tesla)1456 qboolean MarkTeslaArea(edict_t *self, edict_t *tesla)
1457 {
1458 vec3_t mins, maxs;
1459 edict_t *e;
1460 edict_t *tail;
1461 edict_t *area;
1462
1463 if(!tesla || !self)
1464 return false;
1465
1466 area = NULL;
1467
1468 // make sure this tesla doesn't have a bad area around it already...
1469 e = tesla->teamchain;
1470 tail = tesla;
1471 while (e)
1472 {
1473 tail = tail->teamchain;
1474 if(!strcmp(e->classname, "bad_area"))
1475 {
1476 // gi.dprintf("tesla already has a bad area marked\n");
1477 return false;
1478 }
1479 e = e->teamchain;
1480 }
1481
1482 // see if we can grab the trigger directly
1483 if(tesla->teamchain && tesla->teamchain->inuse)
1484 {
1485 edict_t *trigger;
1486
1487 trigger = tesla->teamchain;
1488
1489 // VectorAdd (trigger->s.origin, trigger->mins, mins);
1490 // VectorAdd (trigger->s.origin, trigger->maxs, maxs);
1491 VectorCopy(trigger->absmin, mins);
1492 VectorCopy(trigger->absmax, maxs);
1493
1494 if(tesla->air_finished)
1495 area = SpawnBadArea (mins, maxs, tesla->air_finished, tesla);
1496 else
1497 area = SpawnBadArea (mins, maxs, tesla->nextthink, tesla);
1498 }
1499 // otherwise we just guess at how long it'll last.
1500 else
1501 {
1502
1503 VectorSet (mins, -TESLA_DAMAGE_RADIUS, -TESLA_DAMAGE_RADIUS, tesla->mins[2]);
1504 VectorSet (maxs, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS, TESLA_DAMAGE_RADIUS);
1505
1506 area = SpawnBadArea(mins, maxs, 30, tesla);
1507 }
1508
1509 // if we spawned a bad area, then link it to the tesla
1510 if(area)
1511 {
1512 // gi.dprintf("bad area marker spawned and linked to tesla\n");
1513 tail->teamchain = area;
1514 }
1515 return true;
1516 }
1517
1518 // predictive calculator
1519 // target is who you want to shoot
1520 // start is where the shot comes from
1521 // bolt_speed is how fast the shot is
1522 // eye_height is a boolean to say whether or not to adjust to targets eye_height
1523 // offset is how much time to miss by
1524 // aimdir is the resulting aim direction (pass in NULL if you don't want it)
1525 // aimpoint is the resulting aimpoint (pass in NULL if don't want it)
PredictAim(edict_t * target,vec3_t start,float bolt_speed,qboolean eye_height,float offset,vec3_t aimdir,vec3_t aimpoint)1526 void PredictAim (edict_t *target, vec3_t start, float bolt_speed, qboolean eye_height, float offset, vec3_t aimdir, vec3_t aimpoint)
1527 {
1528 vec3_t dir, vec;
1529 float dist, time;
1530
1531 if (!target || !target->inuse)
1532 {
1533 VectorCopy (vec3_origin, aimdir);
1534 return;
1535 }
1536
1537 VectorSubtract(target->s.origin, start, dir);
1538 if (eye_height)
1539 dir[2] += target->viewheight;
1540 dist = VectorLength(dir);
1541 time = dist / bolt_speed;
1542
1543
1544 VectorMA(target->s.origin, time - offset, target->velocity, vec);
1545
1546 if (eye_height)
1547 vec[2] += target->viewheight;
1548
1549 if (aimdir)
1550 {
1551 VectorSubtract (vec, start, aimdir);
1552 VectorNormalize (aimdir);
1553 }
1554
1555 if (aimpoint)
1556 {
1557 VectorCopy (vec, aimpoint);
1558 }
1559 }
1560
1561
below(edict_t * self,edict_t * other)1562 qboolean below (edict_t *self, edict_t *other)
1563 {
1564 vec3_t vec;
1565 float dot;
1566 vec3_t down;
1567
1568 VectorSubtract (other->s.origin, self->s.origin, vec);
1569 VectorNormalize (vec);
1570 VectorSet (down, 0, 0, -1);
1571 dot = DotProduct (vec, down);
1572
1573 if (dot > 0.95) // 18 degree arc below
1574 return true;
1575 return false;
1576 }
1577
drawbbox(edict_t * self)1578 void drawbbox (edict_t *self)
1579 {
1580 int lines[4][3] = {
1581 {1, 2, 4},
1582 {1, 2, 7},
1583 {1, 4, 5},
1584 {2, 4, 7}
1585 };
1586
1587 int starts[4] = {0, 3, 5, 6};
1588
1589 vec3_t pt[8];
1590 int i, j, k;
1591 vec3_t coords[2];
1592 vec3_t newbox;
1593 vec3_t f,r,u, dir;
1594
1595 VectorCopy (self->absmin, coords[0]);
1596 VectorCopy (self->absmax, coords[1]);
1597
1598 for (i=0; i<=1; i++)
1599 {
1600 for (j=0; j<=1; j++)
1601 {
1602 for (k=0; k<=1; k++)
1603 {
1604 pt[4*i+2*j+k][0] = coords[i][0];
1605 pt[4*i+2*j+k][1] = coords[j][1];
1606 pt[4*i+2*j+k][2] = coords[k][2];
1607 }
1608 }
1609 }
1610
1611 for (i=0; i<= 3; i++)
1612 {
1613 for (j=0; j<= 2; j++)
1614 {
1615 gi.WriteByte (svc_temp_entity);
1616 gi.WriteByte (TE_DEBUGTRAIL);
1617 gi.WritePosition (pt[starts[i]]);
1618 gi.WritePosition (pt[lines[i][j]]);
1619 gi.multicast (pt[starts[i]], MULTICAST_ALL);
1620 }
1621 }
1622
1623 vectoangles2 (self->s.angles, dir);
1624 AngleVectors (dir, f, r, u);
1625
1626 VectorMA (self->s.origin, 50, f, newbox);
1627 gi.WriteByte (svc_temp_entity);
1628 gi.WriteByte (TE_DEBUGTRAIL);
1629 gi.WritePosition (self->s.origin);
1630 gi.WritePosition (newbox);
1631 gi.multicast (self->s.origin, MULTICAST_PVS);
1632 VectorClear (newbox);
1633
1634 VectorMA (self->s.origin, 50, r, newbox);
1635 gi.WriteByte (svc_temp_entity);
1636 gi.WriteByte (TE_DEBUGTRAIL);
1637 gi.WritePosition (self->s.origin);
1638 gi.WritePosition (newbox);
1639 gi.multicast (self->s.origin, MULTICAST_PVS);
1640 VectorClear (newbox);
1641
1642 VectorMA (self->s.origin, 50, u, newbox);
1643 gi.WriteByte (svc_temp_entity);
1644 gi.WriteByte (TE_DEBUGTRAIL);
1645 gi.WritePosition (self->s.origin);
1646 gi.WritePosition (newbox);
1647 gi.multicast (self->s.origin, MULTICAST_PVS);
1648 VectorClear (newbox);
1649 }
1650
1651 //
1652 // New dodge code
1653 //
M_MonsterDodge(edict_t * self,edict_t * attacker,float eta,trace_t * tr)1654 void M_MonsterDodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
1655 {
1656 float r = random();
1657 float height;
1658 qboolean ducker = false, dodger = false;
1659
1660 // this needs to be here since this can be called after the monster has "died"
1661 if (self->health < 1)
1662 return;
1663
1664 if ((self->monsterinfo.duck) && (self->monsterinfo.unduck))
1665 ducker = true;
1666 if ((self->monsterinfo.sidestep) && !(self->monsterinfo.aiflags & AI_STAND_GROUND))
1667 dodger = true;
1668
1669 if ((!ducker) && (!dodger))
1670 return;
1671
1672 // if ((g_showlogic) && (g_showlogic->value))
1673 // {
1674 // if (self->monsterinfo.aiflags & AI_DODGING)
1675 // gi.dprintf ("dodging - ");
1676 // if (self->monsterinfo.aiflags & AI_DUCKED)
1677 // gi.dprintf ("ducked - ");
1678 // }
1679 if (!self->enemy)
1680 {
1681 self->enemy = attacker;
1682 FoundTarget (self);
1683 }
1684
1685 // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
1686 // seeing numbers like 13 and 14)
1687 if ((eta < 0.1) || (eta > 5))
1688 {
1689 // if ((g_showlogic) && (g_showlogic->value))
1690 // gi.dprintf ("timeout\n");
1691 return;
1692 }
1693
1694 // skill level determination..
1695 if (r > (0.25*((skill->value)+1)))
1696 {
1697 // if ((g_showlogic) && (g_showlogic->value))
1698 // gi.dprintf ("skillout\n");
1699 return;
1700 }
1701
1702 // stop charging, since we're going to dodge (somehow) instead
1703 // soldier_stop_charge (self);
1704
1705 if (ducker)
1706 {
1707 height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1
1708
1709 // FIXME, make smarter
1710 // if we only duck, and ducking won't help or we're already ducking, do nothing
1711 //
1712 // need to add monsterinfo.abort_duck() and monsterinfo.next_duck_time
1713
1714 if ((!dodger) && ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED)))
1715 return;
1716 }
1717 else
1718 height = self->absmax[2];
1719
1720 if (dodger)
1721 {
1722 // if we're already dodging, just finish the sequence, i.e. don't do anything else
1723 if (self->monsterinfo.aiflags & AI_DODGING)
1724 {
1725 // if ((g_showlogic) && (g_showlogic->value))
1726 // gi.dprintf ("already dodging\n");
1727 return;
1728 }
1729
1730 // if we're ducking already, or the shot is at our knees
1731 if ((tr->endpos[2] <= height) || (self->monsterinfo.aiflags & AI_DUCKED))
1732 {
1733 vec3_t right, diff;
1734
1735 AngleVectors (self->s.angles, NULL, right, NULL);
1736 VectorSubtract (tr->endpos, self->s.origin, diff);
1737
1738 if (DotProduct (right, diff) < 0)
1739 {
1740 self->monsterinfo.lefty = 0;
1741 // gi.dprintf ("left\n");
1742 } else {
1743 self->monsterinfo.lefty = 1;
1744 // gi.dprintf ("right\n");
1745 }
1746
1747 // if we are currently ducked, unduck
1748
1749 if ((ducker) && (self->monsterinfo.aiflags & AI_DUCKED))
1750 {
1751 // if ((g_showlogic) && (g_showlogic->value))
1752 // gi.dprintf ("unducking - ");
1753 self->monsterinfo.unduck(self);
1754 }
1755
1756 self->monsterinfo.aiflags |= AI_DODGING;
1757 self->monsterinfo.attack_state = AS_SLIDING;
1758
1759 // call the monster specific code here
1760 self->monsterinfo.sidestep (self);
1761 return;
1762 }
1763 }
1764
1765 if (ducker)
1766 {
1767 if (self->monsterinfo.next_duck_time > level.time)
1768 {
1769 // if ((g_showlogic) && (g_showlogic->value))
1770 // gi.dprintf ("ducked too often, not ducking\n");
1771 return;
1772 }
1773
1774 // if ((g_showlogic) && (g_showlogic->value))
1775 // gi.dprintf ("ducking!\n");
1776
1777 monster_done_dodge (self);
1778 // set this prematurely; it doesn't hurt, and prevents extra iterations
1779 self->monsterinfo.aiflags |= AI_DUCKED;
1780
1781 self->monsterinfo.duck (self, eta);
1782 }
1783 }
1784
monster_duck_down(edict_t * self)1785 void monster_duck_down (edict_t *self)
1786 {
1787 // if (self->monsterinfo.aiflags & AI_DUCKED)
1788 // return;
1789 self->monsterinfo.aiflags |= AI_DUCKED;
1790
1791 // if ((g_showlogic) && (g_showlogic->value))
1792 // gi.dprintf ("duck down!\n");
1793 // self->maxs[2] -= 32;
1794 self->maxs[2] = self->monsterinfo.base_height - 32;
1795 self->takedamage = DAMAGE_YES;
1796 if (self->monsterinfo.duck_wait_time < level.time)
1797 self->monsterinfo.duck_wait_time = level.time + 1;
1798 gi.linkentity (self);
1799 }
1800
monster_duck_hold(edict_t * self)1801 void monster_duck_hold (edict_t *self)
1802 {
1803 if (level.time >= self->monsterinfo.duck_wait_time)
1804 self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
1805 else
1806 self->monsterinfo.aiflags |= AI_HOLD_FRAME;
1807 }
1808
monster_duck_up(edict_t * self)1809 void monster_duck_up (edict_t *self)
1810 {
1811 self->monsterinfo.aiflags &= ~AI_DUCKED;
1812 // self->maxs[2] += 32;
1813 self->maxs[2] = self->monsterinfo.base_height;
1814 self->takedamage = DAMAGE_AIM;
1815 self->monsterinfo.next_duck_time = level.time + DUCK_INTERVAL;
1816 gi.linkentity (self);
1817 }
1818
1819 //=========================
1820 //=========================
has_valid_enemy(edict_t * self)1821 qboolean has_valid_enemy (edict_t *self)
1822 {
1823 if (!self->enemy)
1824 return false;
1825
1826 if (!self->enemy->inuse)
1827 return false;
1828
1829 if (self->enemy->health < 1)
1830 return false;
1831
1832 return true;
1833 }
1834
TargetTesla(edict_t * self,edict_t * tesla)1835 void TargetTesla (edict_t *self, edict_t *tesla)
1836 {
1837 if ((!self) || (!tesla))
1838 return;
1839
1840 // PMM - medic bails on healing things
1841 if (self->monsterinfo.aiflags & AI_MEDIC)
1842 {
1843 if (self->enemy)
1844 cleanupHealTarget(self->enemy);
1845 self->monsterinfo.aiflags &= ~AI_MEDIC;
1846 }
1847
1848 // store the player enemy in case we lose track of him.
1849 if(self->enemy && self->enemy->client)
1850 self->monsterinfo.last_player_enemy = self->enemy;
1851
1852 if(self->enemy != tesla)
1853 {
1854 self->oldenemy = self->enemy;
1855 self->enemy = tesla;
1856 if(self->monsterinfo.attack)
1857 {
1858 if (self->health <= 0)
1859 {
1860 // if ((g_showlogic) && (g_showlogic->value))
1861 // gi.dprintf ("bad tesla attack avoided!\n");
1862 return;
1863 }
1864 self->monsterinfo.attack(self);
1865 }
1866 else
1867 {
1868 FoundTarget(self);
1869 }
1870 }
1871 }
1872
1873 // this returns a randomly selected coop player who is visible to self
1874 // returns NULL if bad
1875
PickCoopTarget(edict_t * self)1876 edict_t * PickCoopTarget (edict_t *self)
1877 {
1878 // no more than 4 players in coop, so..
1879 edict_t *targets[4];
1880 int num_targets = 0, targetID;
1881 edict_t *ent;
1882 int player;
1883
1884 // if we're not in coop, this is a noop
1885 if (!coop || !coop->value)
1886 return NULL;
1887
1888 memset (targets, 0, 4*sizeof(edict_t *));
1889
1890 for (player = 1; player <= game.maxclients; player++)
1891 {
1892 ent = &g_edicts[player];
1893 if (!ent->inuse)
1894 continue;
1895 if (!ent->client)
1896 continue;
1897 if (visible(self, ent))
1898 {
1899 // if ((g_showlogic) && (g_showlogic->value))
1900 // gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
1901 targets[num_targets++] = ent;
1902 }
1903 }
1904
1905 /*
1906 ent = g_edicts+1; // skip the worldspawn
1907 // cycle through players
1908 while (ent)
1909 {
1910 if ((ent->client) && (ent->inuse))
1911 {
1912 if (visible(self, ent))
1913 {
1914 if ((g_showlogic) && (g_showlogic->value))
1915 gi.dprintf ("%s: found coop player %s - ", self->classname, ent->client->pers.netname);
1916 targets[num_targets++] = ent;
1917 }
1918 ent++;
1919 }
1920 else
1921 ent = NULL;
1922 }
1923 */
1924
1925 if (!num_targets)
1926 return NULL;
1927
1928 // get a number from 0 to (num_targets-1)
1929 targetID = (random() * (float)num_targets);
1930
1931 // just in case we got a 1.0 from random
1932 if (targetID == num_targets)
1933 targetID--;
1934
1935 // if (g_showlogic && g_showlogic->value)
1936 // gi.dprintf ("using player %s\n", targets[targetID]->client->pers.netname);
1937 return targets[targetID];
1938 }
1939
1940 // only meant to be used in coop
CountPlayers(void)1941 int CountPlayers (void)
1942 {
1943 edict_t *ent;
1944 int count = 0;
1945 int player;
1946
1947 // if we're not in coop, this is a noop
1948 if (!coop || !coop->value)
1949 return 1;
1950
1951 for (player = 1; player <= game.maxclients; player++)
1952 {
1953 ent = &g_edicts[player];
1954 if (!ent->inuse)
1955 continue;
1956 if (!ent->client)
1957 continue;
1958 count++;
1959 }
1960 /*
1961 ent = g_edicts+1; // skip the worldspawn
1962 while (ent)
1963 {
1964 if ((ent->client) && (ent->inuse))
1965 {
1966 ent++;
1967 count++;
1968 }
1969 else
1970 ent = NULL;
1971 }
1972 */
1973 return count;
1974 }
1975
1976 //*******************
1977 // JUMPING AIDS
1978 //*******************
1979
monster_jump_start(edict_t * self)1980 void monster_jump_start (edict_t *self)
1981 {
1982 self->timestamp = level.time;
1983 }
1984
monster_jump_finished(edict_t * self)1985 qboolean monster_jump_finished (edict_t *self)
1986 {
1987 if ((level.time - self->timestamp) > 3)
1988 {
1989 // if (g_showlogic && g_showlogic->value)
1990 // {
1991 // gi.dprintf("%s jump timed out!\n", self->classname);
1992 // }
1993 return true;
1994 }
1995 }
1996
1997
1998
1999