|
|
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: ================= 25: check_dodge 26: 27: This is a support routine used when a client is firing 28: a non-instant attack weapon. It checks to see if a 29: monster's dodge function should be called. 30: ================= 31: */ 32: static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) 33: { 34: vec3_t end; 35: vec3_t v; 36: trace_t tr; 37: float eta; 38: 39: // easy mode only ducks one quarter the time 40: if (skill->value == 0) 41: { 42: if (random() > 0.25) 43: return; 44: } 45: VectorMA (start, 8192, dir, end); 46: tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); 47: if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) 48: { 49: VectorSubtract (tr.endpos, start, v); 50: eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; 51: tr.ent->monsterinfo.dodge (tr.ent, self, eta); 52: } 53: } 54: 55: 56: /* 57: ================= 58: fire_hit 59: 60: Used for all impact (hit/punch/slash) attacks 61: ================= 62: */ 63: qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) 64: { 65: trace_t tr; 66: vec3_t forward, right, up; 67: vec3_t v; 68: vec3_t point; 69: float range; 70: vec3_t dir; 71: 72: //see if enemy is in range 73: VectorSubtract (self->enemy->s.origin, self->s.origin, dir); 74: range = VectorLength(dir); 75: if (range > aim[0]) 76: return false; 77: 78: if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) 79: { 80: // the hit is straight on so back the range up to the edge of their bbox 81: range -= self->enemy->maxs[0]; 82: } 83: else 84: { 85: // this is a side hit so adjust the "right" value out to the edge of their bbox 86: if (aim[1] < 0) 87: aim[1] = self->enemy->mins[0]; 88: else 89: aim[1] = self->enemy->maxs[0]; 90: } 91: 92: VectorMA (self->s.origin, range, dir, point); 93: 94: tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT); 95: if (tr.fraction < 1) 96: { 97: if (!tr.ent->takedamage) 98: return false; 99: // if it will hit any client/monster then hit the one we wanted to hit 100: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) 101: tr.ent = self->enemy; 102: } 103: 104: AngleVectors(self->s.angles, forward, right, up); 105: VectorMA (self->s.origin, range, forward, point); 106: VectorMA (point, aim[1], right, point); 107: VectorMA (point, aim[2], up, point); 108: VectorSubtract (point, self->enemy->s.origin, dir); 109: 110: // do the damage 111: T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); 112: 113: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) 114: return false; 115: 116: // do our special form of knockback here 117: VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v); 118: VectorSubtract (v, point, v); 119: VectorNormalize (v); 120: VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); 121: if (self->enemy->velocity[2] > 0) 122: self->enemy->groundentity = NULL; 123: return true; 124: } 125: 126: 127: /* 128: ================= 129: fire_lead 130: 131: This is an internal support routine used for bullet/pellet based weapons. 132: ================= 133: */ 134: static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) 135: { 136: trace_t tr; 137: vec3_t dir; 138: vec3_t forward, right, up; 139: vec3_t end; 140: float r; 141: float u; 142: vec3_t water_start; 143: qboolean water = false; 144: int content_mask = MASK_SHOT | MASK_WATER; 145: 146: tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); 147: if (!(tr.fraction < 1.0)) 148: { 149: vectoangles (aimdir, dir); 150: AngleVectors (dir, forward, right, up); 151: 152: r = crandom()*hspread; 153: u = crandom()*vspread; 154: VectorMA (start, 8192, forward, end); 155: VectorMA (end, r, right, end); 156: VectorMA (end, u, up, end); 157: 158: if (gi.pointcontents (start) & MASK_WATER) 159: { 160: water = true; 161: VectorCopy (start, water_start); 162: content_mask &= ~MASK_WATER; 163: } 164: 165: tr = gi.trace (start, NULL, NULL, end, self, content_mask); 166: 167: // see if we hit water 168: if (tr.contents & MASK_WATER) 169: { 170: int color; 171: 172: water = true; 173: VectorCopy (tr.endpos, water_start); 174: 175: if (!VectorCompare (start, tr.endpos)) 176: { 177: if (tr.contents & CONTENTS_WATER) 178: { 179: if (strcmp(tr.surface->name, "*brwater") == 0) 180: color = SPLASH_BROWN_WATER; 181: else 182: color = SPLASH_BLUE_WATER; 183: } 184: else if (tr.contents & CONTENTS_SLIME) 185: color = SPLASH_SLIME; 186: else if (tr.contents & CONTENTS_LAVA) 187: color = SPLASH_LAVA; 188: else 189: color = SPLASH_UNKNOWN; 190: 191: if (color != SPLASH_UNKNOWN) 192: { 193: gi.WriteByte (svc_temp_entity); 194: gi.WriteByte (TE_SPLASH); 195: gi.WriteByte (8); 196: gi.WritePosition (tr.endpos); 197: gi.WriteDir (tr.plane.normal); 198: gi.WriteByte (color); 199: gi.multicast (tr.endpos, MULTICAST_PVS); 200: } 201: 202: // change bullet's course when it enters water 203: VectorSubtract (end, start, dir); 204: vectoangles (dir, dir); 205: AngleVectors (dir, forward, right, up); 206: r = crandom()*hspread*2; 207: u = crandom()*vspread*2; 208: VectorMA (water_start, 8192, forward, end); 209: VectorMA (end, r, right, end); 210: VectorMA (end, u, up, end); 211: } 212: 213: // re-trace ignoring water this time 214: tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); 215: } 216: } 217: 218: // send gun puff / flash 219: if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) 220: { 221: if (tr.fraction < 1.0) 222: { 223: if (tr.ent->takedamage) 224: { 225: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); 226: } 227: else 228: { 229: if (strncmp (tr.surface->name, "sky", 3) != 0) 230: { 231: gi.WriteByte (svc_temp_entity); 232: gi.WriteByte (te_impact); 233: gi.WritePosition (tr.endpos); 234: gi.WriteDir (tr.plane.normal); 235: gi.multicast (tr.endpos, MULTICAST_PVS); 236: 237: if (self->client) 238: PlayerNoise(self, tr.endpos, PNOISE_IMPACT); 239: } 240: } 241: } 242: } 243: 244: // if went through water, determine where the end and make a bubble trail 245: if (water) 246: { 247: vec3_t pos; 248: 249: VectorSubtract (tr.endpos, water_start, dir); 250: VectorNormalize (dir); 251: VectorMA (tr.endpos, -2, dir, pos); 252: if (gi.pointcontents (pos) & MASK_WATER) 253: VectorCopy (pos, tr.endpos); 254: else 255: tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); 256: 257: VectorAdd (water_start, tr.endpos, pos); 258: VectorScale (pos, 0.5, pos); 259: 260: gi.WriteByte (svc_temp_entity); 261: gi.WriteByte (TE_BUBBLETRAIL); 262: gi.WritePosition (water_start); 263: gi.WritePosition (tr.endpos); 264: gi.multicast (pos, MULTICAST_PVS); 265: } 266: } 267: 268: 269: /* 270: ================= 271: fire_bullet 272: 273: Fires a single round. Used for machinegun and chaingun. Would be fine for 274: pistols, rifles, etc.... 275: ================= 276: */ 277: void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) 278: { 279: fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); 280: } 281: 282: 283: /* 284: ================= 285: fire_shotgun 286: 287: Shoots shotgun pellets. Used by shotgun and super shotgun. 288: ================= 289: */ 290: void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) 291: { 292: int i; 293: 294: for (i = 0; i < count; i++) 295: fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); 296: } 297: 298: 299: /* 300: ================= 301: fire_blaster 302: 303: Fires a single blaster bolt. Used by the blaster and hyper blaster. 304: ================= 305: */ 306: void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 307: { 308: int mod; 309: 310: if (other == self->owner) 311: return; 312: 313: if (surf && (surf->flags & SURF_SKY)) 314: { 315: G_FreeEdict (self); 316: return; 317: } 318: 319: if (self->owner->client) 320: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); 321: 322: if (other->takedamage) 323: { 324: if (self->spawnflags & 1) 325: mod = MOD_HYPERBLASTER; 326: else 327: mod = MOD_BLASTER; 328: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); 329: } 330: else 331: { 332: gi.WriteByte (svc_temp_entity); 333: gi.WriteByte (TE_BLASTER); 334: gi.WritePosition (self->s.origin); 335: if (!plane) 336: gi.WriteDir (vec3_origin); 337: else 338: gi.WriteDir (plane->normal); 339: gi.multicast (self->s.origin, MULTICAST_PVS); 340: } 341: 342: G_FreeEdict (self); 343: } 344: 345: void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) 346: { 347: edict_t *bolt; 348: trace_t tr; 349: 350: VectorNormalize (dir); 351: 352: bolt = G_Spawn(); 353: bolt->svflags = SVF_PROJECTILE; // special net code is used for projectiles 354: VectorCopy (start, bolt->s.origin); 355: VectorCopy (start, bolt->s.old_origin); 356: vectoangles (dir, bolt->s.angles); 357: VectorScale (dir, speed, bolt->velocity); 358: bolt->movetype = MOVETYPE_FLYMISSILE; 359: bolt->clipmask = MASK_SHOT; 360: bolt->solid = SOLID_BBOX; 361: bolt->s.effects |= effect; 362: VectorClear (bolt->mins); 363: VectorClear (bolt->maxs); 364: bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); 365: bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); 366: bolt->owner = self; 367: bolt->touch = blaster_touch; 368: bolt->nextthink = level.time + 2; 369: bolt->think = G_FreeEdict; 370: bolt->dmg = damage; 371: bolt->classname = "bolt"; 372: if (hyper) 373: bolt->spawnflags = 1; 374: gi.linkentity (bolt); 375: 376: if (self->client) 377: check_dodge (self, bolt->s.origin, dir, speed); 378: 379: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); 380: if (tr.fraction < 1.0) 381: { 382: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); 383: bolt->touch (bolt, tr.ent, NULL, NULL); 384: } 385: } 386: 387: 388: /* 389: ================= 390: fire_grenade 391: ================= 392: */ 393: static void Grenade_Explode (edict_t *ent) 394: { 395: vec3_t origin; 396: int mod; 397: 398: if (ent->owner->client) 399: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); 400: 401: //FIXME: if we are onground then raise our Z just a bit since we are a point? 402: if (ent->enemy) 403: { 404: float points; 405: vec3_t v; 406: vec3_t dir; 407: 408: VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); 409: VectorMA (ent->enemy->s.origin, 0.5, v, v); 410: VectorSubtract (ent->s.origin, v, v); 411: points = ent->dmg - 0.5 * VectorLength (v); 412: VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); 413: if (ent->spawnflags & 1) 414: mod = MOD_HANDGRENADE; 415: else 416: mod = MOD_GRENADE; 417: T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); 418: } 419: 420: if (ent->spawnflags & 2) 421: mod = MOD_HELD_GRENADE; 422: else if (ent->spawnflags & 1) 423: mod = MOD_HG_SPLASH; 424: else 425: mod = MOD_G_SPLASH; 426: T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); 427: 428: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); 429: gi.WriteByte (svc_temp_entity); 430: if (ent->waterlevel) 431: { 432: if (ent->groundentity) 433: gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); 434: else 435: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); 436: } 437: else 438: { 439: if (ent->groundentity) 440: gi.WriteByte (TE_GRENADE_EXPLOSION); 441: else 442: gi.WriteByte (TE_ROCKET_EXPLOSION); 443: } 444: gi.WritePosition (origin); 445: gi.multicast (ent->s.origin, MULTICAST_PHS); 446: 447: G_FreeEdict (ent); 448: } 449: 450: static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) 451: { 452: if (other == ent->owner) 453: return; 454: 455: if (surf && (surf->flags & SURF_SKY)) 456: { 457: G_FreeEdict (ent); 458: return; 459: } 460: 461: if (!other->takedamage) 462: { 463: if (ent->spawnflags & 1) 464: { 465: if (random() > 0.5) 466: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); 467: else 468: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); 469: } 470: else 471: { 472: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); 473: } 474: return; 475: } 476: 477: ent->enemy = other; 478: Grenade_Explode (ent); 479: } 480: 481: void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) 482: { 483: edict_t *grenade; 484: vec3_t dir; 485: vec3_t forward, right, up; 486: 487: vectoangles (aimdir, dir); 488: AngleVectors (dir, forward, right, up); 489: 490: grenade = G_Spawn(); 491: VectorCopy (start, grenade->s.origin); 492: VectorScale (aimdir, speed, grenade->velocity); 493: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); 494: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); 495: VectorSet (grenade->avelocity, 300, 300, 300); 496: grenade->movetype = MOVETYPE_BOUNCE; 497: grenade->clipmask = MASK_SHOT; 498: grenade->solid = SOLID_BBOX; 499: grenade->s.effects |= EF_GRENADE; 500: VectorClear (grenade->mins); 501: VectorClear (grenade->maxs); 502: grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); 503: grenade->owner = self; 504: grenade->touch = Grenade_Touch; 505: grenade->nextthink = level.time + timer; 506: grenade->think = Grenade_Explode; 507: grenade->dmg = damage; 508: grenade->dmg_radius = damage_radius; 509: grenade->classname = "grenade"; 510: 511: gi.linkentity (grenade); 512: } 513: 514: void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) 515: { 516: edict_t *grenade; 517: vec3_t dir; 518: vec3_t forward, right, up; 519: 520: vectoangles (aimdir, dir); 521: AngleVectors (dir, forward, right, up); 522: 523: grenade = G_Spawn(); 524: VectorCopy (start, grenade->s.origin); 525: VectorScale (aimdir, speed, grenade->velocity); 526: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); 527: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); 528: VectorSet (grenade->avelocity, 300, 300, 300); 529: grenade->movetype = MOVETYPE_BOUNCE; 530: grenade->clipmask = MASK_SHOT; 531: grenade->solid = SOLID_BBOX; 532: grenade->s.effects |= EF_GRENADE; 533: VectorClear (grenade->mins); 534: VectorClear (grenade->maxs); 535: grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); 536: grenade->owner = self; 537: grenade->touch = Grenade_Touch; 538: grenade->nextthink = level.time + timer; 539: grenade->think = Grenade_Explode; 540: grenade->dmg = damage; 541: grenade->dmg_radius = damage_radius; 542: grenade->classname = "hgrenade"; 543: if (held) 544: grenade->spawnflags = 3; 545: else 546: grenade->spawnflags = 1; 547: grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); 548: 549: if (timer <= 0.0) 550: Grenade_Explode (grenade); 551: else 552: { 553: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); 554: gi.linkentity (grenade); 555: } 556: } 557: 558: 559: /* 560: ================= 561: fire_rocket 562: ================= 563: */ 564: void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) 565: { 566: vec3_t origin; 567: int n; 568: 569: if (other == ent->owner) 570: return; 571: 572: if (surf && (surf->flags & SURF_SKY)) 573: { 574: G_FreeEdict (ent); 575: return; 576: } 577: 578: if (ent->owner->client) 579: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); 580: 581: // calculate position for the explosion entity 582: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); 583: 584: if (other->takedamage) 585: { 586: T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); 587: } 588: else 589: { 590: // don't throw any debris in net games 591: if (!deathmatch->value && !coop->value) 592: { 593: if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) 594: { 595: n = rand() % 5; 596: while(n--) 597: ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); 598: } 599: } 600: } 601: 602: T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); 603: 604: gi.WriteByte (svc_temp_entity); 605: if (ent->waterlevel) 606: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); 607: else 608: gi.WriteByte (TE_ROCKET_EXPLOSION); 609: gi.WritePosition (origin); 610: gi.multicast (ent->s.origin, MULTICAST_PHS); 611: 612: G_FreeEdict (ent); 613: } 614: 615: void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) 616: { 617: edict_t *rocket; 618: 619: rocket = G_Spawn(); 620: VectorCopy (start, rocket->s.origin); 621: VectorCopy (dir, rocket->movedir); 622: vectoangles (dir, rocket->s.angles); 623: VectorScale (dir, speed, rocket->velocity); 624: rocket->movetype = MOVETYPE_FLYMISSILE; 625: rocket->clipmask = MASK_SHOT; 626: rocket->solid = SOLID_BBOX; 627: rocket->s.effects |= EF_ROCKET; 628: VectorClear (rocket->mins); 629: VectorClear (rocket->maxs); 630: rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); 631: rocket->owner = self; 632: rocket->touch = rocket_touch; 633: rocket->nextthink = level.time + 8000/speed; 634: rocket->think = G_FreeEdict; 635: rocket->dmg = damage; 636: rocket->radius_dmg = radius_damage; 637: rocket->dmg_radius = damage_radius; 638: rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); 639: rocket->classname = "rocket"; 640: 641: if (self->client) 642: check_dodge (self, rocket->s.origin, dir, speed); 643: 644: gi.linkentity (rocket); 645: } 646: 647: 648: /* 649: ================= 650: fire_rail 651: ================= 652: */ 653: void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) 654: { 655: vec3_t from; 656: vec3_t end; 657: trace_t tr; 658: edict_t *ignore; 659: int mask; 660: qboolean water; 661: 662: VectorMA (start, 8192, aimdir, end); 663: VectorCopy (start, from); 664: ignore = self; 665: water = false; 666: mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; 667: while (ignore) 668: { 669: tr = gi.trace (from, NULL, NULL, end, ignore, mask); 670: 671: if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) 672: { 673: mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); 674: water = true; 675: } 676: else 677: { 1.1.1.2 ! root 678: //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) ! 679: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || ! 680: (tr.ent->solid == SOLID_BBOX)) 1.1 root 681: ignore = tr.ent; 682: else 683: ignore = NULL; 684: 685: if ((tr.ent != self) && (tr.ent->takedamage)) 686: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); 687: } 688: 689: VectorCopy (tr.endpos, from); 690: } 691: 692: // send gun puff / flash 693: gi.WriteByte (svc_temp_entity); 694: gi.WriteByte (TE_RAILTRAIL); 695: gi.WritePosition (start); 696: gi.WritePosition (tr.endpos); 697: gi.multicast (self->s.origin, MULTICAST_PHS); 698: // gi.multicast (start, MULTICAST_PHS); 699: if (water) 700: { 701: gi.WriteByte (svc_temp_entity); 702: gi.WriteByte (TE_RAILTRAIL); 703: gi.WritePosition (start); 704: gi.WritePosition (tr.endpos); 705: gi.multicast (tr.endpos, MULTICAST_PHS); 706: } 707: 708: if (self->client) 709: PlayerNoise(self, tr.endpos, PNOISE_IMPACT); 710: } 711: 712: 713: /* 714: ================= 715: fire_bfg 716: ================= 717: */ 718: void bfg_explode (edict_t *self) 719: { 720: edict_t *ent; 721: float points; 722: vec3_t v; 723: float dist; 724: 725: if (self->s.frame == 0) 726: { 727: // the BFG effect 728: ent = NULL; 729: while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) 730: { 731: if (!ent->takedamage) 732: continue; 733: if (ent == self->owner) 734: continue; 735: if (!CanDamage (ent, self)) 736: continue; 737: if (!CanDamage (ent, self->owner)) 738: continue; 739: 740: VectorAdd (ent->mins, ent->maxs, v); 741: VectorMA (ent->s.origin, 0.5, v, v); 742: VectorSubtract (self->s.origin, v, v); 743: dist = VectorLength(v); 744: points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); 745: if (ent == self->owner) 746: points = points * 0.5; 747: 748: gi.WriteByte (svc_temp_entity); 749: gi.WriteByte (TE_BFG_EXPLOSION); 750: gi.WritePosition (ent->s.origin); 751: gi.multicast (ent->s.origin, MULTICAST_PHS); 752: T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); 753: } 754: } 755: 756: self->nextthink = level.time + FRAMETIME; 757: self->s.frame++; 758: if (self->s.frame == 5) 759: self->think = G_FreeEdict; 760: } 761: 762: void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 763: { 764: if (other == self->owner) 765: return; 766: 767: if (surf && (surf->flags & SURF_SKY)) 768: { 769: G_FreeEdict (self); 770: return; 771: } 772: 773: if (self->owner->client) 774: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); 775: 776: // core explosion - prevents firing it into the wall/floor 777: if (other->takedamage) 778: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); 779: T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); 780: 781: gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); 782: self->solid = SOLID_NOT; 783: self->touch = NULL; 784: VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); 785: VectorClear (self->velocity); 786: self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); 787: self->s.frame = 0; 788: self->s.sound = 0; 789: self->s.effects &= ~EF_ANIM_ALLFAST; 790: self->think = bfg_explode; 791: self->nextthink = level.time + FRAMETIME; 792: self->enemy = other; 793: 794: gi.WriteByte (svc_temp_entity); 795: gi.WriteByte (TE_BFG_BIGEXPLOSION); 796: gi.WritePosition (self->s.origin); 797: gi.multicast (self->s.origin, MULTICAST_PVS); 798: } 799: 800: 801: void bfg_think (edict_t *self) 802: { 803: edict_t *ent; 804: edict_t *ignore; 805: vec3_t point; 806: vec3_t dir; 807: vec3_t start; 808: vec3_t end; 809: int dmg; 810: trace_t tr; 811: 812: if (deathmatch->value) 813: dmg = 5; 814: else 815: dmg = 10; 816: 817: ent = NULL; 818: while ((ent = findradius(ent, self->s.origin, 256)) != NULL) 819: { 820: if (ent == self) 821: continue; 822: 823: if (ent == self->owner) 824: continue; 825: 826: if (!ent->takedamage) 827: continue; 828: 829: if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) 830: continue; 831: 832: //ZOID 833: //don't target players in CTF 834: if (ctf->value && ent->client && 835: self->owner->client && 836: ent->client->resp.ctf_team == self->owner->client->resp.ctf_team) 837: continue; 838: //ZOID 839: 840: VectorMA (ent->absmin, 0.5, ent->size, point); 841: 842: VectorSubtract (point, self->s.origin, dir); 843: VectorNormalize (dir); 844: 845: ignore = self; 846: VectorCopy (self->s.origin, start); 847: VectorMA (start, 2048, dir, end); 848: while(1) 849: { 850: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); 851: 852: if (!tr.ent) 853: break; 854: 855: // hurt it if we can 856: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) 857: T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); 858: 859: // if we hit something that's not a monster or player we're done 860: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) 861: { 862: gi.WriteByte (svc_temp_entity); 863: gi.WriteByte (TE_LASER_SPARKS); 864: gi.WriteByte (4); 865: gi.WritePosition (tr.endpos); 866: gi.WriteDir (tr.plane.normal); 867: gi.WriteByte (self->s.skinnum); 868: gi.multicast (tr.endpos, MULTICAST_PVS); 869: break; 870: } 871: 872: ignore = tr.ent; 873: VectorCopy (tr.endpos, start); 874: } 875: 876: gi.WriteByte (svc_temp_entity); 877: gi.WriteByte (TE_BFG_LASER); 878: gi.WritePosition (self->s.origin); 879: gi.WritePosition (tr.endpos); 880: gi.multicast (self->s.origin, MULTICAST_PHS); 881: } 882: 883: self->nextthink = level.time + FRAMETIME; 884: } 885: 886: 887: void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) 888: { 889: edict_t *bfg; 890: 891: bfg = G_Spawn(); 892: VectorCopy (start, bfg->s.origin); 893: VectorCopy (dir, bfg->movedir); 894: vectoangles (dir, bfg->s.angles); 895: VectorScale (dir, speed, bfg->velocity); 896: bfg->movetype = MOVETYPE_FLYMISSILE; 897: bfg->clipmask = MASK_SHOT; 898: bfg->solid = SOLID_BBOX; 899: bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; 900: VectorClear (bfg->mins); 901: VectorClear (bfg->maxs); 902: bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); 903: bfg->owner = self; 904: bfg->touch = bfg_touch; 905: bfg->nextthink = level.time + 8000/speed; 906: bfg->think = G_FreeEdict; 907: bfg->radius_dmg = damage; 908: bfg->dmg_radius = damage_radius; 909: bfg->classname = "bfg blast"; 910: bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); 911: 912: bfg->think = bfg_think; 913: bfg->nextthink = level.time + FRAMETIME; 914: bfg->teammaster = bfg; 915: bfg->teamchain = NULL; 916: 917: if (self->client) 918: check_dodge (self, bfg->s.origin, dir, speed); 919: 920: gi.linkentity (bfg); 921: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.