|
|
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: // RAFAEL ! 43: void monster_fire_blueblaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) ! 44: { ! 45: fire_blueblaster (self, start, dir, damage, speed, effect); ! 46: ! 47: gi.WriteByte (svc_muzzleflash2); ! 48: gi.WriteShort (self - g_edicts); ! 49: gi.WriteByte (MZ_BLUEHYPERBLASTER); ! 50: gi.multicast (start, MULTICAST_PVS); ! 51: } ! 52: ! 53: // RAFAEL ! 54: void monster_fire_ionripper (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) ! 55: { ! 56: fire_ionripper (self, start, dir, damage, speed, effect); ! 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: // RAFAEL ! 65: void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) ! 66: { ! 67: fire_heat (self, start, dir, damage, speed, damage, damage); ! 68: ! 69: gi.WriteByte (svc_muzzleflash2); ! 70: gi.WriteShort (self - g_edicts); ! 71: gi.WriteByte (flashtype); ! 72: gi.multicast (start, MULTICAST_PVS); ! 73: } ! 74: ! 75: // RAFAEL ! 76: void dabeam_hit (edict_t *self) ! 77: { ! 78: edict_t *ignore; ! 79: vec3_t start; ! 80: vec3_t end; ! 81: trace_t tr; ! 82: int count; ! 83: static vec3_t lmins = {-4, -4, -4}; ! 84: static vec3_t lmaxs = {4, 4, 4}; ! 85: ! 86: if (self->spawnflags & 0x80000000) ! 87: count = 8; ! 88: else ! 89: count = 4; ! 90: ! 91: ignore = self; ! 92: VectorCopy (self->s.origin, start); ! 93: VectorMA (start, 2048, self->movedir, end); ! 94: ! 95: while(1) ! 96: { ! 97: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); ! 98: if (!tr.ent) ! 99: break; ! 100: ! 101: // hurt it if we can ! 102: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) ! 103: T_Damage (tr.ent, self, self->owner, self->movedir, tr.endpos, vec3_origin, self->dmg, skill->value, DAMAGE_ENERGY, MOD_TARGET_LASER); ! 104: ! 105: if (self->dmg < 0) // healer ray ! 106: { ! 107: // when player is at 100 health ! 108: // just undo health fix ! 109: // keeping fx ! 110: if (tr.ent->client && tr.ent->health > 100) ! 111: tr.ent->health += self->dmg; ! 112: } ! 113: ! 114: // if we hit something that's not a monster or player or is immune to lasers, we're done ! 115: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) ! 116: { ! 117: if (self->spawnflags & 0x80000000) ! 118: { ! 119: self->spawnflags &= ~0x80000000; ! 120: gi.WriteByte (svc_temp_entity); ! 121: gi.WriteByte (TE_LASER_SPARKS); ! 122: gi.WriteByte (10); ! 123: gi.WritePosition (tr.endpos); ! 124: gi.WriteDir (tr.plane.normal); ! 125: gi.WriteByte (self->s.skinnum); ! 126: gi.multicast (tr.endpos, MULTICAST_PVS); ! 127: } ! 128: break; ! 129: } ! 130: ! 131: ignore = tr.ent; ! 132: VectorCopy (tr.endpos, start); ! 133: } ! 134: ! 135: ! 136: VectorCopy (tr.endpos, self->s.old_origin); ! 137: self->nextthink = level.time + 0.1; ! 138: self->think = G_FreeEdict; ! 139: ! 140: } ! 141: ! 142: // RAFAEL ! 143: void monster_dabeam (edict_t *self) ! 144: { ! 145: vec3_t last_movedir; ! 146: vec3_t point; ! 147: ! 148: self->movetype = MOVETYPE_NONE; ! 149: self->solid = SOLID_NOT; ! 150: self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT; ! 151: self->s.modelindex = 1; ! 152: ! 153: self->s.frame = 2; ! 154: ! 155: if (self->owner->monsterinfo.aiflags & AI_MEDIC) ! 156: self->s.skinnum = 0xf3f3f1f1; ! 157: else ! 158: self->s.skinnum = 0xf2f2f0f0; ! 159: ! 160: if (self->enemy) ! 161: { ! 162: VectorCopy (self->movedir, last_movedir); ! 163: VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point); ! 164: if (self->owner->monsterinfo.aiflags & AI_MEDIC) ! 165: point[0] += sin (level.time) * 8; ! 166: VectorSubtract (point, self->s.origin, self->movedir); ! 167: VectorNormalize (self->movedir); ! 168: if (!VectorCompare(self->movedir, last_movedir)) ! 169: self->spawnflags |= 0x80000000; ! 170: } ! 171: else ! 172: G_SetMovedir (self->s.angles, self->movedir); ! 173: ! 174: self->think = dabeam_hit; ! 175: self->nextthink = level.time + 0.1; ! 176: VectorSet (self->mins, -8, -8, -8); ! 177: VectorSet (self->maxs, 8, 8, 8); ! 178: gi.linkentity (self); ! 179: ! 180: self->spawnflags |= 0x80000001; ! 181: self->svflags &= ~SVF_NOCLIENT; ! 182: } ! 183: ! 184: void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype) ! 185: { ! 186: fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40); ! 187: ! 188: gi.WriteByte (svc_muzzleflash2); ! 189: gi.WriteShort (self - g_edicts); ! 190: gi.WriteByte (flashtype); ! 191: gi.multicast (start, MULTICAST_PVS); ! 192: } ! 193: ! 194: void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) ! 195: { ! 196: fire_rocket (self, start, dir, damage, speed, damage+20, damage); ! 197: ! 198: gi.WriteByte (svc_muzzleflash2); ! 199: gi.WriteShort (self - g_edicts); ! 200: gi.WriteByte (flashtype); ! 201: gi.multicast (start, MULTICAST_PVS); ! 202: } ! 203: ! 204: void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype) ! 205: { ! 206: fire_rail (self, start, aimdir, damage, kick); ! 207: ! 208: gi.WriteByte (svc_muzzleflash2); ! 209: gi.WriteShort (self - g_edicts); ! 210: gi.WriteByte (flashtype); ! 211: gi.multicast (start, MULTICAST_PVS); ! 212: } ! 213: ! 214: void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype) ! 215: { ! 216: fire_bfg (self, start, aimdir, damage, speed, damage_radius); ! 217: ! 218: gi.WriteByte (svc_muzzleflash2); ! 219: gi.WriteShort (self - g_edicts); ! 220: gi.WriteByte (flashtype); ! 221: gi.multicast (start, MULTICAST_PVS); ! 222: } ! 223: ! 224: ! 225: ! 226: // ! 227: // Monster utility functions ! 228: // ! 229: ! 230: static void M_FliesOff (edict_t *self) ! 231: { ! 232: self->s.effects &= ~EF_FLIES; ! 233: self->s.sound = 0; ! 234: } ! 235: ! 236: static void M_FliesOn (edict_t *self) ! 237: { ! 238: if (self->waterlevel) ! 239: return; ! 240: self->s.effects |= EF_FLIES; ! 241: self->s.sound = gi.soundindex ("infantry/inflies1.wav"); ! 242: self->think = M_FliesOff; ! 243: self->nextthink = level.time + 60; ! 244: } ! 245: ! 246: void M_FlyCheck (edict_t *self) ! 247: { ! 248: if (self->waterlevel) ! 249: return; ! 250: ! 251: if (random() > 0.5) ! 252: return; ! 253: ! 254: self->think = M_FliesOn; ! 255: self->nextthink = level.time + 5 + 10 * random(); ! 256: } ! 257: ! 258: void AttackFinished (edict_t *self, float time) ! 259: { ! 260: self->monsterinfo.attack_finished = level.time + time; ! 261: } ! 262: ! 263: ! 264: void M_CheckGround (edict_t *ent) ! 265: { ! 266: vec3_t point; ! 267: trace_t trace; ! 268: ! 269: if (ent->flags & (FL_SWIM|FL_FLY)) ! 270: return; ! 271: ! 272: if (ent->velocity[2] > 100) ! 273: { ! 274: ent->groundentity = NULL; ! 275: return; ! 276: } ! 277: ! 278: // if the hull point one-quarter unit down is solid the entity is on ground ! 279: point[0] = ent->s.origin[0]; ! 280: point[1] = ent->s.origin[1]; ! 281: point[2] = ent->s.origin[2] - 0.25; ! 282: ! 283: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID); ! 284: ! 285: // check steepness ! 286: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) ! 287: { ! 288: ent->groundentity = NULL; ! 289: return; ! 290: } ! 291: ! 292: // ent->groundentity = trace.ent; ! 293: // ent->groundentity_linkcount = trace.ent->linkcount; ! 294: // if (!trace.startsolid && !trace.allsolid) ! 295: // VectorCopy (trace.endpos, ent->s.origin); ! 296: if (!trace.startsolid && !trace.allsolid) ! 297: { ! 298: VectorCopy (trace.endpos, ent->s.origin); ! 299: ent->groundentity = trace.ent; ! 300: ent->groundentity_linkcount = trace.ent->linkcount; ! 301: ent->velocity[2] = 0; ! 302: } ! 303: } ! 304: ! 305: ! 306: void M_CatagorizePosition (edict_t *ent) ! 307: { ! 308: vec3_t point; ! 309: int cont; ! 310: ! 311: // ! 312: // get waterlevel ! 313: // ! 314: point[0] = ent->s.origin[0]; ! 315: point[1] = ent->s.origin[1]; ! 316: point[2] = ent->s.origin[2] + ent->mins[2] + 1; ! 317: cont = gi.pointcontents (point); ! 318: ! 319: if (!(cont & MASK_WATER)) ! 320: { ! 321: ent->waterlevel = 0; ! 322: ent->watertype = 0; ! 323: return; ! 324: } ! 325: ! 326: ent->watertype = cont; ! 327: ent->waterlevel = 1; ! 328: point[2] += 26; ! 329: cont = gi.pointcontents (point); ! 330: if (!(cont & MASK_WATER)) ! 331: return; ! 332: ! 333: ent->waterlevel = 2; ! 334: point[2] += 22; ! 335: cont = gi.pointcontents (point); ! 336: if (cont & MASK_WATER) ! 337: ent->waterlevel = 3; ! 338: } ! 339: ! 340: ! 341: void M_WorldEffects (edict_t *ent) ! 342: { ! 343: int dmg; ! 344: ! 345: if (ent->health > 0) ! 346: { ! 347: if (!(ent->flags & FL_SWIM)) ! 348: { ! 349: if (ent->waterlevel < 3) ! 350: { ! 351: ent->air_finished = level.time + 12; ! 352: } ! 353: else if (ent->air_finished < level.time) ! 354: { // drown! ! 355: if (ent->pain_debounce_time < level.time) ! 356: { ! 357: dmg = 2 + 2 * floor(level.time - ent->air_finished); ! 358: if (dmg > 15) ! 359: dmg = 15; ! 360: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ! 361: ent->pain_debounce_time = level.time + 1; ! 362: } ! 363: } ! 364: } ! 365: else ! 366: { ! 367: if (ent->waterlevel > 0) ! 368: { ! 369: ent->air_finished = level.time + 9; ! 370: } ! 371: else if (ent->air_finished < level.time) ! 372: { // suffocate! ! 373: if (ent->pain_debounce_time < level.time) ! 374: { ! 375: dmg = 2 + 2 * floor(level.time - ent->air_finished); ! 376: if (dmg > 15) ! 377: dmg = 15; ! 378: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ! 379: ent->pain_debounce_time = level.time + 1; ! 380: } ! 381: } ! 382: } ! 383: } ! 384: ! 385: if (ent->waterlevel == 0) ! 386: { ! 387: if (ent->flags & FL_INWATER) ! 388: { ! 389: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); ! 390: ent->flags &= ~FL_INWATER; ! 391: } ! 392: return; ! 393: } ! 394: ! 395: if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA)) ! 396: { ! 397: if (ent->damage_debounce_time < level.time) ! 398: { ! 399: ent->damage_debounce_time = level.time + 0.2; ! 400: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA); ! 401: } ! 402: } ! 403: if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME)) ! 404: { ! 405: if (ent->damage_debounce_time < level.time) ! 406: { ! 407: ent->damage_debounce_time = level.time + 1; ! 408: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME); ! 409: } ! 410: } ! 411: ! 412: if ( !(ent->flags & FL_INWATER) ) ! 413: { ! 414: if (!(ent->svflags & SVF_DEADMONSTER)) ! 415: { ! 416: if (ent->watertype & CONTENTS_LAVA) ! 417: if (random() <= 0.5) ! 418: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0); ! 419: else ! 420: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0); ! 421: else if (ent->watertype & CONTENTS_SLIME) ! 422: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); ! 423: else if (ent->watertype & CONTENTS_WATER) ! 424: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); ! 425: } ! 426: ! 427: ent->flags |= FL_INWATER; ! 428: ent->damage_debounce_time = 0; ! 429: } ! 430: } ! 431: ! 432: ! 433: void M_droptofloor (edict_t *ent) ! 434: { ! 435: vec3_t end; ! 436: trace_t trace; ! 437: ! 438: ent->s.origin[2] += 1; ! 439: VectorCopy (ent->s.origin, end); ! 440: end[2] -= 256; ! 441: ! 442: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); ! 443: ! 444: if (trace.fraction == 1 || trace.allsolid) ! 445: return; ! 446: ! 447: VectorCopy (trace.endpos, ent->s.origin); ! 448: ! 449: gi.linkentity (ent); ! 450: M_CheckGround (ent); ! 451: M_CatagorizePosition (ent); ! 452: } ! 453: ! 454: ! 455: void M_SetEffects (edict_t *ent) ! 456: { ! 457: ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN); ! 458: ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); ! 459: ! 460: if (ent->monsterinfo.aiflags & AI_RESURRECTING) ! 461: { ! 462: ent->s.effects |= EF_COLOR_SHELL; ! 463: ent->s.renderfx |= RF_SHELL_RED; ! 464: } ! 465: ! 466: if (ent->health <= 0) ! 467: return; ! 468: ! 469: if (ent->powerarmor_time > level.time) ! 470: { ! 471: if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN) ! 472: { ! 473: ent->s.effects |= EF_POWERSCREEN; ! 474: } ! 475: else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD) ! 476: { ! 477: ent->s.effects |= EF_COLOR_SHELL; ! 478: ent->s.renderfx |= RF_SHELL_GREEN; ! 479: } ! 480: } ! 481: } ! 482: ! 483: ! 484: void M_MoveFrame (edict_t *self) ! 485: { ! 486: mmove_t *move; ! 487: int index; ! 488: ! 489: move = self->monsterinfo.currentmove; ! 490: self->nextthink = level.time + FRAMETIME; ! 491: ! 492: if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe)) ! 493: { ! 494: self->s.frame = self->monsterinfo.nextframe; ! 495: self->monsterinfo.nextframe = 0; ! 496: } ! 497: else ! 498: { ! 499: if (self->s.frame == move->lastframe) ! 500: { ! 501: if (move->endfunc) ! 502: { ! 503: move->endfunc (self); ! 504: ! 505: // regrab move, endfunc is very likely to change it ! 506: move = self->monsterinfo.currentmove; ! 507: ! 508: // check for death ! 509: if (self->svflags & SVF_DEADMONSTER) ! 510: return; ! 511: } ! 512: } ! 513: ! 514: if (self->s.frame < move->firstframe || self->s.frame > move->lastframe) ! 515: { ! 516: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; ! 517: self->s.frame = move->firstframe; ! 518: } ! 519: else ! 520: { ! 521: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) ! 522: { ! 523: self->s.frame++; ! 524: if (self->s.frame > move->lastframe) ! 525: self->s.frame = move->firstframe; ! 526: } ! 527: } ! 528: } ! 529: ! 530: index = self->s.frame - move->firstframe; ! 531: if (move->frame[index].aifunc) ! 532: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) ! 533: move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale); ! 534: else ! 535: move->frame[index].aifunc (self, 0); ! 536: ! 537: if (move->frame[index].thinkfunc) ! 538: move->frame[index].thinkfunc (self); ! 539: } ! 540: ! 541: ! 542: void monster_think (edict_t *self) ! 543: { ! 544: M_MoveFrame (self); ! 545: if (self->linkcount != self->monsterinfo.linkcount) ! 546: { ! 547: self->monsterinfo.linkcount = self->linkcount; ! 548: M_CheckGround (self); ! 549: } ! 550: M_CatagorizePosition (self); ! 551: M_WorldEffects (self); ! 552: M_SetEffects (self); ! 553: } ! 554: ! 555: ! 556: /* ! 557: ================ ! 558: monster_use ! 559: ! 560: Using a monster makes it angry at the current activator ! 561: ================ ! 562: */ ! 563: void monster_use (edict_t *self, edict_t *other, edict_t *activator) ! 564: { ! 565: if (self->enemy) ! 566: return; ! 567: if (self->health <= 0) ! 568: return; ! 569: if (activator->flags & FL_NOTARGET) ! 570: return; ! 571: if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY)) ! 572: return; ! 573: ! 574: // delay reaction so if the monster is teleported, its sound is still heard ! 575: self->enemy = activator; ! 576: FoundTarget (self); ! 577: } ! 578: ! 579: ! 580: void monster_start_go (edict_t *self); ! 581: ! 582: ! 583: void monster_triggered_spawn (edict_t *self) ! 584: { ! 585: self->s.origin[2] += 1; ! 586: KillBox (self); ! 587: ! 588: self->solid = SOLID_BBOX; ! 589: self->movetype = MOVETYPE_STEP; ! 590: self->svflags &= ~SVF_NOCLIENT; ! 591: self->air_finished = level.time + 12; ! 592: gi.linkentity (self); ! 593: ! 594: monster_start_go (self); ! 595: ! 596: // RAFAEL ! 597: if (strcmp (self->classname, "monster_fixbot") == 0) ! 598: { ! 599: if (self->spawnflags &16 || self->spawnflags &8 || self->spawnflags &4) ! 600: { ! 601: self->enemy = NULL; ! 602: return; ! 603: } ! 604: } ! 605: ! 606: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET)) ! 607: { ! 608: FoundTarget (self); ! 609: } ! 610: else ! 611: { ! 612: self->enemy = NULL; ! 613: } ! 614: } ! 615: ! 616: void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator) ! 617: { ! 618: // we have a one frame delay here so we don't telefrag the guy who activated us ! 619: self->think = monster_triggered_spawn; ! 620: self->nextthink = level.time + FRAMETIME; ! 621: if (activator->client) ! 622: self->enemy = activator; ! 623: self->use = monster_use; ! 624: } ! 625: ! 626: void monster_triggered_start (edict_t *self) ! 627: { ! 628: self->solid = SOLID_NOT; ! 629: self->movetype = MOVETYPE_NONE; ! 630: self->svflags |= SVF_NOCLIENT; ! 631: self->nextthink = 0; ! 632: self->use = monster_triggered_spawn_use; ! 633: } ! 634: ! 635: ! 636: /* ! 637: ================ ! 638: monster_death_use ! 639: ! 640: When a monster dies, it fires all of its targets with the current ! 641: enemy as activator. ! 642: ================ ! 643: */ ! 644: void monster_death_use (edict_t *self) ! 645: { ! 646: self->flags &= ~(FL_FLY|FL_SWIM); ! 647: self->monsterinfo.aiflags &= AI_GOOD_GUY; ! 648: ! 649: if (self->item) ! 650: { ! 651: Drop_Item (self, self->item); ! 652: self->item = NULL; ! 653: } ! 654: ! 655: if (self->deathtarget) ! 656: self->target = self->deathtarget; ! 657: ! 658: if (!self->target) ! 659: return; ! 660: ! 661: G_UseTargets (self, self->enemy); ! 662: } ! 663: ! 664: ! 665: //============================================================================ ! 666: ! 667: qboolean monster_start (edict_t *self) ! 668: { ! 669: if (deathmatch->value) ! 670: { ! 671: G_FreeEdict (self); ! 672: return false; ! 673: } ! 674: ! 675: if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY)) ! 676: { ! 677: self->spawnflags &= ~4; ! 678: self->spawnflags |= 1; ! 679: // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin)); ! 680: } ! 681: ! 682: if (!(self->monsterinfo.aiflags & AI_GOOD_GUY)) ! 683: level.total_monsters++; ! 684: ! 685: self->nextthink = level.time + FRAMETIME; ! 686: self->svflags |= SVF_MONSTER; ! 687: self->s.renderfx |= RF_FRAMELERP; ! 688: self->takedamage = DAMAGE_AIM; ! 689: self->air_finished = level.time + 12; ! 690: self->use = monster_use; ! 691: self->max_health = self->health; ! 692: self->clipmask = MASK_MONSTERSOLID; ! 693: ! 694: self->s.skinnum = 0; ! 695: self->deadflag = DEAD_NO; ! 696: self->svflags &= ~SVF_DEADMONSTER; ! 697: ! 698: if (!self->monsterinfo.checkattack) ! 699: self->monsterinfo.checkattack = M_CheckAttack; ! 700: VectorCopy (self->s.origin, self->s.old_origin); ! 701: ! 702: if (st.item) ! 703: { ! 704: self->item = FindItemByClassname (st.item); ! 705: if (!self->item) ! 706: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); ! 707: } ! 708: ! 709: // randomize what frame they start on ! 710: if (self->monsterinfo.currentmove) ! 711: self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1)); ! 712: ! 713: return true; ! 714: } ! 715: ! 716: void monster_start_go (edict_t *self) ! 717: { ! 718: vec3_t v; ! 719: ! 720: if (self->health <= 0) ! 721: return; ! 722: ! 723: // check for target to combat_point and change to combattarget ! 724: if (self->target) ! 725: { ! 726: qboolean notcombat; ! 727: qboolean fixup; ! 728: edict_t *target; ! 729: ! 730: target = NULL; ! 731: notcombat = false; ! 732: fixup = false; ! 733: while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL) ! 734: { ! 735: if (strcmp(target->classname, "point_combat") == 0) ! 736: { ! 737: self->combattarget = self->target; ! 738: fixup = true; ! 739: } ! 740: else ! 741: { ! 742: notcombat = true; ! 743: } ! 744: } ! 745: if (notcombat && self->combattarget) ! 746: gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); ! 747: if (fixup) ! 748: self->target = NULL; ! 749: } ! 750: ! 751: // validate combattarget ! 752: if (self->combattarget) ! 753: { ! 754: edict_t *target; ! 755: ! 756: target = NULL; ! 757: while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL) ! 758: { ! 759: if (strcmp(target->classname, "point_combat") != 0) ! 760: { ! 761: gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", ! 762: self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], ! 763: self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], ! 764: (int)target->s.origin[2]); ! 765: } ! 766: } ! 767: } ! 768: ! 769: if (self->target) ! 770: { ! 771: self->goalentity = self->movetarget = G_PickTarget(self->target); ! 772: if (!self->movetarget) ! 773: { ! 774: gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); ! 775: self->target = NULL; ! 776: self->monsterinfo.pausetime = 100000000; ! 777: self->monsterinfo.stand (self); ! 778: } ! 779: else if (strcmp (self->movetarget->classname, "path_corner") == 0) ! 780: { ! 781: VectorSubtract (self->goalentity->s.origin, self->s.origin, v); ! 782: self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); ! 783: self->monsterinfo.walk (self); ! 784: self->target = NULL; ! 785: } ! 786: else ! 787: { ! 788: self->goalentity = self->movetarget = NULL; ! 789: self->monsterinfo.pausetime = 100000000; ! 790: self->monsterinfo.stand (self); ! 791: } ! 792: } ! 793: else ! 794: { ! 795: self->monsterinfo.pausetime = 100000000; ! 796: self->monsterinfo.stand (self); ! 797: } ! 798: ! 799: self->think = monster_think; ! 800: self->nextthink = level.time + FRAMETIME; ! 801: } ! 802: ! 803: ! 804: void walkmonster_start_go (edict_t *self) ! 805: { ! 806: if (!(self->spawnflags & 2) && level.time < 1) ! 807: { ! 808: M_droptofloor (self); ! 809: ! 810: if (self->groundentity) ! 811: if (!M_walkmove (self, 0, 0)) ! 812: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); ! 813: } ! 814: ! 815: if (!self->yaw_speed) ! 816: self->yaw_speed = 20; ! 817: self->viewheight = 25; ! 818: ! 819: monster_start_go (self); ! 820: ! 821: if (self->spawnflags & 2) ! 822: monster_triggered_start (self); ! 823: } ! 824: ! 825: void walkmonster_start (edict_t *self) ! 826: { ! 827: self->think = walkmonster_start_go; ! 828: monster_start (self); ! 829: } ! 830: ! 831: ! 832: void flymonster_start_go (edict_t *self) ! 833: { ! 834: if (!M_walkmove (self, 0, 0)) ! 835: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); ! 836: ! 837: if (!self->yaw_speed) ! 838: self->yaw_speed = 10; ! 839: self->viewheight = 25; ! 840: ! 841: monster_start_go (self); ! 842: ! 843: if (self->spawnflags & 2) ! 844: monster_triggered_start (self); ! 845: } ! 846: ! 847: ! 848: void flymonster_start (edict_t *self) ! 849: { ! 850: self->flags |= FL_FLY; ! 851: self->think = flymonster_start_go; ! 852: monster_start (self); ! 853: } ! 854: ! 855: ! 856: void swimmonster_start_go (edict_t *self) ! 857: { ! 858: if (!self->yaw_speed) ! 859: self->yaw_speed = 10; ! 860: self->viewheight = 10; ! 861: ! 862: monster_start_go (self); ! 863: ! 864: if (self->spawnflags & 2) ! 865: monster_triggered_start (self); ! 866: } ! 867: ! 868: void swimmonster_start (edict_t *self) ! 869: { ! 870: self->flags |= FL_SWIM; ! 871: self->think = swimmonster_start_go; ! 872: monster_start (self); ! 873: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.