g_weapon.c (23373B)
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 */ 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 { 678 if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) 679 ignore = tr.ent; 680 else 681 ignore = NULL; 682 683 if ((tr.ent != self) && (tr.ent->takedamage)) 684 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); 685 } 686 687 VectorCopy (tr.endpos, from); 688 } 689 690 // send gun puff / flash 691 gi.WriteByte (svc_temp_entity); 692 gi.WriteByte (TE_RAILTRAIL); 693 gi.WritePosition (start); 694 gi.WritePosition (tr.endpos); 695 gi.multicast (self->s.origin, MULTICAST_PHS); 696 // gi.multicast (start, MULTICAST_PHS); 697 if (water) 698 { 699 gi.WriteByte (svc_temp_entity); 700 gi.WriteByte (TE_RAILTRAIL); 701 gi.WritePosition (start); 702 gi.WritePosition (tr.endpos); 703 gi.multicast (tr.endpos, MULTICAST_PHS); 704 } 705 706 if (self->client) 707 PlayerNoise(self, tr.endpos, PNOISE_IMPACT); 708 } 709 710 711 /* 712 ================= 713 fire_bfg 714 ================= 715 */ 716 void bfg_explode (edict_t *self) 717 { 718 edict_t *ent; 719 float points; 720 vec3_t v; 721 float dist; 722 723 if (self->s.frame == 0) 724 { 725 // the BFG effect 726 ent = NULL; 727 while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) 728 { 729 if (!ent->takedamage) 730 continue; 731 if (ent == self->owner) 732 continue; 733 if (!CanDamage (ent, self)) 734 continue; 735 if (!CanDamage (ent, self->owner)) 736 continue; 737 738 VectorAdd (ent->mins, ent->maxs, v); 739 VectorMA (ent->s.origin, 0.5, v, v); 740 VectorSubtract (self->s.origin, v, v); 741 dist = VectorLength(v); 742 points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); 743 if (ent == self->owner) 744 points = points * 0.5; 745 746 gi.WriteByte (svc_temp_entity); 747 gi.WriteByte (TE_BFG_EXPLOSION); 748 gi.WritePosition (ent->s.origin); 749 gi.multicast (ent->s.origin, MULTICAST_PHS); 750 T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); 751 } 752 } 753 754 self->nextthink = level.time + FRAMETIME; 755 self->s.frame++; 756 if (self->s.frame == 5) 757 self->think = G_FreeEdict; 758 } 759 760 void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) 761 { 762 if (other == self->owner) 763 return; 764 765 if (surf && (surf->flags & SURF_SKY)) 766 { 767 G_FreeEdict (self); 768 return; 769 } 770 771 if (self->owner->client) 772 PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); 773 774 // core explosion - prevents firing it into the wall/floor 775 if (other->takedamage) 776 T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); 777 T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); 778 779 gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); 780 self->solid = SOLID_NOT; 781 self->touch = NULL; 782 VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); 783 VectorClear (self->velocity); 784 self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2"); 785 self->s.frame = 0; 786 self->s.sound = 0; 787 self->s.effects &= ~EF_ANIM_ALLFAST; 788 self->think = bfg_explode; 789 self->nextthink = level.time + FRAMETIME; 790 self->enemy = other; 791 792 gi.WriteByte (svc_temp_entity); 793 gi.WriteByte (TE_BFG_BIGEXPLOSION); 794 gi.WritePosition (self->s.origin); 795 gi.multicast (self->s.origin, MULTICAST_PVS); 796 } 797 798 799 void bfg_think (edict_t *self) 800 { 801 edict_t *ent; 802 edict_t *ignore; 803 vec3_t point; 804 vec3_t dir; 805 vec3_t start; 806 vec3_t end; 807 int dmg; 808 trace_t tr; 809 810 if (deathmatch->value) 811 dmg = 5; 812 else 813 dmg = 10; 814 815 ent = NULL; 816 while ((ent = findradius(ent, self->s.origin, 256)) != NULL) 817 { 818 if (ent == self) 819 continue; 820 821 if (ent == self->owner) 822 continue; 823 824 if (!ent->takedamage) 825 continue; 826 827 if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) 828 continue; 829 830 //ZOID 831 //don't target players in CTF 832 if (ctf->value && ent->client && 833 self->owner->client && 834 ent->client->resp.ctf_team == self->owner->client->resp.ctf_team) 835 continue; 836 //ZOID 837 838 VectorMA (ent->absmin, 0.5, ent->size, point); 839 840 VectorSubtract (point, self->s.origin, dir); 841 VectorNormalize (dir); 842 843 ignore = self; 844 VectorCopy (self->s.origin, start); 845 VectorMA (start, 2048, dir, end); 846 while(1) 847 { 848 tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); 849 850 if (!tr.ent) 851 break; 852 853 // hurt it if we can 854 if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) 855 T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); 856 857 // if we hit something that's not a monster or player we're done 858 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) 859 { 860 gi.WriteByte (svc_temp_entity); 861 gi.WriteByte (TE_LASER_SPARKS); 862 gi.WriteByte (4); 863 gi.WritePosition (tr.endpos); 864 gi.WriteDir (tr.plane.normal); 865 gi.WriteByte (self->s.skinnum); 866 gi.multicast (tr.endpos, MULTICAST_PVS); 867 break; 868 } 869 870 ignore = tr.ent; 871 VectorCopy (tr.endpos, start); 872 } 873 874 gi.WriteByte (svc_temp_entity); 875 gi.WriteByte (TE_BFG_LASER); 876 gi.WritePosition (self->s.origin); 877 gi.WritePosition (tr.endpos); 878 gi.multicast (self->s.origin, MULTICAST_PHS); 879 } 880 881 self->nextthink = level.time + FRAMETIME; 882 } 883 884 885 void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) 886 { 887 edict_t *bfg; 888 889 bfg = G_Spawn(); 890 VectorCopy (start, bfg->s.origin); 891 VectorCopy (dir, bfg->movedir); 892 vectoangles (dir, bfg->s.angles); 893 VectorScale (dir, speed, bfg->velocity); 894 bfg->movetype = MOVETYPE_FLYMISSILE; 895 bfg->clipmask = MASK_SHOT; 896 bfg->solid = SOLID_BBOX; 897 bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; 898 VectorClear (bfg->mins); 899 VectorClear (bfg->maxs); 900 bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2"); 901 bfg->owner = self; 902 bfg->touch = bfg_touch; 903 bfg->nextthink = level.time + 8000/speed; 904 bfg->think = G_FreeEdict; 905 bfg->radius_dmg = damage; 906 bfg->dmg_radius = damage_radius; 907 bfg->classname = "bfg blast"; 908 bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); 909 910 bfg->think = bfg_think; 911 bfg->nextthink = level.time + FRAMETIME; 912 bfg->teammaster = bfg; 913 bfg->teamchain = NULL; 914 915 if (self->client) 916 check_dodge (self, bfg->s.origin, dir, speed); 917 918 gi.linkentity (bfg); 919 }