1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "g_local.h"
30 
31 #define MISSILE_PRESTEP_TIME    50
32 
33 
34 extern void gas_think( gentity_t *gas );
35 extern void gas_touch( gentity_t *gas, gentity_t *other, trace_t *trace );
36 extern void SP_target_smoke( gentity_t *ent );
37 
38 
39 
40 void G_ExplodeMissilePoisonGas( gentity_t *ent );
41 void M_think( gentity_t *ent );
42 
43 
44 // JPW NERVE
45 // think func for below
Shaker_think(gentity_t * ent)46 void Shaker_think( gentity_t *ent ) {
47 	vec3_t vec;      // muzzlebounce, JPW NERVE no longer used
48 	gentity_t   *player;
49 	float len, radius = ent->splashDamage, bounceamt;
50 	int i;
51 	char cmd[64];       //DAJ
52 /* JPW NERVE used for trigger_concussive_dust, currently not working
53 	vec3_t		mins, maxs; // JPW NERVE
54 	static vec3_t	range; // JPW NERVE
55 	int			num,touch[MAX_GENTITIES],scored=0; // JPW NERVE
56 	gentity_t	*hit, *dirtshake; // JPW NERVE
57 */
58 
59 	// NERVE - SMF - we only want to call this once now
60 //	if (level.time > ent->delay)
61 	ent->think = G_FreeEntity;
62 	ent->nextthink = level.time + FRAMETIME;
63 
64 /*
65 // JPW NERVE check if we're close to trigger_concussive_dust fields
66 	range[0] = radius/1.41f; // not exactly right, since we're doing a box trap for a radius, but wtf,
67 	range[1] = radius/1.41f; // this is all eye candy anyway
68 	range[2] = radius/1.41f;
69 
70 	VectorAdd(ent->s.origin,range,maxs);
71 	VectorSubtract(ent->s.origin,range,mins);
72 	num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // get a list of possibles
73 	for ( i=0 ; i<num ; i++ ) {
74 		hit = &g_entities[touch[i]];
75 		if (hit->s.eType & ET_CONCUSSIVE_TRIGGER) { // add a tempent to shake some shit loose
76 			dirtshake = G_Spawn();
77 			dirtshake->nextthink = level.time + radius; // 1000 for aircraft flyby, other term for tumble stagger
78 			VectorAdd(hit->r.maxs,hit->r.mins,vec);
79 			VectorScale(vec,0.5f,vec);
80 			VectorCopy(vec,dirtshake->s.pos.trBase);
81 			VectorCopy(vec,dirtshake->s.origin);
82 			VectorSubtract(vec,ent->s.origin,vec);
83 			dirtshake->nextthink = level.time + 5000;//(radius - VectorLength(vec)); // closer the explosion, the longer the dirtshake
84 			G_Printf("radius=%f dist=%f\n",radius,VectorLength(vec));
85 			dirtshake->think = G_FreeEntity;
86 			dirtshake->s.eType = ET_CONCUSSIVE_TRIGGER + ET_EVENTS;
87 			dirtshake->s.eFlags |= EF_SMOKINGBLACK;
88 			VectorCopy(hit->r.maxs, dirtshake->r.maxs);
89 			VectorCopy(hit->r.mins, dirtshake->r.mins);
90 			dirtshake->s.pos.trType = TR_STATIONARY;
91 			dirtshake->clipmask = 0;
92 			dirtshake->r.svFlags &= ~SVF_NOCLIENT;
93 			SnapVector(dirtshake->r.maxs);
94 			SnapVector(dirtshake->r.mins);
95 			SnapVector(dirtshake->s.pos.trDelta);
96 		}
97 	}
98 */
99 
100 	for ( i = 0; i < level.maxclients; i++ ) {
101 		// skip if not connected
102 		if ( level.clients[i].pers.connected != CON_CONNECTED ) {
103 			continue;
104 		}
105 		// skip if in limbo
106 		if ( level.clients[i].ps.pm_flags & PMF_LIMBO ) {
107 			continue;
108 		}
109 		// skip if not on same team
110 		if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR ) {
111 			continue;
112 		}
113 
114 		// found a live one
115 		player = &g_entities[i];
116 		VectorSubtract( player->r.currentOrigin, ent->s.origin, vec );
117 		len = VectorLength( vec );
118 
119 		if ( len > radius ) { // largest bomb blast = 600
120 			continue;
121 		}
122 
123 		// NERVE - SMF - client side camera shake
124 		//DAJ BUGFIX va() not doing %f's correctly
125 		bounceamt = min( 1.0f, 1.0f - ( len / radius ) );
126 		Com_sprintf( cmd, sizeof(cmd), "shake %.4f", bounceamt );   //DAJ
127 		trap_SendServerCommand( player->s.clientNum, cmd );
128 //DAJ BUGFIX		trap_SendServerCommand( player->s.clientNum, va( "shake %f", &bounceamt));
129 	}
130 }
131 // jpw
132 
133 // JPW NERVE
134 /*
135 =============
136 Ground_Shaker
137 	like concussive_fx but means it
138 =============
139 */
Ground_Shaker(vec3_t origin,float range)140 void Ground_Shaker( vec3_t origin, float range ) {
141 	gentity_t *concussive;
142 
143 	concussive = G_Spawn();
144 	VectorCopy( origin, concussive->s.origin );
145 	concussive->think = Shaker_think;
146 	concussive->nextthink = level.time + FRAMETIME;
147 	concussive->splashDamage = range;
148 	concussive->delay = level.time + 200;       // NERVE - SMF - changed from 1000 to 200
149 	return;
150 }
151 // jpw
152 
153 
154 /*
155 ================
156 G_BounceMissile
157 
158 ================
159 */
G_BounceMissile(gentity_t * ent,trace_t * trace)160 void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
161 	vec3_t velocity;
162 	float dot;
163 	int hitTime;
164 
165 // Arnout: removed this for MP as well (was already gone from SP)
166 /*
167 		// Ridah, if we are a grenade, and we have hit an AI that is waiting to catch us, give them a grenade, and delete ourselves
168 	if ((ent->splashMethodOfDeath == MOD_GRENADE_SPLASH) && (g_entities[trace->entityNum].flags & FL_AI_GRENADE_KICK) &&
169 		(trace->endpos[2] > g_entities[trace->entityNum].r.currentOrigin[2])) {
170 		g_entities[trace->entityNum].grenadeExplodeTime = ent->nextthink;
171 		g_entities[trace->entityNum].flags &= ~FL_AI_GRENADE_KICK;
172 		Add_Ammo( &g_entities[trace->entityNum], WP_GRENADE_LAUNCHER, 1, qfalse );	//----(SA)	modified
173 		G_FreeEntity( ent );
174 		return;
175 	}
176 */
177 	// reflect the velocity on the trace plane
178 	hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
179 	BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
180 	dot = DotProduct( velocity, trace->plane.normal );
181 	VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta );
182 
183 	// RF, record this for mover pushing
184 	if ( trace->plane.normal[2] > 0.2 /*&& VectorLength( ent->s.pos.trDelta ) < 40*/ ) {
185 		ent->s.groundEntityNum = trace->entityNum;
186 	}
187 
188 	if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
189 		if ( ent->s.eFlags & EF_BOUNCE ) {     // both flags marked, do a third type of bounce
190 			VectorScale( ent->s.pos.trDelta, 0.35, ent->s.pos.trDelta );
191 		} else {
192 			VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
193 		}
194 
195 		// check for stop
196 		if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) {
197 //----(SA)	make the world the owner of the dynamite, so the player can shoot it after it stops moving
198 			if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_DYNAMITE2 ) {
199 				ent->r.ownerNum = ENTITYNUM_WORLD;
200 			}
201 //----(SA)	end
202 			G_SetOrigin( ent, trace->endpos );
203 			ent->s.time = level.time / 4;
204 			return;
205 		}
206 	}
207 
208 	SnapVector( ent->s.pos.trDelta );
209 
210 	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
211 	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
212 
213 	SnapVector( ent->s.pos.trBase );
214 	ent->s.pos.trTime = level.time;
215 }
216 
217 /*
218 ================
219 G_MissileImpact
220 	impactDamage is how much damage the impact will do to func_explosives
221 ================
222 */
G_MissileImpact(gentity_t * ent,trace_t * trace,int impactDamage)223 void G_MissileImpact( gentity_t *ent, trace_t *trace, int impactDamage ) {
224 	gentity_t       *other;
225 	qboolean hitClient = qfalse;
226 	vec3_t velocity;
227 
228 	other = &g_entities[trace->entityNum];
229 
230 	// handle func_explosives
231 	if ( other->classname && Q_stricmp( other->classname, "func_explosive" ) == 0 ) {
232 		// the damage is sufficient to "break" the ent (health == 0 is non-breakable)
233 		if ( other->health && impactDamage >= other->health ) {
234 			// check for other->takedamage needs to be inside the health check since it is
235 			// likely that, if successfully destroyed by the missile, in the next runmissile()
236 			// update takedamage would be set to '0' and the func_explosive would not be
237 			// removed yet, causing a bounce.
238 			if ( other->takedamage ) {
239 				BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
240 				G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, impactDamage, 0, ent->methodOfDeath );
241 			}
242 
243 			// its possible of the func_explosive not to die from this and it
244 			// should reflect the missile or explode it not vanish into oblivion
245 			if ( other->health <= 0 ) {
246 				return;
247 			}
248 		}
249 	}
250 
251 	// check for bounce
252 	if ( !other->takedamage &&
253 		 ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
254 		G_BounceMissile( ent, trace );
255 		// JPW NERVE -- spotter White Phosphorous rounds shouldn't bounce noise
256 		if ( !Q_stricmp( ent->classname,"WP" ) ) {
257 			return;
258 		}
259 		// jpw
260 		if ( !Q_stricmp( ent->classname, "flamebarrel" ) ) {
261 			G_AddEvent( ent, EV_FLAMEBARREL_BOUNCE, 0 );
262 		} else {
263 			G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
264 		}
265 		return;
266 	}
267 
268 	if ( other->takedamage && ent->s.density == 1 ) {
269 		G_ExplodeMissilePoisonGas( ent );
270 		return;
271 	}
272 
273 	// impact damage
274 	if ( other->takedamage ) {
275 		if ( ent->damage ) {
276 
277 			if ( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
278 				if ( g_entities[ent->r.ownerNum].client ) {
279 					g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++;
280 				}
281 				hitClient = qtrue;
282 			}
283 			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
284 			if ( VectorLength( velocity ) == 0 ) {
285 				velocity[2] = 1;    // stepped on a grenade
286 			}
287 			G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity,
288 					  ent->s.origin, ent->damage,
289 					  0, ent->methodOfDeath );
290 		} else    // if no damage value, then this is a splash damage grenade only
291 		{
292 			G_BounceMissile( ent, trace );
293 			return;
294 		}
295 	}
296 
297 	// is it cheaper in bandwidth to just remove this ent and create a new
298 	// one, rather than changing the missile into the explosion?
299 
300 	if ( other->takedamage && other->client ) {
301 		G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
302 		ent->s.otherEntityNum = other->s.number;
303 	} else {
304 		// Ridah, try projecting it in the direction it came from, for better decals
305 		vec3_t dir;
306 		BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir );
307 		BG_GetMarkDir( dir, trace->plane.normal, dir );
308 		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
309 	}
310 
311 	ent->freeAfterEvent = qtrue;
312 
313 	// change over to a normal entity right at the point of impact
314 	ent->s.eType = ET_GENERAL;
315 
316 	SnapVectorTowards( trace->endpos, ent->s.pos.trBase );  // save net bandwidth
317 
318 	G_SetOrigin( ent, trace->endpos );
319 
320 	// splash damage (doesn't apply to person directly hit)
321 	if ( ent->splashDamage ) {
322 		if ( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
323 							 other, ent->splashMethodOfDeath ) ) {
324 			if ( !hitClient && g_entities[ent->r.ownerNum].client ) {
325 				g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++;
326 			}
327 		}
328 	}
329 
330 	trap_LinkEntity( ent );
331 }
332 
333 /*
334 ==============
335 Concussive_think
336 ==============
337 */
Concussive_think(gentity_t * ent)338 void Concussive_think( gentity_t *ent ) {
339 	gentity_t *player;
340 	vec3_t dir;
341 	vec3_t kvel;
342 	float grav = 24;
343 	vec3_t vec;
344 	float len;
345 
346 	if ( level.time > ent->delay ) {
347 		ent->think = G_FreeEntity;
348 	}
349 
350 	ent->nextthink = level.time + FRAMETIME;
351 
352 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // JPW NERVE -- in multiplayer this should be handled by ground_shaker
353 		player = AICast_FindEntityForName( "player" );
354 
355 		if ( !player ) {
356 			return;
357 		}
358 
359 		VectorSubtract( player->r.currentOrigin, ent->s.origin, vec );
360 		len = VectorLength( vec );
361 
362 //	G_Printf ("len = %5.3f\n", len);
363 
364 		if ( len > 512 ) {
365 			return;
366 		}
367 
368 		VectorSet( dir, 0, 0, 1 );
369 		VectorScale( dir, grav, kvel );
370 		VectorAdd( player->client->ps.velocity, kvel, player->client->ps.velocity );
371 
372 		if ( !player->client->ps.pm_time ) {
373 			int t;
374 
375 			t = grav * 2;
376 			if ( t < 50 ) {
377 				t = 50;
378 			}
379 			if ( t > 200 ) {
380 				t = 200;
381 			}
382 			player->client->ps.pm_time = t;
383 			player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
384 		}
385 
386 	}
387 }
388 
389 /*
390 ==============
391 Concussive_fx
392 	shake the player
393 	caused by explosives (grenades/dynamite/etc.)
394 ==============
395 */
396 //void Concussive_fx (gentity_t *ent)
Concussive_fx(vec3_t origin)397 void Concussive_fx( vec3_t origin ) {
398 //	gentity_t *tent;
399 //	gentity_t *player;
400 
401 	gentity_t *concussive;
402 
403 	concussive = G_Spawn();
404 //	VectorCopy (ent->s.origin, concussive->s.origin);
405 	VectorCopy( origin, concussive->s.origin );
406 	concussive->think = Concussive_think;
407 	concussive->nextthink = level.time + FRAMETIME;
408 	concussive->delay = level.time + 500;
409 	return;
410 
411 // Grenade and bomb flinching event
412 /*
413 	player = AICast_FindEntityForName( "player" );
414 
415 	if (!player)
416 		return;
417 
418 	if ( trap_InPVS (player->r.currentOrigin, ent->s.origin) )
419 	{
420 		tent = G_TempEntity (ent->s.origin, EV_CONCUSSIVE);
421 		VectorCopy (ent->s.origin, tent->s.origin);
422 		tent->s.density = player->s.number;
423 
424 		// G_Printf ("sending concussive event\n");
425 	}
426 */
427 
428 }
429 
430 
431 
432 /*
433 ==============
434 M_think
435 ==============
436 */
M_think(gentity_t * ent)437 void M_think( gentity_t *ent ) {
438 	gentity_t *tent;
439 
440 	ent->count++;
441 
442 //	if (ent->count == 1)
443 //		Concussive_fx (ent);	//----(SA)	moved to G_ExplodeMissile()
444 
445 	if ( ent->count == ent->health ) {
446 		ent->think = G_FreeEntity;
447 	}
448 
449 	tent = G_TempEntity( ent->s.origin, EV_SMOKE );
450 	VectorCopy( ent->s.origin, tent->s.origin );
451 	if ( ent->s.density == 1 ) {
452 		tent->s.origin[2] += 16;
453 	} else {
454 		// tent->s.origin[2]+=32;
455 		// Note to self Maxx said to lower the spawn loc for the smoke 16 units
456 		tent->s.origin[2] += 16;
457 	}
458 
459 	tent->s.time = 3000;
460 	tent->s.time2 = 100;
461 	tent->s.density = 0;
462 	if ( ent->s.density == 1 ) {
463 		tent->s.angles2[0] = 16;
464 	} else {
465 		// Note to self Maxx changed this to 24
466 		tent->s.angles2[0] = 24;
467 	}
468 	tent->s.angles2[1] = 96;
469 	tent->s.angles2[2] = 50;
470 
471 	ent->nextthink = level.time + FRAMETIME;
472 
473 }
474 
475 /*
476 ================
477 G_ExplodeMissile
478 
479 Explode a missile without an impact
480 ================
481 */
G_ExplodeMissile(gentity_t * ent)482 void G_ExplodeMissile( gentity_t *ent ) {
483 	vec3_t dir;
484 	vec3_t origin;
485 	qboolean small = qfalse;
486 	qboolean zombiespit = qfalse;
487 	int etype;
488 
489 	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
490 	SnapVector( origin );
491 	G_SetOrigin( ent, origin );
492 
493 	// we don't have a valid direction, so just point straight up
494 	dir[0] = dir[1] = 0;
495 	dir[2] = 1;
496 
497 	etype = ent->s.eType;
498 
499 	ent->s.eType = ET_GENERAL;
500 
501 	if ( !Q_stricmp( ent->classname, "props_explosion" ) ) {
502 		G_AddEvent( ent, EV_MISSILE_MISS_SMALL, DirToByte( dir ) );
503 		small = qtrue;
504 	}
505 // JPW NERVE
506 	else if ( !Q_stricmp( ent->classname, "air strike" ) ) {
507 		G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) );
508 		small = qfalse;
509 	}
510 // jpw
511 	else if ( !Q_stricmp( ent->classname, "props_explosion_large" ) ) {
512 		G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) );
513 		small = qfalse;
514 	} else if ( !Q_stricmp( ent->classname, "zombiespit" ) )      {
515 		G_AddEvent( ent, EV_SPIT_MISS, DirToByte( dir ) );
516 		zombiespit = qtrue;
517 	} else if ( !Q_stricmp( ent->classname, "flamebarrel" ) )      {
518 		ent->freeAfterEvent = qtrue;
519 		trap_LinkEntity( ent );
520 		return;
521 	} else {
522 		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
523 	}
524 
525 	ent->freeAfterEvent = qtrue;
526 
527 	// splash damage
528 	if ( ent->splashDamage ) {
529 		if ( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath ) ) {    //----(SA)
530 			if ( g_entities[ent->r.ownerNum].client ) {
531 				g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++;
532 			}
533 		}
534 	}
535 
536 	trap_LinkEntity( ent );
537 
538 	if ( etype == ET_MISSILE ) {
539 		// DHM - Nerve :: ... in single player anyway
540 		if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
541 			if ( ent->s.weapon == WP_VENOM_FULL ) { // no default impact smoke
542 				zombiespit = qtrue;
543 			} else if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_DYNAMITE2 ) {
544 //				// shot heard round the world...
545 				gentity_t *player;
546 				player = AICast_FindEntityForName( "player" );
547 				Concussive_fx( player->r.currentOrigin );
548 			}
549 		}
550 // JPW NERVE -- big nasty dynamite scoring section
551 		else {
552 			if ( g_gametype.integer >= GT_WOLF ) {
553 				if ( ent->s.weapon == WP_DYNAMITE ) { // do some scoring
554 // check if dynamite is in trigger_objective_info field
555 					vec3_t mins, maxs;
556 					//static vec3_t	range = { 18, 18, 18 }; // NOTE can use this to massage throw distance outside trigger field // TTimo unused
557 					int i,num,touch[MAX_GENTITIES];
558 					gentity_t   *hit;
559 
560 					// NERVE - SMF - made this the actual bounding box of dynamite instead of range
561 					VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
562 					VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
563 					num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
564 					VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
565 					VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
566 
567 					for ( i = 0 ; i < num ; i++ ) {
568 						hit = &g_entities[touch[i]];
569 						if ( !hit->target ) {
570 							continue;
571 						}
572 
573 						if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
574 							continue;
575 						}
576 						if ( !strcmp( hit->classname,"trigger_objective_info" ) ) {
577 							if ( !( hit->spawnflags & ( AXIS_OBJECTIVE | ALLIED_OBJECTIVE ) ) ) {
578 								continue;
579 							}
580 
581 							if ( ( ( hit->spawnflags & AXIS_OBJECTIVE ) && ( ent->s.teamNum == TEAM_BLUE ) ) ||
582 								 ( ( hit->spawnflags & ALLIED_OBJECTIVE ) && ( ent->s.teamNum == TEAM_RED ) ) ) {
583 								G_UseTargets( hit,ent );
584 								hit->think = G_FreeEntity;
585 								hit->nextthink = level.time + FRAMETIME;
586 
587 								if ( ent->parent->client ) {
588 									if ( ent->s.teamNum == ent->parent->client->sess.sessionTeam ) { // make sure player hasn't changed teams -- per atvi req
589 										AddScore( ent->parent, hit->accuracy ); // set from map, see g_trigger
590 									}
591 								}
592 							}
593 						}
594 					}
595 				}
596 			}
597 			// give big weapons the shakey shakey
598 			if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_PANZERFAUST || ent->s.weapon == WP_GRENADE_LAUNCHER ||
599 				 ent->s.weapon == WP_GRENADE_PINEAPPLE || ent->s.weapon == WP_ROCKET_LAUNCHER || ent->s.weapon == WP_MORTAR ||
600 				 ent->s.weapon == WP_ARTY ) {
601 				Ground_Shaker( ent->r.currentOrigin, ent->splashDamage * 4 );
602 			}
603 			return;
604 		}
605 // jpw
606 	}
607 
608 
609 	if ( !zombiespit ) {
610 		gentity_t *Msmoke;
611 
612 		Msmoke = G_Spawn();
613 		VectorCopy( ent->r.currentOrigin, Msmoke->s.origin );
614 		if ( small ) {
615 			Msmoke->s.density = 1;
616 		}
617 		Msmoke->think = M_think;
618 		Msmoke->nextthink = level.time + FRAMETIME;
619 
620 		if ( ent->parent && !Q_stricmp( ent->parent->classname, "props_flamebarrel" ) ) {
621 			Msmoke->health = 10;
622 		} else {
623 			Msmoke->health = 5;
624 		}
625 
626 		Concussive_fx( Msmoke->s.origin );
627 	}
628 }
629 
630 /*
631 ================
632 G_MissileDie
633 ================
634 */
G_MissileDie(gentity_t * self,gentity_t * inflictor,gentity_t * attacker,int damage,int mod)635 void G_MissileDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
636 	if ( inflictor == self ) {
637 		return;
638 	}
639 	self->takedamage    = qfalse;
640 	self->think         = G_ExplodeMissile;
641 	self->nextthink     = level.time + 10;
642 }
643 
644 /*
645 ================
646 G_ExplodeMissilePoisonGas
647 
648 Explode a missile without an impact
649 ================
650 */
G_ExplodeMissilePoisonGas(gentity_t * ent)651 void G_ExplodeMissilePoisonGas( gentity_t *ent ) {
652 	vec3_t dir;
653 	vec3_t origin;
654 
655 	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
656 	SnapVector( origin );
657 	G_SetOrigin( ent, origin );
658 
659 	// we don't have a valid direction, so just point straight up
660 	dir[0] = dir[1] = 0;
661 	dir[2] = 1;
662 
663 	ent->freeAfterEvent = qtrue;
664 
665 
666 	{
667 		gentity_t *gas;
668 
669 		gas = G_Spawn();
670 		gas->think = gas_think;
671 		gas->nextthink = level.time + FRAMETIME;
672 		gas->r.contents = CONTENTS_TRIGGER;
673 		gas->touch = gas_touch;
674 		gas->health = 100;
675 		G_SetOrigin( gas, origin );
676 
677 		trap_LinkEntity( gas );
678 	}
679 
680 }
681 
682 /*
683 ================
684 G_RunMissile
685 
686 ================
687 */
G_RunMissile(gentity_t * ent)688 void G_RunMissile( gentity_t *ent ) {
689 	vec3_t origin;
690 	trace_t tr;
691 	int impactDamage;
692 
693 	// Ridah, make AI aware of this danger
694 	// DHM - Nerve :: Only in single player
695 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
696 		AICast_CheckDangerousEntity( ent, DANGER_MISSILE, ent->splashRadius, 0.1, 0.99, qtrue );
697 	}
698 
699 	// get current position
700 	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
701 
702 	if ( ( ent->clipmask & CONTENTS_BODY ) && ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_ARTY
703 												|| ent->s.weapon == WP_GRENADE_LAUNCHER || ent->s.weapon == WP_GRENADE_PINEAPPLE ) ) {
704 
705 		if ( !ent->s.pos.trDelta[0] && !ent->s.pos.trDelta[1] && !ent->s.pos.trDelta[2] ) {
706 			ent->clipmask &= ~CONTENTS_BODY;
707 		}
708 	}
709 
710 	// trace a line from the previous position to the current position,
711 	// ignoring interactions with the missile owner
712 	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
713 				ent->r.ownerNum, ent->clipmask );
714 
715 	VectorCopy( tr.endpos, ent->r.currentOrigin );
716 
717 	if ( tr.startsolid ) {
718 		tr.fraction = 0;
719 	}
720 
721 	trap_LinkEntity( ent );
722 
723 	if ( tr.fraction != 1 ) {
724 		// never explode or bounce on sky
725 		if  (   tr.surfaceFlags & SURF_NOIMPACT ) {
726 			// If grapple, reset owner
727 			if ( ent->parent && ent->parent->client && ent->parent->client->hook == ent ) {
728 				ent->parent->client->hook = NULL;
729 			}
730 			G_FreeEntity( ent );
731 			return;
732 		}
733 
734 		if ( ent->s.weapon == WP_ROCKET_LAUNCHER || ent->s.weapon == WP_PANZERFAUST ) {
735 			impactDamage = 999; // goes through pretty much any func_explosives
736 		} else {
737 			impactDamage = 20;  // "grenade"/"dynamite"		// probably adjust this based on velocity
738 
739 		}
740 		G_MissileImpact( ent, &tr, impactDamage );
741 
742 		if ( ent->s.eType != ET_MISSILE ) {
743 // JPW NERVE
744 			if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
745 				Ground_Shaker( ent->r.currentOrigin, ent->splashDamage * 4 );
746 			}
747 // jpw
748 			return;     // exploded
749 		}
750 	}
751 
752 	// check think function after bouncing
753 	G_RunThink( ent );
754 }
755 
756 /*
757 ================
758 G_PredictBounceMissile
759 
760 ================
761 */
G_PredictBounceMissile(gentity_t * ent,trajectory_t * pos,trace_t * trace,int time)762 void G_PredictBounceMissile( gentity_t *ent, trajectory_t *pos, trace_t *trace, int time ) {
763 	vec3_t velocity, origin;
764 	float dot;
765 	int hitTime;
766 
767 	BG_EvaluateTrajectory( pos, time, origin );
768 
769 	// reflect the velocity on the trace plane
770 	hitTime = time;
771 	BG_EvaluateTrajectoryDelta( pos, hitTime, velocity );
772 	dot = DotProduct( velocity, trace->plane.normal );
773 	VectorMA( velocity, -2 * dot, trace->plane.normal, pos->trDelta );
774 
775 	if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
776 		if ( ent->s.eFlags & EF_BOUNCE ) {     // both flags marked, do a third type of bounce
777 			VectorScale( pos->trDelta, 0.35, pos->trDelta );
778 		} else {
779 			VectorScale( pos->trDelta, 0.65, pos->trDelta );
780 		}
781 
782 		// check for stop
783 		if ( trace->plane.normal[2] > 0.2 && VectorLength( pos->trDelta ) < 40 ) {
784 			VectorCopy( trace->endpos, pos->trBase );
785 			return;
786 		}
787 	}
788 
789 	VectorAdd( origin, trace->plane.normal, pos->trBase );
790 	pos->trTime = time;
791 }
792 
793 /*
794 ================
795 G_PredictMissile
796 
797   selfNum is the character that is checking to see what the missile is going to do
798 
799   returns qfalse if the missile won't explode, otherwise it'll return the time is it expected to explode
800 ================
801 */
G_PredictMissile(gentity_t * ent,int duration,vec3_t endPos,qboolean allowBounce)802 int G_PredictMissile( gentity_t *ent, int duration, vec3_t endPos, qboolean allowBounce ) {
803 	vec3_t origin;
804 	trace_t tr;
805 	int time;
806 	trajectory_t pos;
807 	vec3_t org;
808 	gentity_t backupEnt;
809 
810 	pos = ent->s.pos;
811 	BG_EvaluateTrajectory( &pos, level.time, org );
812 
813 	backupEnt = *ent;
814 
815 	for ( time = level.time + FRAMETIME; time < level.time + duration; time += FRAMETIME ) {
816 
817 		// get current position
818 		BG_EvaluateTrajectory( &pos, time, origin );
819 
820 		// trace a line from the previous position to the current position,
821 		// ignoring interactions with the missile owner
822 		trap_Trace( &tr, org, ent->r.mins, ent->r.maxs, origin,
823 					ent->r.ownerNum, ent->clipmask );
824 
825 		VectorCopy( tr.endpos, org );
826 
827 		if ( tr.startsolid ) {
828 			*ent = backupEnt;
829 			return qfalse;
830 		}
831 
832 		if ( tr.fraction != 1 ) {
833 			// never explode or bounce on sky
834 			if  ( tr.surfaceFlags & SURF_NOIMPACT ) {
835 				*ent = backupEnt;
836 				return qfalse;
837 			}
838 
839 			if ( allowBounce && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
840 				G_PredictBounceMissile( ent, &pos, &tr, time - FRAMETIME + (int)( (float)FRAMETIME * tr.fraction ) );
841 				pos.trTime = time;
842 				continue;
843 			}
844 
845 			// exploded, so drop out of loop
846 			break;
847 		}
848 	}
849 /*
850 	if (!allowBounce && tr.fraction < 1 && tr.entityNum > level.maxclients) {
851 		// go back a bit in time, so we can catch it in the air
852 		time -= 200;
853 		if (time < level.time + FRAMETIME)
854 			time = level.time + FRAMETIME;
855 		BG_EvaluateTrajectory( &pos, time, org );
856 	}
857 */
858 
859 	// get current position
860 	VectorCopy( org, endPos );
861 	// set the entity data back
862 	*ent = backupEnt;
863 	//
864 	if ( allowBounce && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
865 		return ent->nextthink;
866 	} else {    // it will probably explode before it times out
867 		return time;
868 	}
869 }
870 
871 //=============================================================================
872 // DHM - Nerve :: Server side Flamethrower
873 //=============================================================================
874 
875 // copied from cg_flamethrower.c
876 #define FLAME_START_SIZE        1.0
877 #define FLAME_START_MAX_SIZE    100.0   // when the flame is spawned, it should endevour to reach this size
878 #define FLAME_START_SPEED       1200.0  // speed of flame as it leaves the nozzle
879 #define FLAME_MIN_SPEED         60.0
880 
881 // these are calculated (don't change)
882 #define FLAME_LENGTH            ( FLAMETHROWER_RANGE + 50.0 ) // NOTE: only modify the range, since this should always reflect that range
883 
884 #define FLAME_LIFETIME          (int)( ( FLAME_LENGTH / FLAME_START_SPEED ) * 1000 )    // life duration in milliseconds
885 #define FLAME_FRICTION_PER_SEC  ( 2.0f * FLAME_START_SPEED )
886 #define GET_FLAME_SIZE_SPEED( x ) ( ( (float)x / FLAME_LIFETIME ) / 0.3 ) // x is the current sizeMax
887 
888 #define FLAME_THRESHOLD 50
889 
G_FlameDamage(gentity_t * self)890 void G_FlameDamage( gentity_t *self ) {
891 	gentity_t   *body;
892 	int entityList[MAX_GENTITIES];
893 	int i, e, numListedEntities;
894 	float radius, boxradius, dist;
895 	vec3_t mins, maxs, point, v;
896 	trace_t tr;
897 
898 	radius = self->speed;
899 	boxradius = 1.41421356 * radius; // radius * sqrt(2) for bounding box enlargement
900 
901 	for ( i = 0 ; i < 3 ; i++ ) {
902 		mins[i] = self->r.currentOrigin[i] - boxradius;
903 		maxs[i] = self->r.currentOrigin[i] + boxradius;
904 	}
905 
906 	numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
907 
908 	for ( e = 0 ; e < numListedEntities ; e++ ) {
909 		body = &g_entities[entityList[ e ]];
910 
911 		if ( !body->takedamage ) {
912 			continue;
913 		}
914 
915 // JPW NERVE don't catch fire if invulnerable or same team in no FF
916 		if ( body->client ) {
917 			if ( body->client->ps.powerups[PW_INVULNERABLE] >= level.time ) {
918 				body->flameQuota = 0;
919 				body->s.onFireEnd = level.time - 1;
920 				continue;
921 			}
922 			if ( !( g_friendlyFire.integer ) && OnSameTeam( body,self->parent ) ) {
923 				continue;
924 			}
925 		}
926 // jpw
927 
928 // JPW NERVE don't catch fire if under water or invulnerable
929 		if ( body->waterlevel >= 3 ) {
930 			body->flameQuota = 0;
931 			body->s.onFireEnd = level.time - 1;
932 			continue;
933 		}
934 // jpw
935 
936 		if ( !body->r.bmodel ) {
937 			VectorCopy( body->r.currentOrigin, point );
938 			if ( body->client ) {
939 				point[2] += body->client->ps.viewheight;
940 			}
941 			VectorSubtract( point, self->r.currentOrigin, v );
942 		} else {
943 			for ( i = 0 ; i < 3 ; i++ ) {
944 				if ( self->s.origin[i] < body->r.absmin[i] ) {
945 					v[i] = body->r.absmin[i] - self->r.currentOrigin[i];
946 				} else if ( self->r.currentOrigin[i] > body->r.absmax[i] ) {
947 					v[i] = self->r.currentOrigin[i] - body->r.absmax[i];
948 				} else {
949 					v[i] = 0;
950 				}
951 			}
952 		}
953 
954 		dist = VectorLength( v );
955 
956 		// The person who shot the flame only burns when within 1/2 the radius
957 		if ( body->s.number == self->r.ownerNum && dist >= ( radius * 0.5 ) ) {
958 			continue;
959 		}
960 		if ( dist >= radius ) {
961 			continue;
962 		}
963 
964 		// Non-clients that take damage get damaged here
965 		if ( !body->client ) {
966 			if ( body->health > 0 ) {
967 				G_Damage( body, self->parent, self->parent, vec3_origin, self->r.currentOrigin, 2, 0, MOD_FLAMETHROWER );
968 			}
969 			continue;
970 		}
971 
972 		// JPW NERVE -- do a trace to see if there's a wall btwn. body & flame centroid -- prevents damage through walls
973 		trap_Trace( &tr, self->r.currentOrigin, NULL, NULL, point, body->s.number, MASK_SHOT );
974 		if ( tr.fraction < 1.0 ) {
975 			continue;
976 		}
977 		// jpw
978 
979 		// now check the damageQuota to see if we should play a pain animation
980 		// first reduce the current damageQuota with time
981 		if ( body->flameQuotaTime && body->flameQuota > 0 ) {
982 			body->flameQuota -= (int)( ( (float)( level.time - body->flameQuotaTime ) / 1000 ) * 2.5f );
983 			if ( body->flameQuota < 0 ) {
984 				body->flameQuota = 0;
985 			}
986 		}
987 
988 		G_BurnMeGood( self, body );
989 	}
990 }
991 
G_RunFlamechunk(gentity_t * ent)992 void G_RunFlamechunk( gentity_t *ent ) {
993 	vec3_t vel, add;
994 	vec3_t neworg;
995 	trace_t tr;
996 	float speed, dot;
997 
998 	// Adust the current speed of the chunk
999 	if ( level.time - ent->timestamp > 50 ) {
1000 		VectorCopy( ent->s.pos.trDelta, vel );
1001 		speed = VectorNormalize( vel );
1002 		speed -= ( 50.f / 1000.f ) * FLAME_FRICTION_PER_SEC;
1003 
1004 		if ( speed < FLAME_MIN_SPEED ) {
1005 			speed = FLAME_MIN_SPEED;
1006 		}
1007 
1008 		VectorScale( vel, speed, ent->s.pos.trDelta );
1009 	} else {
1010 		speed = FLAME_START_SPEED;
1011 	}
1012 
1013 	// Move the chunk
1014 	VectorScale( ent->s.pos.trDelta, 50.f / 1000.f, add );
1015 	VectorAdd( ent->r.currentOrigin, add, neworg );
1016 
1017 	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, neworg, ent->r.ownerNum, MASK_SHOT | MASK_WATER ); // JPW NERVE
1018 
1019 	if ( tr.startsolid ) {
1020 		VectorCopy( vec3_origin, ent->s.pos.trDelta );
1021 	} else if ( tr.fraction != 1.0f && !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
1022 		VectorCopy( tr.endpos, ent->r.currentOrigin );
1023 
1024 		dot = DotProduct( vel, tr.plane.normal );
1025 		VectorMA( vel, -2 * dot, tr.plane.normal, vel );
1026 		VectorNormalize( vel );
1027 		speed *= 0.5 * ( 0.25 + 0.75 * ( ( dot + 1.0 ) * 0.5 ) );
1028 		VectorScale( vel, speed, ent->s.pos.trDelta );
1029 	} else {
1030 		VectorCopy( neworg, ent->r.currentOrigin );
1031 	}
1032 
1033 	// Do damage to nearby entities, every 100ms
1034 	if ( ent->flameQuotaTime <= level.time ) {
1035 		ent->flameQuotaTime = level.time + 100;
1036 		G_FlameDamage( ent );
1037 	}
1038 
1039 	// Show debugging bbox
1040 	if ( g_debugBullets.integer > 3 ) {
1041 		gentity_t *bboxEnt;
1042 		float size = ent->speed / 2;
1043 		vec3_t b1, b2;
1044 		vec3_t temp;
1045 		VectorSet( temp, -size, -size, -size );
1046 		VectorCopy( ent->r.currentOrigin, b1 );
1047 		VectorCopy( ent->r.currentOrigin, b2 );
1048 		VectorAdd( b1, temp, b1 );
1049 		VectorSet( temp, size, size, size );
1050 		VectorAdd( b2, temp, b2 );
1051 		bboxEnt = G_TempEntity( b1, EV_RAILTRAIL );
1052 		VectorCopy( b2, bboxEnt->s.origin2 );
1053 		bboxEnt->s.dmgFlags = 1;    // ("type")
1054 	}
1055 
1056 	// Adjust the size
1057 	if ( ent->speed < FLAME_START_MAX_SIZE ) {
1058 		ent->speed += 10.f;
1059 
1060 		if ( ent->speed > FLAME_START_MAX_SIZE ) {
1061 			ent->speed = FLAME_START_MAX_SIZE;
1062 		}
1063 	}
1064 
1065 	// Remove after 2 seconds
1066 	if ( level.time - ent->timestamp > ( FLAME_LIFETIME - 150 ) ) { // JPW NERVE increased to 350 from 250 to match visuals better
1067 		G_FreeEntity( ent );
1068 		return;
1069 	}
1070 
1071 	G_RunThink( ent );
1072 }
1073 
1074 /*
1075 =================
1076 fire_flamechunk
1077 =================
1078 */
fire_flamechunk(gentity_t * self,vec3_t start,vec3_t dir)1079 gentity_t *fire_flamechunk( gentity_t *self, vec3_t start, vec3_t dir ) {
1080 	gentity_t   *bolt;
1081 
1082 	// Only spawn every other frame
1083 	if ( self->count2 ) {
1084 		self->count2--;
1085 		return NULL;
1086 	}
1087 
1088 	self->count2 = 1;
1089 	VectorNormalize( dir );
1090 
1091 	bolt = G_Spawn();
1092 	bolt->classname = "flamechunk";
1093 
1094 	bolt->timestamp = level.time;
1095 	bolt->flameQuotaTime = level.time + 50;
1096 	bolt->s.eType = ET_FLAMETHROWER_CHUNK;
1097 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN | SVF_NOCLIENT;
1098 	bolt->s.weapon = self->s.weapon;
1099 	bolt->r.ownerNum = self->s.number;
1100 	bolt->parent = self;
1101 	bolt->methodOfDeath = MOD_FLAMETHROWER;
1102 	bolt->clipmask = MASK_MISSILESHOT;
1103 
1104 	bolt->s.pos.trType = TR_DECCELERATE;
1105 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1106 	bolt->s.pos.trDuration = 800;
1107 
1108 	// 'speed' will be the current size radius of the chunk
1109 	bolt->speed = FLAME_START_SIZE;
1110 	VectorSet( bolt->r.mins, -4, -4, -4 );
1111 	VectorSet( bolt->r.maxs, 4, 4, 4 );
1112 	VectorCopy( start, bolt->s.pos.trBase );
1113 	VectorScale( dir, FLAME_START_SPEED, bolt->s.pos.trDelta );
1114 
1115 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1116 	VectorCopy( start, bolt->r.currentOrigin );
1117 
1118 	return bolt;
1119 }
1120 
1121 //=============================================================================
1122 
1123 //----(SA) removed unused quake3 weapons.
1124 
1125 int G_GetWeaponDamage( int weapon ); // JPW NERVE
1126 
DynaSink(gentity_t * self)1127 void DynaSink( gentity_t *self ) {
1128 
1129 	self->clipmask = 0;
1130 	self->r.contents = 0;
1131 
1132 	if ( self->timestamp < level.time ) {
1133 		self->think = G_FreeEntity;
1134 		self->nextthink = level.time + FRAMETIME;
1135 		return;
1136 	}
1137 
1138 	self->s.pos.trBase[2] -= 0.5f;
1139 	self->nextthink = level.time + 50;
1140 }
1141 /*
1142 =================
1143 fire_grenade
1144 
1145 	NOTE!!!! NOTE!!!!!
1146 
1147 	This accepts a /non-normalized/ direction vector to allow specification
1148 	of how hard it's thrown.  Please scale the vector before calling.
1149 
1150 =================
1151 */
fire_grenade(gentity_t * self,vec3_t start,vec3_t dir,int grenadeWPID)1152 gentity_t *fire_grenade( gentity_t *self, vec3_t start, vec3_t dir, int grenadeWPID ) {
1153 	gentity_t   *bolt;
1154 	qboolean noExplode = qfalse;
1155 
1156 	bolt = G_Spawn();
1157 
1158 	// no self->client for shooter_grenade's
1159 	if ( self->client && self->client->ps.grenadeTimeLeft ) {
1160 		// TTimo
1161 		// was: if( g_gametype.integer < GT_WOLF && grenadeWPID == WP_DYNAMITE || grenadeWPID == WP_DYNAMITE2) {
1162 		// gcc: suggest parentheses around && within ||
1163 		if ( g_gametype.integer < GT_WOLF && ( grenadeWPID == WP_DYNAMITE || grenadeWPID == WP_DYNAMITE2 ) ) {  // remove any fraction of a 5 second 'click'
1164 			self->client->ps.grenadeTimeLeft *= 5;
1165 			self->client->ps.grenadeTimeLeft -= ( self->client->ps.grenadeTimeLeft % 5000 );
1166 			self->client->ps.grenadeTimeLeft += 5000;
1167 			if ( self->client->ps.grenadeTimeLeft < 5000 ) { // allow dropping of dynamite that won't explode (for shooting)
1168 				noExplode = qtrue;
1169 			}
1170 		}
1171 
1172 		if ( !noExplode ) {
1173 			bolt->nextthink = level.time + self->client->ps.grenadeTimeLeft;
1174 		}
1175 	} else {
1176 		bolt->nextthink = level.time + 2500;
1177 	}
1178 
1179 	// TTimo
1180 	// was: if( g_gametype.integer >= GT_WOLF && grenadeWPID == WP_DYNAMITE || grenadeWPID == WP_DYNAMITE2 ) {
1181 	// gcc: suggest parentheses around && within ||
1182 	if ( g_gametype.integer >= GT_WOLF && ( grenadeWPID == WP_DYNAMITE || grenadeWPID == WP_DYNAMITE2 ) ) {
1183 		noExplode = qtrue;
1184 		bolt->nextthink = level.time + 15000;
1185 		bolt->think = DynaSink;
1186 		bolt->timestamp = level.time + 16500;
1187 	}
1188 
1189 	// no self->client for shooter_grenade's
1190 	if ( self->client ) {
1191 		self->client->ps.grenadeTimeLeft = 0;       // reset grenade timer
1192 
1193 	}
1194 	if ( !noExplode ) {
1195 		bolt->think         = G_ExplodeMissile;
1196 	}
1197 
1198 	bolt->s.eType       = ET_MISSILE;
1199 	bolt->r.svFlags     = SVF_USE_CURRENT_ORIGIN | SVF_BROADCAST;
1200 	bolt->s.weapon      = grenadeWPID;
1201 	bolt->r.ownerNum    = self->s.number;
1202 	bolt->parent        = self;
1203 
1204 // JPW NERVE -- commented out bolt->damage and bolt->splashdamage, override with G_GetWeaponDamage()
1205 // so it works with different netgame balance.  didn't uncomment bolt->damage on dynamite 'cause its so *special*
1206 	bolt->damage = G_GetWeaponDamage( grenadeWPID ); // overridden for dynamite
1207 	bolt->splashDamage = G_GetWeaponDamage( grenadeWPID );
1208 // jpw
1209 
1210 	switch ( grenadeWPID ) {
1211 	case WP_GRENADE_LAUNCHER:
1212 		bolt->classname             = "grenade";
1213 //			bolt->damage				= 100;
1214 //			bolt->splashDamage			= 100;
1215 		if ( g_gametype.integer >= GT_WOLF ) {
1216 			bolt->splashRadius          = 300;
1217 		} else {
1218 			bolt->splashRadius          = 150;
1219 		}
1220 		bolt->methodOfDeath         = MOD_GRENADE;
1221 		bolt->splashMethodOfDeath   = MOD_GRENADE_SPLASH;
1222 		bolt->s.eFlags              = EF_BOUNCE_HALF | EF_BOUNCE;
1223 		break;
1224 	case WP_GRENADE_PINEAPPLE:
1225 		bolt->classname             = "grenade";
1226 //			bolt->damage				= 80;
1227 //			bolt->splashDamage			= 80;
1228 		bolt->splashRadius          = 300;
1229 		bolt->methodOfDeath         = MOD_GRENADE;
1230 		bolt->splashMethodOfDeath   = MOD_GRENADE_SPLASH;
1231 		bolt->s.eFlags              = EF_BOUNCE_HALF | EF_BOUNCE;
1232 		break;
1233 // JPW NERVE
1234 	case WP_SMOKE_GRENADE:
1235 		bolt->classname             = "grenade";
1236 		bolt->s.eFlags              = EF_BOUNCE_HALF | EF_BOUNCE;
1237 		break;
1238 // jpw
1239 	case WP_DYNAMITE:
1240 	case WP_DYNAMITE2:
1241 
1242 		bolt->accuracy = 0;     // JPW NERVE sets to score below if dynamite is in trigger_objective_info & it's an objective
1243 		trap_SendServerCommand( self - g_entities, "cp \"Dynamite is set, but NOT armed!\"" );
1244 		// differentiate non-armed dynamite with non-pulsing dlight
1245 		if ( self->client )
1246 			bolt->s.teamNum = self->client->sess.sessionTeam + 4;
1247 		bolt->classname             = "dynamite";
1248 		bolt->damage                = 0;
1249 //			bolt->splashDamage			= 300;
1250 		bolt->splashRadius          = 400;
1251 		bolt->methodOfDeath         = MOD_DYNAMITE;
1252 		bolt->splashMethodOfDeath   = MOD_DYNAMITE_SPLASH;
1253 		bolt->s.eFlags              = ( EF_BOUNCE | EF_BOUNCE_HALF );   // EF_BOUNCE_HEAVY;
1254 
1255 		// dynamite is shootable
1256 		// JPW NERVE only in single player
1257 		if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
1258 			bolt->health                = 5;
1259 			bolt->takedamage            = qtrue;
1260 			bolt->die                   = G_MissileDie;
1261 		} else {
1262 			bolt->health                = 5;
1263 			bolt->takedamage            = qfalse;
1264 		}
1265 		// jpw
1266 
1267 		bolt->r.contents            = CONTENTS_CORPSE;      // (player can walk through)
1268 
1269 		// nope - this causes the dynamite to impact on the players bb when he throws it.
1270 		// will try setting it when it settles
1271 //			bolt->r.ownerNum			= ENTITYNUM_WORLD;	// (SA) make the world the owner of the dynamite, so the player can shoot it without modifying the bullet code to ignore players id for hits
1272 
1273 		// small target cube
1274 		VectorSet( bolt->r.mins, -12, -12, 0 );
1275 		VectorCopy( bolt->r.mins, bolt->r.absmin );
1276 		VectorSet( bolt->r.maxs, 12, 12, 20 );
1277 		VectorCopy( bolt->r.maxs, bolt->r.absmax );
1278 		break;
1279 	}
1280 
1281 // JPW NERVE -- blast radius proportional to damage
1282 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
1283 		bolt->splashRadius = G_GetWeaponDamage( grenadeWPID );
1284 	}
1285 // jpw
1286 
1287 	bolt->clipmask = MASK_MISSILESHOT;
1288 
1289 	bolt->s.pos.trType = TR_GRAVITY;
1290 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1291 	VectorCopy( start, bolt->s.pos.trBase );
1292 	VectorCopy( dir, bolt->s.pos.trDelta );
1293 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1294 
1295 	VectorCopy( start, bolt->r.currentOrigin );
1296 
1297 	return bolt;
1298 }
1299 
1300 //=============================================================================
1301 
1302 
1303 /*
1304 ==============
1305 fire_speargun
1306 ==============
1307 */
1308 #define SPEAR_WATERSPEED    400
1309 #define SPEAR_AIRSPEED      700
1310 
fire_speargun(gentity_t * self,vec3_t start,vec3_t dir)1311 gentity_t *fire_speargun( gentity_t *self, vec3_t start, vec3_t dir ) {
1312 	gentity_t   *bolt;
1313 
1314 	VectorNormalize( dir );
1315 
1316 	bolt = G_Spawn();
1317 	bolt->classname = "spear";
1318 	bolt->nextthink = level.time + 10000;
1319 	bolt->think = G_ExplodeMissile;
1320 	bolt->s.eType = ET_MISSILE;
1321 
1322 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1323 
1324 	bolt->s.weapon = WP_SPEARGUN;
1325 	bolt->r.ownerNum = self->s.number;
1326 	bolt->parent = self;
1327 	bolt->damage = 15;      // (SA) spear damage here
1328 	bolt->splashDamage = 0;
1329 	bolt->methodOfDeath = MOD_SPEARGUN;
1330 	bolt->clipmask = MASK_MISSILESHOT;
1331 
1332 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1333 	VectorCopy( start, bolt->s.pos.trBase );
1334 
1335 	// (SA) Kind of a cheap hack to make the speargun worthless out of the water
1336 	//		This'll probably change to something better
1337 	if ( ( trap_PointContents( start, -1 ) & CONTENTS_WATER ) ) {
1338 		bolt->s.pos.trType = TR_LINEAR;
1339 		VectorScale( dir, SPEAR_WATERSPEED, bolt->s.pos.trDelta );
1340 	} else {
1341 		bolt->s.pos.trType = TR_GRAVITY_LOW;
1342 		VectorScale( dir, SPEAR_AIRSPEED, bolt->s.pos.trDelta );
1343 	}
1344 
1345 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1346 	VectorCopy( start, bolt->r.currentOrigin );
1347 
1348 
1349 	return bolt;
1350 }
1351 
1352 
1353 /*
1354 =================
1355 fire_rocket
1356 =================
1357 */
fire_rocket(gentity_t * self,vec3_t start,vec3_t dir)1358 gentity_t *fire_rocket( gentity_t *self, vec3_t start, vec3_t dir ) {
1359 	gentity_t   *bolt;
1360 
1361 	VectorNormalize( dir );
1362 
1363 	bolt = G_Spawn();
1364 	bolt->classname = "rocket";
1365 	bolt->nextthink = level.time + 20000;   // push it out a little
1366 	bolt->think = G_ExplodeMissile;
1367 	bolt->s.eType = ET_MISSILE;
1368 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN | SVF_BROADCAST;
1369 
1370 	//DHM - Nerve :: Use the correct weapon in multiplayer
1371 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
1372 		bolt->s.weapon = WP_ROCKET_LAUNCHER;
1373 	} else {
1374 		bolt->s.weapon = self->s.weapon;
1375 	}
1376 
1377 	bolt->r.ownerNum = self->s.number;
1378 	bolt->parent = self;
1379 	bolt->damage = G_GetWeaponDamage( WP_ROCKET_LAUNCHER ); // JPW NERVE
1380 	bolt->splashDamage = G_GetWeaponDamage( WP_ROCKET_LAUNCHER ); // JPW NERVE
1381 // JPW NERVE
1382 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
1383 		bolt->splashRadius = G_GetWeaponDamage( WP_ROCKET_LAUNCHER );
1384 	} else {
1385 		bolt->splashRadius = 120;
1386 	}
1387 // jpw
1388 	bolt->methodOfDeath = MOD_ROCKET;
1389 	bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
1390 //	bolt->clipmask = MASK_SHOT;
1391 	bolt->clipmask = MASK_MISSILESHOT;
1392 
1393 	bolt->s.pos.trType = TR_LINEAR;
1394 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1395 	VectorCopy( start, bolt->s.pos.trBase );
1396 // JPW NERVE
1397 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
1398 		VectorScale( dir,2500,bolt->s.pos.trDelta );
1399 	} else {
1400 		VectorScale( dir, 900, bolt->s.pos.trDelta );
1401 	}
1402 // jpw
1403 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1404 	VectorCopy( start, bolt->r.currentOrigin );
1405 
1406 	return bolt;
1407 }
1408 
1409 // Rafael flamebarrel
1410 /*
1411 ======================
1412 fire_flamebarrel
1413 ======================
1414 */
1415 
fire_flamebarrel(gentity_t * self,vec3_t start,vec3_t dir)1416 gentity_t *fire_flamebarrel( gentity_t *self, vec3_t start, vec3_t dir ) {
1417 	gentity_t   *bolt;
1418 
1419 	VectorNormalize( dir );
1420 
1421 	bolt = G_Spawn();
1422 	bolt->classname = "flamebarrel";
1423 	bolt->nextthink = level.time + 3000;
1424 	bolt->think = G_ExplodeMissile;
1425 	bolt->s.eType = ET_FLAMEBARREL;
1426 	bolt->s.eFlags = EF_BOUNCE_HALF;
1427 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1428 	bolt->s.weapon = WP_ROCKET_LAUNCHER;
1429 	bolt->r.ownerNum = self->s.number;
1430 	bolt->parent = self;
1431 	bolt->damage = 100;
1432 	bolt->splashDamage = 20;
1433 	bolt->splashRadius = 60;
1434 	bolt->s.eFlags |= EF_SMOKINGBLACK;
1435 
1436 	bolt->methodOfDeath = MOD_ROCKET;
1437 	bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
1438 
1439 	bolt->clipmask = MASK_MISSILESHOT;
1440 
1441 	bolt->s.pos.trType = TR_GRAVITY;
1442 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1443 	VectorCopy( start, bolt->s.pos.trBase );
1444 	VectorScale( dir, 900 + ( crandom() * 100 ), bolt->s.pos.trDelta );
1445 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1446 	VectorCopy( start, bolt->r.currentOrigin );
1447 
1448 	return bolt;
1449 }
1450 
1451 
1452 // Rafael sniper
1453 /*
1454 =================
1455 fire_lead
1456 =================
1457 */
1458 
fire_lead(gentity_t * self,vec3_t start,vec3_t dir,int damage)1459 void fire_lead( gentity_t *self, vec3_t start, vec3_t dir, int damage ) {
1460 
1461 	trace_t tr;
1462 	vec3_t end;
1463 	gentity_t       *tent;
1464 	gentity_t       *traceEnt;
1465 	vec3_t forward, right, up;
1466 	vec3_t angles;
1467 	float r, u;
1468 	qboolean anti_tank_enable = qfalse;
1469 
1470 	r = crandom() * self->random;
1471 	u = crandom() * self->random;
1472 
1473 	vectoangles( dir, angles );
1474 	AngleVectors( angles, forward, right, up );
1475 
1476 	VectorMA( start, 8192, forward, end );
1477 	VectorMA( end, r, right, end );
1478 	VectorMA( end, u, up, end );
1479 
1480 	trap_Trace( &tr, start, NULL, NULL, end, self->s.number, MASK_SHOT );
1481 	if ( tr.surfaceFlags & SURF_NOIMPACT ) {
1482 		return;
1483 	}
1484 
1485 	traceEnt = &g_entities[ tr.entityNum ];
1486 
1487 	// snap the endpos to integers, but nudged towards the line
1488 	SnapVectorTowards( tr.endpos, start );
1489 
1490 	// send bullet impact
1491 	if ( traceEnt->takedamage && traceEnt->client ) {
1492 		tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
1493 		tent->s.eventParm = traceEnt->s.number;
1494 	} else {
1495 		// Ridah, bullet impact should reflect off surface
1496 		vec3_t reflect;
1497 		float dot;
1498 
1499 		tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );
1500 
1501 		dot = DotProduct( forward, tr.plane.normal );
1502 		VectorMA( forward, -2 * dot, tr.plane.normal, reflect );
1503 		VectorNormalize( reflect );
1504 
1505 		tent->s.eventParm = DirToByte( reflect );
1506 		// done.
1507 	}
1508 	tent->s.otherEntityNum = self->s.number;
1509 
1510 	if ( traceEnt->takedamage ) {
1511 
1512 		if ( self->s.weapon == WP_SNIPER
1513 			 && traceEnt->s.eType == ET_MOVER
1514 			 && traceEnt->aiName[0] ) {
1515 			anti_tank_enable = qtrue;
1516 		}
1517 
1518 		if ( anti_tank_enable ) {
1519 			self->s.weapon = WP_ROCKET_LAUNCHER;
1520 		}
1521 
1522 		G_Damage( traceEnt, self, self, forward, tr.endpos,
1523 				  damage, 0, MOD_MACHINEGUN );
1524 
1525 		if ( anti_tank_enable ) {
1526 			self->s.weapon = WP_SNIPER;
1527 		}
1528 
1529 	}
1530 
1531 }
1532 
1533 
1534 // Rafael sniper
1535 // visible
1536 
1537 /*
1538 ==============
1539 visible
1540 ==============
1541 */
visible(gentity_t * self,gentity_t * other)1542 qboolean visible( gentity_t *self, gentity_t *other ) {
1543 //	vec3_t		spot1;
1544 //	vec3_t		spot2;
1545 	trace_t tr;
1546 	gentity_t   *traceEnt;
1547 
1548 	trap_Trace( &tr, self->r.currentOrigin, NULL, NULL, other->r.currentOrigin, self->s.number, MASK_SHOT );
1549 
1550 	traceEnt = &g_entities[ tr.entityNum ];
1551 
1552 	if ( traceEnt == other ) {
1553 		return qtrue;
1554 	}
1555 
1556 	return qfalse;
1557 
1558 }
1559 
1560 
1561 
1562 /*
1563 ==============
1564 fire_mortar
1565 	dir is a non-normalized direction/power vector
1566 ==============
1567 */
fire_mortar(gentity_t * self,vec3_t start,vec3_t dir)1568 gentity_t *fire_mortar( gentity_t *self, vec3_t start, vec3_t dir ) {
1569 	gentity_t   *bolt;
1570 
1571 //	VectorNormalize (dir);
1572 
1573 	if ( self->spawnflags ) {
1574 		gentity_t   *tent;
1575 		tent = G_TempEntity( self->s.pos.trBase, EV_MORTAREFX );
1576 		tent->s.density = self->spawnflags; // send smoke and muzzle flash flags
1577 		VectorCopy( self->s.pos.trBase, tent->s.origin );
1578 		VectorCopy( self->s.apos.trBase, tent->s.angles );
1579 	}
1580 
1581 	bolt = G_Spawn();
1582 	bolt->classname = "mortar";
1583 	bolt->nextthink = level.time + 20000;   // push it out a little
1584 	bolt->think = G_ExplodeMissile;
1585 	bolt->s.eType = ET_MISSILE;
1586 
1587 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN | SVF_BROADCAST;   // broadcast sound.  not multiplayer friendly, but for mortars it should be okay
1588 	// bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1589 
1590 	bolt->s.weapon = WP_MORTAR;
1591 	bolt->r.ownerNum = self->s.number;
1592 	bolt->parent = self;
1593 	bolt->damage = G_GetWeaponDamage( WP_MORTAR ); // JPW NERVE
1594 	bolt->splashDamage = G_GetWeaponDamage( WP_MORTAR ); // JPW NERVE
1595 	bolt->splashRadius = 120;
1596 	bolt->methodOfDeath = MOD_MORTAR;
1597 	bolt->splashMethodOfDeath = MOD_MORTAR_SPLASH;
1598 	bolt->clipmask = MASK_MISSILESHOT;
1599 
1600 	bolt->s.pos.trType = TR_GRAVITY;
1601 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1602 	VectorCopy( start, bolt->s.pos.trBase );
1603 //	VectorScale( dir, 900, bolt->s.pos.trDelta );
1604 	VectorCopy( dir, bolt->s.pos.trDelta );
1605 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1606 	VectorCopy( start, bolt->r.currentOrigin );
1607 
1608 	return bolt;
1609 }
1610 
1611 
1612 /*
1613 =================
1614 fire_nail
1615 =================
1616 */
1617 #define NAILGUN_SPREAD  1000
1618 
fire_nail(gentity_t * self,vec3_t start,vec3_t forward,vec3_t right,vec3_t up)1619 gentity_t *fire_nail( gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up ) {
1620 	gentity_t   *bolt;
1621 	vec3_t dir;
1622 	vec3_t end;
1623 	float r, u, scale;
1624 
1625 	bolt = G_Spawn();
1626 	bolt->classname = "nail";
1627 	bolt->nextthink = level.time + 10000;
1628 	bolt->think = G_ExplodeMissile;
1629 	bolt->s.eType = ET_MISSILE;
1630 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1631 //	bolt->s.weapon = WP_NAILGUN;
1632 	bolt->s.weapon = WP_VENOM_FULL;
1633 	bolt->r.ownerNum = self->s.number;
1634 	bolt->parent = self;
1635 	bolt->damage = G_GetWeaponDamage( WP_VENOM_FULL );
1636 //	bolt->methodOfDeath = MOD_NAIL;
1637 	bolt->methodOfDeath = MOD_VENOM_FULL;
1638 	bolt->clipmask = MASK_SHOT;
1639 	bolt->target_ent = NULL;
1640 
1641 	bolt->s.pos.trType = TR_LINEAR;
1642 	bolt->s.pos.trTime = level.time;
1643 	VectorCopy( start, bolt->s.pos.trBase );
1644 
1645 	r = random() * M_PI * 2.0f;
1646 	u = sin( r ) * crandom() * NAILGUN_SPREAD * 16;
1647 	r = cos( r ) * crandom() * NAILGUN_SPREAD * 16;
1648 	VectorMA( start, 8192 * 16, forward, end );
1649 	VectorMA( end, r, right, end );
1650 	VectorMA( end, u, up, end );
1651 	VectorSubtract( end, start, dir );
1652 	VectorNormalize( dir );
1653 
1654 // JPW NERVE
1655 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
1656 		scale = 555 + random() * 1800;
1657 	} else {
1658 		scale = 1200 + random() * 2500;
1659 	}
1660 // jpw
1661 	VectorScale( dir, scale, bolt->s.pos.trDelta );
1662 	SnapVector( bolt->s.pos.trDelta );
1663 
1664 	VectorCopy( start, bolt->r.currentOrigin );
1665 
1666 	return bolt;
1667 }
1668 
1669 
1670 /*
1671 =================
1672 fire_prox
1673 =================
1674 */
fire_prox(gentity_t * self,vec3_t start,vec3_t dir)1675 gentity_t *fire_prox( gentity_t *self, vec3_t start, vec3_t dir ) {
1676 	gentity_t   *bolt;
1677 
1678 	VectorNormalize( dir );
1679 
1680 	bolt = G_Spawn();
1681 	bolt->classname = "prox mine";
1682 	bolt->nextthink = level.time + 3000;
1683 	bolt->think = G_ExplodeMissile;
1684 	bolt->s.eType = ET_MISSILE;
1685 	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
1686 //	bolt->s.weapon = WP_PROX_LAUNCHER;
1687 	bolt->s.eFlags = 0;
1688 	bolt->r.ownerNum = self->s.number;
1689 	bolt->parent = self;
1690 	bolt->damage = 0;
1691 	bolt->splashDamage = 100;
1692 	bolt->splashRadius = 150;
1693 //	bolt->methodOfDeath = MOD_PROXIMITY_MINE;
1694 //	bolt->splashMethodOfDeath = MOD_PROXIMITY_MINE;
1695 	bolt->clipmask = MASK_SHOT;
1696 	bolt->target_ent = NULL;
1697 	// count is used to check if the prox mine left the player bbox
1698 	// if count == 1 then the prox mine left the player bbox and can attack to it
1699 	bolt->count = 0;
1700 
1701 	//FIXME: we prolly wanna abuse another field
1702 //	bolt->s.generic1 = self->client->sess.sessionTeam;
1703 
1704 	bolt->s.pos.trType = TR_GRAVITY;
1705 	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
1706 	VectorCopy( start, bolt->s.pos.trBase );
1707 	VectorScale( dir, 700, bolt->s.pos.trDelta );
1708 	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
1709 
1710 	VectorCopy( start, bolt->r.currentOrigin );
1711 
1712 	return bolt;
1713 }
1714