|
|
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.