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