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 Quake III Arena source code; 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 */
G_BounceMissile(gentity_t * ent,trace_t * trace)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 */
G_ExplodeMissile(gentity_t * ent)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 */
ProximityMine_Explode(gentity_t * mine)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 */
ProximityMine_Die(gentity_t * ent,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)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 */
ProximityMine_Trigger(gentity_t * trigger,gentity_t * other,trace_t * trace)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 */
ProximityMine_Activate(gentity_t * ent)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 */
ProximityMine_ExplodeOnPlayer(gentity_t * mine)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 */
ProximityMine_Player(gentity_t * mine,gentity_t * player)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 */
G_MissileImpact(gentity_t * ent,trace_t * trace)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 */
G_RunMissile(gentity_t * ent)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 */
fire_plasma(gentity_t * self,vec3_t start,vec3_t dir)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 */
fire_grenade(gentity_t * self,vec3_t start,vec3_t dir)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 */
fire_bfg(gentity_t * self,vec3_t start,vec3_t dir)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 */
fire_rocket(gentity_t * self,vec3_t start,vec3_t dir)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 */
fire_grapple(gentity_t * self,vec3_t start,vec3_t dir)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 
fire_nail(gentity_t * self,vec3_t start,vec3_t forward,vec3_t right,vec3_t up)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 */
fire_prox(gentity_t * self,vec3_t start,vec3_t dir)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
809