|
|
1.1.1.2 ! root 1: /* ! 2: Copyright (C) 1997-2001 Id Software, Inc. ! 3: ! 4: This program is free software; you can redistribute it and/or ! 5: modify it under the terms of the GNU General Public License ! 6: as published by the Free Software Foundation; either version 2 ! 7: of the License, or (at your option) any later version. ! 8: ! 9: This program is distributed in the hope that it will be useful, ! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! 12: ! 13: See the GNU General Public License for more details. ! 14: ! 15: You should have received a copy of the GNU General Public License ! 16: along with this program; if not, write to the Free Software ! 17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ! 18: ! 19: */ 1.1 root 20: #include "g_local.h" 21: 22: 23: // 24: // monster weapons 25: // 26: 27: //FIXME mosnters should call these with a totally accurate direction 28: // and we can mess it up based on skill. Spread should be for normal 29: // and we can tighten or loosen based on skill. We could muck with 30: // the damages too, but I'm not sure that's such a good idea. 31: void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype) 32: { 33: fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN); 34: 35: gi.WriteByte (svc_muzzleflash2); 36: gi.WriteShort (self - g_edicts); 37: gi.WriteByte (flashtype); 38: gi.multicast (start, MULTICAST_PVS); 39: } 40: 41: 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) 42: { 43: fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN); 44: 45: gi.WriteByte (svc_muzzleflash2); 46: gi.WriteShort (self - g_edicts); 47: gi.WriteByte (flashtype); 48: gi.multicast (start, MULTICAST_PVS); 49: } 50: 51: void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) 52: { 53: fire_blaster (self, start, dir, damage, speed, effect, false); 54: 55: gi.WriteByte (svc_muzzleflash2); 56: gi.WriteShort (self - g_edicts); 57: gi.WriteByte (flashtype); 58: gi.multicast (start, MULTICAST_PVS); 59: } 60: 61: void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype) 62: { 63: fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40); 64: 65: gi.WriteByte (svc_muzzleflash2); 66: gi.WriteShort (self - g_edicts); 67: gi.WriteByte (flashtype); 68: gi.multicast (start, MULTICAST_PVS); 69: } 70: 71: void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) 72: { 73: fire_rocket (self, start, dir, damage, speed, damage+20, damage); 74: 75: gi.WriteByte (svc_muzzleflash2); 76: gi.WriteShort (self - g_edicts); 77: gi.WriteByte (flashtype); 78: gi.multicast (start, MULTICAST_PVS); 79: } 80: 81: void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype) 82: { 83: fire_rail (self, start, aimdir, damage, kick); 84: 85: gi.WriteByte (svc_muzzleflash2); 86: gi.WriteShort (self - g_edicts); 87: gi.WriteByte (flashtype); 88: gi.multicast (start, MULTICAST_PVS); 89: } 90: 91: void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype) 92: { 93: fire_bfg (self, start, aimdir, damage, speed, damage_radius); 94: 95: gi.WriteByte (svc_muzzleflash2); 96: gi.WriteShort (self - g_edicts); 97: gi.WriteByte (flashtype); 98: gi.multicast (start, MULTICAST_PVS); 99: } 100: 101: 102: 103: // 104: // Monster utility functions 105: // 106: 107: static void M_FliesOff (edict_t *self) 108: { 109: self->s.effects &= ~EF_FLIES; 110: self->s.sound = 0; 111: } 112: 113: static void M_FliesOn (edict_t *self) 114: { 115: if (self->waterlevel) 116: return; 117: self->s.effects |= EF_FLIES; 118: self->s.sound = gi.soundindex ("infantry/inflies1.wav"); 119: self->think = M_FliesOff; 120: self->nextthink = level.time + 60; 121: } 122: 123: void M_FlyCheck (edict_t *self) 124: { 125: if (self->waterlevel) 126: return; 127: 128: if (random() > 0.5) 129: return; 130: 131: self->think = M_FliesOn; 132: self->nextthink = level.time + 5 + 10 * random(); 133: } 134: 135: void AttackFinished (edict_t *self, float time) 136: { 137: self->monsterinfo.attack_finished = level.time + time; 138: } 139: 140: 141: void M_CheckGround (edict_t *ent) 142: { 143: vec3_t point; 144: trace_t trace; 145: 146: if (ent->flags & (FL_SWIM|FL_FLY)) 147: return; 148: 149: if (ent->velocity[2] > 100) 150: { 151: ent->groundentity = NULL; 152: return; 153: } 154: 155: // if the hull point one-quarter unit down is solid the entity is on ground 156: point[0] = ent->s.origin[0]; 157: point[1] = ent->s.origin[1]; 158: point[2] = ent->s.origin[2] - 0.25; 159: 160: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID); 161: 162: // check steepness 163: if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) 164: { 165: ent->groundentity = NULL; 166: return; 167: } 168: 169: // ent->groundentity = trace.ent; 170: // ent->groundentity_linkcount = trace.ent->linkcount; 171: // if (!trace.startsolid && !trace.allsolid) 172: // VectorCopy (trace.endpos, ent->s.origin); 173: if (!trace.startsolid && !trace.allsolid) 174: { 175: VectorCopy (trace.endpos, ent->s.origin); 176: ent->groundentity = trace.ent; 177: ent->groundentity_linkcount = trace.ent->linkcount; 178: ent->velocity[2] = 0; 179: } 180: } 181: 182: 183: void M_CatagorizePosition (edict_t *ent) 184: { 185: vec3_t point; 186: int cont; 187: 188: // 189: // get waterlevel 190: // 191: point[0] = ent->s.origin[0]; 192: point[1] = ent->s.origin[1]; 193: point[2] = ent->s.origin[2] + ent->mins[2] + 1; 194: cont = gi.pointcontents (point); 195: 196: if (!(cont & MASK_WATER)) 197: { 198: ent->waterlevel = 0; 199: ent->watertype = 0; 200: return; 201: } 202: 203: ent->watertype = cont; 204: ent->waterlevel = 1; 205: point[2] += 26; 206: cont = gi.pointcontents (point); 207: if (!(cont & MASK_WATER)) 208: return; 209: 210: ent->waterlevel = 2; 211: point[2] += 22; 212: cont = gi.pointcontents (point); 213: if (cont & MASK_WATER) 214: ent->waterlevel = 3; 215: } 216: 217: 218: void M_WorldEffects (edict_t *ent) 219: { 220: int dmg; 221: 222: if (ent->health > 0) 223: { 224: if (!(ent->flags & FL_SWIM)) 225: { 226: if (ent->waterlevel < 3) 227: { 228: ent->air_finished = level.time + 12; 229: } 230: else if (ent->air_finished < level.time) 231: { // drown! 232: if (ent->pain_debounce_time < level.time) 233: { 234: dmg = 2 + 2 * floor(level.time - ent->air_finished); 235: if (dmg > 15) 236: dmg = 15; 237: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); 238: ent->pain_debounce_time = level.time + 1; 239: } 240: } 241: } 242: else 243: { 244: if (ent->waterlevel > 0) 245: { 246: ent->air_finished = level.time + 9; 247: } 248: else if (ent->air_finished < level.time) 249: { // suffocate! 250: if (ent->pain_debounce_time < level.time) 251: { 252: dmg = 2 + 2 * floor(level.time - ent->air_finished); 253: if (dmg > 15) 254: dmg = 15; 255: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); 256: ent->pain_debounce_time = level.time + 1; 257: } 258: } 259: } 260: } 261: 262: if (ent->waterlevel == 0) 263: { 264: if (ent->flags & FL_INWATER) 265: { 266: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); 267: ent->flags &= ~FL_INWATER; 268: } 269: return; 270: } 271: 272: if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA)) 273: { 274: if (ent->damage_debounce_time < level.time) 275: { 276: ent->damage_debounce_time = level.time + 0.2; 277: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA); 278: } 279: } 280: if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME)) 281: { 282: if (ent->damage_debounce_time < level.time) 283: { 284: ent->damage_debounce_time = level.time + 1; 285: T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME); 286: } 287: } 288: 289: if ( !(ent->flags & FL_INWATER) ) 290: { 291: if (!(ent->svflags & SVF_DEADMONSTER)) 292: { 293: if (ent->watertype & CONTENTS_LAVA) 294: if (random() <= 0.5) 295: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0); 296: else 297: gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0); 298: else if (ent->watertype & CONTENTS_SLIME) 299: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); 300: else if (ent->watertype & CONTENTS_WATER) 301: gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); 302: } 303: 304: ent->flags |= FL_INWATER; 305: ent->damage_debounce_time = 0; 306: } 307: } 308: 309: 310: void M_droptofloor (edict_t *ent) 311: { 312: vec3_t end; 313: trace_t trace; 314: 315: ent->s.origin[2] += 1; 316: VectorCopy (ent->s.origin, end); 317: end[2] -= 256; 318: 319: trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); 320: 321: if (trace.fraction == 1 || trace.allsolid) 322: return; 323: 324: VectorCopy (trace.endpos, ent->s.origin); 325: 326: gi.linkentity (ent); 327: M_CheckGround (ent); 328: M_CatagorizePosition (ent); 329: } 330: 331: 332: void M_SetEffects (edict_t *ent) 333: { 334: ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN); 335: ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); 336: 337: if (ent->monsterinfo.aiflags & AI_RESURRECTING) 338: { 339: ent->s.effects |= EF_COLOR_SHELL; 340: ent->s.renderfx |= RF_SHELL_RED; 341: } 342: 343: if (ent->health <= 0) 344: return; 345: 346: if (ent->powerarmor_time > level.time) 347: { 348: if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN) 349: { 350: ent->s.effects |= EF_POWERSCREEN; 351: } 352: else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD) 353: { 354: ent->s.effects |= EF_COLOR_SHELL; 355: ent->s.renderfx |= RF_SHELL_GREEN; 356: } 357: } 358: } 359: 360: 361: void M_MoveFrame (edict_t *self) 362: { 363: mmove_t *move; 364: int index; 365: 366: move = self->monsterinfo.currentmove; 367: self->nextthink = level.time + FRAMETIME; 368: 369: if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe)) 370: { 371: self->s.frame = self->monsterinfo.nextframe; 372: self->monsterinfo.nextframe = 0; 373: } 374: else 375: { 376: if (self->s.frame == move->lastframe) 377: { 378: if (move->endfunc) 379: { 380: move->endfunc (self); 381: 382: // regrab move, endfunc is very likely to change it 383: move = self->monsterinfo.currentmove; 384: 385: // check for death 386: if (self->svflags & SVF_DEADMONSTER) 387: return; 388: } 389: } 390: 391: if (self->s.frame < move->firstframe || self->s.frame > move->lastframe) 392: { 393: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; 394: self->s.frame = move->firstframe; 395: } 396: else 397: { 398: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) 399: { 400: self->s.frame++; 401: if (self->s.frame > move->lastframe) 402: self->s.frame = move->firstframe; 403: } 404: } 405: } 406: 407: index = self->s.frame - move->firstframe; 408: if (move->frame[index].aifunc) 409: if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) 410: move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale); 411: else 412: move->frame[index].aifunc (self, 0); 413: 414: if (move->frame[index].thinkfunc) 415: move->frame[index].thinkfunc (self); 416: } 417: 418: 419: void monster_think (edict_t *self) 420: { 421: M_MoveFrame (self); 422: if (self->linkcount != self->monsterinfo.linkcount) 423: { 424: self->monsterinfo.linkcount = self->linkcount; 425: M_CheckGround (self); 426: } 427: M_CatagorizePosition (self); 428: M_WorldEffects (self); 429: M_SetEffects (self); 430: } 431: 432: 433: /* 434: ================ 435: monster_use 436: 437: Using a monster makes it angry at the current activator 438: ================ 439: */ 440: void monster_use (edict_t *self, edict_t *other, edict_t *activator) 441: { 442: if (self->enemy) 443: return; 444: if (self->health <= 0) 445: return; 446: if (activator->flags & FL_NOTARGET) 447: return; 448: if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY)) 449: return; 450: 451: // delay reaction so if the monster is teleported, its sound is still heard 452: self->enemy = activator; 453: FoundTarget (self); 454: } 455: 456: 457: void monster_start_go (edict_t *self); 458: 459: 460: void monster_triggered_spawn (edict_t *self) 461: { 462: self->s.origin[2] += 1; 463: KillBox (self); 464: 465: self->solid = SOLID_BBOX; 466: self->movetype = MOVETYPE_STEP; 467: self->svflags &= ~SVF_NOCLIENT; 468: self->air_finished = level.time + 12; 469: gi.linkentity (self); 470: 471: monster_start_go (self); 472: 473: if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET)) 474: { 475: FoundTarget (self); 476: } 477: else 478: { 479: self->enemy = NULL; 480: } 481: } 482: 483: void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator) 484: { 485: // we have a one frame delay here so we don't telefrag the guy who activated us 486: self->think = monster_triggered_spawn; 487: self->nextthink = level.time + FRAMETIME; 488: if (activator->client) 489: self->enemy = activator; 490: self->use = monster_use; 491: } 492: 493: void monster_triggered_start (edict_t *self) 494: { 495: self->solid = SOLID_NOT; 496: self->movetype = MOVETYPE_NONE; 497: self->svflags |= SVF_NOCLIENT; 498: self->nextthink = 0; 499: self->use = monster_triggered_spawn_use; 500: } 501: 502: 503: /* 504: ================ 505: monster_death_use 506: 507: When a monster dies, it fires all of its targets with the current 508: enemy as activator. 509: ================ 510: */ 511: void monster_death_use (edict_t *self) 512: { 513: self->flags &= ~(FL_FLY|FL_SWIM); 514: self->monsterinfo.aiflags &= AI_GOOD_GUY; 515: 516: if (self->item) 517: { 518: Drop_Item (self, self->item); 519: self->item = NULL; 520: } 521: 522: if (self->deathtarget) 523: self->target = self->deathtarget; 524: 525: if (!self->target) 526: return; 527: 528: G_UseTargets (self, self->enemy); 529: } 530: 531: 532: //============================================================================ 533: 534: qboolean monster_start (edict_t *self) 535: { 536: if (deathmatch->value) 537: { 538: G_FreeEdict (self); 539: return false; 540: } 541: 542: if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY)) 543: { 544: self->spawnflags &= ~4; 545: self->spawnflags |= 1; 546: // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin)); 547: } 548: 549: if (!(self->monsterinfo.aiflags & AI_GOOD_GUY)) 550: level.total_monsters++; 551: 552: self->nextthink = level.time + FRAMETIME; 553: self->svflags |= SVF_MONSTER; 554: self->s.renderfx |= RF_FRAMELERP; 555: self->takedamage = DAMAGE_AIM; 556: self->air_finished = level.time + 12; 557: self->use = monster_use; 558: self->max_health = self->health; 559: self->clipmask = MASK_MONSTERSOLID; 560: 561: self->s.skinnum = 0; 562: self->deadflag = DEAD_NO; 563: self->svflags &= ~SVF_DEADMONSTER; 564: 565: if (!self->monsterinfo.checkattack) 566: self->monsterinfo.checkattack = M_CheckAttack; 567: VectorCopy (self->s.origin, self->s.old_origin); 568: 569: if (st.item) 570: { 571: self->item = FindItemByClassname (st.item); 572: if (!self->item) 573: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); 574: } 575: 576: // randomize what frame they start on 577: if (self->monsterinfo.currentmove) 578: self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1)); 579: 580: return true; 581: } 582: 583: void monster_start_go (edict_t *self) 584: { 585: vec3_t v; 586: 587: if (self->health <= 0) 588: return; 589: 590: // check for target to combat_point and change to combattarget 591: if (self->target) 592: { 593: qboolean notcombat; 594: qboolean fixup; 595: edict_t *target; 596: 597: target = NULL; 598: notcombat = false; 599: fixup = false; 600: while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL) 601: { 602: if (strcmp(target->classname, "point_combat") == 0) 603: { 604: self->combattarget = self->target; 605: fixup = true; 606: } 607: else 608: { 609: notcombat = true; 610: } 611: } 612: if (notcombat && self->combattarget) 613: gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); 614: if (fixup) 615: self->target = NULL; 616: } 617: 618: // validate combattarget 619: if (self->combattarget) 620: { 621: edict_t *target; 622: 623: target = NULL; 624: while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL) 625: { 626: if (strcmp(target->classname, "point_combat") != 0) 627: { 628: gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", 629: self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], 630: self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], 631: (int)target->s.origin[2]); 632: } 633: } 634: } 635: 636: if (self->target) 637: { 638: self->goalentity = self->movetarget = G_PickTarget(self->target); 639: if (!self->movetarget) 640: { 641: gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); 642: self->target = NULL; 643: self->monsterinfo.pausetime = 100000000; 644: self->monsterinfo.stand (self); 645: } 646: else if (strcmp (self->movetarget->classname, "path_corner") == 0) 647: { 648: VectorSubtract (self->goalentity->s.origin, self->s.origin, v); 649: self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); 650: self->monsterinfo.walk (self); 651: self->target = NULL; 652: } 653: else 654: { 655: self->goalentity = self->movetarget = NULL; 656: self->monsterinfo.pausetime = 100000000; 657: self->monsterinfo.stand (self); 658: } 659: } 660: else 661: { 662: self->monsterinfo.pausetime = 100000000; 663: self->monsterinfo.stand (self); 664: } 665: 666: self->think = monster_think; 667: self->nextthink = level.time + FRAMETIME; 668: } 669: 670: 671: void walkmonster_start_go (edict_t *self) 672: { 673: if (!(self->spawnflags & 2) && level.time < 1) 674: { 675: M_droptofloor (self); 676: 677: if (self->groundentity) 678: if (!M_walkmove (self, 0, 0)) 679: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); 680: } 681: 682: if (!self->yaw_speed) 683: self->yaw_speed = 20; 684: self->viewheight = 25; 685: 686: monster_start_go (self); 687: 688: if (self->spawnflags & 2) 689: monster_triggered_start (self); 690: } 691: 692: void walkmonster_start (edict_t *self) 693: { 694: self->think = walkmonster_start_go; 695: monster_start (self); 696: } 697: 698: 699: void flymonster_start_go (edict_t *self) 700: { 701: if (!M_walkmove (self, 0, 0)) 702: gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin)); 703: 704: if (!self->yaw_speed) 705: self->yaw_speed = 10; 706: self->viewheight = 25; 707: 708: monster_start_go (self); 709: 710: if (self->spawnflags & 2) 711: monster_triggered_start (self); 712: } 713: 714: 715: void flymonster_start (edict_t *self) 716: { 717: self->flags |= FL_FLY; 718: self->think = flymonster_start_go; 719: monster_start (self); 720: } 721: 722: 723: void swimmonster_start_go (edict_t *self) 724: { 725: if (!self->yaw_speed) 726: self->yaw_speed = 10; 727: self->viewheight = 10; 728: 729: monster_start_go (self); 730: 731: if (self->spawnflags & 2) 732: monster_triggered_start (self); 733: } 734: 735: void swimmonster_start (edict_t *self) 736: { 737: self->flags |= FL_SWIM; 738: self->think = swimmonster_start_go; 739: monster_start (self); 740: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.