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