Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

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