|
|
1.1 ! root 1: /* ! 2: ============================================================================== ! 3: ! 4: TANK ! 5: ! 6: ============================================================================== ! 7: */ ! 8: ! 9: #include "g_local.h" ! 10: #include "m_tank.h" ! 11: ! 12: ! 13: void tank_refire_rocket (edict_t *self); ! 14: void tank_doattack_rocket (edict_t *self); ! 15: void tank_reattack_blaster (edict_t *self); ! 16: ! 17: static int sound_thud; ! 18: static int sound_pain; ! 19: static int sound_idle; ! 20: static int sound_die; ! 21: static int sound_step; ! 22: static int sound_sight; ! 23: static int sound_windup; ! 24: static int sound_strike; ! 25: ! 26: // ! 27: // misc ! 28: // ! 29: ! 30: void tank_sight (edict_t *self, edict_t *other) ! 31: { ! 32: gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); ! 33: } ! 34: ! 35: ! 36: void tank_footstep (edict_t *self) ! 37: { ! 38: gi.sound (self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); ! 39: } ! 40: ! 41: void tank_thud (edict_t *self) ! 42: { ! 43: gi.sound (self, CHAN_BODY, sound_thud, 1, ATTN_NORM, 0); ! 44: } ! 45: ! 46: void tank_windup (edict_t *self) ! 47: { ! 48: gi.sound (self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0); ! 49: } ! 50: ! 51: void tank_idle (edict_t *self) ! 52: { ! 53: gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); ! 54: } ! 55: ! 56: ! 57: // ! 58: // stand ! 59: // ! 60: ! 61: mframe_t tank_frames_stand []= ! 62: { ! 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: ai_stand, 0, NULL, ! 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: ai_stand, 0, NULL, ! 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: ai_stand, 0, NULL, ! 91: ai_stand, 0, NULL, ! 92: ai_stand, 0, NULL ! 93: }; ! 94: mmove_t tank_move_stand = {FRAME_stand01, FRAME_stand30, tank_frames_stand, NULL}; ! 95: ! 96: void tank_stand (edict_t *self) ! 97: { ! 98: self->monsterinfo.currentmove = &tank_move_stand; ! 99: } ! 100: ! 101: ! 102: // ! 103: // walk ! 104: // ! 105: ! 106: void tank_walk (edict_t *self); ! 107: ! 108: mframe_t tank_frames_start_walk [] = ! 109: { ! 110: ai_walk, 0, NULL, ! 111: ai_walk, 6, NULL, ! 112: ai_walk, 6, NULL, ! 113: ai_walk, 11, tank_footstep ! 114: }; ! 115: mmove_t tank_move_start_walk = {FRAME_walk01, FRAME_walk04, tank_frames_start_walk, tank_walk}; ! 116: ! 117: mframe_t tank_frames_walk [] = ! 118: { ! 119: ai_walk, 4, NULL, ! 120: ai_walk, 5, NULL, ! 121: ai_walk, 3, NULL, ! 122: ai_walk, 2, NULL, ! 123: ai_walk, 5, NULL, ! 124: ai_walk, 5, NULL, ! 125: ai_walk, 4, NULL, ! 126: ai_walk, 4, tank_footstep, ! 127: ai_walk, 3, NULL, ! 128: ai_walk, 5, NULL, ! 129: ai_walk, 4, NULL, ! 130: ai_walk, 5, NULL, ! 131: ai_walk, 7, NULL, ! 132: ai_walk, 7, NULL, ! 133: ai_walk, 6, NULL, ! 134: ai_walk, 6, tank_footstep ! 135: }; ! 136: mmove_t tank_move_walk = {FRAME_walk05, FRAME_walk20, tank_frames_walk, NULL}; ! 137: ! 138: mframe_t tank_frames_stop_walk [] = ! 139: { ! 140: ai_walk, 3, NULL, ! 141: ai_walk, 3, NULL, ! 142: ai_walk, 2, NULL, ! 143: ai_walk, 2, NULL, ! 144: ai_walk, 4, tank_footstep ! 145: }; ! 146: mmove_t tank_move_stop_walk = {FRAME_walk21, FRAME_walk25, tank_frames_stop_walk, tank_stand}; ! 147: ! 148: void tank_walk (edict_t *self) ! 149: { ! 150: self->monsterinfo.currentmove = &tank_move_walk; ! 151: } ! 152: ! 153: ! 154: // ! 155: // run ! 156: // ! 157: ! 158: void tank_run (edict_t *self); ! 159: ! 160: mframe_t tank_frames_start_run [] = ! 161: { ! 162: ai_run, 0, NULL, ! 163: ai_run, 6, NULL, ! 164: ai_run, 6, NULL, ! 165: ai_run, 11, tank_footstep ! 166: }; ! 167: mmove_t tank_move_start_run = {FRAME_walk01, FRAME_walk04, tank_frames_start_run, tank_run}; ! 168: ! 169: mframe_t tank_frames_run [] = ! 170: { ! 171: ai_run, 4, NULL, ! 172: ai_run, 5, NULL, ! 173: ai_run, 3, NULL, ! 174: ai_run, 2, NULL, ! 175: ai_run, 5, NULL, ! 176: ai_run, 5, NULL, ! 177: ai_run, 4, NULL, ! 178: ai_run, 4, tank_footstep, ! 179: ai_run, 3, NULL, ! 180: ai_run, 5, NULL, ! 181: ai_run, 4, NULL, ! 182: ai_run, 5, NULL, ! 183: ai_run, 7, NULL, ! 184: ai_run, 7, NULL, ! 185: ai_run, 6, NULL, ! 186: ai_run, 6, tank_footstep ! 187: }; ! 188: mmove_t tank_move_run = {FRAME_walk05, FRAME_walk20, tank_frames_run, NULL}; ! 189: ! 190: mframe_t tank_frames_stop_run [] = ! 191: { ! 192: ai_run, 3, NULL, ! 193: ai_run, 3, NULL, ! 194: ai_run, 2, NULL, ! 195: ai_run, 2, NULL, ! 196: ai_run, 4, tank_footstep ! 197: }; ! 198: mmove_t tank_move_stop_run = {FRAME_walk21, FRAME_walk25, tank_frames_stop_run, tank_walk}; ! 199: ! 200: void tank_run (edict_t *self) ! 201: { ! 202: if (self->enemy && self->enemy->client) ! 203: self->monsterinfo.aiflags |= AI_BRUTAL; ! 204: else ! 205: self->monsterinfo.aiflags &= ~AI_BRUTAL; ! 206: ! 207: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 208: { ! 209: self->monsterinfo.currentmove = &tank_move_stand; ! 210: return; ! 211: } ! 212: ! 213: if (self->monsterinfo.currentmove == &tank_move_walk || ! 214: self->monsterinfo.currentmove == &tank_move_start_run) ! 215: { ! 216: self->monsterinfo.currentmove = &tank_move_run; ! 217: } ! 218: else ! 219: { ! 220: self->monsterinfo.currentmove = &tank_move_start_run; ! 221: } ! 222: } ! 223: ! 224: // ! 225: // pain ! 226: // ! 227: ! 228: mframe_t tank_frames_pain1 [] = ! 229: { ! 230: ai_move, 0, NULL, ! 231: ai_move, 0, NULL, ! 232: ai_move, 0, NULL, ! 233: ai_move, 0, NULL ! 234: }; ! 235: mmove_t tank_move_pain1 = {FRAME_pain101, FRAME_pain104, tank_frames_pain1, tank_run}; ! 236: ! 237: mframe_t tank_frames_pain2 [] = ! 238: { ! 239: ai_move, 0, NULL, ! 240: ai_move, 0, NULL, ! 241: ai_move, 0, NULL, ! 242: ai_move, 0, NULL, ! 243: ai_move, 0, NULL ! 244: }; ! 245: mmove_t tank_move_pain2 = {FRAME_pain201, FRAME_pain205, tank_frames_pain2, tank_run}; ! 246: ! 247: mframe_t tank_frames_pain3 [] = ! 248: { ! 249: ai_move, -7, NULL, ! 250: ai_move, 0, NULL, ! 251: ai_move, 0, NULL, ! 252: ai_move, 0, NULL, ! 253: ai_move, 2, NULL, ! 254: ai_move, 0, NULL, ! 255: ai_move, 0, NULL, ! 256: ai_move, 3, NULL, ! 257: ai_move, 0, NULL, ! 258: ai_move, 2, NULL, ! 259: ai_move, 0, NULL, ! 260: ai_move, 0, NULL, ! 261: ai_move, 0, NULL, ! 262: ai_move, 0, NULL, ! 263: ai_move, 0, NULL, ! 264: ai_move, 0, tank_footstep ! 265: }; ! 266: mmove_t tank_move_pain3 = {FRAME_pain301, FRAME_pain316, tank_frames_pain3, tank_run}; ! 267: ! 268: ! 269: void tank_pain (edict_t *self, edict_t *other, float kick, int damage) ! 270: { ! 271: if (self->health < (self->max_health / 2)) ! 272: self->s.skinnum |= 1; ! 273: ! 274: if (damage <= 10) ! 275: return; ! 276: ! 277: if (level.time < self->pain_debounce_time) ! 278: return; ! 279: ! 280: if (damage <= 30) ! 281: if (random() > 0.2) ! 282: return; ! 283: ! 284: // If hard or nightmare, don't go into pain while attacking ! 285: if ( skill->value >= 2) ! 286: { ! 287: if ( (self->s.frame >= FRAME_attak301) && (self->s.frame <= FRAME_attak330) ) ! 288: return; ! 289: if ( (self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak116) ) ! 290: return; ! 291: } ! 292: ! 293: self->pain_debounce_time = level.time + 3; ! 294: gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); ! 295: ! 296: if (skill->value == 3) ! 297: return; // no pain anims in nightmare ! 298: ! 299: // PMM - blindfire cleanup ! 300: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 301: // pmm ! 302: ! 303: if (damage <= 30) ! 304: self->monsterinfo.currentmove = &tank_move_pain1; ! 305: else if (damage <= 60) ! 306: self->monsterinfo.currentmove = &tank_move_pain2; ! 307: else ! 308: self->monsterinfo.currentmove = &tank_move_pain3; ! 309: }; ! 310: ! 311: ! 312: // ! 313: // attacks ! 314: // ! 315: ! 316: void TankBlaster (edict_t *self) ! 317: { ! 318: vec3_t forward, right; ! 319: vec3_t start; ! 320: vec3_t end; ! 321: vec3_t dir; ! 322: int flash_number; ! 323: ! 324: if(!self->enemy || !self->enemy->inuse) //PGM ! 325: return; //PGM ! 326: ! 327: if (self->s.frame == FRAME_attak110) ! 328: flash_number = MZ2_TANK_BLASTER_1; ! 329: else if (self->s.frame == FRAME_attak113) ! 330: flash_number = MZ2_TANK_BLASTER_2; ! 331: else // (self->s.frame == FRAME_attak116) ! 332: flash_number = MZ2_TANK_BLASTER_3; ! 333: ! 334: AngleVectors (self->s.angles, forward, right, NULL); ! 335: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); ! 336: ! 337: VectorCopy (self->enemy->s.origin, end); ! 338: end[2] += self->enemy->viewheight; ! 339: VectorSubtract (end, start, dir); ! 340: ! 341: monster_fire_blaster (self, start, dir, 30, 800, flash_number, EF_BLASTER); ! 342: } ! 343: ! 344: void TankStrike (edict_t *self) ! 345: { ! 346: gi.sound (self, CHAN_WEAPON, sound_strike, 1, ATTN_NORM, 0); ! 347: } ! 348: ! 349: void TankRocket (edict_t *self) ! 350: { ! 351: vec3_t forward, right; ! 352: vec3_t start; ! 353: vec3_t dir; ! 354: vec3_t vec; ! 355: int flash_number; ! 356: trace_t trace; // PGM ! 357: int rocketSpeed; // PGM ! 358: // pmm - blindfire support ! 359: vec3_t target; ! 360: qboolean blindfire = false; ! 361: ! 362: if(!self->enemy || !self->enemy->inuse) //PGM ! 363: return; //PGM ! 364: ! 365: // pmm - blindfire check ! 366: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 367: blindfire = true; ! 368: else ! 369: blindfire = false; ! 370: ! 371: if (self->s.frame == FRAME_attak324) ! 372: flash_number = MZ2_TANK_ROCKET_1; ! 373: else if (self->s.frame == FRAME_attak327) ! 374: flash_number = MZ2_TANK_ROCKET_2; ! 375: else // (self->s.frame == FRAME_attak330) ! 376: flash_number = MZ2_TANK_ROCKET_3; ! 377: ! 378: AngleVectors (self->s.angles, forward, right, NULL); ! 379: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); ! 380: ! 381: rocketSpeed = 500 + (100 * skill->value); // PGM rock & roll.... :) ! 382: ! 383: // PMM ! 384: if (blindfire) ! 385: VectorCopy (self->monsterinfo.blind_fire_target, target); ! 386: else ! 387: VectorCopy (self->enemy->s.origin, target); ! 388: // pmm ! 389: ! 390: // VectorCopy (self->enemy->s.origin, vec); ! 391: // vec[2] += self->enemy->viewheight; ! 392: // VectorSubtract (vec, start, dir); ! 393: ! 394: //PGM ! 395: // PMM - blindfire shooting ! 396: if (blindfire) ! 397: { ! 398: VectorCopy (target, vec); ! 399: VectorSubtract (vec, start, dir); ! 400: } ! 401: // pmm ! 402: // don't shoot at feet if they're above me. ! 403: else if(random() < 0.66 || (start[2] < self->enemy->absmin[2])) ! 404: { ! 405: // gi.dprintf("normal shot\n"); ! 406: VectorCopy (self->enemy->s.origin, vec); ! 407: vec[2] += self->enemy->viewheight; ! 408: VectorSubtract (vec, start, dir); ! 409: } ! 410: else ! 411: { ! 412: // gi.dprintf("shooting at feet!\n"); ! 413: VectorCopy (self->enemy->s.origin, vec); ! 414: vec[2] = self->enemy->absmin[2]; ! 415: VectorSubtract (vec, start, dir); ! 416: } ! 417: //PGM ! 418: ! 419: //====== ! 420: //PMM - lead target (not when blindfiring) ! 421: // 20, 35, 50, 65 chance of leading ! 422: if((!blindfire) && ((random() < (0.2 + ((3 - skill->value) * 0.15))))) ! 423: { ! 424: float dist; ! 425: float time; ! 426: ! 427: // gi.dprintf ("leading target\n"); ! 428: dist = VectorLength (dir); ! 429: time = dist/rocketSpeed; ! 430: VectorMA(vec, time, self->enemy->velocity, vec); ! 431: VectorSubtract(vec, start, dir); ! 432: } ! 433: //PMM - lead target ! 434: //====== ! 435: ! 436: VectorNormalize (dir); ! 437: ! 438: // gi.WriteByte (svc_temp_entity); ! 439: // gi.WriteByte (TE_DEBUGTRAIL); ! 440: // gi.WritePosition (start); ! 441: // gi.WritePosition (vec); ! 442: // gi.multicast (start, MULTICAST_ALL); ! 443: ! 444: // pmm blindfire doesn't check target (done in checkattack) ! 445: // paranoia, make sure we're not shooting a target right next to us ! 446: trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT); ! 447: if (blindfire) ! 448: { ! 449: // blindfire has different fail criteria for the trace ! 450: if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5))) ! 451: monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number); ! 452: else ! 453: { ! 454: // try shifting the target to the left a little (to help counter large offset) ! 455: VectorCopy (target, vec); ! 456: VectorMA (vec, -20, right, vec); ! 457: VectorSubtract(vec, start, dir); ! 458: VectorNormalize (dir); ! 459: trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT); ! 460: if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5))) ! 461: monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number); ! 462: else ! 463: { ! 464: // ok, that failed. try to the right ! 465: VectorCopy (target, vec); ! 466: VectorMA (vec, 20, right, vec); ! 467: VectorSubtract(vec, start, dir); ! 468: VectorNormalize (dir); ! 469: trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT); ! 470: if (!(trace.startsolid || trace.allsolid || (trace.fraction < 0.5))) ! 471: monster_fire_rocket (self, start, dir, 50, rocketSpeed, flash_number); ! 472: else if ((g_showlogic) && (g_showlogic->value)) ! 473: // ok, I give up ! 474: gi.dprintf ("tank avoiding blindfire shot\n"); ! 475: } ! 476: } ! 477: } ! 478: else ! 479: { ! 480: trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT); ! 481: if(trace.ent == self->enemy || trace.ent == world) ! 482: { ! 483: if(trace.fraction > 0.5 || (trace.ent && trace.ent->client)) ! 484: monster_fire_rocket (self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1); ! 485: // else ! 486: // gi.dprintf("didn't make it halfway to target...aborting\n"); ! 487: } ! 488: } ! 489: } ! 490: ! 491: void TankMachineGun (edict_t *self) ! 492: { ! 493: vec3_t dir; ! 494: vec3_t vec; ! 495: vec3_t start; ! 496: vec3_t forward, right; ! 497: int flash_number; ! 498: ! 499: if(!self->enemy || !self->enemy->inuse) //PGM ! 500: return; //PGM ! 501: ! 502: flash_number = MZ2_TANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak406); ! 503: ! 504: AngleVectors (self->s.angles, forward, right, NULL); ! 505: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start); ! 506: ! 507: if (self->enemy) ! 508: { ! 509: VectorCopy (self->enemy->s.origin, vec); ! 510: vec[2] += self->enemy->viewheight; ! 511: VectorSubtract (vec, start, vec); ! 512: vectoangles (vec, vec); ! 513: dir[0] = vec[0]; ! 514: } ! 515: else ! 516: { ! 517: dir[0] = 0; ! 518: } ! 519: if (self->s.frame <= FRAME_attak415) ! 520: dir[1] = self->s.angles[1] - 8 * (self->s.frame - FRAME_attak411); ! 521: else ! 522: dir[1] = self->s.angles[1] + 8 * (self->s.frame - FRAME_attak419); ! 523: dir[2] = 0; ! 524: ! 525: AngleVectors (dir, forward, NULL, NULL); ! 526: ! 527: monster_fire_bullet (self, start, forward, 20, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); ! 528: } ! 529: ! 530: ! 531: mframe_t tank_frames_attack_blast [] = ! 532: { ! 533: ai_charge, 0, NULL, ! 534: ai_charge, 0, NULL, ! 535: ai_charge, 0, NULL, ! 536: ai_charge, 0, NULL, ! 537: ai_charge, -1, NULL, ! 538: ai_charge, -2, NULL, ! 539: ai_charge, -1, NULL, ! 540: ai_charge, -1, NULL, ! 541: ai_charge, 0, NULL, ! 542: ai_charge, 0, TankBlaster, // 10 ! 543: ai_charge, 0, NULL, ! 544: ai_charge, 0, NULL, ! 545: ai_charge, 0, TankBlaster, ! 546: ai_charge, 0, NULL, ! 547: ai_charge, 0, NULL, ! 548: ai_charge, 0, TankBlaster // 16 ! 549: }; ! 550: mmove_t tank_move_attack_blast = {FRAME_attak101, FRAME_attak116, tank_frames_attack_blast, tank_reattack_blaster}; ! 551: ! 552: mframe_t tank_frames_reattack_blast [] = ! 553: { ! 554: ai_charge, 0, NULL, ! 555: ai_charge, 0, NULL, ! 556: ai_charge, 0, TankBlaster, ! 557: ai_charge, 0, NULL, ! 558: ai_charge, 0, NULL, ! 559: ai_charge, 0, TankBlaster // 16 ! 560: }; ! 561: mmove_t tank_move_reattack_blast = {FRAME_attak111, FRAME_attak116, tank_frames_reattack_blast, tank_reattack_blaster}; ! 562: ! 563: mframe_t tank_frames_attack_post_blast [] = ! 564: { ! 565: ai_move, 0, NULL, // 17 ! 566: ai_move, 0, NULL, ! 567: ai_move, 2, NULL, ! 568: ai_move, 3, NULL, ! 569: ai_move, 2, NULL, ! 570: ai_move, -2, tank_footstep // 22 ! 571: }; ! 572: mmove_t tank_move_attack_post_blast = {FRAME_attak117, FRAME_attak122, tank_frames_attack_post_blast, tank_run}; ! 573: ! 574: void tank_reattack_blaster (edict_t *self) ! 575: { ! 576: if (skill->value >= 2) ! 577: if (visible (self, self->enemy)) ! 578: if (self->enemy->health > 0) ! 579: if (random() <= 0.6) ! 580: { ! 581: self->monsterinfo.currentmove = &tank_move_reattack_blast; ! 582: return; ! 583: } ! 584: self->monsterinfo.currentmove = &tank_move_attack_post_blast; ! 585: } ! 586: ! 587: ! 588: void tank_poststrike (edict_t *self) ! 589: { ! 590: self->enemy = NULL; ! 591: tank_run (self); ! 592: } ! 593: ! 594: mframe_t tank_frames_attack_strike [] = ! 595: { ! 596: ai_move, 3, NULL, ! 597: ai_move, 2, NULL, ! 598: ai_move, 2, NULL, ! 599: ai_move, 1, NULL, ! 600: ai_move, 6, NULL, ! 601: ai_move, 7, NULL, ! 602: ai_move, 9, tank_footstep, ! 603: ai_move, 2, NULL, ! 604: ai_move, 1, NULL, ! 605: ai_move, 2, NULL, ! 606: ai_move, 2, tank_footstep, ! 607: ai_move, 2, NULL, ! 608: ai_move, 0, NULL, ! 609: ai_move, 0, NULL, ! 610: ai_move, 0, NULL, ! 611: ai_move, 0, NULL, ! 612: ai_move, -2, NULL, ! 613: ai_move, -2, NULL, ! 614: ai_move, 0, tank_windup, ! 615: ai_move, 0, NULL, ! 616: ai_move, 0, NULL, ! 617: ai_move, 0, NULL, ! 618: ai_move, 0, NULL, ! 619: ai_move, 0, NULL, ! 620: ai_move, 0, NULL, ! 621: ai_move, 0, TankStrike, ! 622: ai_move, 0, NULL, ! 623: ai_move, -1, NULL, ! 624: ai_move, -1, NULL, ! 625: ai_move, -1, NULL, ! 626: ai_move, -1, NULL, ! 627: ai_move, -1, NULL, ! 628: ai_move, -3, NULL, ! 629: ai_move, -10, NULL, ! 630: ai_move, -10, NULL, ! 631: ai_move, -2, NULL, ! 632: ai_move, -3, NULL, ! 633: ai_move, -2, tank_footstep ! 634: }; ! 635: mmove_t tank_move_attack_strike = {FRAME_attak201, FRAME_attak238, tank_frames_attack_strike, tank_poststrike}; ! 636: ! 637: mframe_t tank_frames_attack_pre_rocket [] = ! 638: { ! 639: ai_charge, 0, NULL, ! 640: ai_charge, 0, NULL, ! 641: ai_charge, 0, NULL, ! 642: ai_charge, 0, NULL, ! 643: ai_charge, 0, NULL, ! 644: ai_charge, 0, NULL, ! 645: ai_charge, 0, NULL, ! 646: ai_charge, 0, NULL, ! 647: ai_charge, 0, NULL, ! 648: ai_charge, 0, NULL, // 10 ! 649: ! 650: ai_charge, 0, NULL, ! 651: ai_charge, 1, NULL, ! 652: ai_charge, 2, NULL, ! 653: ai_charge, 7, NULL, ! 654: ai_charge, 7, NULL, ! 655: ai_charge, 7, tank_footstep, ! 656: ai_charge, 0, NULL, ! 657: ai_charge, 0, NULL, ! 658: ai_charge, 0, NULL, ! 659: ai_charge, 0, NULL, // 20 ! 660: ! 661: ai_charge, -3, NULL ! 662: }; ! 663: mmove_t tank_move_attack_pre_rocket = {FRAME_attak301, FRAME_attak321, tank_frames_attack_pre_rocket, tank_doattack_rocket}; ! 664: ! 665: mframe_t tank_frames_attack_fire_rocket [] = ! 666: { ! 667: ai_charge, -3, NULL, // Loop Start 22 ! 668: ai_charge, 0, NULL, ! 669: ai_charge, 0, TankRocket, // 24 ! 670: ai_charge, 0, NULL, ! 671: ai_charge, 0, NULL, ! 672: ai_charge, 0, TankRocket, ! 673: ai_charge, 0, NULL, ! 674: ai_charge, 0, NULL, ! 675: ai_charge, -1, TankRocket // 30 Loop End ! 676: }; ! 677: mmove_t tank_move_attack_fire_rocket = {FRAME_attak322, FRAME_attak330, tank_frames_attack_fire_rocket, tank_refire_rocket}; ! 678: ! 679: mframe_t tank_frames_attack_post_rocket [] = ! 680: { ! 681: ai_charge, 0, NULL, // 31 ! 682: ai_charge, -1, NULL, ! 683: ai_charge, -1, NULL, ! 684: ai_charge, 0, NULL, ! 685: ai_charge, 2, NULL, ! 686: ai_charge, 3, NULL, ! 687: ai_charge, 4, NULL, ! 688: ai_charge, 2, NULL, ! 689: ai_charge, 0, NULL, ! 690: ai_charge, 0, NULL, // 40 ! 691: ! 692: ai_charge, 0, NULL, ! 693: ai_charge, -9, NULL, ! 694: ai_charge, -8, NULL, ! 695: ai_charge, -7, NULL, ! 696: ai_charge, -1, NULL, ! 697: ai_charge, -1, tank_footstep, ! 698: ai_charge, 0, NULL, ! 699: ai_charge, 0, NULL, ! 700: ai_charge, 0, NULL, ! 701: ai_charge, 0, NULL, // 50 ! 702: ! 703: ai_charge, 0, NULL, ! 704: ai_charge, 0, NULL, ! 705: ai_charge, 0, NULL ! 706: }; ! 707: mmove_t tank_move_attack_post_rocket = {FRAME_attak331, FRAME_attak353, tank_frames_attack_post_rocket, tank_run}; ! 708: ! 709: mframe_t tank_frames_attack_chain [] = ! 710: { ! 711: ai_charge, 0, NULL, ! 712: ai_charge, 0, NULL, ! 713: ai_charge, 0, NULL, ! 714: ai_charge, 0, NULL, ! 715: ai_charge, 0, NULL, ! 716: NULL, 0, TankMachineGun, ! 717: NULL, 0, TankMachineGun, ! 718: NULL, 0, TankMachineGun, ! 719: NULL, 0, TankMachineGun, ! 720: NULL, 0, TankMachineGun, ! 721: NULL, 0, TankMachineGun, ! 722: NULL, 0, TankMachineGun, ! 723: NULL, 0, TankMachineGun, ! 724: NULL, 0, TankMachineGun, ! 725: NULL, 0, TankMachineGun, ! 726: NULL, 0, TankMachineGun, ! 727: NULL, 0, TankMachineGun, ! 728: NULL, 0, TankMachineGun, ! 729: NULL, 0, TankMachineGun, ! 730: NULL, 0, TankMachineGun, ! 731: NULL, 0, TankMachineGun, ! 732: NULL, 0, TankMachineGun, ! 733: NULL, 0, TankMachineGun, ! 734: NULL, 0, TankMachineGun, ! 735: ai_charge, 0, NULL, ! 736: ai_charge, 0, NULL, ! 737: ai_charge, 0, NULL, ! 738: ai_charge, 0, NULL, ! 739: ai_charge, 0, NULL ! 740: }; ! 741: mmove_t tank_move_attack_chain = {FRAME_attak401, FRAME_attak429, tank_frames_attack_chain, tank_run}; ! 742: ! 743: void tank_refire_rocket (edict_t *self) ! 744: { ! 745: // PMM - blindfire cleanup ! 746: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 747: { ! 748: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 749: self->monsterinfo.currentmove = &tank_move_attack_post_rocket; ! 750: return; ! 751: } ! 752: // pmm ! 753: ! 754: // Only on hard or nightmare ! 755: if ( skill->value >= 2 ) ! 756: if (self->enemy->health > 0) ! 757: if (visible(self, self->enemy) ) ! 758: if (random() <= 0.4) ! 759: { ! 760: self->monsterinfo.currentmove = &tank_move_attack_fire_rocket; ! 761: return; ! 762: } ! 763: self->monsterinfo.currentmove = &tank_move_attack_post_rocket; ! 764: } ! 765: ! 766: void tank_doattack_rocket (edict_t *self) ! 767: { ! 768: self->monsterinfo.currentmove = &tank_move_attack_fire_rocket; ! 769: } ! 770: ! 771: void tank_attack(edict_t *self) ! 772: { ! 773: vec3_t vec; ! 774: float range; ! 775: float r; ! 776: // PMM ! 777: float chance; ! 778: ! 779: // PMM ! 780: if (!self->enemy || !self->enemy->inuse) ! 781: return; ! 782: ! 783: if (self->enemy->health < 0) ! 784: { ! 785: self->monsterinfo.currentmove = &tank_move_attack_strike; ! 786: self->monsterinfo.aiflags &= ~AI_BRUTAL; ! 787: return; ! 788: } ! 789: ! 790: // PMM ! 791: if (self->monsterinfo.attack_state == AS_BLIND) ! 792: { ! 793: // setup shot probabilities ! 794: if (self->monsterinfo.blind_fire_delay < 1.0) ! 795: chance = 1.0; ! 796: else if (self->monsterinfo.blind_fire_delay < 7.5) ! 797: chance = 0.4; ! 798: else ! 799: chance = 0.1; ! 800: ! 801: r = random(); ! 802: ! 803: // gi.dprintf ("chance = %2.2f, roll = %2.2f\n", chance, r); ! 804: ! 805: self->monsterinfo.blind_fire_delay += 3.2 + 2.0 + random()*3.0; ! 806: ! 807: // don't shoot at the origin ! 808: if (VectorCompare (self->monsterinfo.blind_fire_target, vec3_origin)) ! 809: return; ! 810: ! 811: // don't shoot if the dice say not to ! 812: if (r > chance) ! 813: { ! 814: // if ((g_showlogic) && (g_showlogic->value)) ! 815: // gi.dprintf ("blindfire - NO SHOT\n"); ! 816: return; ! 817: } ! 818: ! 819: // turn on manual steering to signal both manual steering and blindfire ! 820: self->monsterinfo.aiflags |= AI_MANUAL_STEERING; ! 821: self->monsterinfo.currentmove = &tank_move_attack_fire_rocket; ! 822: self->monsterinfo.attack_finished = level.time + 3.0 + 2*random(); ! 823: self->pain_debounce_time = level.time + 5.0; // no pain for a while ! 824: return; ! 825: } ! 826: // pmm ! 827: ! 828: VectorSubtract (self->enemy->s.origin, self->s.origin, vec); ! 829: range = VectorLength (vec); ! 830: ! 831: r = random(); ! 832: ! 833: if (range <= 125) ! 834: { ! 835: if (r < 0.4) ! 836: self->monsterinfo.currentmove = &tank_move_attack_chain; ! 837: else ! 838: self->monsterinfo.currentmove = &tank_move_attack_blast; ! 839: } ! 840: else if (range <= 250) ! 841: { ! 842: if (r < 0.5) ! 843: self->monsterinfo.currentmove = &tank_move_attack_chain; ! 844: else ! 845: self->monsterinfo.currentmove = &tank_move_attack_blast; ! 846: } ! 847: else ! 848: { ! 849: if (r < 0.33) ! 850: self->monsterinfo.currentmove = &tank_move_attack_chain; ! 851: else if (r < 0.66) ! 852: { ! 853: self->monsterinfo.currentmove = &tank_move_attack_pre_rocket; ! 854: self->pain_debounce_time = level.time + 5.0; // no pain for a while ! 855: } ! 856: else ! 857: self->monsterinfo.currentmove = &tank_move_attack_blast; ! 858: } ! 859: } ! 860: ! 861: ! 862: // ! 863: // death ! 864: // ! 865: ! 866: void tank_dead (edict_t *self) ! 867: { ! 868: VectorSet (self->mins, -16, -16, -16); ! 869: VectorSet (self->maxs, 16, 16, -0); ! 870: self->movetype = MOVETYPE_TOSS; ! 871: self->svflags |= SVF_DEADMONSTER; ! 872: self->nextthink = 0; ! 873: gi.linkentity (self); ! 874: } ! 875: ! 876: mframe_t tank_frames_death1 [] = ! 877: { ! 878: ai_move, -7, NULL, ! 879: ai_move, -2, NULL, ! 880: ai_move, -2, NULL, ! 881: ai_move, 1, NULL, ! 882: ai_move, 3, NULL, ! 883: ai_move, 6, NULL, ! 884: ai_move, 1, NULL, ! 885: ai_move, 1, NULL, ! 886: ai_move, 2, NULL, ! 887: ai_move, 0, NULL, ! 888: ai_move, 0, NULL, ! 889: ai_move, 0, NULL, ! 890: ai_move, -2, NULL, ! 891: ai_move, 0, NULL, ! 892: ai_move, 0, NULL, ! 893: ai_move, -3, NULL, ! 894: ai_move, 0, NULL, ! 895: ai_move, 0, NULL, ! 896: ai_move, 0, NULL, ! 897: ai_move, 0, NULL, ! 898: ai_move, 0, NULL, ! 899: ai_move, 0, NULL, ! 900: ai_move, -4, NULL, ! 901: ai_move, -6, NULL, ! 902: ai_move, -4, NULL, ! 903: ai_move, -5, NULL, ! 904: ai_move, -7, NULL, ! 905: ai_move, -15, tank_thud, ! 906: ai_move, -5, NULL, ! 907: ai_move, 0, NULL, ! 908: ai_move, 0, NULL, ! 909: ai_move, 0, NULL ! 910: }; ! 911: mmove_t tank_move_death = {FRAME_death101, FRAME_death132, tank_frames_death1, tank_dead}; ! 912: ! 913: void tank_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 914: { ! 915: int n; ! 916: ! 917: // check for gib ! 918: if (self->health <= self->gib_health) ! 919: { ! 920: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); ! 921: for (n= 0; n < 1 /*4*/; n++) ! 922: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ! 923: for (n= 0; n < 4; n++) ! 924: ThrowGib (self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC); ! 925: ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ! 926: ThrowHead (self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC); ! 927: self->deadflag = DEAD_DEAD; ! 928: return; ! 929: } ! 930: ! 931: if (self->deadflag == DEAD_DEAD) ! 932: return; ! 933: ! 934: // regular death ! 935: gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); ! 936: self->deadflag = DEAD_DEAD; ! 937: self->takedamage = DAMAGE_YES; ! 938: ! 939: self->monsterinfo.currentmove = &tank_move_death; ! 940: ! 941: } ! 942: ! 943: //=========== ! 944: //PGM ! 945: qboolean tank_blocked (edict_t *self, float dist) ! 946: { ! 947: if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) )) ! 948: return true; ! 949: ! 950: if(blocked_checkplat (self, dist)) ! 951: return true; ! 952: ! 953: return false; ! 954: } ! 955: //PGM ! 956: //=========== ! 957: ! 958: // ! 959: // monster_tank ! 960: // ! 961: ! 962: /*QUAKED monster_tank (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight ! 963: */ ! 964: /*QUAKED monster_tank_commander (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight ! 965: */ ! 966: void SP_monster_tank (edict_t *self) ! 967: { ! 968: if (deathmatch->value) ! 969: { ! 970: G_FreeEdict (self); ! 971: return; ! 972: } ! 973: ! 974: self->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2"); ! 975: VectorSet (self->mins, -32, -32, -16); ! 976: VectorSet (self->maxs, 32, 32, 72); ! 977: self->movetype = MOVETYPE_STEP; ! 978: self->solid = SOLID_BBOX; ! 979: ! 980: sound_pain = gi.soundindex ("tank/tnkpain2.wav"); ! 981: sound_thud = gi.soundindex ("tank/tnkdeth2.wav"); ! 982: sound_idle = gi.soundindex ("tank/tnkidle1.wav"); ! 983: sound_die = gi.soundindex ("tank/death.wav"); ! 984: sound_step = gi.soundindex ("tank/step.wav"); ! 985: sound_windup = gi.soundindex ("tank/tnkatck4.wav"); ! 986: sound_strike = gi.soundindex ("tank/tnkatck5.wav"); ! 987: sound_sight = gi.soundindex ("tank/sight1.wav"); ! 988: ! 989: gi.soundindex ("tank/tnkatck1.wav"); ! 990: gi.soundindex ("tank/tnkatk2a.wav"); ! 991: gi.soundindex ("tank/tnkatk2b.wav"); ! 992: gi.soundindex ("tank/tnkatk2c.wav"); ! 993: gi.soundindex ("tank/tnkatk2d.wav"); ! 994: gi.soundindex ("tank/tnkatk2e.wav"); ! 995: gi.soundindex ("tank/tnkatck3.wav"); ! 996: ! 997: if (strcmp(self->classname, "monster_tank_commander") == 0) ! 998: { ! 999: self->health = 1000; ! 1000: self->gib_health = -225; ! 1001: } ! 1002: else ! 1003: { ! 1004: self->health = 750; ! 1005: self->gib_health = -200; ! 1006: } ! 1007: ! 1008: self->mass = 500; ! 1009: ! 1010: self->pain = tank_pain; ! 1011: self->die = tank_die; ! 1012: self->monsterinfo.stand = tank_stand; ! 1013: self->monsterinfo.walk = tank_walk; ! 1014: self->monsterinfo.run = tank_run; ! 1015: self->monsterinfo.dodge = NULL; ! 1016: self->monsterinfo.attack = tank_attack; ! 1017: self->monsterinfo.melee = NULL; ! 1018: self->monsterinfo.sight = tank_sight; ! 1019: self->monsterinfo.idle = tank_idle; ! 1020: self->monsterinfo.blocked = tank_blocked; // PGM ! 1021: ! 1022: gi.linkentity (self); ! 1023: ! 1024: self->monsterinfo.currentmove = &tank_move_stand; ! 1025: self->monsterinfo.scale = MODEL_SCALE; ! 1026: ! 1027: walkmonster_start(self); ! 1028: ! 1029: // PMM ! 1030: self->monsterinfo.aiflags |= AI_IGNORE_SHOTS; ! 1031: self->monsterinfo.blindfire = true; ! 1032: //pmm ! 1033: if (strcmp(self->classname, "monster_tank_commander") == 0) ! 1034: self->s.skinnum = 2; ! 1035: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.