|
|
1.1 ! root 1: /* ! 2: ============================================================================== ! 3: ! 4: GUNNER ! 5: ! 6: ============================================================================== ! 7: */ ! 8: ! 9: #include "g_local.h" ! 10: #include "m_gunner.h" ! 11: ! 12: ! 13: static int sound_pain; ! 14: static int sound_pain2; ! 15: static int sound_death; ! 16: static int sound_idle; ! 17: static int sound_open; ! 18: static int sound_search; ! 19: static int sound_sight; ! 20: ! 21: void gunner_idlesound (edict_t *self) ! 22: { ! 23: gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); ! 24: } ! 25: ! 26: void gunner_sight (edict_t *self, edict_t *other) ! 27: { ! 28: gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); ! 29: } ! 30: ! 31: void gunner_search (edict_t *self) ! 32: { ! 33: gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); ! 34: } ! 35: ! 36: ! 37: qboolean visible (edict_t *self, edict_t *other); ! 38: void GunnerGrenade (edict_t *self); ! 39: void GunnerFire (edict_t *self); ! 40: void gunner_fire_chain(edict_t *self); ! 41: void gunner_refire_chain(edict_t *self); ! 42: ! 43: ! 44: void gunner_stand (edict_t *self); ! 45: ! 46: mframe_t gunner_frames_fidget [] = ! 47: { ! 48: ai_stand, 0, NULL, ! 49: ai_stand, 0, NULL, ! 50: ai_stand, 0, NULL, ! 51: ai_stand, 0, NULL, ! 52: ai_stand, 0, NULL, ! 53: ai_stand, 0, NULL, ! 54: ai_stand, 0, NULL, ! 55: ai_stand, 0, gunner_idlesound, ! 56: ai_stand, 0, NULL, ! 57: ! 58: ai_stand, 0, NULL, ! 59: ai_stand, 0, NULL, ! 60: ai_stand, 0, NULL, ! 61: ai_stand, 0, NULL, ! 62: ai_stand, 0, NULL, ! 63: ai_stand, 0, NULL, ! 64: ai_stand, 0, NULL, ! 65: ai_stand, 0, NULL, ! 66: ai_stand, 0, NULL, ! 67: ai_stand, 0, NULL, ! 68: ! 69: ai_stand, 0, NULL, ! 70: ai_stand, 0, NULL, ! 71: ai_stand, 0, NULL, ! 72: ai_stand, 0, NULL, ! 73: ai_stand, 0, NULL, ! 74: ai_stand, 0, NULL, ! 75: ai_stand, 0, NULL, ! 76: ai_stand, 0, NULL, ! 77: ai_stand, 0, NULL, ! 78: ai_stand, 0, NULL, ! 79: ! 80: ai_stand, 0, NULL, ! 81: ai_stand, 0, NULL, ! 82: ai_stand, 0, NULL, ! 83: ai_stand, 0, NULL, ! 84: ai_stand, 0, NULL, ! 85: ai_stand, 0, NULL, ! 86: ai_stand, 0, NULL, ! 87: ai_stand, 0, NULL, ! 88: ai_stand, 0, NULL, ! 89: ai_stand, 0, NULL, ! 90: ! 91: ai_stand, 0, NULL, ! 92: ai_stand, 0, NULL, ! 93: ai_stand, 0, NULL, ! 94: ai_stand, 0, NULL, ! 95: ai_stand, 0, NULL, ! 96: ai_stand, 0, NULL, ! 97: ai_stand, 0, NULL, ! 98: ai_stand, 0, NULL, ! 99: ai_stand, 0, NULL, ! 100: ai_stand, 0, NULL ! 101: }; ! 102: mmove_t gunner_move_fidget = {FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand}; ! 103: ! 104: void gunner_fidget (edict_t *self) ! 105: { ! 106: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 107: return; ! 108: if (random() <= 0.05) ! 109: self->monsterinfo.currentmove = &gunner_move_fidget; ! 110: } ! 111: ! 112: mframe_t gunner_frames_stand [] = ! 113: { ! 114: ai_stand, 0, NULL, ! 115: ai_stand, 0, NULL, ! 116: ai_stand, 0, NULL, ! 117: ai_stand, 0, NULL, ! 118: ai_stand, 0, NULL, ! 119: ai_stand, 0, NULL, ! 120: ai_stand, 0, NULL, ! 121: ai_stand, 0, NULL, ! 122: ai_stand, 0, NULL, ! 123: ai_stand, 0, gunner_fidget, ! 124: ! 125: ai_stand, 0, NULL, ! 126: ai_stand, 0, NULL, ! 127: ai_stand, 0, NULL, ! 128: ai_stand, 0, NULL, ! 129: ai_stand, 0, NULL, ! 130: ai_stand, 0, NULL, ! 131: ai_stand, 0, NULL, ! 132: ai_stand, 0, NULL, ! 133: ai_stand, 0, NULL, ! 134: ai_stand, 0, gunner_fidget, ! 135: ! 136: ai_stand, 0, NULL, ! 137: ai_stand, 0, NULL, ! 138: ai_stand, 0, NULL, ! 139: ai_stand, 0, NULL, ! 140: ai_stand, 0, NULL, ! 141: ai_stand, 0, NULL, ! 142: ai_stand, 0, NULL, ! 143: ai_stand, 0, NULL, ! 144: ai_stand, 0, NULL, ! 145: ai_stand, 0, gunner_fidget ! 146: }; ! 147: mmove_t gunner_move_stand = {FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL}; ! 148: ! 149: void gunner_stand (edict_t *self) ! 150: { ! 151: self->monsterinfo.currentmove = &gunner_move_stand; ! 152: } ! 153: ! 154: ! 155: mframe_t gunner_frames_walk [] = ! 156: { ! 157: ai_walk, 0, NULL, ! 158: ai_walk, 3, NULL, ! 159: ai_walk, 4, NULL, ! 160: ai_walk, 5, NULL, ! 161: ai_walk, 7, NULL, ! 162: ai_walk, 2, NULL, ! 163: ai_walk, 6, NULL, ! 164: ai_walk, 4, NULL, ! 165: ai_walk, 2, NULL, ! 166: ai_walk, 7, NULL, ! 167: ai_walk, 5, NULL, ! 168: ai_walk, 7, NULL, ! 169: ai_walk, 4, NULL ! 170: }; ! 171: mmove_t gunner_move_walk = {FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL}; ! 172: ! 173: void gunner_walk (edict_t *self) ! 174: { ! 175: self->monsterinfo.currentmove = &gunner_move_walk; ! 176: } ! 177: ! 178: mframe_t gunner_frames_run [] = ! 179: { ! 180: ai_run, 26, NULL, ! 181: ai_run, 9, NULL, ! 182: ai_run, 9, NULL, ! 183: ai_run, 9, monster_done_dodge, ! 184: ai_run, 15, NULL, ! 185: ai_run, 10, NULL, ! 186: ai_run, 13, NULL, ! 187: ai_run, 6, NULL ! 188: }; ! 189: ! 190: mmove_t gunner_move_run = {FRAME_run01, FRAME_run08, gunner_frames_run, NULL}; ! 191: ! 192: void gunner_run (edict_t *self) ! 193: { ! 194: monster_done_dodge(self); ! 195: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 196: self->monsterinfo.currentmove = &gunner_move_stand; ! 197: else ! 198: self->monsterinfo.currentmove = &gunner_move_run; ! 199: } ! 200: ! 201: mframe_t gunner_frames_runandshoot [] = ! 202: { ! 203: ai_run, 32, NULL, ! 204: ai_run, 15, NULL, ! 205: ai_run, 10, NULL, ! 206: ai_run, 18, NULL, ! 207: ai_run, 8, NULL, ! 208: ai_run, 20, NULL ! 209: }; ! 210: ! 211: mmove_t gunner_move_runandshoot = {FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL}; ! 212: ! 213: void gunner_runandshoot (edict_t *self) ! 214: { ! 215: self->monsterinfo.currentmove = &gunner_move_runandshoot; ! 216: } ! 217: ! 218: mframe_t gunner_frames_pain3 [] = ! 219: { ! 220: ai_move, -3, NULL, ! 221: ai_move, 1, NULL, ! 222: ai_move, 1, NULL, ! 223: ai_move, 0, NULL, ! 224: ai_move, 1, NULL ! 225: }; ! 226: mmove_t gunner_move_pain3 = {FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run}; ! 227: ! 228: mframe_t gunner_frames_pain2 [] = ! 229: { ! 230: ai_move, -2, NULL, ! 231: ai_move, 11, NULL, ! 232: ai_move, 6, NULL, ! 233: ai_move, 2, NULL, ! 234: ai_move, -1, NULL, ! 235: ai_move, -7, NULL, ! 236: ai_move, -2, NULL, ! 237: ai_move, -7, NULL ! 238: }; ! 239: mmove_t gunner_move_pain2 = {FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run}; ! 240: ! 241: mframe_t gunner_frames_pain1 [] = ! 242: { ! 243: ai_move, 2, NULL, ! 244: ai_move, 0, NULL, ! 245: ai_move, -5, NULL, ! 246: ai_move, 3, NULL, ! 247: ai_move, -1, NULL, ! 248: ai_move, 0, NULL, ! 249: ai_move, 0, NULL, ! 250: ai_move, 0, NULL, ! 251: ai_move, 0, NULL, ! 252: ai_move, 1, NULL, ! 253: ai_move, 1, NULL, ! 254: ai_move, 2, NULL, ! 255: ai_move, 1, NULL, ! 256: ai_move, 0, NULL, ! 257: ai_move, -2, NULL, ! 258: ai_move, -2, NULL, ! 259: ai_move, 0, NULL, ! 260: ai_move, 0, NULL ! 261: }; ! 262: mmove_t gunner_move_pain1 = {FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run}; ! 263: ! 264: void gunner_pain (edict_t *self, edict_t *other, float kick, int damage) ! 265: { ! 266: if (self->health < (self->max_health / 2)) ! 267: self->s.skinnum = 1; ! 268: ! 269: monster_done_dodge (self); ! 270: ! 271: if (!self->groundentity) ! 272: { ! 273: if ((g_showlogic) && (g_showlogic->value)) ! 274: gi.dprintf ("gunner: pain avoided due to no ground\n"); ! 275: return; ! 276: } ! 277: ! 278: if (level.time < self->pain_debounce_time) ! 279: return; ! 280: ! 281: self->pain_debounce_time = level.time + 3; ! 282: ! 283: if (rand()&1) ! 284: gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); ! 285: else ! 286: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); ! 287: ! 288: if (skill->value == 3) ! 289: return; // no pain anims in nightmare ! 290: ! 291: if (damage <= 10) ! 292: self->monsterinfo.currentmove = &gunner_move_pain3; ! 293: else if (damage <= 25) ! 294: self->monsterinfo.currentmove = &gunner_move_pain2; ! 295: else ! 296: self->monsterinfo.currentmove = &gunner_move_pain1; ! 297: ! 298: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 299: ! 300: // PMM - clear duck flag ! 301: if (self->monsterinfo.aiflags & AI_DUCKED) ! 302: monster_duck_up(self); ! 303: } ! 304: ! 305: void gunner_dead (edict_t *self) ! 306: { ! 307: VectorSet (self->mins, -16, -16, -24); ! 308: VectorSet (self->maxs, 16, 16, -8); ! 309: self->movetype = MOVETYPE_TOSS; ! 310: self->svflags |= SVF_DEADMONSTER; ! 311: self->nextthink = 0; ! 312: gi.linkentity (self); ! 313: } ! 314: ! 315: mframe_t gunner_frames_death [] = ! 316: { ! 317: ai_move, 0, NULL, ! 318: ai_move, 0, NULL, ! 319: ai_move, 0, NULL, ! 320: ai_move, -7, NULL, ! 321: ai_move, -3, NULL, ! 322: ai_move, -5, NULL, ! 323: ai_move, 8, NULL, ! 324: ai_move, 6, NULL, ! 325: ai_move, 0, NULL, ! 326: ai_move, 0, NULL, ! 327: ai_move, 0, NULL ! 328: }; ! 329: mmove_t gunner_move_death = {FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead}; ! 330: ! 331: void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 332: { ! 333: int n; ! 334: ! 335: // check for gib ! 336: if (self->health <= self->gib_health) ! 337: { ! 338: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); ! 339: for (n= 0; n < 2; n++) ! 340: ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ! 341: for (n= 0; n < 4; n++) ! 342: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ! 343: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); ! 344: self->deadflag = DEAD_DEAD; ! 345: return; ! 346: } ! 347: ! 348: if (self->deadflag == DEAD_DEAD) ! 349: return; ! 350: ! 351: // regular death ! 352: gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); ! 353: self->deadflag = DEAD_DEAD; ! 354: self->takedamage = DAMAGE_YES; ! 355: self->monsterinfo.currentmove = &gunner_move_death; ! 356: } ! 357: ! 358: // PMM - changed to duck code for new dodge ! 359: ! 360: // ! 361: // this is specific to the gunner, leave it be ! 362: // ! 363: void gunner_duck_down (edict_t *self) ! 364: { ! 365: // if (self->monsterinfo.aiflags & AI_DUCKED) ! 366: // return; ! 367: self->monsterinfo.aiflags |= AI_DUCKED; ! 368: if (skill->value >= 2) ! 369: { ! 370: if (random() > 0.5) ! 371: GunnerGrenade (self); ! 372: } ! 373: ! 374: // self->maxs[2] -= 32; ! 375: self->maxs[2] = self->monsterinfo.base_height - 32; ! 376: self->takedamage = DAMAGE_YES; ! 377: if (self->monsterinfo.duck_wait_time < level.time) ! 378: self->monsterinfo.duck_wait_time = level.time + 1; ! 379: gi.linkentity (self); ! 380: } ! 381: ! 382: mframe_t gunner_frames_duck [] = ! 383: { ! 384: ai_move, 1, gunner_duck_down, ! 385: ai_move, 1, NULL, ! 386: ai_move, 1, monster_duck_hold, ! 387: ai_move, 0, NULL, ! 388: ai_move, -1, NULL, ! 389: ai_move, -1, NULL, ! 390: ai_move, 0, monster_duck_up, ! 391: ai_move, -1, NULL ! 392: }; ! 393: mmove_t gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run}; ! 394: ! 395: // PMM - gunner dodge moved below so I know about attack sequences ! 396: ! 397: void gunner_opengun (edict_t *self) ! 398: { ! 399: gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0); ! 400: } ! 401: ! 402: void GunnerFire (edict_t *self) ! 403: { ! 404: vec3_t start; ! 405: vec3_t forward, right; ! 406: vec3_t target; ! 407: vec3_t aim; ! 408: int flash_number; ! 409: ! 410: if(!self->enemy || !self->enemy->inuse) //PGM ! 411: return; //PGM ! 412: ! 413: flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216); ! 414: ! 415: AngleVectors (self->s.angles, forward, right, NULL); ! 416: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); ! 417: ! 418: // project enemy back a bit and target there ! 419: VectorCopy (self->enemy->s.origin, target); ! 420: VectorMA (target, -0.2, self->enemy->velocity, target); ! 421: target[2] += self->enemy->viewheight; ! 422: ! 423: VectorSubtract (target, start, aim); ! 424: VectorNormalize (aim); ! 425: monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); ! 426: } ! 427: ! 428: qboolean gunner_grenade_check(edict_t *self) ! 429: { ! 430: vec3_t start; ! 431: vec3_t forward, right; ! 432: trace_t tr; ! 433: vec3_t target, dir; ! 434: ! 435: if(!self->enemy) ! 436: return false; ! 437: ! 438: // if the player is above my head, use machinegun. ! 439: ! 440: // check for flag telling us that we're blindfiring ! 441: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 442: { ! 443: if (self->s.origin[2]+self->viewheight < self->monsterinfo.blind_fire_target[2]) ! 444: { ! 445: if(g_showlogic && g_showlogic->value) ! 446: gi.dprintf("blind_fire_target is above my head, using machinegun\n"); ! 447: return false; ! 448: } ! 449: } ! 450: else if(self->absmax[2] <= self->enemy->absmin[2]) ! 451: { ! 452: if(g_showlogic && g_showlogic->value) ! 453: gi.dprintf("player is above my head, using machinegun\n"); ! 454: return false; ! 455: } ! 456: ! 457: // check to see that we can trace to the player before we start ! 458: // tossing grenades around. ! 459: AngleVectors (self->s.angles, forward, right, NULL); ! 460: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_GUNNER_GRENADE_1], forward, right, start); ! 461: ! 462: // pmm - check for blindfire flag ! 463: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 464: VectorCopy (self->monsterinfo.blind_fire_target, target); ! 465: else ! 466: VectorCopy (self->enemy->s.origin, target); ! 467: ! 468: // see if we're too close ! 469: VectorSubtract (self->s.origin, target, dir); ! 470: ! 471: if (VectorLength(dir) < 100) ! 472: return false; ! 473: ! 474: tr = gi.trace(start, vec3_origin, vec3_origin, target, self, MASK_SHOT); ! 475: if(tr.ent == self->enemy || tr.fraction == 1) ! 476: return true; ! 477: ! 478: if(g_showlogic && g_showlogic->value) ! 479: gi.dprintf("can't trace to target, using machinegun\n"); ! 480: return false; ! 481: } ! 482: ! 483: void GunnerGrenade (edict_t *self) ! 484: { ! 485: vec3_t start; ! 486: vec3_t forward, right, up; ! 487: vec3_t aim; ! 488: int flash_number; ! 489: float spread; ! 490: float pitch; ! 491: // PMM ! 492: vec3_t target; ! 493: qboolean blindfire; ! 494: ! 495: if(!self->enemy || !self->enemy->inuse) //PGM ! 496: return; //PGM ! 497: ! 498: // pmm ! 499: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 500: blindfire = true; ! 501: ! 502: if (self->s.frame == FRAME_attak105) ! 503: { ! 504: spread = .02; ! 505: flash_number = MZ2_GUNNER_GRENADE_1; ! 506: } ! 507: else if (self->s.frame == FRAME_attak108) ! 508: { ! 509: spread = .05; ! 510: flash_number = MZ2_GUNNER_GRENADE_2; ! 511: } ! 512: else if (self->s.frame == FRAME_attak111) ! 513: { ! 514: spread = .08; ! 515: flash_number = MZ2_GUNNER_GRENADE_3; ! 516: } ! 517: else // (self->s.frame == FRAME_attak114) ! 518: { ! 519: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 520: spread = .11; ! 521: flash_number = MZ2_GUNNER_GRENADE_4; ! 522: } ! 523: ! 524: // pmm ! 525: // if we're shooting blind and we still can't see our enemy ! 526: if ((blindfire) && (!visible(self, self->enemy))) ! 527: { ! 528: // and we have a valid blind_fire_target ! 529: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin)) ! 530: return; ! 531: ! 532: // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target)); ! 533: // gi.dprintf ("GunnerGrenade: ideal yaw is %f\n", self->ideal_yaw); ! 534: VectorCopy (self->monsterinfo.blind_fire_target, target); ! 535: } ! 536: else ! 537: VectorCopy (self->s.origin, target); ! 538: // pmm ! 539: ! 540: AngleVectors (self->s.angles, forward, right, up); //PGM ! 541: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); ! 542: ! 543: //PGM ! 544: if(self->enemy) ! 545: { ! 546: float dist; ! 547: ! 548: // VectorSubtract(self->enemy->s.origin, self->s.origin, aim); ! 549: VectorSubtract(target, self->s.origin, aim); ! 550: dist = VectorLength(aim); ! 551: ! 552: // aim up if they're on the same level as me and far away. ! 553: if((dist > 512) && (aim[2] < 64) && (aim[2] > -64)) ! 554: { ! 555: aim[2] += (dist - 512); ! 556: } ! 557: ! 558: VectorNormalize (aim); ! 559: pitch = aim[2]; ! 560: if(pitch > 0.4) ! 561: { ! 562: pitch = 0.4; ! 563: } ! 564: else if(pitch < -0.5) ! 565: pitch = -0.5; ! 566: } ! 567: //PGM ! 568: ! 569: //FIXME : do a spread -225 -75 75 225 degrees around forward ! 570: // VectorCopy (forward, aim); ! 571: VectorMA (forward, spread, right, aim); ! 572: VectorMA (aim, pitch, up, aim); ! 573: ! 574: monster_fire_grenade (self, start, aim, 50, 600, flash_number); ! 575: } ! 576: ! 577: mframe_t gunner_frames_attack_chain [] = ! 578: { ! 579: /* ! 580: ai_charge, 0, NULL, ! 581: ai_charge, 0, NULL, ! 582: ai_charge, 0, NULL, ! 583: ai_charge, 0, NULL, ! 584: ai_charge, 0, NULL, ! 585: ai_charge, 0, NULL, ! 586: ai_charge, 0, NULL, ! 587: ai_charge, 0, NULL, ! 588: */ ! 589: ai_charge, 0, gunner_opengun, ! 590: ai_charge, 0, NULL, ! 591: ai_charge, 0, NULL, ! 592: ai_charge, 0, NULL, ! 593: ai_charge, 0, NULL, ! 594: ai_charge, 0, NULL, ! 595: ai_charge, 0, NULL ! 596: }; ! 597: mmove_t gunner_move_attack_chain = {FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain}; ! 598: ! 599: mframe_t gunner_frames_fire_chain [] = ! 600: { ! 601: ai_charge, 0, GunnerFire, ! 602: ai_charge, 0, GunnerFire, ! 603: ai_charge, 0, GunnerFire, ! 604: ai_charge, 0, GunnerFire, ! 605: ai_charge, 0, GunnerFire, ! 606: ai_charge, 0, GunnerFire, ! 607: ai_charge, 0, GunnerFire, ! 608: ai_charge, 0, GunnerFire ! 609: }; ! 610: mmove_t gunner_move_fire_chain = {FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain}; ! 611: ! 612: mframe_t gunner_frames_endfire_chain [] = ! 613: { ! 614: ai_charge, 0, NULL, ! 615: ai_charge, 0, NULL, ! 616: ai_charge, 0, NULL, ! 617: ai_charge, 0, NULL, ! 618: ai_charge, 0, NULL, ! 619: ai_charge, 0, NULL, ! 620: ai_charge, 0, NULL ! 621: }; ! 622: mmove_t gunner_move_endfire_chain = {FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run}; ! 623: ! 624: void gunner_blind_check (edict_t *self) ! 625: { ! 626: vec3_t aim; ! 627: ! 628: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 629: { ! 630: VectorSubtract(self->monsterinfo.blind_fire_target, self->s.origin, aim); ! 631: self->ideal_yaw = vectoyaw(aim); ! 632: ! 633: // gi.dprintf ("blind_fire_target = %s\n", vtos (self->monsterinfo.blind_fire_target)); ! 634: // gi.dprintf ("gunner_attack: ideal yaw is %f\n", self->ideal_yaw); ! 635: } ! 636: } ! 637: ! 638: mframe_t gunner_frames_attack_grenade [] = ! 639: { ! 640: ai_charge, 0, gunner_blind_check, ! 641: ai_charge, 0, NULL, ! 642: ai_charge, 0, NULL, ! 643: ai_charge, 0, NULL, ! 644: ai_charge, 0, GunnerGrenade, ! 645: ai_charge, 0, NULL, ! 646: ai_charge, 0, NULL, ! 647: ai_charge, 0, GunnerGrenade, ! 648: ai_charge, 0, NULL, ! 649: ai_charge, 0, NULL, ! 650: ai_charge, 0, GunnerGrenade, ! 651: ai_charge, 0, NULL, ! 652: ai_charge, 0, NULL, ! 653: ai_charge, 0, GunnerGrenade, ! 654: ai_charge, 0, NULL, ! 655: ai_charge, 0, NULL, ! 656: ai_charge, 0, NULL, ! 657: ai_charge, 0, NULL, ! 658: ai_charge, 0, NULL, ! 659: ai_charge, 0, NULL, ! 660: ai_charge, 0, NULL ! 661: }; ! 662: mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run}; ! 663: ! 664: void gunner_attack(edict_t *self) ! 665: { ! 666: float chance, r; ! 667: ! 668: monster_done_dodge(self); ! 669: ! 670: // PMM ! 671: if (self->monsterinfo.attack_state == AS_BLIND) ! 672: { ! 673: // setup shot probabilities ! 674: if (self->monsterinfo.blind_fire_delay < 1.0) ! 675: chance = 1.0; ! 676: else if (self->monsterinfo.blind_fire_delay < 7.5) ! 677: chance = 0.4; ! 678: else ! 679: chance = 0.1; ! 680: ! 681: r = random(); ! 682: ! 683: // gi.dprintf ("chance = %2.2f, roll = %2.2f\n", chance, r); ! 684: ! 685: // minimum of 2 seconds, plus 0-3, after the shots are done ! 686: self->monsterinfo.blind_fire_delay += 2.1 + 2.0 + random()*3.0; ! 687: ! 688: // don't shoot at the origin ! 689: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin)) ! 690: return; ! 691: ! 692: // don't shoot if the dice say not to ! 693: if (r > chance) ! 694: { ! 695: // if ((g_showlogic) && (g_showlogic->value)) ! 696: // gi.dprintf ("blindfire - NO SHOT\n"); ! 697: return; ! 698: } ! 699: ! 700: // turn on manual steering to signal both manual steering and blindfire ! 701: self->monsterinfo.aiflags |= AI_MANUAL_STEERING; ! 702: if (gunner_grenade_check(self)) ! 703: { ! 704: self->monsterinfo.currentmove = &gunner_move_attack_grenade; ! 705: self->monsterinfo.attack_finished = level.time + 2*random(); ! 706: return; ! 707: } ! 708: // pmm - should this be active? ! 709: // else ! 710: // self->monsterinfo.currentmove = &gunner_move_attack_chain; ! 711: // if ((g_showlogic) && (g_showlogic->value)) ! 712: // gi.dprintf ("blind grenade check failed, doing nothing\n"); ! 713: ! 714: // turn off blindfire flag ! 715: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 716: return; ! 717: } ! 718: // pmm ! 719: ! 720: // PGM - gunner needs to use his chaingun if he's being attacked by a tesla. ! 721: if ((range (self, self->enemy) == RANGE_MELEE) || self->bad_area) ! 722: { ! 723: self->monsterinfo.currentmove = &gunner_move_attack_chain; ! 724: } ! 725: else ! 726: { ! 727: if (random() <= 0.5 && gunner_grenade_check(self)) ! 728: self->monsterinfo.currentmove = &gunner_move_attack_grenade; ! 729: else ! 730: self->monsterinfo.currentmove = &gunner_move_attack_chain; ! 731: } ! 732: } ! 733: ! 734: void gunner_fire_chain(edict_t *self) ! 735: { ! 736: self->monsterinfo.currentmove = &gunner_move_fire_chain; ! 737: } ! 738: ! 739: void gunner_refire_chain(edict_t *self) ! 740: { ! 741: if (self->enemy->health > 0) ! 742: if ( visible (self, self->enemy) ) ! 743: if (random() <= 0.5) ! 744: { ! 745: self->monsterinfo.currentmove = &gunner_move_fire_chain; ! 746: return; ! 747: } ! 748: self->monsterinfo.currentmove = &gunner_move_endfire_chain; ! 749: } ! 750: /* ! 751: void gunner_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr) ! 752: { ! 753: // original quake2 dodge code ! 754: ! 755: if (random() > 0.25) ! 756: return; ! 757: ! 758: if (!self->enemy) ! 759: self->enemy = attacker; ! 760: ! 761: self->monsterinfo.currentmove = &gunner_move_duck; ! 762: ! 763: //=========== ! 764: //PMM - rogue rewrite of gunner dodge code. ! 765: float r; ! 766: float height; ! 767: int shooting = 0; ! 768: ! 769: if (!self->enemy) ! 770: { ! 771: self->enemy = attacker; ! 772: FoundTarget (self); ! 773: } ! 774: ! 775: // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was ! 776: // seeing numbers like 13 and 14) ! 777: if ((eta < 0.1) || (eta > 5)) ! 778: return; ! 779: ! 780: r = random(); ! 781: if (r > (0.25*((skill->value)+1))) ! 782: return; ! 783: ! 784: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) || ! 785: (self->monsterinfo.currentmove == &gunner_move_fire_chain) || ! 786: (self->monsterinfo.currentmove == &gunner_move_attack_grenade) ! 787: ) ! 788: { ! 789: shooting = 1; ! 790: } ! 791: if (self->monsterinfo.aiflags & AI_DODGING) ! 792: { ! 793: height = self->absmax[2]; ! 794: } ! 795: else ! 796: { ! 797: height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1 ! 798: } ! 799: ! 800: // check to see if it makes sense to duck ! 801: if (tr->endpos[2] <= height) ! 802: { ! 803: vec3_t right, diff; ! 804: if (shooting) ! 805: { ! 806: self->monsterinfo.attack_state = AS_SLIDING; ! 807: return; ! 808: } ! 809: AngleVectors (self->s.angles, NULL, right, NULL); ! 810: VectorSubtract (tr->endpos, self->s.origin, diff); ! 811: if (DotProduct (right, diff) < 0) ! 812: { ! 813: self->monsterinfo.lefty = 1; ! 814: } ! 815: // if it doesn't sense to duck, try to strafe away ! 816: monster_done_dodge (self); ! 817: self->monsterinfo.currentmove = &gunner_move_run; ! 818: self->monsterinfo.attack_state = AS_SLIDING; ! 819: return; ! 820: } ! 821: ! 822: if (skill->value == 0) ! 823: { ! 824: self->monsterinfo.currentmove = &gunner_move_duck; ! 825: // PMM - stupid dodge ! 826: self->monsterinfo.duck_wait_time = level.time + eta + 1; ! 827: self->monsterinfo.aiflags |= AI_DODGING; ! 828: return; ! 829: } ! 830: ! 831: if (!shooting) ! 832: { ! 833: self->monsterinfo.currentmove = &gunner_move_duck; ! 834: self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value)); ! 835: self->monsterinfo.aiflags |= AI_DODGING; ! 836: } ! 837: return; ! 838: //PMM ! 839: //=========== ! 840: } ! 841: */ ! 842: //=========== ! 843: //PGM ! 844: void gunner_jump_now (edict_t *self) ! 845: { ! 846: vec3_t forward,up; ! 847: ! 848: AngleVectors (self->s.angles, forward, NULL, up); ! 849: VectorMA(self->velocity, 100, forward, self->velocity); ! 850: VectorMA(self->velocity, 300, up, self->velocity); ! 851: } ! 852: ! 853: void gunner_jump2_now (edict_t *self) ! 854: { ! 855: vec3_t forward,up; ! 856: ! 857: AngleVectors (self->s.angles, forward, NULL, up); ! 858: VectorMA(self->velocity, 150, forward, self->velocity); ! 859: VectorMA(self->velocity, 400, up, self->velocity); ! 860: } ! 861: ! 862: void gunner_jump_wait_land (edict_t *self) ! 863: { ! 864: if(self->groundentity == NULL) ! 865: self->monsterinfo.nextframe = self->s.frame; ! 866: else ! 867: self->monsterinfo.nextframe = self->s.frame + 1; ! 868: } ! 869: ! 870: mframe_t gunner_frames_jump [] = ! 871: { ! 872: ai_move, 0, NULL, ! 873: ai_move, 0, NULL, ! 874: ai_move, 0, NULL, ! 875: ai_move, 0, gunner_jump_now, ! 876: ai_move, 0, NULL, ! 877: ai_move, 0, NULL, ! 878: ai_move, 0, gunner_jump_wait_land, ! 879: ai_move, 0, NULL, ! 880: ai_move, 0, NULL, ! 881: ai_move, 0, NULL ! 882: }; ! 883: mmove_t gunner_move_jump = { FRAME_jump01, FRAME_jump10, gunner_frames_jump, gunner_run }; ! 884: ! 885: mframe_t gunner_frames_jump2 [] = ! 886: { ! 887: ai_move, -8, NULL, ! 888: ai_move, -4, NULL, ! 889: ai_move, -4, NULL, ! 890: ai_move, 0, gunner_jump_now, ! 891: ai_move, 0, NULL, ! 892: ai_move, 0, NULL, ! 893: ai_move, 0, gunner_jump_wait_land, ! 894: ai_move, 0, NULL, ! 895: ai_move, 0, NULL, ! 896: ai_move, 0, NULL ! 897: }; ! 898: mmove_t gunner_move_jump2 = { FRAME_jump01, FRAME_jump10, gunner_frames_jump2, gunner_run }; ! 899: ! 900: void gunner_jump (edict_t *self) ! 901: { ! 902: if(!self->enemy) ! 903: return; ! 904: ! 905: monster_done_dodge (self); ! 906: ! 907: if(self->enemy->s.origin[2] > self->s.origin[2]) ! 908: self->monsterinfo.currentmove = &gunner_move_jump2; ! 909: else ! 910: self->monsterinfo.currentmove = &gunner_move_jump; ! 911: } ! 912: ! 913: //=========== ! 914: //PGM ! 915: qboolean gunner_blocked (edict_t *self, float dist) ! 916: { ! 917: if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) )) ! 918: return true; ! 919: ! 920: if(blocked_checkplat (self, dist)) ! 921: return true; ! 922: ! 923: if(blocked_checkjump (self, dist, 192, 40)) ! 924: { ! 925: gunner_jump(self); ! 926: return true; ! 927: } ! 928: ! 929: return false; ! 930: } ! 931: //PGM ! 932: //=========== ! 933: ! 934: // PMM - new duck code ! 935: void gunner_duck (edict_t *self, float eta) ! 936: { ! 937: if ((self->monsterinfo.currentmove == &gunner_move_jump2) || ! 938: (self->monsterinfo.currentmove == &gunner_move_jump)) ! 939: { ! 940: return; ! 941: } ! 942: ! 943: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) || ! 944: (self->monsterinfo.currentmove == &gunner_move_fire_chain) || ! 945: (self->monsterinfo.currentmove == &gunner_move_attack_grenade) ! 946: ) ! 947: { ! 948: // if we're shooting, and not on easy, don't dodge ! 949: if (skill->value) ! 950: { ! 951: self->monsterinfo.aiflags &= ~AI_DUCKED; ! 952: return; ! 953: } ! 954: } ! 955: ! 956: if (skill->value == 0) ! 957: // PMM - stupid dodge ! 958: self->monsterinfo.duck_wait_time = level.time + eta + 1; ! 959: else ! 960: self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value)); ! 961: ! 962: // has to be done immediately otherwise he can get stuck ! 963: gunner_duck_down(self); ! 964: ! 965: self->monsterinfo.nextframe = FRAME_duck01; ! 966: self->monsterinfo.currentmove = &gunner_move_duck; ! 967: return; ! 968: } ! 969: ! 970: void gunner_sidestep (edict_t *self) ! 971: { ! 972: if ((self->monsterinfo.currentmove == &gunner_move_jump2) || ! 973: (self->monsterinfo.currentmove == &gunner_move_jump)) ! 974: { ! 975: return; ! 976: } ! 977: ! 978: if ((self->monsterinfo.currentmove == &gunner_move_attack_chain) || ! 979: (self->monsterinfo.currentmove == &gunner_move_fire_chain) || ! 980: (self->monsterinfo.currentmove == &gunner_move_attack_grenade) ! 981: ) ! 982: { ! 983: // if we're shooting, and not on easy, don't dodge ! 984: if (skill->value) ! 985: { ! 986: self->monsterinfo.aiflags &= ~AI_DODGING; ! 987: return; ! 988: } ! 989: } ! 990: ! 991: if (self->monsterinfo.currentmove != &gunner_move_run) ! 992: self->monsterinfo.currentmove = &gunner_move_run; ! 993: } ! 994: ! 995: ! 996: /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight ! 997: */ ! 998: void SP_monster_gunner (edict_t *self) ! 999: { ! 1000: if (deathmatch->value) ! 1001: { ! 1002: G_FreeEdict (self); ! 1003: return; ! 1004: } ! 1005: ! 1006: sound_death = gi.soundindex ("gunner/death1.wav"); ! 1007: sound_pain = gi.soundindex ("gunner/gunpain2.wav"); ! 1008: sound_pain2 = gi.soundindex ("gunner/gunpain1.wav"); ! 1009: sound_idle = gi.soundindex ("gunner/gunidle1.wav"); ! 1010: sound_open = gi.soundindex ("gunner/gunatck1.wav"); ! 1011: sound_search = gi.soundindex ("gunner/gunsrch1.wav"); ! 1012: sound_sight = gi.soundindex ("gunner/sight1.wav"); ! 1013: ! 1014: gi.soundindex ("gunner/gunatck2.wav"); ! 1015: gi.soundindex ("gunner/gunatck3.wav"); ! 1016: ! 1017: self->movetype = MOVETYPE_STEP; ! 1018: self->solid = SOLID_BBOX; ! 1019: self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2"); ! 1020: VectorSet (self->mins, -16, -16, -24); ! 1021: VectorSet (self->maxs, 16, 16, 32); ! 1022: ! 1023: self->health = 175; ! 1024: self->gib_health = -70; ! 1025: self->mass = 200; ! 1026: ! 1027: self->pain = gunner_pain; ! 1028: self->die = gunner_die; ! 1029: ! 1030: self->monsterinfo.stand = gunner_stand; ! 1031: self->monsterinfo.walk = gunner_walk; ! 1032: self->monsterinfo.run = gunner_run; ! 1033: // pmm ! 1034: self->monsterinfo.dodge = M_MonsterDodge; ! 1035: self->monsterinfo.duck = gunner_duck; ! 1036: self->monsterinfo.unduck = monster_duck_up; ! 1037: self->monsterinfo.sidestep = gunner_sidestep; ! 1038: // self->monsterinfo.dodge = gunner_dodge; ! 1039: // pmm ! 1040: self->monsterinfo.attack = gunner_attack; ! 1041: self->monsterinfo.melee = NULL; ! 1042: self->monsterinfo.sight = gunner_sight; ! 1043: self->monsterinfo.search = gunner_search; ! 1044: self->monsterinfo.blocked = gunner_blocked; //PGM ! 1045: ! 1046: gi.linkentity (self); ! 1047: ! 1048: self->monsterinfo.currentmove = &gunner_move_stand; ! 1049: self->monsterinfo.scale = MODEL_SCALE; ! 1050: ! 1051: // PMM ! 1052: self->monsterinfo.blindfire = true; ! 1053: ! 1054: walkmonster_start (self); ! 1055: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.