|
|
1.1 ! root 1: /* ! 2: ============================================================================== ! 3: ! 4: carrier ! 5: ! 6: ============================================================================== ! 7: */ ! 8: ! 9: // self->timestamp used for frame calculations in grenade & spawn code ! 10: // self->wait used to prevent rapid refire of rocket launcher ! 11: ! 12: #include "g_local.h" ! 13: #include "m_carrier.h" ! 14: ! 15: #define CARRIER_ROCKET_TIME 2 // number of seconds between rocket shots ! 16: #define CARRIER_ROCKET_SPEED 750 ! 17: #define NUM_FLYERS_SPAWNED 6 // max # of flyers he can spawn ! 18: ! 19: #define RAIL_FIRE_TIME 3 ! 20: ! 21: void BossExplode (edict_t *self); ! 22: void Grenade_Explode (edict_t *ent); ! 23: ! 24: qboolean infront (edict_t *self, edict_t *other); ! 25: qboolean inback (edict_t *self, edict_t *other); ! 26: qboolean below (edict_t *self, edict_t *other); ! 27: void drawbbox (edict_t *self); ! 28: ! 29: //char *ED_NewString (char *string); ! 30: void ED_CallSpawn (edict_t *ent); ! 31: ! 32: static int sound_pain1; ! 33: static int sound_pain2; ! 34: static int sound_pain3; ! 35: static int sound_death; ! 36: static int sound_search1; ! 37: static int sound_rail; ! 38: ! 39: float orig_yaw_speed; ! 40: ! 41: vec3_t flyer_mins = {-16, -16, -24}; ! 42: vec3_t flyer_maxs = {16, 16, 16}; ! 43: ! 44: extern mmove_t flyer_move_attack2, flyer_move_attack3, flyer_move_kamikaze; ! 45: ! 46: ! 47: void carrier_run (edict_t *self); ! 48: void carrier_stand (edict_t *self); ! 49: void carrier_dead (edict_t *self); ! 50: void carrier_attack (edict_t *self); ! 51: void carrier_attack_mg (edict_t *self); ! 52: void carrier_reattack_mg (edict_t *self); ! 53: void carrier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); ! 54: ! 55: void carrier_attack_gren (edict_t *self); ! 56: void carrier_reattack_gren (edict_t *self); ! 57: ! 58: void carrier_start_spawn (edict_t *self); ! 59: void carrier_spawn_check (edict_t *self); ! 60: void carrier_prep_spawn (edict_t *self); ! 61: ! 62: void CarrierMachineGunHold (edict_t *self); ! 63: void CarrierRocket (edict_t *self); ! 64: ! 65: ! 66: ! 67: // code starts here ! 68: void carrier_search (edict_t *self) ! 69: { ! 70: if (random() < 0.5) ! 71: gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0); ! 72: } ! 73: ! 74: // ! 75: // this is the smarts for the rocket launcher in coop ! 76: // ! 77: // if there is a player behind/below the carrier, and we can shoot, and we can trace a LOS to them .. ! 78: // pick one of the group, and let it rip ! 79: void CarrierCoopCheck (edict_t *self) ! 80: { ! 81: // no more than 4 players in coop, so.. ! 82: edict_t *targets[4]; ! 83: int num_targets = 0, target, player; ! 84: edict_t *ent; ! 85: trace_t tr; ! 86: ! 87: // if we're not in coop, this is a noop ! 88: if (!coop || !coop->value) ! 89: return; ! 90: // if we are, and we have recently fired, bail ! 91: if (self->wait > level.time) ! 92: return; ! 93: ! 94: memset (targets, 0, 4*sizeof(edict_t *)); ! 95: ! 96: // cycle through players ! 97: for (player = 1; player <= game.maxclients; player++) ! 98: { ! 99: ent = &g_edicts[player]; ! 100: if (!ent->inuse) ! 101: continue; ! 102: if (!ent->client) ! 103: continue; ! 104: if (inback(self, ent) || below(self, ent)) ! 105: { ! 106: tr = gi.trace (self->s.origin, NULL, NULL, ent->s.origin, self, MASK_SOLID); ! 107: if (tr.fraction == 1.0) ! 108: { ! 109: if ((g_showlogic) && (g_showlogic->value)) ! 110: gi.dprintf ("Carrier: found a player who I can shoot\n"); ! 111: targets[num_targets++] = ent; ! 112: } ! 113: } ! 114: } ! 115: ! 116: if (!num_targets) ! 117: return; ! 118: ! 119: // get a number from 0 to (num_targets-1) ! 120: target = random() * num_targets; ! 121: ! 122: // just in case we got a 1.0 from random ! 123: if (target == num_targets) ! 124: target--; ! 125: ! 126: // make sure to prevent rapid fire rockets ! 127: self->wait = level.time + CARRIER_ROCKET_TIME; ! 128: ! 129: // save off the real enemy ! 130: ent = self->enemy; ! 131: // set the new guy as temporary enemy ! 132: self->enemy = targets[target]; ! 133: CarrierRocket (self); ! 134: // put the real enemy back ! 135: self->enemy = ent; ! 136: ! 137: // we're done ! 138: return; ! 139: } ! 140: ! 141: void CarrierGrenade (edict_t *self) ! 142: { ! 143: vec3_t start; ! 144: vec3_t forward, right, up; ! 145: vec3_t aim; ! 146: int flash_number; ! 147: float direction; // from lower left to upper right, or lower right to upper left ! 148: float spreadR, spreadU; ! 149: int mytime; ! 150: ! 151: CarrierCoopCheck(self); ! 152: ! 153: if (!self->enemy) ! 154: return; ! 155: ! 156: if (random() < 0.5) ! 157: direction = -1.0; ! 158: else ! 159: direction = 1.0; ! 160: ! 161: mytime = (int)((level.time - self->timestamp)/0.4); ! 162: ! 163: if (mytime == 0) ! 164: { ! 165: spreadR = 0.15 * direction; ! 166: // spreadU = 0.1 * direction; ! 167: spreadU = 0.1 - 0.1 * direction; ! 168: } ! 169: else if (mytime == 1) ! 170: { ! 171: spreadR = 0; ! 172: // spreadU = 0; ! 173: spreadU = 0.1; ! 174: } ! 175: else if (mytime == 2) ! 176: { ! 177: spreadR = -0.15 * direction; ! 178: // spreadU = -0.1 * direction; ! 179: spreadU = 0.1 - -0.1 * direction; ! 180: } ! 181: else if (mytime == 3) ! 182: { ! 183: spreadR = 0; ! 184: // spreadU = 0; ! 185: spreadU = 0.1; ! 186: } ! 187: else ! 188: { ! 189: // error, shoot straight ! 190: if ((g_showlogic) && (g_showlogic->value)) ! 191: gi.dprintf ("CarrierGrenade: bad time %2.2f %2.2f\n", level.time, self->timestamp); ! 192: spreadR = 0; ! 193: spreadU = 0; ! 194: } ! 195: ! 196: AngleVectors (self->s.angles, forward, right, up); ! 197: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_GRENADE], forward, right, start); ! 198: ! 199: VectorSubtract (self->enemy->s.origin, start, aim); ! 200: VectorNormalize (aim); ! 201: ! 202: VectorMA (aim, spreadR, right, aim); ! 203: VectorMA (aim, spreadU, up, aim); ! 204: ! 205: if(aim[2] > 0.15) ! 206: aim[2] = 0.15; ! 207: else if(aim[2] < -0.5) ! 208: aim[2] = -0.5; ! 209: ! 210: flash_number = MZ2_GUNNER_GRENADE_1; ! 211: monster_fire_grenade (self, start, aim, 50, 600, flash_number); ! 212: } ! 213: ! 214: void CarrierPredictiveRocket (edict_t *self) ! 215: { ! 216: vec3_t forward, right; ! 217: vec3_t start; ! 218: vec3_t dir; ! 219: ! 220: // if ((g_showlogic) && (g_showlogic->value)) ! 221: // gi.dprintf("predictive fire\n"); ! 222: ! 223: AngleVectors (self->s.angles, forward, right, NULL); ! 224: ! 225: //1 ! 226: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_1], forward, right, start); ! 227: PredictAim (self->enemy, start, CARRIER_ROCKET_SPEED, false, -0.3, dir, NULL); ! 228: monster_fire_rocket (self, start, dir, 50, CARRIER_ROCKET_SPEED, MZ2_CARRIER_ROCKET_1); ! 229: ! 230: //2 ! 231: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_2], forward, right, start); ! 232: PredictAim (self->enemy, start, CARRIER_ROCKET_SPEED, false, -0.15, dir, NULL); ! 233: monster_fire_rocket (self, start, dir, 50, CARRIER_ROCKET_SPEED, MZ2_CARRIER_ROCKET_2); ! 234: ! 235: //3 ! 236: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_3], forward, right, start); ! 237: PredictAim (self->enemy, start, CARRIER_ROCKET_SPEED, false, 0, dir, NULL); ! 238: monster_fire_rocket (self, start, dir, 50, CARRIER_ROCKET_SPEED, MZ2_CARRIER_ROCKET_3); ! 239: ! 240: //4 ! 241: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_4], forward, right, start); ! 242: PredictAim (self->enemy, start, CARRIER_ROCKET_SPEED, false, 0.15, dir, NULL); ! 243: monster_fire_rocket (self, start, dir, 50, CARRIER_ROCKET_SPEED, MZ2_CARRIER_ROCKET_4); ! 244: } ! 245: ! 246: void CarrierRocket (edict_t *self) ! 247: { ! 248: vec3_t forward, right; ! 249: vec3_t start; ! 250: vec3_t dir; ! 251: vec3_t vec; ! 252: ! 253: if(self->enemy) ! 254: { ! 255: if(self->enemy->client && random() < 0.5) ! 256: { ! 257: CarrierPredictiveRocket(self); ! 258: return; ! 259: } ! 260: } ! 261: else ! 262: return; ! 263: ! 264: AngleVectors (self->s.angles, forward, right, NULL); ! 265: ! 266: //1 ! 267: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_1], forward, right, start); ! 268: VectorCopy (self->enemy->s.origin, vec); ! 269: // vec[2] += self->enemy->viewheight; ! 270: vec[2] -= 15; ! 271: VectorSubtract (vec, start, dir); ! 272: VectorNormalize (dir); ! 273: VectorMA (dir, 0.4, right, dir); ! 274: VectorNormalize (dir); ! 275: monster_fire_rocket (self, start, dir, 50, 500, MZ2_CARRIER_ROCKET_1); ! 276: ! 277: //2 ! 278: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_2], forward, right, start); ! 279: VectorCopy (self->enemy->s.origin, vec); ! 280: // vec[2] += self->enemy->viewheight; ! 281: VectorSubtract (vec, start, dir); ! 282: VectorNormalize (dir); ! 283: VectorMA (dir, 0.025, right, dir); ! 284: VectorNormalize (dir); ! 285: monster_fire_rocket (self, start, dir, 50, 500, MZ2_CARRIER_ROCKET_2); ! 286: ! 287: //3 ! 288: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_3], forward, right, start); ! 289: VectorCopy (self->enemy->s.origin, vec); ! 290: // vec[2] += self->enemy->viewheight; ! 291: VectorSubtract (vec, start, dir); ! 292: VectorNormalize (dir); ! 293: VectorMA (dir, -0.025, right, dir); ! 294: VectorNormalize (dir); ! 295: monster_fire_rocket (self, start, dir, 50, 500, MZ2_CARRIER_ROCKET_3); ! 296: ! 297: //4 ! 298: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_ROCKET_4], forward, right, start); ! 299: VectorCopy (self->enemy->s.origin, vec); ! 300: // vec[2] += self->enemy->viewheight; ! 301: vec[2] -= 15; ! 302: VectorSubtract (vec, start, dir); ! 303: VectorNormalize (dir); ! 304: VectorMA (dir, -0.4, right, dir); ! 305: VectorNormalize (dir); ! 306: monster_fire_rocket (self, start, dir, 50, 500, MZ2_CARRIER_ROCKET_4); ! 307: ! 308: //5 ! 309: // G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_4], forward, right, start); ! 310: // VectorCopy (self->enemy->s.origin, vec); ! 311: // vec[2] += self->enemy->viewheight; ! 312: // VectorSubtract (vec, start, dir); ! 313: // VectorNormalize (dir); ! 314: // monster_fire_rocket (self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_2); ! 315: } ! 316: ! 317: void carrier_firebullet_right (edict_t *self) ! 318: { ! 319: vec3_t forward, right, target; ! 320: vec3_t start; ! 321: int flashnum; ! 322: ! 323: // if we're in manual steering mode, it means we're leaning down .. use the lower shot ! 324: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 325: flashnum = MZ2_CARRIER_MACHINEGUN_R2; ! 326: else ! 327: flashnum = MZ2_CARRIER_MACHINEGUN_R1; ! 328: ! 329: AngleVectors (self->s.angles, forward, right, NULL); ! 330: G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start); ! 331: ! 332: // VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target); ! 333: VectorMA (self->enemy->s.origin, 0.2, self->enemy->velocity, target); ! 334: target[2] += self->enemy->viewheight; ! 335: /* ! 336: gi.WriteByte (svc_temp_entity); ! 337: gi.WriteByte (TE_DEBUGTRAIL); ! 338: gi.WritePosition (start); ! 339: gi.WritePosition (target); ! 340: gi.multicast (start, MULTICAST_ALL); ! 341: */ ! 342: VectorSubtract (target, start, forward); ! 343: VectorNormalize (forward); ! 344: ! 345: monster_fire_bullet (self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD*3, DEFAULT_BULLET_VSPREAD, flashnum); ! 346: } ! 347: ! 348: void carrier_firebullet_left (edict_t *self) ! 349: { ! 350: vec3_t forward, right, target; ! 351: vec3_t start; ! 352: int flashnum; ! 353: ! 354: // if we're in manual steering mode, it means we're leaning down .. use the lower shot ! 355: if (self->monsterinfo.aiflags & AI_MANUAL_STEERING) ! 356: flashnum = MZ2_CARRIER_MACHINEGUN_L2; ! 357: else ! 358: flashnum = MZ2_CARRIER_MACHINEGUN_L1; ! 359: ! 360: AngleVectors (self->s.angles, forward, right, NULL); ! 361: G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start); ! 362: ! 363: // VectorMA (self->enemy->s.origin, 0.2, self->enemy->velocity, target); ! 364: VectorMA (self->enemy->s.origin, -0.2, self->enemy->velocity, target); ! 365: ! 366: target[2] += self->enemy->viewheight; ! 367: VectorSubtract (target, start, forward); ! 368: /* ! 369: gi.WriteByte (svc_temp_entity); ! 370: gi.WriteByte (TE_DEBUGTRAIL); ! 371: gi.WritePosition (start); ! 372: gi.WritePosition (target); ! 373: gi.multicast (start, MULTICAST_ALL); ! 374: */ ! 375: VectorNormalize (forward); ! 376: ! 377: monster_fire_bullet (self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD*3, DEFAULT_BULLET_VSPREAD, flashnum); ! 378: } ! 379: ! 380: void CarrierMachineGun (edict_t *self) ! 381: { ! 382: CarrierCoopCheck(self); ! 383: if (self->enemy) ! 384: carrier_firebullet_left(self); ! 385: if (self->enemy) ! 386: carrier_firebullet_right(self); ! 387: } ! 388: ! 389: void CarrierSpawn (edict_t *self) ! 390: { ! 391: vec3_t f, r, offset, startpoint, spawnpoint; ! 392: edict_t *ent; ! 393: int mytime; ! 394: ! 395: // VectorSet (offset, 105, 0, -30); // real distance needed is (sqrt (56*56*2) + sqrt(16*16*2)) or 101.8 ! 396: VectorSet (offset, 105, 0, -58); // real distance needed is (sqrt (56*56*2) + sqrt(16*16*2)) or 101.8 ! 397: AngleVectors (self->s.angles, f, r, NULL); ! 398: ! 399: G_ProjectSource (self->s.origin, offset, f, r, startpoint); ! 400: ! 401: // the +0.1 is because level.time is sometimes a little low ! 402: mytime = (int)((level.time + 0.1 - self->timestamp)/0.5); ! 403: if ((g_showlogic) && (g_showlogic->value)) ! 404: gi.dprintf ("mytime = %d, (%2.2f)\n", mytime, level.time - self->timestamp); ! 405: ! 406: if (FindSpawnPoint (startpoint, flyer_mins, flyer_maxs, spawnpoint, 32)) ! 407: { ! 408: // the second flier should be a kamikaze flyer ! 409: if (mytime != 2) ! 410: ent = CreateMonster (spawnpoint, self->s.angles, "monster_flyer"); ! 411: else ! 412: ent = CreateMonster (spawnpoint, self->s.angles, "monster_kamikaze"); ! 413: ! 414: if (!ent) ! 415: return; ! 416: ! 417: self->monsterinfo.monster_slots--; ! 418: // if ((g_showlogic) && (g_showlogic->value)) ! 419: // gi.dprintf ("carrier: post-spawn : %d slots left\n", carrier_monster_slots); ! 420: ! 421: ent->nextthink = level.time; ! 422: ent->think (ent); ! 423: ! 424: ent->monsterinfo.aiflags |= AI_SPAWNED_CARRIER|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS; ! 425: ! 426: if ((self->enemy->inuse) && (self->enemy->health > 0)) ! 427: { ! 428: ent->enemy = self->enemy; ! 429: FoundTarget (ent); ! 430: if (mytime == 1) ! 431: { ! 432: ent->monsterinfo.lefty = 0; ! 433: ent->monsterinfo.attack_state = AS_SLIDING; ! 434: ent->monsterinfo.currentmove = &flyer_move_attack3; ! 435: } ! 436: else if (mytime == 2) ! 437: { ! 438: ent->monsterinfo.lefty = 0; ! 439: ent->monsterinfo.attack_state = AS_STRAIGHT; ! 440: ent->monsterinfo.currentmove = &flyer_move_kamikaze; ! 441: ent->mass = 100; ! 442: ent->monsterinfo.aiflags |= AI_CHARGING; ! 443: } ! 444: else if (mytime == 3) ! 445: { ! 446: ent->monsterinfo.lefty = 1; ! 447: ent->monsterinfo.attack_state = AS_SLIDING; ! 448: ent->monsterinfo.currentmove = &flyer_move_attack3; ! 449: } ! 450: else if ((g_showlogic) && (g_showlogic->value)) ! 451: gi.dprintf ("carrier: unexpected time %d!\n", mytime); ! 452: } ! 453: } ! 454: } ! 455: ! 456: void carrier_prep_spawn (edict_t *self) ! 457: { ! 458: CarrierCoopCheck(self); ! 459: self->monsterinfo.aiflags |= AI_MANUAL_STEERING; ! 460: self->timestamp = level.time; ! 461: self->yaw_speed = 10; ! 462: CarrierMachineGun(self); ! 463: } ! 464: ! 465: void carrier_spawn_check (edict_t *self) ! 466: { ! 467: // gi.dprintf ("times - %2.2f %2.2f\n", level.time, self->timestamp); ! 468: CarrierCoopCheck(self); ! 469: CarrierMachineGun(self); ! 470: CarrierSpawn (self); ! 471: ! 472: if (level.time > (self->timestamp + 1.1)) // 0.5 seconds per flyer. this gets three ! 473: { ! 474: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 475: self->yaw_speed = orig_yaw_speed; ! 476: return; ! 477: } ! 478: else ! 479: self->monsterinfo.nextframe = FRAME_spawn08; ! 480: } ! 481: ! 482: void carrier_ready_spawn (edict_t *self) ! 483: { ! 484: float current_yaw; ! 485: vec3_t offset, f, r, startpoint, spawnpoint; ! 486: ! 487: CarrierCoopCheck(self); ! 488: CarrierMachineGun(self); ! 489: ! 490: current_yaw = anglemod(self->s.angles[YAW]); ! 491: ! 492: // gi.dprintf ("yaws = %2.2f %2.2f\n", current_yaw, self->ideal_yaw); ! 493: ! 494: if (fabs(current_yaw - self->ideal_yaw) > 0.1) ! 495: { ! 496: self->monsterinfo.aiflags |= AI_HOLD_FRAME; ! 497: self->timestamp += FRAMETIME; ! 498: return; ! 499: } ! 500: ! 501: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 502: ! 503: VectorSet (offset, 105,0,-58); ! 504: AngleVectors (self->s.angles, f, r, NULL); ! 505: G_ProjectSource (self->s.origin, offset, f, r, startpoint); ! 506: if (FindSpawnPoint (startpoint, flyer_mins, flyer_maxs, spawnpoint, 32)) ! 507: { ! 508: SpawnGrow_Spawn (spawnpoint, 0); ! 509: } ! 510: } ! 511: ! 512: void carrier_start_spawn (edict_t *self) ! 513: { ! 514: int mytime; ! 515: float enemy_yaw; ! 516: vec3_t temp; ! 517: // vec3_t offset, f, r, startpoint; ! 518: ! 519: CarrierCoopCheck(self); ! 520: if (!orig_yaw_speed) ! 521: orig_yaw_speed = self->yaw_speed; ! 522: ! 523: if (!self->enemy) ! 524: return; ! 525: ! 526: mytime = (int)((level.time - self->timestamp)/0.5); ! 527: ! 528: VectorSubtract (self->enemy->s.origin, self->s.origin, temp); ! 529: enemy_yaw = vectoyaw2(temp); ! 530: ! 531: // note that the offsets are based on a forward of 105 from the end angle ! 532: if (mytime == 0) ! 533: { ! 534: self->ideal_yaw = anglemod(enemy_yaw - 30); ! 535: // VectorSet (offset, 90.9, 52.5, 0); ! 536: } ! 537: else if (mytime == 1) ! 538: { ! 539: self->ideal_yaw = anglemod(enemy_yaw); ! 540: // VectorSet (offset, 90.9, -52.5, 0); ! 541: } ! 542: else if (mytime == 2) ! 543: { ! 544: self->ideal_yaw = anglemod(enemy_yaw + 30); ! 545: // VectorSet (offset, 90.9, -52.5, 0); ! 546: } ! 547: else if ((g_showlogic) && (g_showlogic->value)) ! 548: gi.dprintf ("carrier: bad spawntime\n"); ! 549: ! 550: CarrierMachineGun (self); ! 551: } ! 552: ! 553: mframe_t carrier_frames_stand [] = ! 554: { ! 555: // ai_stand, 0, drawbbox, ! 556: ai_stand, 0, NULL, ! 557: ai_stand, 0, NULL, ! 558: ai_stand, 0, NULL, ! 559: ai_stand, 0, NULL, ! 560: ai_stand, 0, NULL, ! 561: ai_stand, 0, NULL, ! 562: ai_stand, 0, NULL, ! 563: ai_stand, 0, NULL, ! 564: ai_stand, 0, NULL, ! 565: ai_stand, 0, NULL, ! 566: ai_stand, 0, NULL, ! 567: ai_stand, 0, NULL, ! 568: ai_stand, 0, NULL ! 569: }; ! 570: mmove_t carrier_move_stand = {FRAME_search01, FRAME_search13, carrier_frames_stand, NULL}; ! 571: ! 572: mframe_t carrier_frames_walk [] = ! 573: { ! 574: // ai_walk, 12, drawbbox, ! 575: ai_walk, 4, NULL, ! 576: ai_walk, 4, NULL, ! 577: ai_walk, 4, NULL, ! 578: ai_walk, 4, NULL, ! 579: ai_walk, 4, NULL, ! 580: ai_walk, 4, NULL, ! 581: ai_walk, 4, NULL, ! 582: ai_walk, 4, NULL, ! 583: ai_walk, 4, NULL, ! 584: ai_walk, 4, NULL, ! 585: ai_walk, 4, NULL, ! 586: ai_walk, 4, NULL, ! 587: ai_walk, 4, NULL ! 588: }; ! 589: mmove_t carrier_move_walk = {FRAME_search01, FRAME_search13, carrier_frames_walk, NULL}; ! 590: ! 591: ! 592: mframe_t carrier_frames_run [] = ! 593: { ! 594: // ai_run, 12, drawbbox, ! 595: ai_run, 6, CarrierCoopCheck, ! 596: ai_run, 6, CarrierCoopCheck, ! 597: ai_run, 6, CarrierCoopCheck, ! 598: ai_run, 6, CarrierCoopCheck, ! 599: ai_run, 6, CarrierCoopCheck, ! 600: ai_run, 6, CarrierCoopCheck, ! 601: ai_run, 6, CarrierCoopCheck, ! 602: ai_run, 6, CarrierCoopCheck, ! 603: ai_run, 6, CarrierCoopCheck, ! 604: ai_run, 6, CarrierCoopCheck, ! 605: ai_run, 6, CarrierCoopCheck, ! 606: ai_run, 6, CarrierCoopCheck, ! 607: ai_run, 6, CarrierCoopCheck ! 608: }; ! 609: mmove_t carrier_move_run = {FRAME_search01, FRAME_search13, carrier_frames_run, NULL}; ! 610: ! 611: mframe_t carrier_frames_attack_pre_mg [] = ! 612: { ! 613: ai_charge, 4, CarrierCoopCheck, ! 614: ai_charge, 4, CarrierCoopCheck, ! 615: ai_charge, 4, CarrierCoopCheck, ! 616: ai_charge, 4, CarrierCoopCheck, ! 617: ai_charge, 4, CarrierCoopCheck, ! 618: ai_charge, 4, CarrierCoopCheck, ! 619: ai_charge, 4, CarrierCoopCheck, ! 620: ai_charge, 4, carrier_attack_mg ! 621: }; ! 622: mmove_t carrier_move_attack_pre_mg = {FRAME_firea01, FRAME_firea08, carrier_frames_attack_pre_mg, NULL}; ! 623: ! 624: ! 625: // Loop this ! 626: mframe_t carrier_frames_attack_mg [] = ! 627: { ! 628: ai_charge, -2, CarrierMachineGun, ! 629: ai_charge, -2, CarrierMachineGun, ! 630: ai_charge, -2, carrier_reattack_mg ! 631: /* ! 632: ai_charge, 0, CarrierMachineGunHold, ! 633: // ai_charge, 0, CarrierMachineGun, ! 634: ai_charge, 0, CarrierMachineGun, ! 635: ai_charge, 0, carrier_reattack_mg ! 636: */ ! 637: }; ! 638: mmove_t carrier_move_attack_mg = {FRAME_firea09, FRAME_firea11, carrier_frames_attack_mg, NULL}; ! 639: ! 640: mframe_t carrier_frames_attack_post_mg [] = ! 641: { ! 642: ai_charge, 4, CarrierCoopCheck, ! 643: ai_charge, 4, CarrierCoopCheck, ! 644: ai_charge, 4, CarrierCoopCheck, ! 645: ai_charge, 4, CarrierCoopCheck ! 646: }; ! 647: mmove_t carrier_move_attack_post_mg = {FRAME_firea12, FRAME_firea15, carrier_frames_attack_post_mg, carrier_run}; ! 648: ! 649: mframe_t carrier_frames_attack_pre_gren [] = ! 650: { ! 651: ai_charge, 4, CarrierCoopCheck, ! 652: ai_charge, 4, CarrierCoopCheck, ! 653: ai_charge, 4, CarrierCoopCheck, ! 654: ai_charge, 4, CarrierCoopCheck, ! 655: ai_charge, 4, CarrierCoopCheck, ! 656: ai_charge, 4, carrier_attack_gren ! 657: }; ! 658: mmove_t carrier_move_attack_pre_gren = {FRAME_fireb01, FRAME_fireb06, carrier_frames_attack_pre_gren, NULL}; ! 659: ! 660: mframe_t carrier_frames_attack_gren [] = ! 661: { ! 662: ai_charge, -15, CarrierGrenade, ! 663: ai_charge, 4, CarrierCoopCheck, ! 664: ai_charge, 4, CarrierCoopCheck, ! 665: ai_charge, 4, carrier_reattack_gren ! 666: }; ! 667: mmove_t carrier_move_attack_gren = {FRAME_fireb07, FRAME_fireb10, carrier_frames_attack_gren, NULL}; ! 668: ! 669: mframe_t carrier_frames_attack_post_gren [] = ! 670: { ! 671: ai_charge, 4, CarrierCoopCheck, ! 672: ai_charge, 4, CarrierCoopCheck, ! 673: ai_charge, 4, CarrierCoopCheck, ! 674: ai_charge, 4, CarrierCoopCheck, ! 675: ai_charge, 4, CarrierCoopCheck, ! 676: ai_charge, 4, CarrierCoopCheck ! 677: }; ! 678: mmove_t carrier_move_attack_post_gren = {FRAME_fireb11, FRAME_fireb16, carrier_frames_attack_post_gren, carrier_run}; ! 679: ! 680: mframe_t carrier_frames_attack_rocket [] = ! 681: { ! 682: ai_charge, 15, CarrierRocket ! 683: }; ! 684: mmove_t carrier_move_attack_rocket = {FRAME_fireb01, FRAME_fireb01, carrier_frames_attack_rocket, carrier_run}; ! 685: ! 686: void CarrierRail (edict_t *self) ! 687: { ! 688: vec3_t start; ! 689: vec3_t dir; ! 690: vec3_t forward, right; ! 691: ! 692: CarrierCoopCheck(self); ! 693: AngleVectors (self->s.angles, forward, right, NULL); ! 694: G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_CARRIER_RAILGUN], forward, right, start); ! 695: ! 696: // calc direction to where we targeted ! 697: VectorSubtract (self->pos1, start, dir); ! 698: VectorNormalize (dir); ! 699: ! 700: monster_fire_railgun (self, start, dir, 50, 100, MZ2_CARRIER_RAILGUN); ! 701: self->monsterinfo.attack_finished = level.time + RAIL_FIRE_TIME; ! 702: } ! 703: ! 704: void CarrierSaveLoc (edict_t *self) ! 705: { ! 706: CarrierCoopCheck(self); ! 707: VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot ! 708: self->pos1[2] += self->enemy->viewheight; ! 709: }; ! 710: ! 711: mframe_t carrier_frames_attack_rail [] = ! 712: { ! 713: ai_charge, 2, CarrierCoopCheck, ! 714: ai_charge, 2, CarrierSaveLoc, ! 715: ai_charge, 2, CarrierCoopCheck, ! 716: ai_charge, -20, CarrierRail, ! 717: ai_charge, 2, CarrierCoopCheck, ! 718: ai_charge, 2, CarrierCoopCheck, ! 719: ai_charge, 2, CarrierCoopCheck, ! 720: ai_charge, 2, CarrierCoopCheck, ! 721: ai_charge, 2, CarrierCoopCheck ! 722: }; ! 723: mmove_t carrier_move_attack_rail = {FRAME_search01, FRAME_search09, carrier_frames_attack_rail, carrier_run}; ! 724: ! 725: mframe_t carrier_frames_spawn [] = ! 726: { ! 727: ai_charge, -2, CarrierMachineGun, ! 728: ai_charge, -2, CarrierMachineGun, ! 729: ai_charge, -2, CarrierMachineGun, ! 730: ai_charge, -2, CarrierMachineGun, ! 731: ai_charge, -2, CarrierMachineGun, ! 732: ai_charge, -2, CarrierMachineGun, ! 733: ai_charge, -2, carrier_prep_spawn, // 7 - end of wind down ! 734: ai_charge, -2, carrier_start_spawn, // 8 - start of spawn ! 735: ai_charge, -2, carrier_ready_spawn, ! 736: ai_charge, -2, CarrierMachineGun, ! 737: ai_charge, -2, CarrierMachineGun, ! 738: ai_charge, -10, carrier_spawn_check, //12 - actual spawn ! 739: ai_charge, -2, CarrierMachineGun, //13 - begin of wind down ! 740: ai_charge, -2, CarrierMachineGun, ! 741: ai_charge, -2, CarrierMachineGun, ! 742: ai_charge, -2, CarrierMachineGun, ! 743: ai_charge, -2, CarrierMachineGun, ! 744: ai_charge, -2, carrier_reattack_mg //18 - end of wind down ! 745: }; ! 746: mmove_t carrier_move_spawn = {FRAME_spawn01, FRAME_spawn18, carrier_frames_spawn, NULL}; ! 747: ! 748: mframe_t carrier_frames_pain_heavy [] = ! 749: { ! 750: ai_move, 0, NULL, ! 751: ai_move, 0, NULL, ! 752: ai_move, 0, NULL, ! 753: ai_move, 0, NULL, ! 754: ai_move, 0, NULL, ! 755: ai_move, 0, NULL, ! 756: ai_move, 0, NULL, ! 757: ai_move, 0, NULL, ! 758: ai_move, 0, NULL, ! 759: ai_move, 0, NULL ! 760: }; ! 761: mmove_t carrier_move_pain_heavy = {FRAME_death01, FRAME_death10, carrier_frames_pain_heavy, carrier_run}; ! 762: ! 763: mframe_t carrier_frames_pain_light [] = ! 764: { ! 765: ai_move, 0, NULL, ! 766: ai_move, 0, NULL, ! 767: ai_move, 0, NULL, ! 768: ai_move, 0, NULL ! 769: }; ! 770: mmove_t carrier_move_pain_light = {FRAME_spawn01, FRAME_spawn04, carrier_frames_pain_light, carrier_run}; ! 771: ! 772: mframe_t carrier_frames_death [] = ! 773: { ! 774: ai_move, 0, NULL, ! 775: ai_move, 0, NULL, ! 776: ai_move, 0, NULL, ! 777: ai_move, 0, NULL, ! 778: ai_move, 0, NULL, ! 779: ai_move, 0, NULL, ! 780: ai_move, 0, NULL, ! 781: ai_move, 0, NULL, ! 782: ai_move, 0, NULL, ! 783: ai_move, 0, NULL, ! 784: ai_move, 0, NULL, ! 785: ai_move, 0, NULL, ! 786: ai_move, 0, NULL, ! 787: ai_move, 0, NULL, ! 788: ai_move, 0, NULL, ! 789: ai_move, 0, BossExplode ! 790: }; ! 791: mmove_t carrier_move_death = {FRAME_death01, FRAME_death16, carrier_frames_death, carrier_dead}; ! 792: ! 793: void carrier_stand (edict_t *self) ! 794: { ! 795: // gi.dprintf ("carrier stand\n"); ! 796: self->monsterinfo.currentmove = &carrier_move_stand; ! 797: } ! 798: ! 799: void carrier_run (edict_t *self) ! 800: { ! 801: ! 802: // gi.dprintf ("carrier run - %2.2f - %s \n", level.time, self->enemy->classname); ! 803: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 804: ! 805: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 806: self->monsterinfo.currentmove = &carrier_move_stand; ! 807: else ! 808: self->monsterinfo.currentmove = &carrier_move_run; ! 809: } ! 810: ! 811: void carrier_walk (edict_t *self) ! 812: { ! 813: self->monsterinfo.currentmove = &carrier_move_walk; ! 814: } ! 815: ! 816: void CarrierMachineGunHold (edict_t *self) ! 817: { ! 818: // self->monsterinfo.aiflags |= AI_HOLD_FRAME; ! 819: // self->yaw_speed = 0; ! 820: // self->monsterinfo.currentmove = &carrier_move_attack_mg; ! 821: CarrierMachineGun (self); ! 822: } ! 823: ! 824: void carrier_attack (edict_t *self) ! 825: { ! 826: vec3_t vec; ! 827: float range, luck; ! 828: qboolean enemy_inback, enemy_infront, enemy_below; ! 829: ! 830: // gi.dprintf ("carrier attack\n"); ! 831: ! 832: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 833: ! 834: if (!self->enemy) ! 835: return; ! 836: ! 837: enemy_inback = inback(self, self->enemy); ! 838: enemy_infront = infront (self, self->enemy); ! 839: enemy_below = below (self, self->enemy); ! 840: ! 841: if (self->bad_area) ! 842: { ! 843: if ((enemy_inback) || (enemy_below)) ! 844: self->monsterinfo.currentmove = &carrier_move_attack_rocket; ! 845: else if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished)) ! 846: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 847: else ! 848: { ! 849: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 850: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 851: } ! 852: return; ! 853: } ! 854: ! 855: if (self->monsterinfo.attack_state == AS_BLIND) ! 856: { ! 857: self->monsterinfo.currentmove = &carrier_move_spawn; ! 858: return; ! 859: } ! 860: ! 861: if (!enemy_inback && !enemy_infront && !enemy_below) // to side and not under ! 862: { ! 863: if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished)) ! 864: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 865: else ! 866: { ! 867: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 868: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 869: } ! 870: return; ! 871: } ! 872: ! 873: /* if ((g_showlogic) && (g_showlogic->value)) ! 874: { ! 875: gi.dprintf ("checking enemy .."); ! 876: if (enemy_inback) ! 877: gi.dprintf (" in back\n"); ! 878: else if (enemy_infront) ! 879: gi.dprintf (" in front\n"); ! 880: else ! 881: gi.dprintf (" inaccessible\n"); ! 882: } ! 883: */ ! 884: if (enemy_infront) ! 885: { ! 886: VectorSubtract (self->enemy->s.origin, self->s.origin, vec); ! 887: range = VectorLength (vec); ! 888: if (range <= 125) ! 889: { ! 890: if ((random() < 0.8) || (level.time < self->monsterinfo.attack_finished)) ! 891: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 892: else ! 893: { ! 894: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 895: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 896: } ! 897: } ! 898: else if (range < 600) ! 899: { ! 900: luck = random(); ! 901: if (self->monsterinfo.monster_slots > 2) ! 902: { ! 903: if (luck <= 0.20) ! 904: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 905: else if (luck <= 0.40) ! 906: self->monsterinfo.currentmove = &carrier_move_attack_pre_gren; ! 907: else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished)) ! 908: { ! 909: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 910: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 911: } ! 912: else ! 913: self->monsterinfo.currentmove = &carrier_move_spawn; ! 914: } ! 915: else ! 916: { ! 917: if (luck <= 0.30) ! 918: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 919: else if (luck <= 0.65) ! 920: self->monsterinfo.currentmove = &carrier_move_attack_pre_gren; ! 921: else if (level.time >= self->monsterinfo.attack_finished) ! 922: { ! 923: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 924: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 925: } ! 926: else ! 927: self->monsterinfo.currentmove = &carrier_move_spawn; ! 928: } ! 929: } ! 930: else // won't use grenades at this range ! 931: { ! 932: luck = random(); ! 933: if (self->monsterinfo.monster_slots > 2) ! 934: { ! 935: if (luck < 0.3) ! 936: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 937: else if ((luck < 0.65) && !(level.time < self->monsterinfo.attack_finished)) ! 938: { ! 939: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 940: VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot ! 941: self->pos1[2] += self->enemy->viewheight; ! 942: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 943: } ! 944: else ! 945: self->monsterinfo.currentmove = &carrier_move_spawn; ! 946: } ! 947: else ! 948: { ! 949: if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished)) ! 950: self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; ! 951: else ! 952: { ! 953: gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); ! 954: self->monsterinfo.currentmove = &carrier_move_attack_rail; ! 955: } ! 956: } ! 957: } ! 958: } ! 959: else if ((enemy_below) || (enemy_inback)) ! 960: { ! 961: self->monsterinfo.currentmove = &carrier_move_attack_rocket; ! 962: } ! 963: } ! 964: ! 965: void carrier_attack_mg (edict_t *self) ! 966: { ! 967: CarrierCoopCheck(self); ! 968: self->monsterinfo.currentmove = &carrier_move_attack_mg; ! 969: } ! 970: ! 971: void carrier_reattack_mg (edict_t *self) ! 972: { ! 973: CarrierCoopCheck(self); ! 974: if ( infront(self, self->enemy) ) ! 975: if (random() <= 0.5) ! 976: if ((random() < 0.7) || (self->monsterinfo.monster_slots <= 2)) ! 977: self->monsterinfo.currentmove = &carrier_move_attack_mg; ! 978: else ! 979: self->monsterinfo.currentmove = &carrier_move_spawn; ! 980: else ! 981: self->monsterinfo.currentmove = &carrier_move_attack_post_mg; ! 982: else ! 983: self->monsterinfo.currentmove = &carrier_move_attack_post_mg; ! 984: } ! 985: ! 986: ! 987: void carrier_attack_gren (edict_t *self) ! 988: { ! 989: // gi.dprintf ("carrier_attack_gren - %2.2f\n",level.time); ! 990: CarrierCoopCheck(self); ! 991: self->timestamp = level.time; ! 992: self->monsterinfo.currentmove = &carrier_move_attack_gren; ! 993: } ! 994: ! 995: void carrier_reattack_gren (edict_t *self) ! 996: { ! 997: CarrierCoopCheck(self); ! 998: // gi.dprintf ("carrier_reattack - %2.2f", level.time); ! 999: if ( infront(self, self->enemy) ) ! 1000: if (self->timestamp + 1.3 > level.time ) // four grenades ! 1001: { ! 1002: // gi.dprintf (" attacking\n"); ! 1003: self->monsterinfo.currentmove = &carrier_move_attack_gren; ! 1004: return; ! 1005: } ! 1006: // gi.dprintf ("not attacking\n"); ! 1007: self->monsterinfo.currentmove = &carrier_move_attack_post_gren; ! 1008: } ! 1009: ! 1010: ! 1011: void carrier_pain (edict_t *self, edict_t *other, float kick, int damage) ! 1012: { ! 1013: qboolean changed = false; ! 1014: ! 1015: if (self->health < (self->max_health / 2)) ! 1016: self->s.skinnum = 1; ! 1017: ! 1018: if (skill->value == 3) ! 1019: return; // no pain anims in nightmare ! 1020: ! 1021: // gi.dprintf ("carrier pain\n"); ! 1022: if (level.time < self->pain_debounce_time) ! 1023: return; ! 1024: ! 1025: self->pain_debounce_time = level.time + 3; ! 1026: ! 1027: // right now, the carrier doesn't go into his pain animations ! 1028: if (damage < 10) ! 1029: { ! 1030: gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0); ! 1031: } ! 1032: else if (damage < 30) ! 1033: { ! 1034: gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0); ! 1035: if (random() < 0.5) ! 1036: { ! 1037: changed = true; ! 1038: self->monsterinfo.currentmove = &carrier_move_pain_light; ! 1039: } ! 1040: } ! 1041: else ! 1042: { ! 1043: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0); ! 1044: self->monsterinfo.currentmove = &carrier_move_pain_heavy; ! 1045: changed = true; ! 1046: } ! 1047: ! 1048: // if we changed frames, clean up our little messes ! 1049: if (changed) ! 1050: { ! 1051: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 1052: self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; ! 1053: self->yaw_speed = orig_yaw_speed; ! 1054: } ! 1055: } ! 1056: ! 1057: void carrier_dead (edict_t *self) ! 1058: { ! 1059: VectorSet (self->mins, -56, -56, 0); ! 1060: VectorSet (self->maxs, 56, 56, 80); ! 1061: self->movetype = MOVETYPE_TOSS; ! 1062: self->svflags |= SVF_DEADMONSTER; ! 1063: self->nextthink = 0; ! 1064: gi.linkentity (self); ! 1065: } ! 1066: ! 1067: void carrier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 1068: { ! 1069: gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0); ! 1070: self->deadflag = DEAD_DEAD; ! 1071: self->takedamage = DAMAGE_NO; ! 1072: self->count = 0; ! 1073: self->monsterinfo.currentmove = &carrier_move_death; ! 1074: } ! 1075: ! 1076: qboolean Carrier_CheckAttack (edict_t *self) ! 1077: { ! 1078: vec3_t spot1, spot2; ! 1079: vec3_t temp; ! 1080: float chance; ! 1081: trace_t tr; ! 1082: qboolean enemy_infront, enemy_inback, enemy_below; ! 1083: int enemy_range; ! 1084: float enemy_yaw; ! 1085: ! 1086: if (self->enemy->health > 0) ! 1087: { ! 1088: // see if any entities are in the way of the shot ! 1089: VectorCopy (self->s.origin, spot1); ! 1090: spot1[2] += self->viewheight; ! 1091: VectorCopy (self->enemy->s.origin, spot2); ! 1092: spot2[2] += self->enemy->viewheight; ! 1093: ! 1094: tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); ! 1095: ! 1096: // do we have a clear shot? ! 1097: if (tr.ent != self->enemy) ! 1098: { ! 1099: // go ahead and spawn stuff if we're mad a a client ! 1100: if (self->enemy->client && self->monsterinfo.monster_slots >= 2) ! 1101: { ! 1102: self->monsterinfo.attack_state = AS_BLIND; ! 1103: return true; ! 1104: } ! 1105: ! 1106: // PGM - we want them to go ahead and shoot at info_notnulls if they can. ! 1107: if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM ! 1108: return false; ! 1109: } ! 1110: } ! 1111: ! 1112: enemy_infront = infront(self, self->enemy); ! 1113: enemy_inback = inback(self, self->enemy); ! 1114: enemy_below = below (self, self->enemy); ! 1115: ! 1116: enemy_range = range(self, self->enemy); ! 1117: VectorSubtract (self->enemy->s.origin, self->s.origin, temp); ! 1118: enemy_yaw = vectoyaw2(temp); ! 1119: ! 1120: self->ideal_yaw = enemy_yaw; ! 1121: ! 1122: // PMM - shoot out the back if appropriate ! 1123: if ((enemy_inback) || (!enemy_infront && enemy_below)) ! 1124: { ! 1125: // this is using wait because the attack is supposed to be independent ! 1126: if (level.time >= self->wait) ! 1127: { ! 1128: self->wait = level.time + CARRIER_ROCKET_TIME; ! 1129: self->monsterinfo.attack(self); ! 1130: if (random() < 0.6) ! 1131: self->monsterinfo.attack_state = AS_SLIDING; ! 1132: else ! 1133: self->monsterinfo.attack_state = AS_STRAIGHT; ! 1134: return true; ! 1135: } ! 1136: } ! 1137: ! 1138: // melee attack ! 1139: if (enemy_range == RANGE_MELEE) ! 1140: { ! 1141: self->monsterinfo.attack_state = AS_MISSILE; ! 1142: return true; ! 1143: } ! 1144: ! 1145: // if (level.time < self->monsterinfo.attack_finished) ! 1146: // return false; ! 1147: ! 1148: if (self->monsterinfo.aiflags & AI_STAND_GROUND) ! 1149: { ! 1150: chance = 0.4; ! 1151: } ! 1152: else if (enemy_range == RANGE_MELEE) ! 1153: { ! 1154: chance = 0.8; ! 1155: } ! 1156: else if (enemy_range == RANGE_NEAR) ! 1157: { ! 1158: chance = 0.8; ! 1159: } ! 1160: else if (enemy_range == RANGE_MID) ! 1161: { ! 1162: chance = 0.8; ! 1163: } ! 1164: else if (enemy_range == RANGE_FAR) ! 1165: { ! 1166: chance = 0.5; ! 1167: } ! 1168: ! 1169: // PGM - go ahead and shoot every time if it's a info_notnull ! 1170: if ((random () < chance) || (self->enemy->solid == SOLID_NOT)) ! 1171: { ! 1172: self->monsterinfo.attack_state = AS_MISSILE; ! 1173: // self->monsterinfo.attack_finished = level.time + 2*random(); ! 1174: return true; ! 1175: } ! 1176: ! 1177: if (self->flags & FL_FLY) ! 1178: { ! 1179: if (random() < 0.6) ! 1180: self->monsterinfo.attack_state = AS_SLIDING; ! 1181: else ! 1182: self->monsterinfo.attack_state = AS_STRAIGHT; ! 1183: } ! 1184: ! 1185: return false; ! 1186: } ! 1187: ! 1188: void CarrierPrecache () ! 1189: { ! 1190: gi.soundindex ("flyer/flysght1.wav"); ! 1191: gi.soundindex ("flyer/flysrch1.wav"); ! 1192: gi.soundindex ("flyer/flypain1.wav"); ! 1193: gi.soundindex ("flyer/flypain2.wav"); ! 1194: gi.soundindex ("flyer/flyatck2.wav"); ! 1195: gi.soundindex ("flyer/flyatck1.wav"); ! 1196: gi.soundindex ("flyer/flydeth1.wav"); ! 1197: gi.soundindex ("flyer/flyatck3.wav"); ! 1198: gi.soundindex ("flyer/flyidle1.wav"); ! 1199: gi.soundindex ("weapons/rockfly.wav"); ! 1200: gi.soundindex ("infantry/infatck1.wav"); ! 1201: gi.soundindex ("gunner/gunatck3.wav"); ! 1202: gi.soundindex ("weapons/grenlb1b.wav"); ! 1203: gi.soundindex ("tank/rocket.wav"); ! 1204: ! 1205: gi.modelindex ("models/monsters/flyer/tris.md2"); ! 1206: gi.modelindex ("models/objects/rocket/tris.md2"); ! 1207: gi.modelindex ("models/objects/debris2/tris.md2"); ! 1208: gi.modelindex ("models/objects/grenade/tris.md2"); ! 1209: gi.modelindex("models/items/spawngro/tris.md2"); ! 1210: gi.modelindex("models/items/spawngro2/tris.md2"); ! 1211: gi.modelindex ("models/objects/gibs/sm_metal/tris.md2"); ! 1212: gi.modelindex ("models/objects/gibs/gear/tris.md2"); ! 1213: } ! 1214: ! 1215: ! 1216: /*QUAKED monster_carrier (1 .5 0) (-56 -56 -44) (56 56 44) Ambush Trigger_Spawn Sight ! 1217: */ ! 1218: void SP_monster_carrier (edict_t *self) ! 1219: { ! 1220: if (deathmatch->value) ! 1221: { ! 1222: G_FreeEdict (self); ! 1223: return; ! 1224: } ! 1225: ! 1226: sound_pain1 = gi.soundindex ("bosshovr/bhvpain1.wav"); ! 1227: sound_pain2 = gi.soundindex ("bosshovr/bhvpain2.wav"); ! 1228: sound_pain3 = gi.soundindex ("bosshovr/bhvpain3.wav"); ! 1229: sound_death = gi.soundindex ("bosshovr/bhvdeth1.wav"); ! 1230: sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav"); ! 1231: sound_rail = gi.soundindex ("gladiator/railgun.wav"); ! 1232: ! 1233: self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav"); ! 1234: ! 1235: self->movetype = MOVETYPE_STEP; ! 1236: self->solid = SOLID_BBOX; ! 1237: self->s.modelindex = gi.modelindex ("models/monsters/carrier/tris.md2"); ! 1238: VectorSet (self->mins, -56, -56, -44); ! 1239: VectorSet (self->maxs, 56, 56, 44); ! 1240: ! 1241: // 2000 - 4000 health ! 1242: self->health = max (2000, 2000 + 1000*((skill->value)-1)); ! 1243: self->gib_health = -200; ! 1244: self->mass = 1000; ! 1245: ! 1246: self->yaw_speed = 15; ! 1247: orig_yaw_speed = self->yaw_speed; ! 1248: // self->yaw_speed = 1; ! 1249: ! 1250: self->flags |= FL_IMMUNE_LASER; ! 1251: self->monsterinfo.aiflags |= AI_IGNORE_SHOTS; ! 1252: ! 1253: self->pain = carrier_pain; ! 1254: self->die = carrier_die; ! 1255: ! 1256: self->monsterinfo.melee = NULL; ! 1257: self->monsterinfo.stand = carrier_stand; ! 1258: self->monsterinfo.walk = carrier_walk; ! 1259: self->monsterinfo.run = carrier_run; ! 1260: self->monsterinfo.attack = carrier_attack; ! 1261: self->monsterinfo.search = carrier_search; ! 1262: self->monsterinfo.checkattack = Carrier_CheckAttack; ! 1263: gi.linkentity (self); ! 1264: ! 1265: self->monsterinfo.currentmove = &carrier_move_stand; ! 1266: self->monsterinfo.scale = MODEL_SCALE; ! 1267: ! 1268: CarrierPrecache(); ! 1269: ! 1270: flymonster_start (self); ! 1271: ! 1272: self->monsterinfo.attack_finished = 0; ! 1273: switch ((int)skill->value) ! 1274: { ! 1275: case 0: ! 1276: self->monsterinfo.monster_slots = 3; ! 1277: break; ! 1278: case 1: ! 1279: case 2: ! 1280: self->monsterinfo.monster_slots = 6; ! 1281: break; ! 1282: case 3: ! 1283: self->monsterinfo.monster_slots = 9; ! 1284: break; ! 1285: default: ! 1286: self->monsterinfo.monster_slots = 6; ! 1287: break; ! 1288: } ! 1289: } ! 1290:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.