g_missile.c (21620B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // 23 #include "g_local.h" 24 25 #define MISSILE_PRESTEP_TIME 50 26 27 /* 28 ================ 29 G_BounceMissile 30 31 ================ 32 */ 33 void G_BounceMissile( gentity_t *ent, trace_t *trace ) { 34 vec3_t velocity; 35 float dot; 36 int hitTime; 37 38 // reflect the velocity on the trace plane 39 hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; 40 BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); 41 dot = DotProduct( velocity, trace->plane.normal ); 42 VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); 43 44 if ( ent->s.eFlags & EF_BOUNCE_HALF ) { 45 VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta ); 46 // check for stop 47 if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) { 48 G_SetOrigin( ent, trace->endpos ); 49 return; 50 } 51 } 52 53 VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin); 54 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); 55 ent->s.pos.trTime = level.time; 56 } 57 58 59 /* 60 ================ 61 G_ExplodeMissile 62 63 Explode a missile without an impact 64 ================ 65 */ 66 void G_ExplodeMissile( gentity_t *ent ) { 67 vec3_t dir; 68 vec3_t origin; 69 70 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); 71 SnapVector( origin ); 72 G_SetOrigin( ent, origin ); 73 74 // we don't have a valid direction, so just point straight up 75 dir[0] = dir[1] = 0; 76 dir[2] = 1; 77 78 ent->s.eType = ET_GENERAL; 79 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); 80 81 ent->freeAfterEvent = qtrue; 82 83 // splash damage 84 if ( ent->splashDamage ) { 85 if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent 86 , ent->splashMethodOfDeath ) ) { 87 g_entities[ent->r.ownerNum].client->accuracy_hits++; 88 } 89 } 90 91 trap_LinkEntity( ent ); 92 } 93 94 95 #ifdef MISSIONPACK 96 /* 97 ================ 98 ProximityMine_Explode 99 ================ 100 */ 101 static void ProximityMine_Explode( gentity_t *mine ) { 102 G_ExplodeMissile( mine ); 103 // if the prox mine has a trigger free it 104 if (mine->activator) { 105 G_FreeEntity(mine->activator); 106 mine->activator = NULL; 107 } 108 } 109 110 /* 111 ================ 112 ProximityMine_Die 113 ================ 114 */ 115 static void ProximityMine_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) { 116 ent->think = ProximityMine_Explode; 117 ent->nextthink = level.time + 1; 118 } 119 120 /* 121 ================ 122 ProximityMine_Trigger 123 ================ 124 */ 125 void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ) { 126 vec3_t v; 127 gentity_t *mine; 128 129 if( !other->client ) { 130 return; 131 } 132 133 // trigger is a cube, do a distance test now to act as if it's a sphere 134 VectorSubtract( trigger->s.pos.trBase, other->s.pos.trBase, v ); 135 if( VectorLength( v ) > trigger->parent->splashRadius ) { 136 return; 137 } 138 139 140 if ( g_gametype.integer >= GT_TEAM ) { 141 // don't trigger same team mines 142 if (trigger->parent->s.generic1 == other->client->sess.sessionTeam) { 143 return; 144 } 145 } 146 147 // ok, now check for ability to damage so we don't get triggered thru walls, closed doors, etc... 148 if( !CanDamage( other, trigger->s.pos.trBase ) ) { 149 return; 150 } 151 152 // trigger the mine! 153 mine = trigger->parent; 154 mine->s.loopSound = 0; 155 G_AddEvent( mine, EV_PROXIMITY_MINE_TRIGGER, 0 ); 156 mine->nextthink = level.time + 500; 157 158 G_FreeEntity( trigger ); 159 } 160 161 /* 162 ================ 163 ProximityMine_Activate 164 ================ 165 */ 166 static void ProximityMine_Activate( gentity_t *ent ) { 167 gentity_t *trigger; 168 float r; 169 170 ent->think = ProximityMine_Explode; 171 ent->nextthink = level.time + g_proxMineTimeout.integer; 172 173 ent->takedamage = qtrue; 174 ent->health = 1; 175 ent->die = ProximityMine_Die; 176 177 ent->s.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav" ); 178 179 // build the proximity trigger 180 trigger = G_Spawn (); 181 182 trigger->classname = "proxmine_trigger"; 183 184 r = ent->splashRadius; 185 VectorSet( trigger->r.mins, -r, -r, -r ); 186 VectorSet( trigger->r.maxs, r, r, r ); 187 188 G_SetOrigin( trigger, ent->s.pos.trBase ); 189 190 trigger->parent = ent; 191 trigger->r.contents = CONTENTS_TRIGGER; 192 trigger->touch = ProximityMine_Trigger; 193 194 trap_LinkEntity (trigger); 195 196 // set pointer to trigger so the entity can be freed when the mine explodes 197 ent->activator = trigger; 198 } 199 200 /* 201 ================ 202 ProximityMine_ExplodeOnPlayer 203 ================ 204 */ 205 static void ProximityMine_ExplodeOnPlayer( gentity_t *mine ) { 206 gentity_t *player; 207 208 player = mine->enemy; 209 player->client->ps.eFlags &= ~EF_TICKING; 210 211 if ( player->client->invulnerabilityTime > level.time ) { 212 G_Damage( player, mine->parent, mine->parent, vec3_origin, mine->s.origin, 1000, DAMAGE_NO_KNOCKBACK, MOD_JUICED ); 213 player->client->invulnerabilityTime = 0; 214 G_TempEntity( player->client->ps.origin, EV_JUICED ); 215 } 216 else { 217 G_SetOrigin( mine, player->s.pos.trBase ); 218 // make sure the explosion gets to the client 219 mine->r.svFlags &= ~SVF_NOCLIENT; 220 mine->splashMethodOfDeath = MOD_PROXIMITY_MINE; 221 G_ExplodeMissile( mine ); 222 } 223 } 224 225 /* 226 ================ 227 ProximityMine_Player 228 ================ 229 */ 230 static void ProximityMine_Player( gentity_t *mine, gentity_t *player ) { 231 if( mine->s.eFlags & EF_NODRAW ) { 232 return; 233 } 234 235 G_AddEvent( mine, EV_PROXIMITY_MINE_STICK, 0 ); 236 237 if( player->s.eFlags & EF_TICKING ) { 238 player->activator->splashDamage += mine->splashDamage; 239 player->activator->splashRadius *= 1.50; 240 mine->think = G_FreeEntity; 241 mine->nextthink = level.time; 242 return; 243 } 244 245 player->client->ps.eFlags |= EF_TICKING; 246 player->activator = mine; 247 248 mine->s.eFlags |= EF_NODRAW; 249 mine->r.svFlags |= SVF_NOCLIENT; 250 mine->s.pos.trType = TR_LINEAR; 251 VectorClear( mine->s.pos.trDelta ); 252 253 mine->enemy = player; 254 mine->think = ProximityMine_ExplodeOnPlayer; 255 if ( player->client->invulnerabilityTime > level.time ) { 256 mine->nextthink = level.time + 2 * 1000; 257 } 258 else { 259 mine->nextthink = level.time + 10 * 1000; 260 } 261 } 262 #endif 263 264 /* 265 ================ 266 G_MissileImpact 267 ================ 268 */ 269 void G_MissileImpact( gentity_t *ent, trace_t *trace ) { 270 gentity_t *other; 271 qboolean hitClient = qfalse; 272 #ifdef MISSIONPACK 273 vec3_t forward, impactpoint, bouncedir; 274 int eFlags; 275 #endif 276 other = &g_entities[trace->entityNum]; 277 278 // check for bounce 279 if ( !other->takedamage && 280 ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { 281 G_BounceMissile( ent, trace ); 282 G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); 283 return; 284 } 285 286 #ifdef MISSIONPACK 287 if ( other->takedamage ) { 288 if ( ent->s.weapon != WP_PROX_LAUNCHER ) { 289 if ( other->client && other->client->invulnerabilityTime > level.time ) { 290 // 291 VectorCopy( ent->s.pos.trDelta, forward ); 292 VectorNormalize( forward ); 293 if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { 294 VectorCopy( bouncedir, trace->plane.normal ); 295 eFlags = ent->s.eFlags & EF_BOUNCE_HALF; 296 ent->s.eFlags &= ~EF_BOUNCE_HALF; 297 G_BounceMissile( ent, trace ); 298 ent->s.eFlags |= eFlags; 299 } 300 ent->target_ent = other; 301 return; 302 } 303 } 304 } 305 #endif 306 // impact damage 307 if (other->takedamage) { 308 // FIXME: wrong damage direction? 309 if ( ent->damage ) { 310 vec3_t velocity; 311 312 if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { 313 g_entities[ent->r.ownerNum].client->accuracy_hits++; 314 hitClient = qtrue; 315 } 316 BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); 317 if ( VectorLength( velocity ) == 0 ) { 318 velocity[2] = 1; // stepped on a grenade 319 } 320 G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, 321 ent->s.origin, ent->damage, 322 0, ent->methodOfDeath); 323 } 324 } 325 326 #ifdef MISSIONPACK 327 if( ent->s.weapon == WP_PROX_LAUNCHER ) { 328 if( ent->s.pos.trType != TR_GRAVITY ) { 329 return; 330 } 331 332 // if it's a player, stick it on to them (flag them and remove this entity) 333 if( other->s.eType == ET_PLAYER && other->health > 0 ) { 334 ProximityMine_Player( ent, other ); 335 return; 336 } 337 338 SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); 339 G_SetOrigin( ent, trace->endpos ); 340 ent->s.pos.trType = TR_STATIONARY; 341 VectorClear( ent->s.pos.trDelta ); 342 343 G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); 344 345 ent->think = ProximityMine_Activate; 346 ent->nextthink = level.time + 2000; 347 348 vectoangles( trace->plane.normal, ent->s.angles ); 349 ent->s.angles[0] += 90; 350 351 // link the prox mine to the other entity 352 ent->enemy = other; 353 ent->die = ProximityMine_Die; 354 VectorCopy(trace->plane.normal, ent->movedir); 355 VectorSet(ent->r.mins, -4, -4, -4); 356 VectorSet(ent->r.maxs, 4, 4, 4); 357 trap_LinkEntity(ent); 358 359 return; 360 } 361 #endif 362 363 if (!strcmp(ent->classname, "hook")) { 364 gentity_t *nent; 365 vec3_t v; 366 367 nent = G_Spawn(); 368 if ( other->takedamage && other->client ) { 369 370 G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); 371 nent->s.otherEntityNum = other->s.number; 372 373 ent->enemy = other; 374 375 v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; 376 v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; 377 v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; 378 379 SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth 380 } else { 381 VectorCopy(trace->endpos, v); 382 G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); 383 ent->enemy = NULL; 384 } 385 386 SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth 387 388 nent->freeAfterEvent = qtrue; 389 // change over to a normal entity right at the point of impact 390 nent->s.eType = ET_GENERAL; 391 ent->s.eType = ET_GRAPPLE; 392 393 G_SetOrigin( ent, v ); 394 G_SetOrigin( nent, v ); 395 396 ent->think = Weapon_HookThink; 397 ent->nextthink = level.time + FRAMETIME; 398 399 ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; 400 VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); 401 402 trap_LinkEntity( ent ); 403 trap_LinkEntity( nent ); 404 405 return; 406 } 407 408 // is it cheaper in bandwidth to just remove this ent and create a new 409 // one, rather than changing the missile into the explosion? 410 411 if ( other->takedamage && other->client ) { 412 G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); 413 ent->s.otherEntityNum = other->s.number; 414 } else if( trace->surfaceFlags & SURF_METALSTEPS ) { 415 G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); 416 } else { 417 G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); 418 } 419 420 ent->freeAfterEvent = qtrue; 421 422 // change over to a normal entity right at the point of impact 423 ent->s.eType = ET_GENERAL; 424 425 SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth 426 427 G_SetOrigin( ent, trace->endpos ); 428 429 // splash damage (doesn't apply to person directly hit) 430 if ( ent->splashDamage ) { 431 if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, 432 other, ent->splashMethodOfDeath ) ) { 433 if( !hitClient ) { 434 g_entities[ent->r.ownerNum].client->accuracy_hits++; 435 } 436 } 437 } 438 439 trap_LinkEntity( ent ); 440 } 441 442 /* 443 ================ 444 G_RunMissile 445 ================ 446 */ 447 void G_RunMissile( gentity_t *ent ) { 448 vec3_t origin; 449 trace_t tr; 450 int passent; 451 452 // get current position 453 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); 454 455 // if this missile bounced off an invulnerability sphere 456 if ( ent->target_ent ) { 457 passent = ent->target_ent->s.number; 458 } 459 #ifdef MISSIONPACK 460 // prox mines that left the owner bbox will attach to anything, even the owner 461 else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { 462 passent = ENTITYNUM_NONE; 463 } 464 #endif 465 else { 466 // ignore interactions with the missile owner 467 passent = ent->r.ownerNum; 468 } 469 // trace a line from the previous position to the current position 470 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); 471 472 if ( tr.startsolid || tr.allsolid ) { 473 // make sure the tr.entityNum is set to the entity we're stuck in 474 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); 475 tr.fraction = 0; 476 } 477 else { 478 VectorCopy( tr.endpos, ent->r.currentOrigin ); 479 } 480 481 trap_LinkEntity( ent ); 482 483 if ( tr.fraction != 1 ) { 484 // never explode or bounce on sky 485 if ( tr.surfaceFlags & SURF_NOIMPACT ) { 486 // If grapple, reset owner 487 if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { 488 ent->parent->client->hook = NULL; 489 } 490 G_FreeEntity( ent ); 491 return; 492 } 493 G_MissileImpact( ent, &tr ); 494 if ( ent->s.eType != ET_MISSILE ) { 495 return; // exploded 496 } 497 } 498 #ifdef MISSIONPACK 499 // if the prox mine wasn't yet outside the player body 500 if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { 501 // check if the prox mine is outside the owner bbox 502 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); 503 if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { 504 ent->count = 1; 505 } 506 } 507 #endif 508 // check think function after bouncing 509 G_RunThink( ent ); 510 } 511 512 513 //============================================================================= 514 515 /* 516 ================= 517 fire_plasma 518 519 ================= 520 */ 521 gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) { 522 gentity_t *bolt; 523 524 VectorNormalize (dir); 525 526 bolt = G_Spawn(); 527 bolt->classname = "plasma"; 528 bolt->nextthink = level.time + 10000; 529 bolt->think = G_ExplodeMissile; 530 bolt->s.eType = ET_MISSILE; 531 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 532 bolt->s.weapon = WP_PLASMAGUN; 533 bolt->r.ownerNum = self->s.number; 534 bolt->parent = self; 535 bolt->damage = 20; 536 bolt->splashDamage = 15; 537 bolt->splashRadius = 20; 538 bolt->methodOfDeath = MOD_PLASMA; 539 bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH; 540 bolt->clipmask = MASK_SHOT; 541 bolt->target_ent = NULL; 542 543 bolt->s.pos.trType = TR_LINEAR; 544 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 545 VectorCopy( start, bolt->s.pos.trBase ); 546 VectorScale( dir, 2000, bolt->s.pos.trDelta ); 547 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth 548 549 VectorCopy (start, bolt->r.currentOrigin); 550 551 return bolt; 552 } 553 554 //============================================================================= 555 556 557 /* 558 ================= 559 fire_grenade 560 ================= 561 */ 562 gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) { 563 gentity_t *bolt; 564 565 VectorNormalize (dir); 566 567 bolt = G_Spawn(); 568 bolt->classname = "grenade"; 569 bolt->nextthink = level.time + 2500; 570 bolt->think = G_ExplodeMissile; 571 bolt->s.eType = ET_MISSILE; 572 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 573 bolt->s.weapon = WP_GRENADE_LAUNCHER; 574 bolt->s.eFlags = EF_BOUNCE_HALF; 575 bolt->r.ownerNum = self->s.number; 576 bolt->parent = self; 577 bolt->damage = 100; 578 bolt->splashDamage = 100; 579 bolt->splashRadius = 150; 580 bolt->methodOfDeath = MOD_GRENADE; 581 bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH; 582 bolt->clipmask = MASK_SHOT; 583 bolt->target_ent = NULL; 584 585 bolt->s.pos.trType = TR_GRAVITY; 586 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 587 VectorCopy( start, bolt->s.pos.trBase ); 588 VectorScale( dir, 700, bolt->s.pos.trDelta ); 589 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth 590 591 VectorCopy (start, bolt->r.currentOrigin); 592 593 return bolt; 594 } 595 596 //============================================================================= 597 598 599 /* 600 ================= 601 fire_bfg 602 ================= 603 */ 604 gentity_t *fire_bfg (gentity_t *self, vec3_t start, vec3_t dir) { 605 gentity_t *bolt; 606 607 VectorNormalize (dir); 608 609 bolt = G_Spawn(); 610 bolt->classname = "bfg"; 611 bolt->nextthink = level.time + 10000; 612 bolt->think = G_ExplodeMissile; 613 bolt->s.eType = ET_MISSILE; 614 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 615 bolt->s.weapon = WP_BFG; 616 bolt->r.ownerNum = self->s.number; 617 bolt->parent = self; 618 bolt->damage = 100; 619 bolt->splashDamage = 100; 620 bolt->splashRadius = 120; 621 bolt->methodOfDeath = MOD_BFG; 622 bolt->splashMethodOfDeath = MOD_BFG_SPLASH; 623 bolt->clipmask = MASK_SHOT; 624 bolt->target_ent = NULL; 625 626 bolt->s.pos.trType = TR_LINEAR; 627 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 628 VectorCopy( start, bolt->s.pos.trBase ); 629 VectorScale( dir, 2000, bolt->s.pos.trDelta ); 630 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth 631 VectorCopy (start, bolt->r.currentOrigin); 632 633 return bolt; 634 } 635 636 //============================================================================= 637 638 639 /* 640 ================= 641 fire_rocket 642 ================= 643 */ 644 gentity_t *fire_rocket (gentity_t *self, vec3_t start, vec3_t dir) { 645 gentity_t *bolt; 646 647 VectorNormalize (dir); 648 649 bolt = G_Spawn(); 650 bolt->classname = "rocket"; 651 bolt->nextthink = level.time + 15000; 652 bolt->think = G_ExplodeMissile; 653 bolt->s.eType = ET_MISSILE; 654 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 655 bolt->s.weapon = WP_ROCKET_LAUNCHER; 656 bolt->r.ownerNum = self->s.number; 657 bolt->parent = self; 658 bolt->damage = 100; 659 bolt->splashDamage = 100; 660 bolt->splashRadius = 120; 661 bolt->methodOfDeath = MOD_ROCKET; 662 bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; 663 bolt->clipmask = MASK_SHOT; 664 bolt->target_ent = NULL; 665 666 bolt->s.pos.trType = TR_LINEAR; 667 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 668 VectorCopy( start, bolt->s.pos.trBase ); 669 VectorScale( dir, 900, bolt->s.pos.trDelta ); 670 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth 671 VectorCopy (start, bolt->r.currentOrigin); 672 673 return bolt; 674 } 675 676 /* 677 ================= 678 fire_grapple 679 ================= 680 */ 681 gentity_t *fire_grapple (gentity_t *self, vec3_t start, vec3_t dir) { 682 gentity_t *hook; 683 684 VectorNormalize (dir); 685 686 hook = G_Spawn(); 687 hook->classname = "hook"; 688 hook->nextthink = level.time + 10000; 689 hook->think = Weapon_HookFree; 690 hook->s.eType = ET_MISSILE; 691 hook->r.svFlags = SVF_USE_CURRENT_ORIGIN; 692 hook->s.weapon = WP_GRAPPLING_HOOK; 693 hook->r.ownerNum = self->s.number; 694 hook->methodOfDeath = MOD_GRAPPLE; 695 hook->clipmask = MASK_SHOT; 696 hook->parent = self; 697 hook->target_ent = NULL; 698 699 hook->s.pos.trType = TR_LINEAR; 700 hook->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 701 hook->s.otherEntityNum = self->s.number; // use to match beam in client 702 VectorCopy( start, hook->s.pos.trBase ); 703 VectorScale( dir, 800, hook->s.pos.trDelta ); 704 SnapVector( hook->s.pos.trDelta ); // save net bandwidth 705 VectorCopy (start, hook->r.currentOrigin); 706 707 self->client->hook = hook; 708 709 return hook; 710 } 711 712 713 #ifdef MISSIONPACK 714 /* 715 ================= 716 fire_nail 717 ================= 718 */ 719 #define NAILGUN_SPREAD 500 720 721 gentity_t *fire_nail( gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up ) { 722 gentity_t *bolt; 723 vec3_t dir; 724 vec3_t end; 725 float r, u, scale; 726 727 bolt = G_Spawn(); 728 bolt->classname = "nail"; 729 bolt->nextthink = level.time + 10000; 730 bolt->think = G_ExplodeMissile; 731 bolt->s.eType = ET_MISSILE; 732 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 733 bolt->s.weapon = WP_NAILGUN; 734 bolt->r.ownerNum = self->s.number; 735 bolt->parent = self; 736 bolt->damage = 20; 737 bolt->methodOfDeath = MOD_NAIL; 738 bolt->clipmask = MASK_SHOT; 739 bolt->target_ent = NULL; 740 741 bolt->s.pos.trType = TR_LINEAR; 742 bolt->s.pos.trTime = level.time; 743 VectorCopy( start, bolt->s.pos.trBase ); 744 745 r = random() * M_PI * 2.0f; 746 u = sin(r) * crandom() * NAILGUN_SPREAD * 16; 747 r = cos(r) * crandom() * NAILGUN_SPREAD * 16; 748 VectorMA( start, 8192 * 16, forward, end); 749 VectorMA (end, r, right, end); 750 VectorMA (end, u, up, end); 751 VectorSubtract( end, start, dir ); 752 VectorNormalize( dir ); 753 754 scale = 555 + random() * 1800; 755 VectorScale( dir, scale, bolt->s.pos.trDelta ); 756 SnapVector( bolt->s.pos.trDelta ); 757 758 VectorCopy( start, bolt->r.currentOrigin ); 759 760 return bolt; 761 } 762 763 764 /* 765 ================= 766 fire_prox 767 ================= 768 */ 769 gentity_t *fire_prox( gentity_t *self, vec3_t start, vec3_t dir ) { 770 gentity_t *bolt; 771 772 VectorNormalize (dir); 773 774 bolt = G_Spawn(); 775 bolt->classname = "prox mine"; 776 bolt->nextthink = level.time + 3000; 777 bolt->think = G_ExplodeMissile; 778 bolt->s.eType = ET_MISSILE; 779 bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; 780 bolt->s.weapon = WP_PROX_LAUNCHER; 781 bolt->s.eFlags = 0; 782 bolt->r.ownerNum = self->s.number; 783 bolt->parent = self; 784 bolt->damage = 0; 785 bolt->splashDamage = 100; 786 bolt->splashRadius = 150; 787 bolt->methodOfDeath = MOD_PROXIMITY_MINE; 788 bolt->splashMethodOfDeath = MOD_PROXIMITY_MINE; 789 bolt->clipmask = MASK_SHOT; 790 bolt->target_ent = NULL; 791 // count is used to check if the prox mine left the player bbox 792 // if count == 1 then the prox mine left the player bbox and can attack to it 793 bolt->count = 0; 794 795 //FIXME: we prolly wanna abuse another field 796 bolt->s.generic1 = self->client->sess.sessionTeam; 797 798 bolt->s.pos.trType = TR_GRAVITY; 799 bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame 800 VectorCopy( start, bolt->s.pos.trBase ); 801 VectorScale( dir, 700, bolt->s.pos.trDelta ); 802 SnapVector( bolt->s.pos.trDelta ); // save net bandwidth 803 804 VectorCopy (start, bolt->r.currentOrigin); 805 806 return bolt; 807 } 808 #endif