|
|
1.1 ! root 1: #include "g_local.h" ! 2: ! 3: ! 4: // ! 5: // monster weapons ! 6: // ! 7: ! 8: //FIXME mosnters should call these with a totally accurate direction ! 9: // and we can mess it up based on skill. Spread should be for normal ! 10: // and we can tighten or loosen based on skill. We could muck with ! 11: // the damages too, but I'm not sure that's such a good idea. ! 12: void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype) ! 13: { ! 14: fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN); ! 15: ! 16: gi.WriteByte (svc_muzzleflash2); ! 17: gi.WriteShort (self - g_edicts); ! 18: gi.WriteByte (flashtype); ! 19: gi.multicast (start, MULTICAST_PVS); ! 20: } ! 21: ! 22: void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype) ! 23: { ! 24: fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN); ! 25: ! 26: gi.WriteByte (svc_muzzleflash2); ! 27: gi.WriteShort (self - g_edicts); ! 28: gi.WriteByte (flashtype); ! 29: gi.multicast (start, MULTICAST_PVS); ! 30: } ! 31: ! 32: void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) ! 33: { ! 34: fire_blaster (self, start, dir, damage, speed, effect, false); ! 35: ! 36: gi.WriteByte (svc_muzzleflash2); ! 37: gi.WriteShort (self - g_edicts); ! 38: gi.WriteByte (flashtype); ! 39: gi.multicast (start, MULTICAST_PVS); ! 40: } ! 41: ! 42: //ROGUE ! 43: void monster_fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) ! 44: { ! 45: fire_blaster2 (self, start, dir, damage, speed, effect, false); ! 46: ! 47: gi.WriteByte (svc_muzzleflash2); ! 48: gi.WriteShort (self - g_edicts); ! 49: gi.WriteByte (flashtype); ! 50: gi.multicast (start, MULTICAST_PVS); ! 51: } ! 52: ! 53: // FIXME -- add muzzle flash ! 54: void monster_fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy, int flashtype) ! 55: { ! 56: fire_tracker (self, start, dir, damage, speed, enemy); ! 57: ! 58: gi.WriteByte (svc_muzzleflash2); ! 59: gi.WriteShort (self - g_edicts); ! 60: gi.WriteByte (flashtype); ! 61: gi.multicast (start, MULTICAST_PVS); ! 62: } ! 63: ! 64: void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, int damage, int kick, int flashtype) ! 65: { ! 66: fire_heat (self, start, dir, offset, damage, kick, true); ! 67: ! 68: gi.WriteByte (svc_muzzleflash2); ! 69: gi.WriteShort (self - g_edicts); ! 70: gi.WriteByte (flashtype); ! 71: gi.multicast (start, MULTICAST_PVS); ! 72: } ! 73: //ROGUE ! 74: ! 75: void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype) ! 76: { ! 77: fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40); ! 78: ! 79: gi.WriteByte (svc_muzzleflash2); ! 80: gi.WriteShort (self - g_edicts); ! 81: gi.WriteByte (flashtype); ! 82: gi.multicast (start, MULTICAST_PVS); ! 83: } ! 84: ! 85: void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) ! 86: { ! 87: fire_rocket (self, start, dir, damage, speed, damage+20, damage); ! 88: ! 89: gi.WriteByte (svc_muzzleflash2); ! 90: gi.WriteShort (self - g_edicts); ! 91: gi.WriteByte (flashtype); ! 92: gi.multicast (start, MULTICAST_PVS); ! 93: } ! 94: ! 95: void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype) ! 96: { ! 97: // PMM ! 98: if (!(gi.pointcontents (start) & MASK_SOLID)) ! 99: fire_rail (self, start, aimdir, damage, kick); ! 100: ! 101: gi.WriteByte (svc_muzzleflash2); ! 102: gi.WriteShort (self - g_edicts); ! 103: gi.WriteByte (flashtype); ! 104: gi.multicast (start, MULTICAST_PVS); ! 105: } ! 106: ! 107: void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype) ! 108: { ! 109: fire_bfg (self, start, aimdir, damage, speed, damage_radius); ! 110: ! 111: gi.WriteByte (svc_muzzleflash2); ! 112: gi.WriteShort (self - g_edicts); ! 113: gi.WriteByte (flashtype); ! 114: gi.multicast (start, MULTICAST_PVS); ! 115: } ! 116: ! 117: ! 118: ! 119: // ! 120: // Monster utility functions ! 121: // ! 122: ! 123: void M_FliesOff (edict_t *self) ! 124: { ! 125: self->s.effects &= ~EF_FLIES; ! 126: self->s.sound = 0; ! 127: } ! 128: ! 129: void M_FliesOn (edict_t *self) ! 130: { ! 131: if (self->waterlevel) ! 132: return; ! 133: self->s.effects |= EF_FLIES; ! 134: self->s.sound = gi.soundindex ("infantry/inflies1.wav"); ! 135: self->think = M_FliesOff; ! 136: self->nextthink = level.time + 60; ! 137: } ! 138: ! 139: void M_FlyCheck (edict_t *self) ! 140: { ! 141: if (self->waterlevel) ! 142: return; ! 143: ! 144: if (random() > 0.5) ! 145: return; ! 146: ! 147: self->think = M_FliesOn; ! 148: self->nextthink = level.time + 5 + 10 * random(); ! 149: } ! 150: ! 151: void AttackFinished (edict_t *self, float time) ! 152: { ! 153: self->monsterinfo.attack_finished = level.time + time; ! 154: } ! 155: ! 156: ! 157: void M_CheckGround (edict_t *ent) ! 158: { ! 159: vec3_t point; ! 160: trace_t trace; ! 161: ! 162: if (ent->flags & (FL_SWIM|FL_FLY)) ! 163: return; ! 164: ! 165: #ifdef ROGUE_GRAVITY ! 166: if ((ent->velocity[2] * ent->gravityVector[2]) < -100) // PGM ! 167: #else ! 168: if (ent->velocity[2] > 100) ! 169: #endif ! 170: { ! 171: ent->groundentity = NULL; ! 172: return; ! 173: } ! 174: ! 175: // if the hull point one-quarter unit down is solid the entity is on ground ! 176: point[0] = ent->s.origin[0]; ! 177: point[1] = ent->s.origin[1]; ! 178: #ifdef ROGUE_GRAVITY ! 179: point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]); //PGM ! 180: #else ! 181: point[2] = ent->s.origin[2] - 0.25; ! 182: #endif ! 183: ! 184: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID); ! 185: ! 186: // check steepness ! 187: #ifdef ROGUE_GRAVITY ! 188: //PGM ! 189: if ( ent->gravityVector[2] < 0) // normal gravity ! 190: { ! 191: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) ! 192: { ! 193: ent->groundentity = NULL; ! 194: return; ! 195: } ! 196: } ! 197: else // inverted gravity ! 198: { ! 199: if ( trace.plane.normal[2] > -0.7 && !trace.startsolid) ! 200: { ! 201: ent->groundentity = NULL; ! 202: return; ! 203: } ! 204: } ! 205: //PGM ! 206: #else ! 207: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) ! 208: { ! 209: ent->groundentity = NULL; ! 210: return; ! 211: } ! 212: #endif ! 213: ! 214: // ent->groundentity = trace.ent; ! 215: // ent->groundentity_linkcount = trace.ent->linkcount; ! 216: // if (!trace.startsolid && !trace.allsolid) ! 217: // VectorCopy (trace.endpos, ent->s.origin); ! 218: if (!trace.startsolid && !trace.allsolid) ! 219: { ! 220: VectorCopy (trace.endpos, ent->s.origin); ! 221: ent->groundentity = trace.ent; ! 222: ent->groundentity_linkcount = trace.ent->linkcount; ! 223: ent->velocity[2] = 0; ! 224: } ! 225: } ! 226: ! 227: ! 228: void M_CatagorizePosition (edict_t *ent) ! 229: { ! 230: vec3_t point; ! 231: int cont; ! 232: ! 233: // ! 234: // get waterlevel ! 235: // ! 236: point[0] = ent->s.origin[0]; ! 237: point[1] = ent->s.origin[1]; ! 238: point[2] = ent->s.origin[2] + ent->mins[2] + 1; ! 239: cont = gi.pointcontents (point); ! 240: ! 241: if (!(cont & MASK_WATER)) ! 242: { ! 243: ent->waterlevel = 0; ! 244: ent->watertype = 0; ! 245: return; ! 246: } ! 247: ! 248: ent->watertype = cont; ! 249: ent->waterlevel = 1; ! 250: point[2] += 26; ! 251: cont = gi.pointcontents (point); ! 252: if (!(cont & MASK_WATER)) ! 253: return; ! 254: ! 255: ent->waterlevel = 2; ! 256: point[2] += 22; ! 257: cont = gi.pointcontents (point); ! 258: if (cont & MASK_WATER) ! 259: ent->waterlevel = 3; ! 260: } ! 261: ! 262: ! 263: void M_WorldEffects (edict_t *ent) ! 264: { ! 265: int dmg; ! 266: ! 267: if (ent->health > 0) ! 268: { ! 269: if (!(ent->flags & FL_SWIM)) ! 270: { ! 271: if (ent->waterlevel < 3) ! 272: { ! 273: ent->air_finished = level.time + 12; ! 274: } ! 275: else if (ent->air_finished < level.time) ! 276: { // drown! ! 277: if (ent->pain_debounce_time < level.time) ! 278: { ! 279: dmg = 2 + 2 * floor(level.time - ent->air_finished); ! 280: if (dmg > 15) ! 281: dmg = 15; ! 282: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ! 283: ent->pain_debounce_time = level.time + 1; ! 284: } ! 285: } ! 286: } ! 287: else ! 288: { ! 289: if (ent->waterlevel > 0) ! 290: { ! 291: ent->air_finished = level.time + 9; ! 292: } ! 293: else if (ent->air_finished < level.time) ! 294: { // suffocate! ! 295: if (ent->pain_debounce_time < level.time) ! 296: { ! 297: dmg = 2 + 2 * floor(level.time - ent->air_finished); ! 298: if (dmg > 15) ! 299: dmg = 15; ! 300: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ! 301: ent->pain_debounce_time = level.time + 1; ! 302: } ! 303: } ! 304: } ! 305: } ! 306: ! 307: if (ent->waterlevel == 0) ! 308: { ! 309: if (ent->flags & FL_INWATER) ! 310: { ! 311: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); ! 312: ent->flags &= ~FL_INWATER; ! 313: } ! 314: return; ! 315: } ! 316: ! 317: if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA)) ! 318: { ! 319: if (ent->damage_debounce_time < level.time) ! 320: { ! 321: ent->damage_debounce_time = level.time + 0.2; ! 322: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA); ! 323: } ! 324: } ! 325: if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME)) ! 326: { ! 327: if (ent->damage_debounce_time < level.time) ! 328: { ! 329: ent->damage_debounce_time = level.time + 1; ! 330: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME); ! 331: } ! 332: } ! 333: ! 334: if ( !(ent->flags & FL_INWATER) ) ! 335: { ! 336: if (!(ent->svflags & SVF_DEADMONSTER)) ! 337: { ! 338: if (ent->watertype & CONTENTS_LAVA) ! 339: if (random() <= 0.5) ! 340: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0); ! 341: else ! 342: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0); ! 343: else if (ent->watertype & CONTENTS_SLIME) ! 344: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); ! 345: else if (ent->watertype & CONTENTS_WATER) ! 346: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); ! 347: } ! 348: ! 349: ent->flags |= FL_INWATER; ! 350: ent->damage_debounce_time = 0; ! 351: } ! 352: } ! 353: ! 354: ! 355: void M_droptofloor (edict_t *ent) ! 356: { ! 357: vec3_t end; ! 358: trace_t trace; ! 359: ! 360: #ifdef ROGUE_GRAVITY ! 361: //PGM ! 362: if(ent->gravityVector[2] < 0) ! 363: { ! 364: ent->s.origin[2] += 1; ! 365: VectorCopy (ent->s.origin, end); ! 366: end[2] -= 256; ! 367: } ! 368: else ! 369: { ! 370: ent->s.origin[2] -= 1; ! 371: VectorCopy (ent->s.origin, end); ! 372: end[2] += 256; ! 373: } ! 374: //PGM ! 375: #else ! 376: ent->s.origin[2] += 1; ! 377: VectorCopy (ent->s.origin, end); ! 378: end[2] -= 256; ! 379: #endif ! 380: ! 381: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); ! 382: ! 383: if (trace.fraction == 1 || trace.allsolid) ! 384: return; ! 385: ! 386: VectorCopy (trace.endpos, ent->s.origin); ! 387: ! 388: gi.linkentity (ent); ! 389: M_CheckGround (ent); ! 390: M_CatagorizePosition (ent); ! 391: } ! 392: ! 393: ! 394: void M_SetEffects (edict_t *ent) ! 395: { ! 396: int remaining; ! 397: ! 398: ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN|EF_DOUBLE|EF_QUAD|EF_PENT); ! 399: ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE|RF_SHELL_DOUBLE); ! 400: ! 401: if (ent->monsterinfo.aiflags & AI_RESURRECTING) ! 402: { ! 403: ent->s.effects |= EF_COLOR_SHELL; ! 404: ent->s.renderfx |= RF_SHELL_RED; ! 405: } ! 406: ! 407: if (ent->health <= 0) ! 408: return; ! 409: ! 410: if (ent->powerarmor_time > level.time) ! 411: { ! 412: if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN) ! 413: { ! 414: ent->s.effects |= EF_POWERSCREEN; ! 415: } ! 416: else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD) ! 417: { ! 418: ent->s.effects |= EF_COLOR_SHELL; ! 419: ent->s.renderfx |= RF_SHELL_GREEN; ! 420: } ! 421: } ! 422: // PMM - new monster powerups ! 423: if (ent->monsterinfo.quad_framenum > level.framenum) ! 424: { ! 425: remaining = ent->monsterinfo.quad_framenum - level.framenum; ! 426: if (remaining > 30 || (remaining & 4) ) ! 427: ent->s.effects |= EF_QUAD; ! 428: } ! 429: else ! 430: ent->s.effects &= ~EF_QUAD; ! 431: ! 432: if (ent->monsterinfo.double_framenum > level.framenum) ! 433: { ! 434: remaining = ent->monsterinfo.double_framenum - level.framenum; ! 435: if (remaining > 30 || (remaining & 4) ) ! 436: ent->s.effects |= EF_DOUBLE; ! 437: } ! 438: else ! 439: ent->s.effects &= ~EF_DOUBLE; ! 440: ! 441: if (ent->monsterinfo.invincible_framenum > level.framenum) ! 442: { ! 443: remaining = ent->monsterinfo.invincible_framenum - level.framenum; ! 444: if (remaining > 30 || (remaining & 4) ) ! 445: ent->s.effects |= EF_PENT; ! 446: } ! 447: else ! 448: ent->s.effects &= ~EF_PENT; ! 449: ! 450: // PMM ! 451: // PMM - testing ! 452: // ent->s.effects |= EF_COLOR_SHELL; ! 453: // ent->s.renderfx |= RF_SHELL_HALF_DAM; ! 454: /* ! 455: if (fmod (level.time, 4.0) > 2.0) ! 456: { ! 457: gi.dprintf ("invulnerable "); ! 458: ent->s.renderfx |= RF_SHELL_RED; ! 459: } ! 460: else ! 461: ent->s.renderfx &= ~RF_SHELL_RED; ! 462: ! 463: if (fmod (level.time, 8.0) > 4.0) ! 464: { ! 465: gi.dprintf ("shield "); ! 466: ent->s.renderfx |= RF_SHELL_GREEN; ! 467: } ! 468: else ! 469: ent->s.renderfx &= ~RF_SHELL_GREEN; ! 470: ! 471: if (fmod (level.time, 16.0) > 8.0) ! 472: { ! 473: gi.dprintf ("quad "); ! 474: ent->s.renderfx |= RF_SHELL_BLUE;\ ! 475: } ! 476: else ! 477: ent->s.renderfx &= ~RF_SHELL_BLUE; ! 478: ! 479: if (fmod (level.time, 32.0) > 16.0) ! 480: { ! 481: gi.dprintf ("double "); ! 482: ent->s.renderfx |= RF_SHELL_DOUBLE; ! 483: } ! 484: else ! 485: ent->s.renderfx &= ~RF_SHELL_DOUBLE; ! 486: ! 487: if (fmod (level.time, 64.0) > 32.0) ! 488: { ! 489: gi.dprintf ("half "); ! 490: ent->s.renderfx |= RF_SHELL_HALF_DAM; ! 491: } ! 492: else ! 493: ent->s.renderfx &= ~RF_SHELL_HALF_DAM; ! 494: ! 495: gi.dprintf ("\n"); ! 496: */ ! 497: } ! 498: ! 499: ! 500: void M_MoveFrame (edict_t *self) ! 501: { ! 502: mmove_t *move; ! 503: int index; ! 504: ! 505: move = self->monsterinfo.currentmove; ! 506: self->nextthink = level.time + FRAMETIME; ! 507: ! 508: if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe)) ! 509: { ! 510: self->s.frame = self->monsterinfo.nextframe; ! 511: self->monsterinfo.nextframe = 0; ! 512: } ! 513: else ! 514: { ! 515: if (self->s.frame == move->lastframe) ! 516: { ! 517: if (move->endfunc) ! 518: { ! 519: move->endfunc (self); ! 520: ! 521: // regrab move, endfunc is very likely to change it ! 522: move = self->monsterinfo.currentmove; ! 523: ! 524: // check for death ! 525: if (self->svflags & SVF_DEADMONSTER) ! 526: return; ! 527: } ! 528: } ! 529: ! 530: if (self->s.frame < move->firstframe || self->s.frame > move->lastframe) ! 531: { ! 532: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 533: self->s.frame = move->firstframe; ! 534: } ! 535: else ! 536: { ! 537: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) ! 538: { ! 539: self->s.frame++; ! 540: if (self->s.frame > move->lastframe) ! 541: self->s.frame = move->firstframe; ! 542: } ! 543: } ! 544: } ! 545: ! 546: index = self->s.frame - move->firstframe; ! 547: ! 548: if (move->frame[index].aifunc) ! 549: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) ! 550: move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale); ! 551: else ! 552: move->frame[index].aifunc (self, 0); ! 553: ! 554: if (move->frame[index].thinkfunc) ! 555: move->frame[index].thinkfunc (self); ! 556: } ! 557: ! 558: ! 559: void monster_think (edict_t *self) ! 560: { ! 561: M_MoveFrame (self); ! 562: if (self->linkcount != self->monsterinfo.linkcount) ! 563: { ! 564: self->monsterinfo.linkcount = self->linkcount; ! 565: M_CheckGround (self); ! 566: } ! 567: M_CatagorizePosition (self); ! 568: M_WorldEffects (self); ! 569: M_SetEffects (self); ! 570: } ! 571: ! 572: ! 573: /* ! 574: ================ ! 575: monster_use ! 576: ! 577: Using a monster makes it angry at the current activator ! 578: ================ ! 579: */ ! 580: void monster_use (edict_t *self, edict_t *other, edict_t *activator) ! 581: { ! 582: if (self->enemy) ! 583: return; ! 584: if (self->health <= 0) ! 585: return; ! 586: if (activator->flags & FL_NOTARGET) ! 587: return; ! 588: if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY)) ! 589: return; ! 590: if (activator->flags & FL_DISGUISED) // PGM ! 591: return; // PGM ! 592: ! 593: // delay reaction so if the monster is teleported, its sound is still heard ! 594: self->enemy = activator; ! 595: FoundTarget (self); ! 596: } ! 597: ! 598: ! 599: void monster_start_go (edict_t *self); ! 600: ! 601: ! 602: void monster_triggered_spawn (edict_t *self) ! 603: { ! 604: self->s.origin[2] += 1; ! 605: KillBox (self); ! 606: ! 607: self->solid = SOLID_BBOX; ! 608: self->movetype = MOVETYPE_STEP; ! 609: self->svflags &= ~SVF_NOCLIENT; ! 610: self->air_finished = level.time + 12; ! 611: gi.linkentity (self); ! 612: ! 613: monster_start_go (self); ! 614: ! 615: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET)) ! 616: { ! 617: if(!(self->enemy->flags & FL_DISGUISED)) // PGM ! 618: FoundTarget (self); ! 619: } ! 620: else ! 621: { ! 622: self->enemy = NULL; ! 623: } ! 624: } ! 625: ! 626: void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator) ! 627: { ! 628: // we have a one frame delay here so we don't telefrag the guy who activated us ! 629: self->think = monster_triggered_spawn; ! 630: self->nextthink = level.time + FRAMETIME; ! 631: if (activator->client) ! 632: self->enemy = activator; ! 633: self->use = monster_use; ! 634: } ! 635: ! 636: void monster_triggered_start (edict_t *self) ! 637: { ! 638: self->solid = SOLID_NOT; ! 639: self->movetype = MOVETYPE_NONE; ! 640: self->svflags |= SVF_NOCLIENT; ! 641: self->nextthink = 0; ! 642: self->use = monster_triggered_spawn_use; ! 643: } ! 644: ! 645: ! 646: /* ! 647: ================ ! 648: monster_death_use ! 649: ! 650: When a monster dies, it fires all of its targets with the current ! 651: enemy as activator. ! 652: ================ ! 653: */ ! 654: void monster_death_use (edict_t *self) ! 655: { ! 656: self->flags &= ~(FL_FLY|FL_SWIM); ! 657: self->monsterinfo.aiflags &= AI_GOOD_GUY; ! 658: ! 659: if (self->item) ! 660: { ! 661: Drop_Item (self, self->item); ! 662: self->item = NULL; ! 663: } ! 664: ! 665: if (self->deathtarget) ! 666: self->target = self->deathtarget; ! 667: ! 668: if (!self->target) ! 669: return; ! 670: ! 671: G_UseTargets (self, self->enemy); ! 672: } ! 673: ! 674: ! 675: //============================================================================ ! 676: ! 677: qboolean monster_start (edict_t *self) ! 678: { ! 679: if (deathmatch->value) ! 680: { ! 681: G_FreeEdict (self); ! 682: return false; ! 683: } ! 684: ! 685: if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY)) ! 686: { ! 687: self->spawnflags &= ~4; ! 688: self->spawnflags |= 1; ! 689: // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin)); ! 690: } ! 691: ! 692: if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT))) ! 693: level.total_monsters++; ! 694: ! 695: self->nextthink = level.time + FRAMETIME; ! 696: self->svflags |= SVF_MONSTER; ! 697: self->s.renderfx |= RF_FRAMELERP; ! 698: self->takedamage = DAMAGE_AIM; ! 699: self->air_finished = level.time + 12; ! 700: self->use = monster_use; ! 701: self->max_health = self->health; ! 702: self->clipmask = MASK_MONSTERSOLID; ! 703: ! 704: self->s.skinnum = 0; ! 705: self->deadflag = DEAD_NO; ! 706: self->svflags &= ~SVF_DEADMONSTER; ! 707: ! 708: if (!self->monsterinfo.checkattack) ! 709: self->monsterinfo.checkattack = M_CheckAttack; ! 710: VectorCopy (self->s.origin, self->s.old_origin); ! 711: ! 712: if (st.item) ! 713: { ! 714: self->item = FindItemByClassname (st.item); ! 715: if (!self->item) ! 716: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); ! 717: } ! 718: ! 719: // randomize what frame they start on ! 720: if (self->monsterinfo.currentmove) ! 721: self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1)); ! 722: ! 723: // PMM - get this so I don't have to do it in all of the monsters ! 724: self->monsterinfo.base_height = self->maxs[2]; ! 725: ! 726: // PMM - clear these ! 727: self->monsterinfo.quad_framenum = 0; ! 728: self->monsterinfo.double_framenum = 0; ! 729: self->monsterinfo.invincible_framenum = 0; ! 730: ! 731: return true; ! 732: } ! 733: ! 734: void monster_start_go (edict_t *self) ! 735: { ! 736: vec3_t v; ! 737: ! 738: if (self->health <= 0) ! 739: return; ! 740: ! 741: // check for target to combat_point and change to combattarget ! 742: if (self->target) ! 743: { ! 744: qboolean notcombat; ! 745: qboolean fixup; ! 746: edict_t *target; ! 747: ! 748: target = NULL; ! 749: notcombat = false; ! 750: fixup = false; ! 751: while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL) ! 752: { ! 753: if (strcmp(target->classname, "point_combat") == 0) ! 754: { ! 755: self->combattarget = self->target; ! 756: fixup = true; ! 757: } ! 758: else ! 759: { ! 760: notcombat = true; ! 761: } ! 762: } ! 763: if (notcombat && self->combattarget) ! 764: gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); ! 765: if (fixup) ! 766: self->target = NULL; ! 767: } ! 768: ! 769: // validate combattarget ! 770: if (self->combattarget) ! 771: { ! 772: edict_t *target; ! 773: ! 774: target = NULL; ! 775: while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL) ! 776: { ! 777: if (strcmp(target->classname, "point_combat") != 0) ! 778: { ! 779: gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", ! 780: self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], ! 781: self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], ! 782: (int)target->s.origin[2]); ! 783: } ! 784: } ! 785: } ! 786: ! 787: if (self->target) ! 788: { ! 789: self->goalentity = self->movetarget = G_PickTarget(self->target); ! 790: if (!self->movetarget) ! 791: { ! 792: gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); ! 793: self->target = NULL; ! 794: self->monsterinfo.pausetime = 100000000; ! 795: self->monsterinfo.stand (self); ! 796: } ! 797: else if (strcmp (self->movetarget->classname, "path_corner") == 0) ! 798: { ! 799: VectorSubtract (self->goalentity->s.origin, self->s.origin, v); ! 800: self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); ! 801: self->monsterinfo.walk (self); ! 802: self->target = NULL; ! 803: } ! 804: else ! 805: { ! 806: self->goalentity = self->movetarget = NULL; ! 807: self->monsterinfo.pausetime = 100000000; ! 808: self->monsterinfo.stand (self); ! 809: } ! 810: } ! 811: else ! 812: { ! 813: self->monsterinfo.pausetime = 100000000; ! 814: self->monsterinfo.stand (self); ! 815: } ! 816: ! 817: self->think = monster_think; ! 818: self->nextthink = level.time + FRAMETIME; ! 819: } ! 820: ! 821: ! 822: void walkmonster_start_go (edict_t *self) ! 823: { ! 824: if (!(self->spawnflags & 2) && level.time < 1) ! 825: { ! 826: M_droptofloor (self); ! 827: ! 828: if (self->groundentity) ! 829: if (!M_walkmove (self, 0, 0)) ! 830: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); ! 831: } ! 832: ! 833: if (!self->yaw_speed) ! 834: self->yaw_speed = 20; ! 835: // PMM - stalkers are too short for this ! 836: if (!(strcmp(self->classname, "monster_stalker"))) ! 837: self->viewheight = 15; ! 838: else ! 839: self->viewheight = 25; ! 840: ! 841: monster_start_go (self); ! 842: ! 843: if (self->spawnflags & 2) ! 844: monster_triggered_start (self); ! 845: } ! 846: ! 847: void walkmonster_start (edict_t *self) ! 848: { ! 849: self->think = walkmonster_start_go; ! 850: monster_start (self); ! 851: } ! 852: ! 853: ! 854: void flymonster_start_go (edict_t *self) ! 855: { ! 856: if (!M_walkmove (self, 0, 0)) ! 857: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); ! 858: ! 859: if (!self->yaw_speed) ! 860: self->yaw_speed = 10; ! 861: self->viewheight = 25; ! 862: ! 863: monster_start_go (self); ! 864: ! 865: if (self->spawnflags & 2) ! 866: monster_triggered_start (self); ! 867: } ! 868: ! 869: ! 870: void flymonster_start (edict_t *self) ! 871: { ! 872: self->flags |= FL_FLY; ! 873: self->think = flymonster_start_go; ! 874: monster_start (self); ! 875: } ! 876: ! 877: ! 878: void swimmonster_start_go (edict_t *self) ! 879: { ! 880: if (!self->yaw_speed) ! 881: self->yaw_speed = 10; ! 882: self->viewheight = 10; ! 883: ! 884: monster_start_go (self); ! 885: ! 886: if (self->spawnflags & 2) ! 887: monster_triggered_start (self); ! 888: } ! 889: ! 890: void swimmonster_start (edict_t *self) ! 891: { ! 892: self->flags |= FL_SWIM; ! 893: self->think = swimmonster_start_go; ! 894: monster_start (self); ! 895: } ! 896: ! 897: //ROGUE ! 898: ! 899: void stationarymonster_start_go (edict_t *self); ! 900: ! 901: void stationarymonster_triggered_spawn (edict_t *self) ! 902: { ! 903: KillBox (self); ! 904: ! 905: self->solid = SOLID_BBOX; ! 906: self->movetype = MOVETYPE_NONE; ! 907: self->svflags &= ~SVF_NOCLIENT; ! 908: self->air_finished = level.time + 12; ! 909: gi.linkentity (self); ! 910: ! 911: // FIXME - why doesn't this happen with real monsters? ! 912: self->spawnflags &= ~2; ! 913: ! 914: stationarymonster_start_go (self); ! 915: ! 916: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET)) ! 917: { ! 918: if(!(self->enemy->flags & FL_DISGUISED)) // PGM ! 919: FoundTarget (self); ! 920: } ! 921: else ! 922: { ! 923: self->enemy = NULL; ! 924: } ! 925: } ! 926: ! 927: void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator) ! 928: { ! 929: // we have a one frame delay here so we don't telefrag the guy who activated us ! 930: self->think = stationarymonster_triggered_spawn; ! 931: self->nextthink = level.time + FRAMETIME; ! 932: if (activator->client) ! 933: self->enemy = activator; ! 934: self->use = monster_use; ! 935: } ! 936: ! 937: void stationarymonster_triggered_start (edict_t *self) ! 938: { ! 939: self->solid = SOLID_NOT; ! 940: self->movetype = MOVETYPE_NONE; ! 941: self->svflags |= SVF_NOCLIENT; ! 942: self->nextthink = 0; ! 943: self->use = stationarymonster_triggered_spawn_use; ! 944: } ! 945: ! 946: void stationarymonster_start_go (edict_t *self) ! 947: { ! 948: if (!M_walkmove (self, 0, 0)) ! 949: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); ! 950: ! 951: if (!self->yaw_speed) ! 952: self->yaw_speed = 20; ! 953: // self->viewheight = 25; ! 954: ! 955: monster_start_go (self); ! 956: ! 957: if (self->spawnflags & 2) ! 958: stationarymonster_triggered_start (self); ! 959: } ! 960: ! 961: void stationarymonster_start (edict_t *self) ! 962: { ! 963: self->think = stationarymonster_start_go; ! 964: monster_start (self); ! 965: } ! 966: ! 967: void monster_done_dodge (edict_t *self) ! 968: { ! 969: // if ((g_showlogic) && (g_showlogic->value)) ! 970: // gi.dprintf ("%s done dodging\n", self->classname); ! 971: self->monsterinfo.aiflags &= ~AI_DODGING; ! 972: } ! 973: //ROGUE
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.