|
|
1.1 ! root 1: /* ! 2: ============================================================================== ! 3: ! 4: MEDIC ! 5: ! 6: ============================================================================== ! 7: */ ! 8: ! 9: #include "g_local.h" ! 10: #include "m_medic.h" ! 11: ! 12: qboolean visible (edict_t *self, edict_t *other); ! 13: ! 14: ! 15: static int sound_idle1; ! 16: static int sound_pain1; ! 17: static int sound_pain2; ! 18: static int sound_die; ! 19: static int sound_sight; ! 20: static int sound_search; ! 21: static int sound_hook_launch; ! 22: static int sound_hook_hit; ! 23: static int sound_hook_heal; ! 24: static int sound_hook_retract; ! 25: ! 26: ! 27: edict_t *medic_FindDeadMonster (edict_t *self) ! 28: { ! 29: edict_t *ent = NULL; ! 30: edict_t *best = NULL; ! 31: ! 32: while ((ent = findradius(ent, self->s.origin, 1024)) != NULL) ! 33: { ! 34: if (ent == self) ! 35: continue; ! 36: if (!(ent->svflags & SVF_MONSTER)) ! 37: continue; ! 38: if (ent->monsterinfo.aiflags & AI_GOOD_GUY) ! 39: continue; ! 40: if (ent->owner) ! 41: continue; ! 42: if (ent->health > 0) ! 43: continue; ! 44: if (ent->nextthink) ! 45: continue; ! 46: if (!visible(self, ent)) ! 47: continue; ! 48: if (!best) ! 49: { ! 50: best = ent; ! 51: continue; ! 52: } ! 53: if (ent->max_health <= best->max_health) ! 54: continue; ! 55: best = ent; ! 56: } ! 57: ! 58: return best; ! 59: } ! 60: ! 61: void medic_idle (edict_t *self) ! 62: { ! 63: edict_t *ent; ! 64: ! 65: gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0); ! 66: ! 67: ent = medic_FindDeadMonster(self); ! 68: if (ent) ! 69: { ! 70: self->enemy = ent; ! 71: self->enemy->owner = self; ! 72: self->monsterinfo.aiflags |= AI_MEDIC; ! 73: FoundTarget (self); ! 74: } ! 75: } ! 76: ! 77: void medic_search (edict_t *self) ! 78: { ! 79: edict_t *ent; ! 80: ! 81: gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0); ! 82: ! 83: if (!self->oldenemy) ! 84: { ! 85: ent = medic_FindDeadMonster(self); ! 86: if (ent) ! 87: { ! 88: self->oldenemy = self->enemy; ! 89: self->enemy = ent; ! 90: self->enemy->owner = self; ! 91: self->monsterinfo.aiflags |= AI_MEDIC; ! 92: FoundTarget (self); ! 93: } ! 94: } ! 95: } ! 96: ! 97: void medic_sight (edict_t *self, edict_t *other) ! 98: { ! 99: gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); ! 100: } ! 101: ! 102: ! 103: mframe_t medic_frames_stand [] = ! 104: { ! 105: ai_stand, 0, medic_idle, ! 106: ai_stand, 0, NULL, ! 107: ai_stand, 0, NULL, ! 108: ai_stand, 0, NULL, ! 109: ai_stand, 0, NULL, ! 110: ai_stand, 0, NULL, ! 111: ai_stand, 0, NULL, ! 112: ai_stand, 0, NULL, ! 113: ai_stand, 0, NULL, ! 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, NULL, ! 124: ai_stand, 0, NULL, ! 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, NULL, ! 135: ai_stand, 0, NULL, ! 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, NULL, ! 146: ai_stand, 0, NULL, ! 147: ai_stand, 0, NULL, ! 148: ai_stand, 0, NULL, ! 149: ai_stand, 0, NULL, ! 150: ai_stand, 0, NULL, ! 151: ai_stand, 0, NULL, ! 152: ai_stand, 0, NULL, ! 153: ai_stand, 0, NULL, ! 154: ai_stand, 0, NULL, ! 155: ai_stand, 0, NULL, ! 156: ai_stand, 0, NULL, ! 157: ai_stand, 0, NULL, ! 158: ai_stand, 0, NULL, ! 159: ai_stand, 0, NULL, ! 160: ai_stand, 0, NULL, ! 161: ai_stand, 0, NULL, ! 162: ai_stand, 0, NULL, ! 163: ai_stand, 0, NULL, ! 164: ai_stand, 0, NULL, ! 165: ai_stand, 0, NULL, ! 166: ai_stand, 0, NULL, ! 167: ai_stand, 0, NULL, ! 168: ai_stand, 0, NULL, ! 169: ai_stand, 0, NULL, ! 170: ai_stand, 0, NULL, ! 171: ai_stand, 0, NULL, ! 172: ai_stand, 0, NULL, ! 173: ai_stand, 0, NULL, ! 174: ai_stand, 0, NULL, ! 175: ai_stand, 0, NULL, ! 176: ai_stand, 0, NULL, ! 177: ai_stand, 0, NULL, ! 178: ai_stand, 0, NULL, ! 179: ai_stand, 0, NULL, ! 180: ai_stand, 0, NULL, ! 181: ai_stand, 0, NULL, ! 182: ai_stand, 0, NULL, ! 183: ai_stand, 0, NULL, ! 184: ai_stand, 0, NULL, ! 185: ai_stand, 0, NULL, ! 186: ai_stand, 0, NULL, ! 187: ai_stand, 0, NULL, ! 188: ai_stand, 0, NULL, ! 189: ai_stand, 0, NULL, ! 190: ai_stand, 0, NULL, ! 191: ai_stand, 0, NULL, ! 192: ai_stand, 0, NULL, ! 193: ai_stand, 0, NULL, ! 194: ai_stand, 0, NULL, ! 195: ! 196: }; ! 197: mmove_t medic_move_stand = {FRAME_wait1, FRAME_wait90, medic_frames_stand, NULL}; ! 198: ! 199: void medic_stand (edict_t *self) ! 200: { ! 201: self->monsterinfo.currentmove = &medic_move_stand; ! 202: } ! 203: ! 204: ! 205: mframe_t medic_frames_walk [] = ! 206: { ! 207: ai_walk, 6.2, NULL, ! 208: ai_walk, 18.1, NULL, ! 209: ai_walk, 1, NULL, ! 210: ai_walk, 9, NULL, ! 211: ai_walk, 10, NULL, ! 212: ai_walk, 9, NULL, ! 213: ai_walk, 11, NULL, ! 214: ai_walk, 11.6, NULL, ! 215: ai_walk, 2, NULL, ! 216: ai_walk, 9.9, NULL, ! 217: ai_walk, 14, NULL, ! 218: ai_walk, 9.3, NULL ! 219: }; ! 220: mmove_t medic_move_walk = {FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL}; ! 221: ! 222: void medic_walk (edict_t *self) ! 223: { ! 224: self->monsterinfo.currentmove = &medic_move_walk; ! 225: } ! 226: ! 227: ! 228: mframe_t medic_frames_run [] = ! 229: { ! 230: ai_run, 18, NULL, ! 231: ai_run, 22.5, NULL, ! 232: ai_run, 25.4, NULL, ! 233: ai_run, 23.4, NULL, ! 234: ai_run, 24, NULL, ! 235: ai_run, 35.6, NULL ! 236: ! 237: }; ! 238: mmove_t medic_move_run = {FRAME_run1, FRAME_run6, medic_frames_run, NULL}; ! 239: ! 240: void medic_run (edict_t *self) ! 241: { ! 242: if (!(self->monsterinfo.aiflags & AI_MEDIC)) ! 243: { ! 244: edict_t *ent; ! 245: ! 246: ent = medic_FindDeadMonster(self); ! 247: if (ent) ! 248: { ! 249: self->oldenemy = self->enemy; ! 250: self->enemy = ent; ! 251: self->enemy->owner = self; ! 252: self->monsterinfo.aiflags |= AI_MEDIC; ! 253: FoundTarget (self); ! 254: return; ! 255: } ! 256: } ! 257: ! 258: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 259: self->monsterinfo.currentmove = &medic_move_stand; ! 260: else ! 261: self->monsterinfo.currentmove = &medic_move_run; ! 262: } ! 263: ! 264: ! 265: mframe_t medic_frames_pain1 [] = ! 266: { ! 267: ai_move, 0, NULL, ! 268: ai_move, 0, NULL, ! 269: ai_move, 0, NULL, ! 270: ai_move, 0, NULL, ! 271: ai_move, 0, NULL, ! 272: ai_move, 0, NULL, ! 273: ai_move, 0, NULL, ! 274: ai_move, 0, NULL ! 275: }; ! 276: mmove_t medic_move_pain1 = {FRAME_paina1, FRAME_paina8, medic_frames_pain1, medic_run}; ! 277: ! 278: mframe_t medic_frames_pain2 [] = ! 279: { ! 280: ai_move, 0, NULL, ! 281: ai_move, 0, NULL, ! 282: ai_move, 0, NULL, ! 283: ai_move, 0, NULL, ! 284: ai_move, 0, NULL, ! 285: ai_move, 0, NULL, ! 286: ai_move, 0, NULL, ! 287: ai_move, 0, NULL, ! 288: ai_move, 0, NULL, ! 289: ai_move, 0, NULL, ! 290: ai_move, 0, NULL, ! 291: ai_move, 0, NULL, ! 292: ai_move, 0, NULL, ! 293: ai_move, 0, NULL, ! 294: ai_move, 0, NULL ! 295: }; ! 296: mmove_t medic_move_pain2 = {FRAME_painb1, FRAME_painb15, medic_frames_pain2, medic_run}; ! 297: ! 298: void medic_pain (edict_t *self, edict_t *other, float kick, int damage) ! 299: { ! 300: if (self->health < (self->max_health / 2)) ! 301: self->s.skinnum = 1; ! 302: ! 303: if (level.time < self->pain_debounce_time) ! 304: return; ! 305: ! 306: self->pain_debounce_time = level.time + 3; ! 307: ! 308: if (skill->value == 3) ! 309: return; // no pain anims in nightmare ! 310: ! 311: if (random() < 0.5) ! 312: { ! 313: self->monsterinfo.currentmove = &medic_move_pain1; ! 314: gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); ! 315: } ! 316: else ! 317: { ! 318: self->monsterinfo.currentmove = &medic_move_pain2; ! 319: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); ! 320: } ! 321: } ! 322: ! 323: void medic_fire_blaster (edict_t *self) ! 324: { ! 325: vec3_t start; ! 326: vec3_t forward, right; ! 327: vec3_t end; ! 328: vec3_t dir; ! 329: int effect; ! 330: ! 331: if ((self->s.frame == FRAME_attack9) || (self->s.frame == FRAME_attack12)) ! 332: effect = EF_BLASTER; ! 333: else if ((self->s.frame == FRAME_attack19) || (self->s.frame == FRAME_attack22) || (self->s.frame == FRAME_attack25) || (self->s.frame == FRAME_attack28)) ! 334: effect = EF_HYPERBLASTER; ! 335: else ! 336: effect = 0; ! 337: ! 338: AngleVectors (self->s.angles, forward, right, NULL); ! 339: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MEDIC_BLASTER_1], forward, right, start); ! 340: ! 341: VectorCopy (self->enemy->s.origin, end); ! 342: end[2] += self->enemy->viewheight; ! 343: VectorSubtract (end, start, dir); ! 344: ! 345: monster_fire_blaster (self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_1, effect); ! 346: } ! 347: ! 348: ! 349: void medic_dead (edict_t *self) ! 350: { ! 351: VectorSet (self->mins, -16, -16, -24); ! 352: VectorSet (self->maxs, 16, 16, -8); ! 353: self->movetype = MOVETYPE_TOSS; ! 354: self->svflags |= SVF_DEADMONSTER; ! 355: self->nextthink = 0; ! 356: gi.linkentity (self); ! 357: } ! 358: ! 359: mframe_t medic_frames_death [] = ! 360: { ! 361: ai_move, 0, NULL, ! 362: ai_move, 0, NULL, ! 363: ai_move, 0, NULL, ! 364: ai_move, 0, NULL, ! 365: ai_move, 0, NULL, ! 366: ai_move, 0, NULL, ! 367: ai_move, 0, NULL, ! 368: ai_move, 0, NULL, ! 369: ai_move, 0, NULL, ! 370: ai_move, 0, NULL, ! 371: ai_move, 0, NULL, ! 372: ai_move, 0, NULL, ! 373: ai_move, 0, NULL, ! 374: ai_move, 0, NULL, ! 375: ai_move, 0, NULL, ! 376: ai_move, 0, NULL, ! 377: ai_move, 0, NULL, ! 378: ai_move, 0, NULL, ! 379: ai_move, 0, NULL, ! 380: ai_move, 0, NULL, ! 381: ai_move, 0, NULL, ! 382: ai_move, 0, NULL, ! 383: ai_move, 0, NULL, ! 384: ai_move, 0, NULL, ! 385: ai_move, 0, NULL, ! 386: ai_move, 0, NULL, ! 387: ai_move, 0, NULL, ! 388: ai_move, 0, NULL, ! 389: ai_move, 0, NULL, ! 390: ai_move, 0, NULL ! 391: }; ! 392: mmove_t medic_move_death = {FRAME_death1, FRAME_death30, medic_frames_death, medic_dead}; ! 393: ! 394: void medic_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 395: { ! 396: int n; ! 397: ! 398: // if we had a pending patient, free him up for another medic ! 399: if ((self->enemy) && (self->enemy->owner == self)) ! 400: self->enemy->owner = NULL; ! 401: ! 402: // check for gib ! 403: if (self->health <= self->gib_health) ! 404: { ! 405: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); ! 406: for (n= 0; n < 2; n++) ! 407: ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); ! 408: for (n= 0; n < 4; n++) ! 409: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ! 410: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); ! 411: self->deadflag = DEAD_DEAD; ! 412: return; ! 413: } ! 414: ! 415: if (self->deadflag == DEAD_DEAD) ! 416: return; ! 417: ! 418: // regular death ! 419: gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); ! 420: self->deadflag = DEAD_DEAD; ! 421: self->takedamage = DAMAGE_YES; ! 422: ! 423: self->monsterinfo.currentmove = &medic_move_death; ! 424: } ! 425: ! 426: ! 427: void medic_duck_down (edict_t *self) ! 428: { ! 429: if (self->monsterinfo.aiflags & AI_DUCKED) ! 430: return; ! 431: self->monsterinfo.aiflags |= AI_DUCKED; ! 432: self->maxs[2] -= 32; ! 433: self->takedamage = DAMAGE_YES; ! 434: self->monsterinfo.pausetime = level.time + 1; ! 435: gi.linkentity (self); ! 436: } ! 437: ! 438: void medic_duck_hold (edict_t *self) ! 439: { ! 440: if (level.time >= self->monsterinfo.pausetime) ! 441: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 442: else ! 443: self->monsterinfo.aiflags |= AI_HOLD_FRAME; ! 444: } ! 445: ! 446: void medic_duck_up (edict_t *self) ! 447: { ! 448: self->monsterinfo.aiflags &= ~AI_DUCKED; ! 449: self->maxs[2] += 32; ! 450: self->takedamage = DAMAGE_AIM; ! 451: gi.linkentity (self); ! 452: } ! 453: ! 454: mframe_t medic_frames_duck [] = ! 455: { ! 456: ai_move, -1, NULL, ! 457: ai_move, -1, NULL, ! 458: ai_move, -1, medic_duck_down, ! 459: ai_move, -1, medic_duck_hold, ! 460: ai_move, -1, NULL, ! 461: ai_move, -1, NULL, ! 462: ai_move, -1, medic_duck_up, ! 463: ai_move, -1, NULL, ! 464: ai_move, -1, NULL, ! 465: ai_move, -1, NULL, ! 466: ai_move, -1, NULL, ! 467: ai_move, -1, NULL, ! 468: ai_move, -1, NULL, ! 469: ai_move, -1, NULL, ! 470: ai_move, -1, NULL, ! 471: ai_move, -1, NULL ! 472: }; ! 473: mmove_t medic_move_duck = {FRAME_duck1, FRAME_duck16, medic_frames_duck, medic_run}; ! 474: ! 475: void medic_dodge (edict_t *self, edict_t *attacker, float eta) ! 476: { ! 477: if (random() > 0.25) ! 478: return; ! 479: ! 480: if (!self->enemy) ! 481: self->enemy = attacker; ! 482: ! 483: self->monsterinfo.currentmove = &medic_move_duck; ! 484: } ! 485: ! 486: mframe_t medic_frames_attackHyperBlaster [] = ! 487: { ! 488: ai_charge, 0, NULL, ! 489: ai_charge, 0, NULL, ! 490: ai_charge, 0, NULL, ! 491: ai_charge, 0, NULL, ! 492: ai_charge, 0, medic_fire_blaster, ! 493: ai_charge, 0, medic_fire_blaster, ! 494: ai_charge, 0, medic_fire_blaster, ! 495: ai_charge, 0, medic_fire_blaster, ! 496: ai_charge, 0, medic_fire_blaster, ! 497: ai_charge, 0, medic_fire_blaster, ! 498: ai_charge, 0, medic_fire_blaster, ! 499: ai_charge, 0, medic_fire_blaster, ! 500: ai_charge, 0, medic_fire_blaster, ! 501: ai_charge, 0, medic_fire_blaster, ! 502: ai_charge, 0, medic_fire_blaster, ! 503: ai_charge, 0, medic_fire_blaster ! 504: }; ! 505: mmove_t medic_move_attackHyperBlaster = {FRAME_attack15, FRAME_attack30, medic_frames_attackHyperBlaster, medic_run}; ! 506: ! 507: ! 508: void medic_continue (edict_t *self) ! 509: { ! 510: if (visible (self, self->enemy) ) ! 511: if (random() <= 0.95) ! 512: self->monsterinfo.currentmove = &medic_move_attackHyperBlaster; ! 513: } ! 514: ! 515: ! 516: mframe_t medic_frames_attackBlaster [] = ! 517: { ! 518: ai_charge, 0, NULL, ! 519: ai_charge, 5, NULL, ! 520: ai_charge, 5, NULL, ! 521: ai_charge, 3, NULL, ! 522: ai_charge, 2, NULL, ! 523: ai_charge, 0, NULL, ! 524: ai_charge, 0, NULL, ! 525: ai_charge, 0, NULL, ! 526: ai_charge, 0, medic_fire_blaster, ! 527: ai_charge, 0, NULL, ! 528: ai_charge, 0, NULL, ! 529: ai_charge, 0, medic_fire_blaster, ! 530: ai_charge, 0, NULL, ! 531: ai_charge, 0, medic_continue // Change to medic_continue... Else, go to frame 32 ! 532: }; ! 533: mmove_t medic_move_attackBlaster = {FRAME_attack1, FRAME_attack14, medic_frames_attackBlaster, medic_run}; ! 534: ! 535: ! 536: void medic_hook_launch (edict_t *self) ! 537: { ! 538: gi.sound (self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0); ! 539: } ! 540: ! 541: void ED_CallSpawn (edict_t *ent); ! 542: ! 543: static vec3_t medic_cable_offsets[] = ! 544: { ! 545: 45.0, -9.2, 15.5, ! 546: 48.4, -9.7, 15.2, ! 547: 47.8, -9.8, 15.8, ! 548: 47.3, -9.3, 14.3, ! 549: 45.4, -10.1, 13.1, ! 550: 41.9, -12.7, 12.0, ! 551: 37.8, -15.8, 11.2, ! 552: 34.3, -18.4, 10.7, ! 553: 32.7, -19.7, 10.4, ! 554: 32.7, -19.7, 10.4 ! 555: }; ! 556: ! 557: void medic_cable_attack (edict_t *self) ! 558: { ! 559: vec3_t offset, start, end, f, r; ! 560: trace_t tr; ! 561: vec3_t dir, angles; ! 562: float distance; ! 563: ! 564: if (!self->enemy->inuse) ! 565: return; ! 566: ! 567: AngleVectors (self->s.angles, f, r, NULL); ! 568: VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset); ! 569: G_ProjectSource (self->s.origin, offset, f, r, start); ! 570: ! 571: // check for max distance ! 572: VectorSubtract (start, self->enemy->s.origin, dir); ! 573: distance = VectorLength(dir); ! 574: if (distance > 256) ! 575: return; ! 576: ! 577: // check for min/max pitch ! 578: vectoangles (dir, angles); ! 579: if (angles[0] < -180) ! 580: angles[0] += 360; ! 581: if (fabs(angles[0]) > 45) ! 582: return; ! 583: ! 584: tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SHOT); ! 585: if (tr.fraction != 1.0 && tr.ent != self->enemy) ! 586: return; ! 587: ! 588: if (self->s.frame == FRAME_attack43) ! 589: { ! 590: gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0); ! 591: self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; ! 592: } ! 593: else if (self->s.frame == FRAME_attack50) ! 594: { ! 595: self->enemy->spawnflags = 0; ! 596: self->enemy->monsterinfo.aiflags = 0; ! 597: self->enemy->target = NULL; ! 598: self->enemy->targetname = NULL; ! 599: self->enemy->combattarget = NULL; ! 600: self->enemy->deathtarget = NULL; ! 601: self->enemy->owner = self; ! 602: ED_CallSpawn (self->enemy); ! 603: self->enemy->owner = NULL; ! 604: if (self->enemy->think) ! 605: { ! 606: self->enemy->nextthink = level.time; ! 607: self->enemy->think (self->enemy); ! 608: } ! 609: self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; ! 610: if (self->oldenemy && self->oldenemy->client) ! 611: { ! 612: self->enemy->enemy = self->oldenemy; ! 613: FoundTarget (self->enemy); ! 614: } ! 615: } ! 616: else ! 617: { ! 618: if (self->s.frame == FRAME_attack44) ! 619: gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0); ! 620: } ! 621: ! 622: // adjust start for beam origin being in middle of a segment ! 623: VectorMA (start, 8, f, start); ! 624: ! 625: // adjust end z for end spot since the monster is currently dead ! 626: VectorCopy (self->enemy->s.origin, end); ! 627: end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2; ! 628: ! 629: gi.WriteByte (svc_temp_entity); ! 630: gi.WriteByte (TE_MEDIC_CABLE_ATTACK); ! 631: gi.WriteShort (self - g_edicts); ! 632: gi.WritePosition (start); ! 633: gi.WritePosition (end); ! 634: gi.multicast (self->s.origin, MULTICAST_PVS); ! 635: } ! 636: ! 637: void medic_hook_retract (edict_t *self) ! 638: { ! 639: gi.sound (self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0); ! 640: self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING; ! 641: } ! 642: ! 643: mframe_t medic_frames_attackCable [] = ! 644: { ! 645: ai_move, 2, NULL, ! 646: ai_move, 3, NULL, ! 647: ai_move, 5, NULL, ! 648: ai_move, 4.4, NULL, ! 649: ai_charge, 4.7, NULL, ! 650: ai_charge, 5, NULL, ! 651: ai_charge, 6, NULL, ! 652: ai_charge, 4, NULL, ! 653: ai_charge, 0, NULL, ! 654: ai_move, 0, medic_hook_launch, ! 655: ai_move, 0, medic_cable_attack, ! 656: ai_move, 0, medic_cable_attack, ! 657: ai_move, 0, medic_cable_attack, ! 658: ai_move, 0, medic_cable_attack, ! 659: ai_move, 0, medic_cable_attack, ! 660: ai_move, 0, medic_cable_attack, ! 661: ai_move, 0, medic_cable_attack, ! 662: ai_move, 0, medic_cable_attack, ! 663: ai_move, 0, medic_cable_attack, ! 664: ai_move, -15, medic_hook_retract, ! 665: ai_move, -1.5, NULL, ! 666: ai_move, -1.2, NULL, ! 667: ai_move, -3, NULL, ! 668: ai_move, -2, NULL, ! 669: ai_move, 0.3, NULL, ! 670: ai_move, 0.7, NULL, ! 671: ai_move, 1.2, NULL, ! 672: ai_move, 1.3, NULL ! 673: }; ! 674: mmove_t medic_move_attackCable = {FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run}; ! 675: ! 676: ! 677: void medic_attack(edict_t *self) ! 678: { ! 679: if (self->monsterinfo.aiflags & AI_MEDIC) ! 680: self->monsterinfo.currentmove = &medic_move_attackCable; ! 681: else ! 682: self->monsterinfo.currentmove = &medic_move_attackBlaster; ! 683: } ! 684: ! 685: qboolean medic_checkattack (edict_t *self) ! 686: { ! 687: if (self->monsterinfo.aiflags & AI_MEDIC) ! 688: { ! 689: medic_attack(self); ! 690: return true; ! 691: } ! 692: ! 693: return M_CheckAttack (self); ! 694: } ! 695: ! 696: ! 697: /*QUAKED monster_medic (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight ! 698: */ ! 699: void SP_monster_medic (edict_t *self) ! 700: { ! 701: if (deathmatch->value) ! 702: { ! 703: G_FreeEdict (self); ! 704: return; ! 705: } ! 706: ! 707: sound_idle1 = gi.soundindex ("medic/idle.wav"); ! 708: sound_pain1 = gi.soundindex ("medic/medpain1.wav"); ! 709: sound_pain2 = gi.soundindex ("medic/medpain2.wav"); ! 710: sound_die = gi.soundindex ("medic/meddeth1.wav"); ! 711: sound_sight = gi.soundindex ("medic/medsght1.wav"); ! 712: sound_search = gi.soundindex ("medic/medsrch1.wav"); ! 713: sound_hook_launch = gi.soundindex ("medic/medatck2.wav"); ! 714: sound_hook_hit = gi.soundindex ("medic/medatck3.wav"); ! 715: sound_hook_heal = gi.soundindex ("medic/medatck4.wav"); ! 716: sound_hook_retract = gi.soundindex ("medic/medatck5.wav"); ! 717: ! 718: gi.soundindex ("medic/medatck1.wav"); ! 719: ! 720: self->movetype = MOVETYPE_STEP; ! 721: self->solid = SOLID_BBOX; ! 722: self->s.modelindex = gi.modelindex ("models/monsters/medic/tris.md2"); ! 723: VectorSet (self->mins, -24, -24, -24); ! 724: VectorSet (self->maxs, 24, 24, 32); ! 725: ! 726: self->health = 300; ! 727: self->gib_health = -130; ! 728: self->mass = 400; ! 729: ! 730: self->pain = medic_pain; ! 731: self->die = medic_die; ! 732: ! 733: self->monsterinfo.stand = medic_stand; ! 734: self->monsterinfo.walk = medic_walk; ! 735: self->monsterinfo.run = medic_run; ! 736: self->monsterinfo.dodge = medic_dodge; ! 737: self->monsterinfo.attack = medic_attack; ! 738: self->monsterinfo.melee = NULL; ! 739: self->monsterinfo.sight = medic_sight; ! 740: self->monsterinfo.idle = medic_idle; ! 741: self->monsterinfo.search = medic_search; ! 742: self->monsterinfo.checkattack = medic_checkattack; ! 743: ! 744: gi.linkentity (self); ! 745: ! 746: self->monsterinfo.currentmove = &medic_move_stand; ! 747: self->monsterinfo.scale = MODEL_SCALE; ! 748: ! 749: walkmonster_start (self); ! 750: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.