|
|
1.1.1.3 ! 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(); 1.1.1.2 root 353: bolt->svflags = SVF_DEADMONSTER; 354: // yes, I know it looks weird that projectiles are deadmonsters 355: // what this means is that when prediction is used against the object 356: // (blaster/hyperblaster shots), the player won't be solid clipped against 357: // the object. Right now trying to run into a firing hyperblaster 358: // is very jerky since you are predicted 'against' the shots. 1.1 root 359: VectorCopy (start, bolt->s.origin); 360: VectorCopy (start, bolt->s.old_origin); 361: vectoangles (dir, bolt->s.angles); 362: VectorScale (dir, speed, bolt->velocity); 363: bolt->movetype = MOVETYPE_FLYMISSILE; 364: bolt->clipmask = MASK_SHOT; 365: bolt->solid = SOLID_BBOX; 366: bolt->s.effects |= effect; 367: VectorClear (bolt->mins); 368: VectorClear (bolt->maxs); 369: bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2"); 370: bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); 371: bolt->owner = self; 372: bolt->touch = blaster_touch; 373: bolt->nextthink = level.time + 2; 374: bolt->think = G_FreeEdict; 375: bolt->dmg = damage; 376: bolt->classname = "bolt"; 377: if (hyper) 378: bolt->spawnflags = 1; 379: gi.linkentity (bolt); 380: 381: if (self->client) 382: check_dodge (self, bolt->s.origin, dir, speed); 383: 384: tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); 385: if (tr.fraction < 1.0) 386: { 387: VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); 388: bolt->touch (bolt, tr.ent, NULL, NULL); 389: } 390: } 391: 392: 393: /* 394: ================= 395: fire_grenade 396: ================= 397: */ 398: static void Grenade_Explode (edict_t *ent) 399: { 400: vec3_t origin; 401: int mod; 402: 403: if (ent->owner->client) 404: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); 405: 406: //FIXME: if we are onground then raise our Z just a bit since we are a point? 407: if (ent->enemy) 408: { 409: float points; 410: vec3_t v; 411: vec3_t dir; 412: 413: VectorAdd (ent->enemy->mins, ent->enemy->maxs, v); 414: VectorMA (ent->enemy->s.origin, 0.5, v, v); 415: VectorSubtract (ent->s.origin, v, v); 416: points = ent->dmg - 0.5 * VectorLength (v); 417: VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); 418: if (ent->spawnflags & 1) 419: mod = MOD_HANDGRENADE; 420: else 421: mod = MOD_GRENADE; 422: T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); 423: } 424: 425: if (ent->spawnflags & 2) 426: mod = MOD_HELD_GRENADE; 427: else if (ent->spawnflags & 1) 428: mod = MOD_HG_SPLASH; 429: else 430: mod = MOD_G_SPLASH; 431: T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); 432: 433: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); 434: gi.WriteByte (svc_temp_entity); 435: if (ent->waterlevel) 436: { 437: if (ent->groundentity) 438: gi.WriteByte (TE_GRENADE_EXPLOSION_WATER); 439: else 440: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); 441: } 442: else 443: { 444: if (ent->groundentity) 445: gi.WriteByte (TE_GRENADE_EXPLOSION); 446: else 447: gi.WriteByte (TE_ROCKET_EXPLOSION); 448: } 449: gi.WritePosition (origin); 450: gi.multicast (ent->s.origin, MULTICAST_PHS); 451: 452: G_FreeEdict (ent); 453: } 454: 455: static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) 456: { 457: if (other == ent->owner) 458: return; 459: 460: if (surf && (surf->flags & SURF_SKY)) 461: { 462: G_FreeEdict (ent); 463: return; 464: } 465: 466: if (!other->takedamage) 467: { 468: if (ent->spawnflags & 1) 469: { 470: if (random() > 0.5) 471: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); 472: else 473: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); 474: } 475: else 476: { 477: gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); 478: } 479: return; 480: } 481: 482: ent->enemy = other; 483: Grenade_Explode (ent); 484: } 485: 486: void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) 487: { 488: edict_t *grenade; 489: vec3_t dir; 490: vec3_t forward, right, up; 491: 492: vectoangles (aimdir, dir); 493: AngleVectors (dir, forward, right, up); 494: 495: grenade = G_Spawn(); 496: VectorCopy (start, grenade->s.origin); 497: VectorScale (aimdir, speed, grenade->velocity); 498: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); 499: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); 500: VectorSet (grenade->avelocity, 300, 300, 300); 501: grenade->movetype = MOVETYPE_BOUNCE; 502: grenade->clipmask = MASK_SHOT; 503: grenade->solid = SOLID_BBOX; 504: grenade->s.effects |= EF_GRENADE; 505: VectorClear (grenade->mins); 506: VectorClear (grenade->maxs); 507: grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2"); 508: grenade->owner = self; 509: grenade->touch = Grenade_Touch; 510: grenade->nextthink = level.time + timer; 511: grenade->think = Grenade_Explode; 512: grenade->dmg = damage; 513: grenade->dmg_radius = damage_radius; 514: grenade->classname = "grenade"; 515: 516: gi.linkentity (grenade); 517: } 518: 519: void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) 520: { 521: edict_t *grenade; 522: vec3_t dir; 523: vec3_t forward, right, up; 524: 525: vectoangles (aimdir, dir); 526: AngleVectors (dir, forward, right, up); 527: 528: grenade = G_Spawn(); 529: VectorCopy (start, grenade->s.origin); 530: VectorScale (aimdir, speed, grenade->velocity); 531: VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); 532: VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); 533: VectorSet (grenade->avelocity, 300, 300, 300); 534: grenade->movetype = MOVETYPE_BOUNCE; 535: grenade->clipmask = MASK_SHOT; 536: grenade->solid = SOLID_BBOX; 537: grenade->s.effects |= EF_GRENADE; 538: VectorClear (grenade->mins); 539: VectorClear (grenade->maxs); 540: grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); 541: grenade->owner = self; 542: grenade->touch = Grenade_Touch; 543: grenade->nextthink = level.time + timer; 544: grenade->think = Grenade_Explode; 545: grenade->dmg = damage; 546: grenade->dmg_radius = damage_radius; 547: grenade->classname = "hgrenade"; 548: if (held) 549: grenade->spawnflags = 3; 550: else 551: grenade->spawnflags = 1; 552: grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); 553: 554: if (timer <= 0.0) 555: Grenade_Explode (grenade); 556: else 557: { 558: gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); 559: gi.linkentity (grenade); 560: } 561: } 562: 563: 564: /* 565: ================= 566: fire_rocket 567: ================= 568: */ 569: void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) 570: { 571: vec3_t origin; 572: int n; 573: 574: if (other == ent->owner) 575: return; 576: 577: if (surf && (surf->flags & SURF_SKY)) 578: { 579: G_FreeEdict (ent); 580: return; 581: } 582: 583: if (ent->owner->client) 584: PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); 585: 586: // calculate position for the explosion entity 587: VectorMA (ent->s.origin, -0.02, ent->velocity, origin); 588: 589: if (other->takedamage) 590: { 591: T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); 592: } 593: else 594: { 595: // don't throw any debris in net games 596: if (!deathmatch->value && !coop->value) 597: { 598: if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING))) 599: { 600: n = rand() % 5; 601: while(n--) 602: ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); 603: } 604: } 605: } 606: 607: T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); 608: 609: gi.WriteByte (svc_temp_entity); 610: if (ent->waterlevel) 611: gi.WriteByte (TE_ROCKET_EXPLOSION_WATER); 612: else 613: gi.WriteByte (TE_ROCKET_EXPLOSION); 614: gi.WritePosition (origin); 615: gi.multicast (ent->s.origin, MULTICAST_PHS); 616: 617: G_FreeEdict (ent); 618: } 619: 620: void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) 621: { 622: edict_t *rocket; 623: 624: rocket = G_Spawn(); 625: VectorCopy (start, rocket->s.origin); 626: VectorCopy (dir, rocket->movedir); 627: vectoangles (dir, rocket->s.angles); 628: VectorScale (dir, speed, rocket->velocity); 629: rocket->movetype = MOVETYPE_FLYMISSILE; 630: rocket->clipmask = MASK_SHOT; 631: rocket->solid = SOLID_BBOX; 632: rocket->s.effects |= EF_ROCKET; 633: VectorClear (rocket->mins); 634: VectorClear (rocket->maxs); 635: rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2"); 636: rocket->owner = self; 637: rocket->touch = rocket_touch; 638: rocket->nextthink = level.time + 8000/speed; 639: rocket->think = G_FreeEdict; 640: rocket->dmg = damage; 641: rocket->radius_dmg = radius_damage; 642: rocket->dmg_radius = damage_radius; 643: rocket->s.sound = gi.soundindex ("weapons/rockfly.wav"); 644: rocket->classname = "rocket"; 645: 646: if (self->client) 647: check_dodge (self, rocket->s.origin, dir, speed); 648: 649: gi.linkentity (rocket); 650: } 651: 652: 653: /* 654: ================= 655: fire_rail 656: ================= 657: */ 658: void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) 659: { 660: vec3_t from; 661: vec3_t end; 662: trace_t tr; 663: edict_t *ignore; 664: int mask; 665: qboolean water; 666: 667: VectorMA (start, 8192, aimdir, end); 668: VectorCopy (start, from); 669: ignore = self; 670: water = false; 671: mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA; 672: while (ignore) 673: { 674: tr = gi.trace (from, NULL, NULL, end, ignore, mask); 675: 676: if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA)) 677: { 678: mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA); 679: water = true; 680: } 681: else 682: { 1.1.1.3 ! root 683: //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) ! 684: if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || ! 685: (tr.ent->solid == SOLID_BBOX)) 1.1 root 686: ignore = tr.ent; 687: else 688: ignore = NULL; 689: 690: if ((tr.ent != self) && (tr.ent->takedamage)) 691: T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); 692: } 693: 694: VectorCopy (tr.endpos, from); 695: } 696: 697: // send gun puff / flash 698: gi.WriteByte (svc_temp_entity); 699: gi.WriteByte (TE_RAILTRAIL); 700: gi.WritePosition (start); 701: gi.WritePosition (tr.endpos); 702: gi.multicast (self->s.origin, MULTICAST_PHS); 703: // gi.multicast (start, MULTICAST_PHS); 704: if (water) 705: { 706: gi.WriteByte (svc_temp_entity); 707: gi.WriteByte (TE_RAILTRAIL); 708: gi.WritePosition (start); 709: gi.WritePosition (tr.endpos); 710: gi.multicast (tr.endpos, MULTICAST_PHS); 711: } 712: 713: if (self->client) 714: PlayerNoise(self, tr.endpos, PNOISE_IMPACT); 715: } 716: 717: 718: /* 719: ================= 720: fire_bfg 721: ================= 722: */ 723: void bfg_explode (edict_t *self) 724: { 725: edict_t *ent; 726: float points; 727: vec3_t v; 728: float dist; 729: 730: if (self->s.frame == 0) 731: { 732: // the BFG effect 733: ent = NULL; 734: while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) 735: { 736: if (!ent->takedamage) 737: continue; 738: if (ent == self->owner) 739: continue; 740: if (!CanDamage (ent, self)) 741: continue; 742: if (!CanDamage (ent, self->owner)) 743: continue; 744: 745: VectorAdd (ent->mins, ent->maxs, v); 746: VectorMA (ent->s.origin, 0.5, v, v); 747: VectorSubtract (self->s.origin, v, v); 748: dist = VectorLength(v); 749: points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); 750: if (ent == self->owner) 751: points = points * 0.5; 752: 753: gi.WriteByte (svc_temp_entity); 754: gi.WriteByte (TE_BFG_EXPLOSION); 755: gi.WritePosition (ent->s.origin); 756: gi.multicast (ent->s.origin, MULTICAST_PHS); 757: T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); 758: } 759: } 760: 761: self->nextthink = level.time + FRAMETIME; 762: self->s.frame++; 763: if (self->s.frame == 5) 764: self->think = G_FreeEdict; 765: } 766: 767: void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 768: { 769: if (other == self->owner) 770: return; 771: 772: if (surf && (surf->flags & SURF_SKY)) 773: { 774: G_FreeEdict (self); 775: return; 776: } 777: 778: if (self->owner->client) 779: PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); 780: 781: // core explosion - prevents firing it into the wall/floor 782: if (other->takedamage) 783: T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); 784: T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); 785: 786: gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); 787: self->solid = SOLID_NOT; 788: self->touch = NULL; 789: VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); 790: VectorClear (self->velocity); 791: self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); 792: self->s.frame = 0; 793: self->s.sound = 0; 794: self->s.effects &= ~EF_ANIM_ALLFAST; 795: self->think = bfg_explode; 796: self->nextthink = level.time + FRAMETIME; 797: self->enemy = other; 798: 799: gi.WriteByte (svc_temp_entity); 800: gi.WriteByte (TE_BFG_BIGEXPLOSION); 801: gi.WritePosition (self->s.origin); 802: gi.multicast (self->s.origin, MULTICAST_PVS); 803: } 804: 805: 806: void bfg_think (edict_t *self) 807: { 808: edict_t *ent; 809: edict_t *ignore; 810: vec3_t point; 811: vec3_t dir; 812: vec3_t start; 813: vec3_t end; 814: int dmg; 815: trace_t tr; 816: 817: if (deathmatch->value) 818: dmg = 5; 819: else 820: dmg = 10; 821: 822: ent = NULL; 823: while ((ent = findradius(ent, self->s.origin, 256)) != NULL) 824: { 825: if (ent == self) 826: continue; 827: 828: if (ent == self->owner) 829: continue; 830: 831: if (!ent->takedamage) 832: continue; 833: 834: if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) 835: continue; 836: 837: VectorMA (ent->absmin, 0.5, ent->size, point); 838: 839: VectorSubtract (point, self->s.origin, dir); 840: VectorNormalize (dir); 841: 842: ignore = self; 843: VectorCopy (self->s.origin, start); 844: VectorMA (start, 2048, dir, end); 845: while(1) 846: { 847: tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); 848: 849: if (!tr.ent) 850: break; 851: 852: // hurt it if we can 853: if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) 854: T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); 855: 856: // if we hit something that's not a monster or player we're done 857: if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) 858: { 859: gi.WriteByte (svc_temp_entity); 860: gi.WriteByte (TE_LASER_SPARKS); 861: gi.WriteByte (4); 862: gi.WritePosition (tr.endpos); 863: gi.WriteDir (tr.plane.normal); 864: gi.WriteByte (self->s.skinnum); 865: gi.multicast (tr.endpos, MULTICAST_PVS); 866: break; 867: } 868: 869: ignore = tr.ent; 870: VectorCopy (tr.endpos, start); 871: } 872: 873: gi.WriteByte (svc_temp_entity); 874: gi.WriteByte (TE_BFG_LASER); 875: gi.WritePosition (self->s.origin); 876: gi.WritePosition (tr.endpos); 877: gi.multicast (self->s.origin, MULTICAST_PHS); 878: } 879: 880: self->nextthink = level.time + FRAMETIME; 881: } 882: 883: 884: void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) 885: { 886: edict_t *bfg; 887: 888: bfg = G_Spawn(); 889: VectorCopy (start, bfg->s.origin); 890: VectorCopy (dir, bfg->movedir); 891: vectoangles (dir, bfg->s.angles); 892: VectorScale (dir, speed, bfg->velocity); 893: bfg->movetype = MOVETYPE_FLYMISSILE; 894: bfg->clipmask = MASK_SHOT; 895: bfg->solid = SOLID_BBOX; 896: bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; 897: VectorClear (bfg->mins); 898: VectorClear (bfg->maxs); 899: bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); 900: bfg->owner = self; 901: bfg->touch = bfg_touch; 902: bfg->nextthink = level.time + 8000/speed; 903: bfg->think = G_FreeEdict; 904: bfg->radius_dmg = damage; 905: bfg->dmg_radius = damage_radius; 906: bfg->classname = "bfg blast"; 907: bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); 908: 909: bfg->think = bfg_think; 910: bfg->nextthink = level.time + FRAMETIME; 911: bfg->teammaster = bfg; 912: bfg->teamchain = NULL; 913: 914: if (self->client) 915: check_dodge (self, bfg->s.origin, dir, speed); 916: 917: gi.linkentity (bfg); 918: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.