1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player 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 single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP 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 SP 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 SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP 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 SP 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 /*
30  * name:		cg_weapons.c
31  *
32  * desc:		events and effects dealing with weapons
33  *
34 */
35 
36 #include "cg_local.h"
37 
38 int wolfkickModel;
39 int hWeaponSnd;
40 int hflakWeaponSnd;
41 int notebookModel;
42 int propellerModel;
43 
44 vec3_t ejectBrassCasingOrigin;
45 
46 //----(SA)
47 // forward decs
48 static int getAltWeapon( int weapnum );
49 int getEquivWeapon( int weapnum );
50 int CG_WeaponIndex( int weapnum, int *bank, int *cycle );
51 static qboolean CG_WeaponHasAmmo( int i );
52 static int maxWeapBanks = MAX_WEAP_BANKS, maxWeapsInBank = MAX_WEAPS_IN_BANK; // JPW NERVE
53 
54 int weapBanks[MAX_WEAP_BANKS][MAX_WEAPS_IN_BANK] = {
55 	// bank
56 	{0,                     0,                      0           },  //	0 (empty)
57 
58 	{WP_KNIFE,              0,                      0           },  //	1
59 	{WP_LUGER,              WP_COLT,                0           },  //	2	// WP_AKIMBO
60 	{WP_MP40,               WP_THOMPSON,            WP_STEN     },  //	3
61 	{WP_MAUSER,             WP_GARAND,              0           },  //	4
62 	{WP_FG42,               0,                      0           },  //	5
63 	{WP_GRENADE_LAUNCHER,   WP_GRENADE_PINEAPPLE,   WP_DYNAMITE },  //	6
64 	{WP_PANZERFAUST,        0,                      0           },  //	7
65 	{WP_VENOM,              0,                      0           },  //	8
66 	{WP_FLAMETHROWER,       0,                      0           },  //	9
67 	{WP_TESLA,              0,                      0           }   //	10
68 };
69 
70 // JPW NERVE -- in mutiplayer, characters get knife/special on button 1, pistols on 2, 2-handed on 3
71 int weapBanksMultiPlayer[MAX_WEAP_BANKS_MP][MAX_WEAPS_IN_BANK_MP] = {
72 	{0,                     0,                      0,          0,          0,          0,              0,          0           },  // empty bank '0'
73 	{WP_KNIFE,              0,                      0,          0,          0,          0,              0,          0           },
74 	{WP_LUGER,              WP_COLT,                0,          0,          0,          0,              0,          0           },
75 	{WP_MP40,               WP_THOMPSON,            WP_STEN,    WP_MAUSER,  WP_GARAND,  WP_PANZERFAUST, WP_VENOM,   WP_FLAMETHROWER     },
76 	{WP_GRENADE_LAUNCHER,   WP_GRENADE_PINEAPPLE,   0,          0,          0,          0,              0,          0,          },
77 	{WP_CLASS_SPECIAL,      0,                      0,          0,          0,          0,              0,          0,          },
78 	{WP_DYNAMITE,           0,                      0,          0,          0,          0,              0,          0           }
79 };
80 // jpw
81 
82 //----(SA)	end
83 
84 
85 /*
86 ==============
87 CG_MachineGunEjectBrassNew
88 ==============
89 */
CG_MachineGunEjectBrassNew(centity_t * cent)90 static void CG_MachineGunEjectBrassNew( centity_t *cent ) {
91 	localEntity_t   *le;
92 	refEntity_t     *re;
93 	vec3_t velocity, xvelocity;
94 	float waterScale = 1.0f;
95 	vec3_t v[3];
96 
97 	if ( cg_brassTime.integer <= 0 ) {
98 		return;
99 	}
100 
101 	le = CG_AllocLocalEntity();
102 	re = &le->refEntity;
103 
104 	velocity[0] = 16;
105 	velocity[1] = -50 + 40 * crandom();
106 	velocity[2] = 100 + 50 * crandom();
107 
108 	le->leType = LE_FRAGMENT;
109 	le->startTime = cg.time;
110 	le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
111 
112 	le->pos.trType = TR_GRAVITY;
113 	le->pos.trTime = cg.time - ( rand() & 15 );
114 
115 	AnglesToAxis( cent->lerpAngles, v );
116 
117 	VectorCopy( ejectBrassCasingOrigin, re->origin );
118 
119 	VectorCopy( re->origin, le->pos.trBase );
120 
121 	if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) { //----(SA)	modified since slime is no longer deadly
122 //	if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
123 		waterScale = 0.10;
124 	}
125 
126 	xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
127 	xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
128 	xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
129 	VectorScale( xvelocity, waterScale, le->pos.trDelta );
130 
131 	AxisCopy( axisDefault, re->axis );
132 	re->hModel = cgs.media.smallgunBrassModel;
133 
134 	le->bounceFactor = 0.4 * waterScale;
135 
136 	le->angles.trType = TR_LINEAR;
137 	le->angles.trTime = cg.time;
138 	le->angles.trBase[0] = rand() & 31;
139 	le->angles.trBase[1] = rand() & 31;
140 	le->angles.trBase[2] = rand() & 31;
141 	le->angles.trDelta[0] = 2;
142 	le->angles.trDelta[1] = 1;
143 	le->angles.trDelta[2] = 0;
144 
145 	le->leFlags = LEF_TUMBLE;
146 
147 
148 	{
149 		int contents;
150 		vec3_t end;
151 		VectorCopy( cent->lerpOrigin, end );
152 		end[2] -= 24;
153 		contents = trap_CM_PointContents( end, 0 );
154 		if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
155 			le->leBounceSoundType = LEBS_NONE;
156 		} else {
157 			le->leBounceSoundType = LEBS_BRASS;
158 		}
159 	}
160 
161 	le->leMarkType = LEMT_NONE;
162 }
163 
164 /*
165 ==========================
166 CG_MachineGunEjectBrass
167 ==========================
168 */
169 
CG_MachineGunEjectBrass(centity_t * cent)170 static void CG_MachineGunEjectBrass( centity_t *cent ) {
171 	localEntity_t   *le;
172 	refEntity_t     *re;
173 	vec3_t velocity, xvelocity;
174 	vec3_t offset, xoffset;
175 	float waterScale = 1.0f;
176 	vec3_t v[3];
177 
178 	if ( cg_brassTime.integer <= 0 ) {
179 		return;
180 	}
181 
182 	if ( !( cg.snap->ps.persistant[PERS_HWEAPON_USE] ) && ( cent->currentState.clientNum == cg.snap->ps.clientNum ) ) {
183 		CG_MachineGunEjectBrassNew( cent );
184 		return;
185 	}
186 
187 	le = CG_AllocLocalEntity();
188 	re = &le->refEntity;
189 
190 	// velocity[0] = 0;
191 	velocity[0] = 16; // Maxx Kaufman offset value
192 	velocity[1] = -50 + 40 * crandom();
193 	velocity[2] = 100 + 50 * crandom();
194 
195 	le->leType = LE_FRAGMENT;
196 	le->startTime = cg.time;
197 	le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
198 
199 	le->pos.trType = TR_GRAVITY;
200 	le->pos.trTime = cg.time - ( rand() & 15 );
201 
202 	AnglesToAxis( cent->lerpAngles, v );
203 
204 	if ( cg.snap->ps.persistant[PERS_HWEAPON_USE] ) {
205 		offset[0] = 32;
206 		offset[1] = -4;
207 		offset[2] = 0;
208 	} else if ( cg.predictedPlayerState.weapon == WP_MP40 || cg.predictedPlayerState.weapon == WP_THOMPSON )     {
209 		offset[0] = 20; // Maxx Kaufman offset value
210 		offset[1] = -4;
211 		offset[2] = 24;
212 	} else if ( cg.predictedPlayerState.weapon == WP_VENOM )     {
213 		offset[0] = 12;
214 		offset[1] = -4;
215 		offset[2] = 24;
216 	} else {
217 		VectorClear( offset );
218 	}
219 
220 	xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
221 	xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
222 	xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
223 	VectorAdd( cent->lerpOrigin, xoffset, re->origin );
224 
225 	VectorCopy( re->origin, le->pos.trBase );
226 
227 	if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) { //----(SA)	modified since slime is no longer deadly
228 //	if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
229 		waterScale = 0.10;
230 	}
231 
232 	xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
233 	xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
234 	xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
235 	VectorScale( xvelocity, waterScale, le->pos.trDelta );
236 
237 	AxisCopy( axisDefault, re->axis );
238 	re->hModel = cgs.media.machinegunBrassModel;
239 
240 	le->bounceFactor = 0.4 * waterScale;
241 
242 	le->angles.trType = TR_LINEAR;
243 	le->angles.trTime = cg.time;
244 	le->angles.trBase[0] = rand() & 31;
245 	le->angles.trBase[1] = rand() & 31;
246 	le->angles.trBase[2] = rand() & 31;
247 	le->angles.trDelta[0] = 2;
248 	le->angles.trDelta[1] = 1;
249 	le->angles.trDelta[2] = 0;
250 
251 	le->leFlags = LEF_TUMBLE;
252 
253 	{
254 		int contents;
255 		vec3_t end;
256 		VectorCopy( cent->lerpOrigin, end );
257 		end[2] -= 24;
258 		contents = trap_CM_PointContents( end, 0 );
259 		if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
260 			le->leBounceSoundType = LEBS_NONE;
261 		} else {
262 			le->leBounceSoundType = LEBS_BRASS;
263 		}
264 	}
265 
266 	le->leMarkType = LEMT_NONE;
267 }
268 
269 
270 //----(SA)	added
271 /*
272 ==============
273 CG_PanzerFaustEjectBrass
274 	toss the 'used' panzerfaust casing (unit is one-shot, disposable)
275 ==============
276 */
CG_PanzerFaustEjectBrass(centity_t * cent)277 static void CG_PanzerFaustEjectBrass( centity_t *cent ) {
278 	localEntity_t   *le;
279 	refEntity_t     *re;
280 	vec3_t velocity, xvelocity;
281 	vec3_t offset, xoffset;
282 	float waterScale = 1.0f;
283 	vec3_t v[3];
284 
285 	le = CG_AllocLocalEntity();
286 	re = &le->refEntity;
287 
288 //	velocity[0] = 16;
289 //	velocity[1] = -50 + 40 * crandom();
290 //	velocity[2] = 100 + 50 * crandom();
291 
292 	velocity[0] = 16;
293 	velocity[1] = -200;
294 	velocity[2] = 0;
295 
296 	le->leType = LE_FRAGMENT;
297 	le->startTime = cg.time;
298 //	le->startTime = cg.time + 2000;
299 	le->endTime = le->startTime + ( cg_brassTime.integer * 8 ) + ( cg_brassTime.integer * random() );
300 
301 	le->pos.trType = TR_GRAVITY;
302 	le->pos.trTime = cg.time - ( rand() & 15 );
303 //	le->pos.trTime = cg.time - 2000;
304 
305 	AnglesToAxis( cent->lerpAngles, v );
306 
307 //	offset[0] = 12;
308 //	offset[1] = -4;
309 //	offset[2] = 24;
310 
311 	offset[0] = -24;    // forward
312 	offset[1] = -4; // left
313 	offset[2] = 24; // up
314 
315 	xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
316 	xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
317 	xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
318 	VectorAdd( cent->lerpOrigin, xoffset, re->origin );
319 
320 	VectorCopy( re->origin, le->pos.trBase );
321 
322 	if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) {
323 		waterScale = 0.10;
324 	}
325 
326 	xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
327 	xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
328 	xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
329 	VectorScale( xvelocity, waterScale, le->pos.trDelta );
330 
331 	AxisCopy( axisDefault, re->axis );
332 
333 	// (SA) make it bigger
334 	le->sizeScale = 3.0f;
335 
336 	re->hModel = cgs.media.panzerfaustBrassModel;
337 
338 	le->bounceFactor = 0.4 * waterScale;
339 
340 	le->angles.trType = TR_LINEAR;
341 	le->angles.trTime = cg.time;
342 //	le->angles.trBase[0] = rand()&31;
343 //	le->angles.trBase[1] = rand()&31;
344 //	le->angles.trBase[2] = rand()&31;
345 	le->angles.trBase[0] = 0;
346 	le->angles.trBase[1] = cent->currentState.apos.trBase[1];   // rotate to match the player
347 	le->angles.trBase[2] = 0;
348 //	le->angles.trDelta[0] = 2;
349 //	le->angles.trDelta[1] = 1;
350 //	le->angles.trDelta[2] = 0;
351 	le->angles.trDelta[0] = 0;
352 	le->angles.trDelta[1] = 0;
353 	le->angles.trDelta[2] = 0;
354 
355 	le->leFlags = LEF_TUMBLE | LEF_SMOKING;   // (SA) probably doesn't need to be 'tumble' since it doesn't really rotate much when flying
356 
357 	le->leBounceSoundType = LEBS_NONE;
358 
359 	le->leMarkType = LEMT_NONE;
360 }
361 /*
362 ==============
363 CG_SpearTrail
364 	simple bubble trail behind a missile
365 ==============
366 */
CG_SpearTrail(centity_t * ent,const weaponInfo_t * wi)367 void CG_SpearTrail( centity_t *ent, const weaponInfo_t *wi ) {
368 	int contents, lastContents;
369 	vec3_t origin, lastPos;
370 	entityState_t   *es;
371 
372 	es = &ent->currentState;
373 	BG_EvaluateTrajectory( &es->pos, cg.time, origin );
374 	contents = CG_PointContents( origin, -1 );
375 
376 	BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
377 	lastContents = CG_PointContents( lastPos, -1 );
378 
379 	ent->trailTime = cg.time;
380 
381 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
382 		if ( contents & lastContents & CONTENTS_WATER ) {
383 			CG_BubbleTrail( lastPos, origin, 1, 8 );
384 		}
385 	}
386 }
387 
388 
389 // JPW NERVE -- LT pyro for marking air strikes
390 /*
391 ==========================
392 CG_PyroSmokeTrail
393 ==========================
394 */
CG_PyroSmokeTrail(centity_t * ent,const weaponInfo_t * wi)395 void CG_PyroSmokeTrail( centity_t *ent, const weaponInfo_t *wi ) {
396 	int step;
397 	vec3_t origin, lastPos, dir;
398 	int startTime;
399 	entityState_t   *es;
400 	int t;
401 	float rnd;
402 	static float grounddir = 99;
403 
404 	if ( grounddir == 99 ) { // pick a wind direction -- cheap trick because it can be different
405 		grounddir = crandom(); // on different clients, but it's all smoke and mirrors anyway
406 	}
407 	step = 30;
408 	es = &ent->currentState;
409 	startTime = ent->trailTime;
410 	t = step * ( ( startTime + step ) / step );
411 
412 	BG_EvaluateTrajectory( &es->pos, cg.time, origin );
413 	BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
414 
415 	ent->trailTime = cg.time;
416 
417 	// drop fire trail sprites
418 	for ( ; t <= ent->trailTime ; t += step ) {
419 
420 		BG_EvaluateTrajectory( &es->pos, t, lastPos );
421 //		VectorCopy ( ent->lerpOrigin, lastPos );
422 
423 		if ( ent->currentState.density ) { // corkscrew effect
424 			vec3_t right;
425 			vec3_t angles;
426 			VectorCopy( ent->currentState.apos.trBase, angles );
427 			angles[ROLL] += cg.time % 360;
428 			AngleVectors( angles, NULL, right, NULL );
429 			VectorMA( lastPos, ent->currentState.density, right, lastPos );
430 		}
431 
432 		dir[0] = crandom() * 5; // compute offset from flare base
433 		dir[1] = crandom() * 5;
434 		dir[2] = 0;
435 		VectorAdd( lastPos,dir,origin ); // store in origin
436 
437 		rnd = random();
438 
439 		dir[0] = random() * 0.25;
440 		dir[1] = grounddir; // simulate a little wind so it looks natural
441 		dir[2] = random(); // one direction (so smoke goes side-like)
442 		VectorNormalize( dir );
443 		VectorScale( dir,45,dir ); // was 75
444 
445 		if ( !ent->currentState.otherEntityNum2 ) { // axis team, generate red smoke
446 			CG_SmokePuff( origin, dir,
447 							   25 + rnd * 110, // width
448 							   rnd * 0.5 + 0.5, rnd * 0.5 + 0.5, 1, 0.5,
449 							   4800 + ( rand() % 2800 ), // duration was 2800+
450 							   t,
451 							   0,
452 							   0,
453 							   cgs.media.smokePuffShader );
454 		} else {
455 			CG_SmokePuff( origin, dir,
456 							   25 + rnd * 110, // width
457 							   1.0, rnd * 0.5 + 0.5, rnd * 0.5 + 0.5, 0.5,
458 							   4800 + ( rand() % 2800 ), // duration was 2800+
459 							   t,
460 							   0,
461 							   0,
462 							   cgs.media.smokePuffShader );
463 		}
464 	}
465 }
466 // jpw
467 
468 
469 // Ridah, new trail effects
470 /*
471 ==========================
472 CG_RocketTrail
473 ==========================
474 */
CG_RocketTrail(centity_t * ent,const weaponInfo_t * wi)475 void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
476 	int step;
477 	vec3_t origin, lastPos;
478 	int contents;
479 	int lastContents, startTime;
480 	entityState_t   *es;
481 	int t;
482 //	localEntity_t	*le;
483 
484 	if ( ent->currentState.eType == ET_FLAMEBARREL ) {
485 		step = 30;
486 	} else if ( ent->currentState.eType == ET_FP_PARTS ) {
487 		step = 50;
488 	} else if ( ent->currentState.eType == ET_RAMJET ) {
489 		step = 10;
490 	} else {
491 		step = 10;
492 	}
493 
494 	es = &ent->currentState;
495 	startTime = ent->trailTime;
496 	t = step * ( ( startTime + step ) / step );
497 
498 	BG_EvaluateTrajectory( &es->pos, cg.time, origin );
499 	contents = CG_PointContents( origin, -1 );
500 
501 	// if object (e.g. grenade) is stationary, don't toss up smoke
502 	if ( ( ent->currentState.eType != ET_RAMJET ) && es->pos.trType == TR_STATIONARY ) {
503 		ent->trailTime = cg.time;
504 		return;
505 	}
506 
507 	BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
508 	lastContents = CG_PointContents( lastPos, -1 );
509 
510 	ent->trailTime = cg.time;
511 
512 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
513 		if ( contents & lastContents & CONTENTS_WATER ) {
514 			CG_BubbleTrail( lastPos, origin, 3, 8 );
515 		}
516 		return;
517 	}
518 
519 	// drop fire trail sprites
520 	for ( ; t <= ent->trailTime ; t += step ) {
521 		float rnd;
522 
523 		BG_EvaluateTrajectory( &es->pos, t, lastPos );
524 		/*
525 		le = CG_SmokePuff( lastPos, vec3_origin,
526 					  5,	// width
527 					  1, 1, 1, 0.33,
528 					  150 + rand()%350, // duration
529 					  t,
530 					  0,
531 					  cgs.media.flameThrowerhitShader );
532 
533 		// use the optimized local entity add
534 		le->leType = LE_SCALE_FADE;
535 		*/
536 		rnd = random();
537 		if ( ent->currentState.eType == ET_FLAMEBARREL ) {
538 			if ( ( rand() % 100 ) > 50 ) {
539 				CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, 100 + (int)( rnd * 400 ), 5, 7 + (int)( rnd * 10 ) ); // fire
540 
541 			}
542 			CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ) );    // smoke
543 		} else if ( ent->currentState.eType == ET_FP_PARTS )     {
544 			if ( ( rand() % 100 ) > 50 ) {
545 				CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, 100 + (int)( rnd * 400 ), 5, 7 + (int)( rnd * 10 ) ); // fire
546 
547 			}
548 			CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ) );    // smoke
549 		} else if ( ent->currentState.eType == ET_RAMJET )     {
550 			int duration;
551 
552 			VectorCopy( ent->lerpOrigin, lastPos );
553 			duration = 100;
554 			CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, duration + (int)( rnd * 100 ), 5, 5 + (int)( rnd * 10 ) ); // fire
555 			CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 400 + (int)( rnd * 750 ), 12, 24 + (int)( rnd * 30 ) );    // smoke
556 		} else if ( ent->currentState.eType == ET_FIRE_COLUMN || ent->currentState.eType == ET_FIRE_COLUMN_SMOKE )     {
557 			int duration;
558 			int sizeStart;
559 			int sizeEnd;
560 
561 			//VectorCopy (ent->lerpOrigin, lastPos);
562 
563 			if ( ent->currentState.density ) { // corkscrew effect
564 				vec3_t right;
565 				vec3_t angles;
566 				VectorCopy( ent->currentState.apos.trBase, angles );
567 				angles[ROLL] += cg.time % 360;
568 				AngleVectors( angles, NULL, right, NULL );
569 				VectorMA( lastPos, ent->currentState.density, right, lastPos );
570 			}
571 
572 			duration = ent->currentState.angles[0];
573 			sizeStart = ent->currentState.angles[1];
574 			sizeEnd = ent->currentState.angles[2];
575 
576 			if ( !duration ) {
577 				duration = 100;
578 			}
579 
580 			if ( !sizeStart ) {
581 				sizeStart = 5;
582 			}
583 
584 			if ( !sizeEnd ) {
585 				sizeEnd = 7;
586 			}
587 
588 			CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, duration + (int)( rnd * 400 ), sizeStart, sizeEnd + (int)( rnd * 10 ) );   // fire
589 
590 			if ( ent->currentState.eType == ET_FIRE_COLUMN_SMOKE && ( rand() % 100 ) > 50 ) {
591 				CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ) );    // smoke
592 			}
593 		} else
594 		{
595 			//CG_ParticleExplosion( "twiltb", lastPos, vec3_origin, 300+(int)(rnd*100), 4, 14+(int)(rnd*8) );	// fire
596 			CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ) );    // smoke
597 		}
598 	}
599 /*
600 	// spawn a smoke junction
601 	if ((cg.time - ent->lastTrailTime) >= 50 + rand()%50) {
602 		ent->headJuncIndex = CG_AddSmokeJunc( ent->headJuncIndex,
603 												cgs.media.smokeTrailShader,
604 												origin,
605 												4500, 0.4, 20, 80 );
606 		ent->lastTrailTime = cg.time;
607 	}
608 */
609 // done.
610 }
611 
612 // Ridah
613 /*
614 ==========================
615 CG_GrenadeTrail
616 ==========================
617 */
CG_GrenadeTrail(centity_t * ent,const weaponInfo_t * wi)618 static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) {
619 	int step;
620 	vec3_t origin, lastPos;
621 	int contents;
622 	int lastContents, startTime;
623 	entityState_t   *es;
624 	int t;
625 
626 	step = 15;  // nice and smooth curves
627 
628 	es = &ent->currentState;
629 	startTime = ent->trailTime;
630 	t = step * ( ( startTime + step ) / step );
631 
632 	BG_EvaluateTrajectory( &es->pos, cg.time, origin );
633 	contents = CG_PointContents( origin, -1 );
634 
635 	// if object (e.g. grenade) is stationary, don't toss up smoke
636 	if ( es->pos.trType == TR_STATIONARY ) {
637 		ent->trailTime = cg.time;
638 		return;
639 	}
640 
641 	BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
642 	lastContents = CG_PointContents( lastPos, -1 );
643 
644 	ent->trailTime = cg.time;
645 
646 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
647 		if ( contents & lastContents & CONTENTS_WATER ) {
648 			CG_BubbleTrail( lastPos, origin, 2, 8 );
649 		}
650 		return;
651 	}
652 
653 //----(SA)	trying this back on for DM
654 
655 	// spawn smoke junctions
656 	for ( ; t <= ent->trailTime ; t += step ) {
657 		BG_EvaluateTrajectory( &es->pos, t, origin );
658 		ent->headJuncIndex = CG_AddSmokeJunc( ent->headJuncIndex,
659 											  cgs.media.smokeTrailShader,
660 											  origin,
661 //												1500, 0.3, 10, 50 );
662 											  1000, 0.3, 2, 20 );
663 		ent->lastTrailTime = cg.time;
664 	}
665 //----(SA)	end
666 }
667 // done.
668 
669 
670 
671 
672 /*
673 ==========================
674 CG_NailgunEjectBrass
675 ==========================
676 */
677 /*
678 static void CG_NailgunEjectBrass( centity_t *cent ) {
679 	localEntity_t	*smoke;
680 	vec3_t			origin;
681 	vec3_t			v[3];
682 	vec3_t			offset;
683 	vec3_t			xoffset;
684 	vec3_t			up;
685 
686 	AnglesToAxis( cent->lerpAngles, v );
687 
688 	offset[0] = 0;
689 	offset[1] = -12;
690 	offset[2] = 24;
691 
692 	xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
693 	xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
694 	xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
695 	VectorAdd( cent->lerpOrigin, xoffset, origin );
696 
697 	VectorSet( up, 0, 0, 64 );
698 
699 	smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader );
700 	// use the optimized local entity add
701 	smoke->leType = LE_SCALE_FADE;
702 }
703 
704 static void CG_NailTrail( centity_t *ent, const weaponInfo_t *wi ) {
705 	int		step;
706 	vec3_t	origin, lastPos;
707 	int		t;
708 	int		startTime, contents;
709 	int		lastContents;
710 	entityState_t	*es;
711 	vec3_t	up;
712 	localEntity_t	*smoke;
713 
714 	up[0] = 0;
715 	up[1] = 0;
716 	up[2] = 0;
717 
718 	step = 50;
719 
720 	es = &ent->currentState;
721 	startTime = ent->trailTime;
722 	t = step * ( (startTime + step) / step );
723 
724 	BG_EvaluateTrajectory( &es->pos, cg.time, origin );
725 	contents = CG_PointContents( origin, -1 );
726 
727 	// if object (e.g. grenade) is stationary, don't toss up smoke
728 	if ( es->pos.trType == TR_STATIONARY ) {
729 		ent->trailTime = cg.time;
730 		return;
731 	}
732 
733 	BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
734 	lastContents = CG_PointContents( lastPos, -1 );
735 
736 	ent->trailTime = cg.time;
737 
738 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
739 		if ( contents & lastContents & CONTENTS_WATER ) {
740 			CG_BubbleTrail( lastPos, origin, 1, 8 );
741 		}
742 		return;
743 	}
744 
745 	for ( ; t <= ent->trailTime ; t += step ) {
746 		BG_EvaluateTrajectory( &es->pos, t, lastPos );
747 
748 		smoke = CG_SmokePuff( lastPos, up,
749 					  wi->trailRadius,
750 					  1, 1, 1, 0.33f,
751 					  wi->wiTrailTime,
752 					  t,
753 					  0,
754 					  0,
755 					  cgs.media.nailPuffShader );
756 		// use the optimized local entity add
757 		smoke->leType = LE_SCALE_FADE;
758 	}
759 
760 }
761 */
762 
763 /*
764 ==========================
765 CG_RailTrail
766 	SA: re-inserted this as a debug mechanism for bullets
767 ==========================
768 */
CG_RailTrail2(clientInfo_t * ci,vec3_t start,vec3_t end)769 void CG_RailTrail2( clientInfo_t *ci, vec3_t start, vec3_t end ) {
770 	localEntity_t   *le;
771 	refEntity_t     *re;
772 
773 	le = CG_AllocLocalEntity();
774 	re = &le->refEntity;
775 
776 	le->leType = LE_FADE_RGB;
777 	le->startTime = cg.time;
778 	le->endTime = cg.time + cg_railTrailTime.value;
779 	le->lifeRate = 1.0 / ( le->endTime - le->startTime );
780 
781 	re->shaderTime = cg.time / 1000.0f;
782 	re->reType = RT_RAIL_CORE;
783 	re->customShader = cgs.media.railCoreShader;
784 
785 	VectorCopy( start, re->origin );
786 	VectorCopy( end, re->oldorigin );
787 
788 //	// still allow different colors so we can tell AI shots from player shots, etc.
789 	if ( ci ) {
790 		le->color[0] = ci->color[0] * 0.75;
791 		le->color[1] = ci->color[1] * 0.75;
792 		le->color[2] = ci->color[2] * 0.75;
793 	} else {
794 		le->color[0] = 1;
795 		le->color[1] = 0;
796 		le->color[2] = 0;
797 	}
798 	le->color[3] = 1.0f;
799 
800 	AxisClear( re->axis );
801 }
802 
803 //void CG_RailTrailBox( clientInfo_t *ci, vec3_t start, vec3_t end) {
804 /*
805 ==============
806 CG_RailTrail
807 	modified so we could draw boxes for debugging as well
808 ==============
809 */
CG_RailTrail(clientInfo_t * ci,vec3_t start,vec3_t end,int type)810 void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end, int type ) {  //----(SA)	added 'type'
811 	vec3_t diff, v1, v2, v3, v4, v5, v6;
812 
813 	if ( !type ) { // just a line
814 		CG_RailTrail2( ci, start, end );
815 		return;
816 	}
817 
818 	// type '1' (box)
819 
820 	VectorSubtract( start, end, diff );
821 
822 	VectorCopy( start, v1 );
823 	VectorCopy( start, v2 );
824 	VectorCopy( start, v3 );
825 	v1[0] -= diff[0];
826 	v2[1] -= diff[1];
827 	v3[2] -= diff[2];
828 	CG_RailTrail2( ci, start, v1 );
829 	CG_RailTrail2( ci, start, v2 );
830 	CG_RailTrail2( ci, start, v3 );
831 
832 	VectorCopy( end, v4 );
833 	VectorCopy( end, v5 );
834 	VectorCopy( end, v6 );
835 	v4[0] += diff[0];
836 	v5[1] += diff[1];
837 	v6[2] += diff[2];
838 	CG_RailTrail2( ci, end, v4 );
839 	CG_RailTrail2( ci, end, v5 );
840 	CG_RailTrail2( ci, end, v6 );
841 
842 	CG_RailTrail2( ci, v2, v6 );
843 	CG_RailTrail2( ci, v6, v1 );
844 	CG_RailTrail2( ci, v1, v5 );
845 
846 	CG_RailTrail2( ci, v2, v4 );
847 	CG_RailTrail2( ci, v4, v3 );
848 	CG_RailTrail2( ci, v3, v5 );
849 
850 }
851 
852 
853 
854 
855 
856 /*
857 ======================
858 CG_ParseWeaponConfig
859 	read information for weapon animations (first/length/fps)
860 ======================
861 */
CG_ParseWeaponConfig(const char * filename,weaponInfo_t * wi,int weaponNum)862 static qboolean CG_ParseWeaponConfig( const char *filename, weaponInfo_t *wi, int weaponNum ) {
863 	char        *text_p, *prev;
864 	int len;
865 	int i;
866 	float fps;
867 	char        *token;
868 	qboolean newfmt = qfalse;       //----(SA)
869 	char text[20000];
870 	fileHandle_t f;
871 
872 	// load the file
873 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
874 	if ( len <= 0 ) {
875 		return qfalse;
876 	}
877 
878 	if ( len >= sizeof( text ) - 1 ) {
879 		CG_Printf( "File %s too long\n", filename );
880 		return qfalse;
881 	}
882 
883 	trap_FS_Read( text, len, f );
884 	text[len] = 0;
885 	trap_FS_FCloseFile( f );
886 
887 	// parse the text
888 	text_p = text;
889 
890 	// read optional parameters
891 	while ( 1 ) {
892 		prev = text_p;  // so we can unget
893 		token = COM_Parse( &text_p );
894 		if ( !token[0] ) {                     // get the variable
895 			break;
896 		}
897 		if ( !Q_stricmp( token, "whatever_variable" ) ) {
898 			token = COM_Parse( &text_p );   // get the value
899 			if ( !token[0] ) {
900 				break;
901 			}
902 			continue;
903 		}
904 
905 		if ( !Q_stricmp( token, "newfmt" ) ) {
906 			newfmt = qtrue;
907 			continue;
908 		}
909 
910 		// if it is a number, start parsing animations
911 		if ( Q_isnumeric( token[0] ) ) {
912 			text_p = prev;  // unget the token
913 			break;
914 		}
915 		Com_Printf( "unknown token in weapon cfg '%s' is %s\n", token, filename );
916 	}
917 
918 
919 	for ( i = 0 ; i < MAX_WP_ANIMATIONS  ; i++ ) {
920 
921 		token = COM_Parse( &text_p );   // first frame
922 		if ( !token[0] ) {
923 			// don't show warning for weapon cfg without altswitch that does not require it.
924 			if ( i == WEAP_ALTSWITCHFROM && weapAlts[weaponNum] == WP_NONE ) {
925 				for ( ; i < MAX_WP_ANIMATIONS  ; i++ ) {
926 					Com_Memcpy( &wi->weapAnimations[i], &wi->weapAnimations[WEAP_IDLE1], sizeof( wi->weapAnimations[0] ) );
927 				}
928 			}
929 			break;
930 		}
931 		wi->weapAnimations[i].firstFrame = atoi( token );
932 
933 		token = COM_Parse( &text_p );   // length
934 		if ( !token[0] ) {
935 			break;
936 		}
937 		wi->weapAnimations[i].numFrames = atoi( token );
938 
939 		token = COM_Parse( &text_p );   // fps
940 		if ( !token[0] ) {
941 			break;
942 		}
943 		fps = atof( token );
944 		if ( fps == 0 ) {
945 			fps = 1;
946 		}
947 
948 		wi->weapAnimations[i].frameLerp = 1000 / fps;
949 		wi->weapAnimations[i].initialLerp = 1000 / fps;
950 
951 		token = COM_Parse( &text_p );   // looping frames
952 		if ( !token[0] ) {
953 			break;
954 		}
955 		wi->weapAnimations[i].loopFrames = atoi( token );
956 		if ( wi->weapAnimations[i].loopFrames > wi->weapAnimations[i].numFrames ) {
957 			wi->weapAnimations[i].loopFrames = wi->weapAnimations[i].numFrames;
958 		} else if ( wi->weapAnimations[i].loopFrames < 0 ) {
959 			wi->weapAnimations[i].loopFrames = 0;
960 		}
961 
962 
963 		// store animation/draw bits in '.moveSpeed'
964 
965 		wi->weapAnimations[i].moveSpeed = 0;
966 
967 		if ( newfmt ) {
968 			token = COM_Parse( &text_p );   // barrel anim bits
969 			if ( !token[0] ) {
970 				break;
971 			}
972 			wi->weapAnimations[i].moveSpeed = atoi( token );
973 
974 			token = COM_Parse( &text_p );   // animated weapon
975 			if ( !token[0] ) {
976 				break;
977 			}
978 			if ( atoi( token ) ) {
979 				wi->weapAnimations[i].moveSpeed |= ( 1 << W_MAX_PARTS );    // set the bit one higher than can be set by the barrel bits
980 
981 			}
982 			token = COM_Parse( &text_p );   // barrel hide bits (so objects can be flagged to not be drawn during all sequences (a reloading hand that comes in from off screen for that one animation for example)
983 			if ( !token[0] ) {
984 				break;
985 			}
986 			wi->weapAnimations[i].moveSpeed |= ( ( atoi( token ) ) << 8 ); // use 2nd byte for draw bits
987 		}
988 
989 	}
990 
991 	if ( i != MAX_WP_ANIMATIONS ) {
992 		CG_Printf( "Error parsing weapon animation file: %s\n", filename );
993 		return qfalse;
994 	}
995 
996 
997 	return qtrue;
998 }
999 
1000 
1001 /*
1002 =================
1003 CG_RegisterWeapon
1004 
1005 The server says this item is used on this level
1006 =================
1007 */
CG_RegisterWeapon(int weaponNum)1008 void CG_RegisterWeapon( int weaponNum ) {
1009 	weaponInfo_t    *weaponInfo;
1010 	gitem_t         *item, *ammo;
1011 	char path[MAX_QPATH], comppath[MAX_QPATH];
1012 	vec3_t mins, maxs;
1013 	int i;
1014 
1015 	weaponInfo = &cg_weapons[weaponNum];
1016 
1017 	// don't bother trying
1018 	switch ( weaponNum ) {
1019 	case WP_NONE:
1020 	case WP_CLASS_SPECIAL:
1021 	case WP_MONSTER_ATTACK1:
1022 	case WP_MONSTER_ATTACK2:
1023 	case WP_MONSTER_ATTACK3:
1024 	case WP_GAUNTLET:
1025 	case WP_SNIPER:
1026 	case WP_MORTAR:
1027 
1028 // (SA) i don't know about these, but we don't have models for 'em
1029 	case WP_GRENADE_SMOKE:
1030 	case WP_MEDIC_HEAL:
1031 		return;
1032 	default:
1033 		break;
1034 	}
1035 
1036 	if ( weaponInfo->registered ) {
1037 		return;
1038 	}
1039 
1040 	memset( weaponInfo, 0, sizeof( *weaponInfo ) );
1041 	weaponInfo->registered = qtrue;
1042 
1043 	for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
1044 		if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) {
1045 			weaponInfo->item = item;
1046 			break;
1047 		}
1048 	}
1049 	if ( !item->classname ) {
1050 		CG_Error( "Couldn't find weapon %i", weaponNum );
1051 	}
1052 
1053 	CG_RegisterItemVisuals( item - bg_itemlist );
1054 
1055 	// load cmodel before model so filecache works
1056 
1057 	// alternate view weapon
1058 	weaponInfo->weaponModel[W_TP_MODEL] = trap_R_RegisterModel( item->world_model[W_TP_MODEL] );
1059 	weaponInfo->weaponModel[W_FP_MODEL] = trap_R_RegisterModel( item->world_model[W_FP_MODEL] );
1060 	weaponInfo->weaponModel[W_SKTP_MODEL] = trap_R_RegisterModel( item->world_model[W_SKTP_MODEL] );
1061 
1062 	if ( !weaponInfo->weaponModel[W_FP_MODEL] || !cg_drawFPGun.integer ) {
1063 		weaponInfo->weaponModel[W_FP_MODEL] = weaponInfo->weaponModel[W_TP_MODEL];
1064 	}
1065 
1066 	if ( !weaponInfo->weaponModel[W_TP_MODEL] ) {
1067 		// left commented out since we have level-loading optimization issues to still resolve.
1068 		//	ie. every weapon and it's associated effects/parts/sounds etc. are loaded for every level.
1069 		// This was turned off when we started (the "only load what the level calls for" thing) because when
1070 		// DM does a "give all" and fires, he doesn't want to wait for everything to load.  So perhaps a "cacheallweaps" or something.
1071 //		CG_Printf( "Couldn't register weapon model %i (unable to load view model)\n", weaponNum );
1072 // RF, I need to be able to run the game, I dont have the silencer weapon (19)
1073 #ifndef _DEBUG
1074 //		CG_Error( "Couldn't register weapon model %i (unable to load view model)", weaponNum );
1075 #endif
1076 		return;
1077 	}
1078 
1079 
1080 	// load weapon config
1081 //----(SA)	modified.  use first person model for finding weapon config name, not third
1082 	if ( item->world_model[W_FP_MODEL] ) {
1083 		COM_StripFilename( item->world_model[W_FP_MODEL], path );
1084 		if ( !CG_ParseWeaponConfig( va( "%sweapon.cfg", path ), weaponInfo, weaponNum ) ) {
1085 			CG_Error( "Couldn't register weapon %i (%s) (failed to parse weapon.cfg)", weaponNum, path );
1086 		}
1087 	}
1088 //----(SA)	end
1089 
1090 	// calc midpoint for rotation
1091 	trap_R_ModelBounds( weaponInfo->weaponModel[W_TP_MODEL], mins, maxs );
1092 
1093 	for ( i = 0 ; i < 3 ; i++ ) {
1094 		weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
1095 	}
1096 
1097 	weaponInfo->weaponIcon[0] = trap_R_RegisterShader( item->icon );
1098 	weaponInfo->weaponIcon[1] = trap_R_RegisterShader( va( "%s_select", item->icon ) );    // get the 'selected' icon as well
1099 
1100 	// JOSEPH 4-17-00
1101 	weaponInfo->ammoIcon = trap_R_RegisterShader( item->ammoicon );
1102 	// END JOSEPH
1103 
1104 	for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
1105 		if ( ( ammo->giType == IT_AMMO && ammo->giTag == BG_FindAmmoForWeapon( weaponNum ) ) ) {
1106 			break;
1107 		}
1108 	}
1109 	if ( ammo->classname && ammo->world_model[0] ) {
1110 		weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] );
1111 	}
1112 
1113 	if ( item->world_model[W_FP_MODEL] ) {
1114 		Q_strncpyz( comppath, item->world_model[W_FP_MODEL], sizeof(comppath) ); // first try the fp view weap
1115 	} else if ( item->world_model[W_TP_MODEL] ) {
1116 		Q_strncpyz( comppath, item->world_model[W_TP_MODEL], sizeof(comppath) ); // not there, use the standard view hand
1117 	}
1118 
1119 	if ( !cg_drawFPGun.integer && item->world_model[W_TP_MODEL] ) { // then if it didn't find the 1st person one or you are set to not use one
1120 		Q_strncpyz( comppath, item->world_model[W_TP_MODEL], sizeof(comppath) ); // use the standard view hand
1121 	}
1122 
1123 	for ( i = W_TP_MODEL; i < W_NUM_TYPES; i++ )
1124 	{
1125 		int j;
1126 
1127 		if ( !item->world_model[i] ) {
1128 			Q_strncpyz( path, comppath, sizeof(path) );
1129 		} else {
1130 			Q_strncpyz( path, item->world_model[i], sizeof(path) );
1131 		}
1132 
1133 		COM_StripExtension( path, path, sizeof(path) );
1134 		Q_strcat( path, sizeof(path), "_flash.md3" );
1135 		weaponInfo->flashModel[i] = trap_R_RegisterModel( path );
1136 
1137 
1138 		for ( j = 0; j < W_MAX_PARTS; j++ ) {
1139 			if ( !item->world_model[i] ) {
1140 				Q_strncpyz( path, comppath, sizeof(path) );
1141 			} else {
1142 				Q_strncpyz( path, item->world_model[i], sizeof(path) );
1143 			}
1144 			COM_StripExtension( path, path, sizeof(path) );
1145 			if ( j == W_PART_1 ) {
1146 				Q_strcat( path, sizeof(path), "_barrel.md3" );
1147 			} else {
1148 				Q_strcat( path, sizeof(path), va( "_barrel%d.md3", j + 1 ) );
1149 			}
1150 			weaponInfo->wpPartModels[i][j] = trap_R_RegisterModel( path );
1151 		}
1152 
1153 		// used for spinning belt on venom
1154 		if ( i == W_FP_MODEL ) {
1155 			if ( !item->world_model[2] ) {
1156 				Q_strncpyz( path, comppath, sizeof(path) );
1157 			} else {
1158 				Q_strncpyz( path, item->world_model[2], sizeof(path) );
1159 			}
1160 			COM_StripExtension( path, path, sizeof(path) );
1161 			Q_strcat( path, sizeof(path), "_barrel6b.md3" );
1162 			weaponInfo->wpPartModels[i][W_PART_7] = trap_R_RegisterModel( path );
1163 		}
1164 	}
1165 
1166 
1167 	// sniper scope model
1168 	if ( weaponNum == WP_MAUSER || weaponNum == WP_GARAND ) {
1169 
1170 		if ( !item->world_model[W_FP_MODEL] ) {
1171 			Q_strncpyz( path, comppath, sizeof(path) );
1172 		} else {
1173 			Q_strncpyz( path, item->world_model[W_FP_MODEL], sizeof(path) );
1174 		}
1175 		COM_StripExtension( path, path, sizeof(path) );
1176 		Q_strcat( path, sizeof(path), "_scope.md3" );
1177 		weaponInfo->modModel[0] = trap_R_RegisterModel( path );
1178 	}
1179 
1180 	if ( !item->world_model[W_FP_MODEL] ) {
1181 		Q_strncpyz( path, comppath, sizeof(path) );
1182 	} else {
1183 		Q_strncpyz( path, item->world_model[W_FP_MODEL], sizeof(path) );
1184 	}
1185 	COM_StripExtension( path, path, sizeof(path) );
1186 	Q_strcat( path, sizeof(path), "_hand.md3" );
1187 	weaponInfo->handsModel = trap_R_RegisterModel( path );
1188 
1189 	if ( !weaponInfo->handsModel ) {
1190 		weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
1191 	}
1192 
1193 //----(SA)	weapon pickup 'stand'
1194 	if ( !item->world_model[W_TP_MODEL] ) {
1195 		Q_strncpyz( path, comppath, sizeof(path) );
1196 	} else {
1197 		Q_strncpyz( path, item->world_model[W_TP_MODEL], sizeof(path) );
1198 	}
1199 	COM_StripExtension( path, path, sizeof(path) );
1200 	Q_strcat( path, sizeof(path), "_stand.md3" );
1201 	weaponInfo->standModel = trap_R_RegisterModel( path );
1202 //----(SA)	end
1203 
1204 	switch ( weaponNum ) {
1205 	case WP_MONSTER_ATTACK1:
1206 	case WP_MONSTER_ATTACK2:
1207 	case WP_MONSTER_ATTACK3:
1208 		break;
1209 
1210 
1211 	case WP_AKIMBO: //----(SA)	added
1212 		// same as colt
1213 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1214 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/colt/coltf1.wav" );
1215 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40e1.wav" ); // use same as mp40
1216 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1217 
1218 		// unique
1219 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/colt/colt_reload2.wav" );
1220 		break;
1221 
1222 	case WP_COLT:
1223 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1224 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/colt/coltf1.wav" );
1225 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40e1.wav" ); // use same as mp40
1226 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/colt/colt_reload.wav" );
1227 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1228 		break;
1229 
1230 
1231 	case WP_KNIFE:
1232 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/knife/knife_slash1.wav" );
1233 		weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/knife/knife_slash2.wav" );
1234 		break;
1235 
1236 	case WP_LUGER:
1237 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1238 
1239 		weaponInfo->switchSound[0] = trap_S_RegisterSound( "sound/weapons/luger/silencerremove.wav" );   //----(SA)	added
1240 
1241 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/luger/lugerf1.wav" );
1242 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40e1.wav" ); // use same as mp40
1243 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/luger/luger_reload.wav" );
1244 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1245 		break;
1246 
1247 	case WP_SILENCER:   // luger mod
1248 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1249 
1250 		weaponInfo->switchSound[0] = trap_S_RegisterSound( "sound/weapons/luger/silencerattatch.wav" );  //----(SA)	added
1251 
1252 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/luger/silencerf1.wav" );
1253 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/luger/luger_reload.wav" );
1254 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1255 		break;
1256 
1257 	case WP_MAUSER:
1258 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1259 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/mauser/mauserf1.wav" );
1260 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mauser/mausere1.wav" );
1261 		weaponInfo->lastShotSound[0] = trap_S_RegisterSound( "sound/weapons/mauser/mauserf1_last.wav" );
1262 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/mauser/mauser_reload.wav" );
1263 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1264 		break;
1265 	case WP_SNIPERRIFLE:
1266 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1267 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/mauser/sniperf1.wav" );
1268 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mauser/mausere1.wav" );
1269 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/mauser/sniper_reload.wav" );
1270 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1271 		break;
1272 
1273 	case WP_GARAND:
1274 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1275 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/garand/garandf1.wav" );
1276 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/garand/garand_reload.wav" );
1277 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1278 		break;
1279 	case WP_SNOOPERSCOPE:
1280 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1281 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/garand/snooperf1.wav" );
1282 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/garand/snooper_reload.wav" );
1283 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1284 		break;
1285 
1286 	case WP_THOMPSON:
1287 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1288 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/thompson/thompson.wav" );
1289 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40e1.wav" ); // use same as mp40
1290 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/thompson/thompson_reload.wav" );
1291 		weaponInfo->overheatSound = trap_S_RegisterSound( "sound/weapons/thompson/thompson_overheat.wav" );
1292 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1293 		break;
1294 
1295 	case WP_MP40:
1296 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1297 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40f1.wav" );
1298 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/mp40/mp40e1.wav" );
1299 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/mp40/mp40_reload.wav" );
1300 		weaponInfo->overheatSound = trap_S_RegisterSound( "sound/weapons/mp40/mp40_overheat.wav" );
1301 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1302 		break;
1303 
1304 	case WP_STEN:
1305 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1306 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/sten/stenf1.wav" );
1307 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/sten/sten_reload.wav" );
1308 		weaponInfo->overheatSound = trap_S_RegisterSound( "sound/weapons/sten/sten_overheat.wav" );
1309 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1310 		break;
1311 
1312 	case WP_FG42:
1313 	case WP_FG42SCOPE:
1314 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1315 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/fg42/fg42f1.wav" );
1316 		weaponInfo->flashEchoSound[0] = trap_S_RegisterSound( "sound/weapons/fg42/fg42e1.wav" );
1317 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/fg42/fg42_reload.wav" );
1318 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1319 		break;
1320 //----(SA)	end
1321 
1322 
1323 	case WP_PANZERFAUST:
1324 		weaponInfo->ejectBrassFunc      = CG_PanzerFaustEjectBrass;
1325 		weaponInfo->missileModel        = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
1326 		weaponInfo->missileSound        = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav" );
1327 		weaponInfo->missileTrailFunc    = CG_RocketTrail;
1328 		weaponInfo->missileDlight       = 200;
1329 		MAKERGB( weaponInfo->flashDlightColor, 0.75, 0.3, 0.0 );
1330 		MAKERGB( weaponInfo->missileDlightColor, 0.75, 0.3, 0.0 );
1331 		weaponInfo->flashSound[0]       = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav" );
1332 		weaponInfo->reloadSound         = trap_S_RegisterSound( "sound/weapons/rocket/rocklf_reload.wav" );
1333 		cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" );
1334 		break;
1335 
1336 	case WP_MORTAR:
1337 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/mortar/mortarf1.wav" );
1338 		weaponInfo->missileTrailFunc = CG_GrenadeTrail;
1339 		weaponInfo->missileDlight = 400;
1340 		weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav" );
1341 		weaponInfo->wiTrailTime = 300;
1342 		weaponInfo->trailRadius = 32;
1343 		MAKERGB( weaponInfo->flashDlightColor, 1, 0.7, 0.5 );
1344 		break;
1345 // JPW NERVE
1346 	case WP_GRENADE_SMOKE:
1347 		weaponInfo->missileModel = trap_R_RegisterModel( "models/weapons2/grenade/pineapple.md3" );
1348 		weaponInfo->missileTrailFunc    = CG_PyroSmokeTrail;
1349 		weaponInfo->missileDlight       = 200;
1350 		weaponInfo->wiTrailTime         = 4000;
1351 		weaponInfo->trailRadius         = 256;
1352 		break;
1353 // jpw
1354 // DHM - Nerve - temp effects
1355 	case WP_CLASS_SPECIAL:
1356 	case WP_MEDIC_HEAL:
1357 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/knife/knife_slash1.wav" );
1358 		weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/knife/knife_slash2.wav" );
1359 		break;
1360 // dhm
1361 	case WP_GRENADE_LAUNCHER:
1362 	case WP_GRENADE_PINEAPPLE:
1363 		if ( weaponNum == WP_GRENADE_LAUNCHER ) {
1364 			weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" );
1365 		} else {
1366 			weaponInfo->missileModel = trap_R_RegisterModel( "models/weapons2/grenade/pineapple.md3" );
1367 		}
1368 		weaponInfo->missileTrailFunc = CG_GrenadeTrail;
1369 		weaponInfo->wiTrailTime = 700;
1370 //		weaponInfo->wiTrailTime = 2000;
1371 		weaponInfo->wiTrailTime = 1000;
1372 		weaponInfo->trailRadius = 32;
1373 		MAKERGB( weaponInfo->flashDlightColor, 1, 0.7, 0.5 );
1374 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav" );
1375 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/grenade/grenlf_reload.wav" );
1376 		cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
1377 		break;
1378 
1379 	case WP_DYNAMITE:
1380 		weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/dynamite.md3" );
1381 //		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav" );
1382 //		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/grenade/grenlf_reload.wav" );
1383 		cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
1384 		break;
1385 
1386 	case WP_VENOM:
1387 		MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.6, 0.23 );
1388 		weaponInfo->spinupSound = trap_S_RegisterSound( "sound/weapons/venom/venomsu1.wav" );    //----(SA)	added
1389 		weaponInfo->spindownSound = trap_S_RegisterSound( "sound/weapons/venom/venomsd1.wav" );  //----(SA)	added
1390 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/venom/venomf1.wav" );
1391 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/venom/venom_reload.wav" );
1392 		weaponInfo->overheatSound = trap_S_RegisterSound( "sound/weapons/venom/venom_overheat.wav" );
1393 		weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
1394 		break;
1395 
1396 	case WP_FLAMETHROWER:
1397 		//MAKERGB( weaponInfo->flashDlightColor, 1.0, 0.7, 0.4 );
1398 		break;
1399 
1400 	case WP_TESLA:
1401 		MAKERGB( weaponInfo->flashDlightColor, 0.2, 0.6, 1 );
1402 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/tesla/teslaf1.wav" );
1403 		weaponInfo->reloadSound = trap_S_RegisterSound( "sound/weapons/tesla/tesla_reload.wav" );
1404 		weaponInfo->overheatSound = trap_S_RegisterSound( "sound/weapons/tesla/tesla_overheat.wav" );
1405 		break;
1406 
1407 
1408 	case WP_GAUNTLET:
1409 		MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 1 );
1410 		//weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav" );
1411 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav" );
1412 		break;
1413 
1414 	default:
1415 		MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 );
1416 		weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav" );
1417 		break;
1418 	}
1419 }
1420 
1421 /*
1422 =================
1423 CG_RegisterItemVisuals
1424 
1425 The server says this item is used on this level
1426 =================
1427 */
CG_RegisterItemVisuals(int itemNum)1428 void CG_RegisterItemVisuals( int itemNum ) {
1429 	itemInfo_t      *itemInfo;
1430 	gitem_t         *item;
1431 	int i;
1432 
1433 	itemInfo = &cg_items[ itemNum ];
1434 	if ( itemInfo->registered ) {
1435 		return;
1436 	}
1437 
1438 	item = &bg_itemlist[ itemNum ];
1439 
1440 	memset( itemInfo, 0, sizeof( *itemInfo ) );
1441 
1442 	for ( i = 0; i < MAX_ITEM_MODELS; i++ )
1443 		itemInfo->models[i] = trap_R_RegisterModel( item->world_model[i] );
1444 
1445 
1446 	itemInfo->icons[0] = trap_R_RegisterShader( item->icon );
1447 	if ( item->giType == IT_HOLDABLE ) {
1448 		// (SA) register alternate icons (since holdables can have multiple uses, they might have different icons to represent how many uses are left)
1449 		for ( i = 1; i < MAX_ITEM_ICONS; i++ )
1450 			itemInfo->icons[i] = trap_R_RegisterShader( va( "%s%i", item->icon, i + 1 ) );
1451 	}
1452 
1453 	if ( item->giType == IT_WEAPON ) {
1454 		CG_RegisterWeapon( item->giTag );
1455 	}
1456 
1457 	itemInfo->registered = qtrue;   //----(SA)	moved this down after the registerweapon()
1458 
1459 	wolfkickModel = trap_R_RegisterModel( "models/weapons2/foot/v_wolfoot_10f.md3" );
1460 	hWeaponSnd = trap_S_RegisterSound( "sound/weapons/mg42/37mm.wav" );
1461 
1462 	hflakWeaponSnd = trap_S_RegisterSound( "sound/weapons/flak/flak.wav" );
1463 	notebookModel = trap_R_RegisterModel( "models/mapobjects/book/book.md3" );
1464 	propellerModel = trap_R_RegisterModel( "models/mapobjects/vehicles/m109_prop.md3" );
1465 
1466 // JPW NERVE had to put this somewhere, this seems OK
1467 	if ( cg_gameType.integer != GT_WOLF ) {
1468 		maxWeapBanks = MAX_WEAP_BANKS;
1469 		maxWeapsInBank = MAX_WEAPS_IN_BANK;
1470 	} else {
1471 		trap_R_RegisterModel( "models/mapobjects/vehicles/m109.md3" );
1472 		CG_RegisterWeapon( WP_GRENADE_SMOKE ); // register WP_CLASS_SPECIAL visuals here
1473 		CG_RegisterWeapon( WP_MEDIC_HEAL );
1474 		maxWeapBanks = MAX_WEAP_BANKS_MP;
1475 		maxWeapsInBank = MAX_WEAPS_IN_BANK_MP;
1476 	}
1477 // if player runs out of SMG ammunition, it shouldn't *also* deplete pistol ammunition.  If you change this, change
1478 // g_spawn.c as well
1479 	if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
1480 		item = BG_FindItem( "Thompson" );
1481 		item->giAmmoIndex = WP_THOMPSON;
1482 		item = BG_FindItem( "Sten" );
1483 		item->giAmmoIndex = WP_STEN;
1484 		item = BG_FindItem( "MP40" );
1485 		item->giAmmoIndex = WP_MP40;
1486 	}
1487 // jpw
1488 }
1489 
1490 
1491 /*
1492 ========================================================================================
1493 
1494 VIEW WEAPON
1495 
1496 ========================================================================================
1497 */
1498 
1499 
1500 //
1501 // weapon animations
1502 //
1503 
1504 /*
1505 ==============
1506 CG_GetPartFramesFromWeap
1507 	get animation info from the parent if necessary
1508 ==============
1509 */
CG_GetPartFramesFromWeap(centity_t * cent,refEntity_t * part,refEntity_t * parent,int partid,weaponInfo_t * wi)1510 qboolean CG_GetPartFramesFromWeap( centity_t *cent, refEntity_t *part, refEntity_t *parent, int partid, weaponInfo_t *wi ) {
1511 	int i;
1512 	int frameoffset = 0;
1513 	animation_t *anim;
1514 
1515 	anim = cent->pe.weap.animation;
1516 
1517 	if ( partid == W_MAX_PARTS ) {
1518 		return qtrue;   // primary weap model drawn for all frames right now
1519 	}
1520 
1521 	// check draw bit
1522 	if ( anim->moveSpeed & ( 1 << ( partid + 8 ) ) ) {    // hide bits are in high byte
1523 		return qfalse;  // not drawn for current sequence
1524 	}
1525 
1526 	// find part's start frame for this animation sequence
1527 	for ( i = 0; i < cent->pe.weap.animationNumber; i++ ) {
1528 		if ( wi->weapAnimations[i].moveSpeed & ( 1 << partid ) ) {     // this part has animation for this sequence
1529 			frameoffset += wi->weapAnimations[i].numFrames;
1530 		}
1531 	}
1532 
1533 	// now set the correct frame into the part
1534 	if ( anim->moveSpeed & ( 1 << partid ) ) {
1535 		part->backlerp  = parent->backlerp;
1536 		part->oldframe  = frameoffset + ( parent->oldframe - anim->firstFrame );
1537 		part->frame     = frameoffset + ( parent->frame - anim->firstFrame );
1538 	}
1539 
1540 	return qtrue;
1541 }
1542 
1543 
1544 /*
1545 ===============
1546 CG_SetWeapLerpFrameAnimation
1547 
1548 may include ANIM_TOGGLEBIT
1549 ===============
1550 */
CG_SetWeapLerpFrameAnimation(weaponInfo_t * wi,lerpFrame_t * lf,int newAnimation)1551 static void CG_SetWeapLerpFrameAnimation( weaponInfo_t *wi, lerpFrame_t *lf, int newAnimation ) {
1552 	animation_t *anim;
1553 
1554 	lf->animationNumber = newAnimation;
1555 	newAnimation &= ~ANIM_TOGGLEBIT;
1556 
1557 	if ( newAnimation < 0 || newAnimation >= MAX_WP_ANIMATIONS ) {
1558 		CG_Error( "Bad animation number (CG_SWLFA): %i", newAnimation );
1559 	}
1560 
1561 	anim = &wi->weapAnimations[ newAnimation ];
1562 
1563 	lf->animation       = anim;
1564 	lf->animationTime   = lf->frameTime + anim->initialLerp;
1565 
1566 	if ( cg_debugAnim.integer & 2 ) {
1567 		CG_Printf( "Weap Anim: %d\n", newAnimation );
1568 	}
1569 }
1570 
1571 
1572 /*
1573 ===============
1574 CG_ClearWeapLerpFrame
1575 ===============
1576 */
CG_ClearWeapLerpFrame(weaponInfo_t * wi,lerpFrame_t * lf,int animationNumber)1577 void CG_ClearWeapLerpFrame( weaponInfo_t *wi, lerpFrame_t *lf, int animationNumber ) {
1578 	lf->frameTime = lf->oldFrameTime = cg.time;
1579 	CG_SetWeapLerpFrameAnimation( wi, lf, animationNumber );
1580 	lf->oldFrame = lf->frame = lf->animation->firstFrame;
1581 
1582 }
1583 
1584 
1585 /*
1586 ===============
1587 CG_RunWeapLerpFrame
1588 
1589 Sets cg.snap, cg.oldFrame, and cg.backlerp
1590 cg.time should be between oldFrameTime and frameTime after exit
1591 ===============
1592 */
CG_RunWeapLerpFrame(clientInfo_t * ci,weaponInfo_t * wi,lerpFrame_t * lf,int newAnimation,float speedScale)1593 static void CG_RunWeapLerpFrame( clientInfo_t *ci, weaponInfo_t *wi, lerpFrame_t *lf, int newAnimation, float speedScale ) {
1594 	int f;
1595 	animation_t *anim;
1596 
1597 	// debugging tool to get no animations
1598 	if ( cg_animSpeed.integer == 0 ) {
1599 		lf->oldFrame = lf->frame = lf->backlerp = 0;
1600 		return;
1601 	}
1602 
1603 	// see if the animation sequence is switching
1604 	if ( !lf->animation ) {
1605 		CG_ClearWeapLerpFrame( wi, lf, newAnimation );
1606 	} else if ( newAnimation != lf->animationNumber )   {
1607 		if ( ( newAnimation & ~ANIM_TOGGLEBIT ) == WEAP_RAISE ||
1608 			 ( newAnimation & ~ANIM_TOGGLEBIT ) == WEAP_ALTSWITCHFROM ||
1609 			 ( newAnimation & ~ANIM_TOGGLEBIT ) == WEAP_ALTSWITCHTO ) {
1610 			CG_ClearWeapLerpFrame( wi, lf, newAnimation );   // clear when switching to raise (since it should be out of view anyway)
1611 		} else {
1612 			CG_SetWeapLerpFrameAnimation( wi, lf, newAnimation );
1613 		}
1614 	}
1615 
1616 	// if we have passed the current frame, move it to
1617 	// oldFrame and calculate a new frame
1618 	if ( cg.time >= lf->frameTime ) {
1619 		lf->oldFrame = lf->frame;
1620 		lf->oldFrameTime = lf->frameTime;
1621 
1622 		// get the next frame based on the animation
1623 		anim = lf->animation;
1624 		if ( !anim->frameLerp ) {
1625 			return;     // shouldn't happen
1626 		}
1627 		if ( cg.time < lf->animationTime ) {
1628 			lf->frameTime = lf->animationTime;      // initial lerp
1629 		} else {
1630 			lf->frameTime = lf->oldFrameTime + anim->frameLerp;
1631 		}
1632 		f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
1633 		f *= speedScale;        // adjust for haste, etc
1634 		if ( f >= anim->numFrames ) {
1635 			f -= anim->numFrames;
1636 			if ( anim->loopFrames ) {
1637 				f %= anim->loopFrames;
1638 				f += anim->numFrames - anim->loopFrames;
1639 			} else {
1640 				f = anim->numFrames - 1;
1641 				// the animation is stuck at the end, so it
1642 				// can immediately transition to another sequence
1643 				lf->frameTime = cg.time;
1644 			}
1645 		}
1646 		lf->frame = anim->firstFrame + f;
1647 		if ( cg.time > lf->frameTime ) {
1648 			lf->frameTime = cg.time;
1649 			if ( cg_debugAnim.integer ) {
1650 //				CG_Printf( "Clamp lf->frameTime\n" );
1651 			}
1652 		}
1653 	}
1654 
1655 	if ( lf->frameTime > cg.time + 200 ) {
1656 		lf->frameTime = cg.time;
1657 	}
1658 
1659 	if ( lf->oldFrameTime > cg.time ) {
1660 		lf->oldFrameTime = cg.time;
1661 	}
1662 	// calculate current lerp value
1663 	if ( lf->frameTime == lf->oldFrameTime ) {
1664 		lf->backlerp = 0;
1665 	} else {
1666 		lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
1667 	}
1668 }
1669 
1670 
1671 
1672 /*
1673 ==============
1674 CG_WeaponAnimation
1675 ==============
1676 */
1677 
1678 //----(SA)	modified.  this is now client-side only (server does not dictate weapon animation info)
CG_WeaponAnimation(playerState_t * ps,weaponInfo_t * weapon,int * weapOld,int * weap,float * weapBackLerp)1679 static void CG_WeaponAnimation( playerState_t *ps, weaponInfo_t *weapon, int *weapOld, int *weap, float *weapBackLerp ) {
1680 
1681 	centity_t *cent = &cg.predictedPlayerEntity;
1682 	clientInfo_t *ci = &cgs.clientinfo[ ps->clientNum ];
1683 
1684 	if ( cg_noPlayerAnims.integer ) {
1685 		*weapOld = *weap = 0;
1686 		return;
1687 	}
1688 
1689 	CG_RunWeapLerpFrame( ci, weapon, &cent->pe.weap, ps->weapAnim, 1 );
1690 
1691 	*weapOld        = cent->pe.weap.oldFrame;
1692 	*weap           = cent->pe.weap.frame;
1693 	*weapBackLerp   = cent->pe.weap.backlerp;
1694 
1695 	if ( cg_debugAnim.integer == 3 ) {
1696 		CG_Printf( "oldframe: %d   frame: %d   backlerp: %f\n", cent->pe.weap.oldFrame, cent->pe.weap.frame, cent->pe.weap.backlerp );
1697 	}
1698 }
1699 
1700 ////////////////////////////////////////////////////////////////////////
1701 ////////////////////////////////////////////////////////////////////////
1702 
1703 
1704 // (SA) it wasn't used anyway
1705 
1706 
1707 /*
1708 ==============
1709 CG_CalculateWeaponPosition
1710 ==============
1711 */
CG_CalculateWeaponPosition(vec3_t origin,vec3_t angles)1712 static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
1713 	float scale;
1714 	int delta;
1715 	float fracsin, leanscale;
1716 
1717 	VectorCopy( cg.refdef.vieworg, origin );
1718 	VectorCopy( cg.refdefViewAngles, angles );
1719 
1720 	// adjust 'lean' into weapon
1721 	if ( cg.predictedPlayerState.leanf != 0 ) {
1722 		vec3_t right, up;
1723 
1724 		leanscale = 1.0f;
1725 
1726 		switch ( cg.predictedPlayerState.weapon ) {
1727 		case WP_GARAND:
1728 			leanscale = 3.0f;
1729 			break;
1730 		case WP_FLAMETHROWER:
1731 		case WP_TESLA:
1732 		case WP_MAUSER:
1733 			leanscale = 2.0f;
1734 			break;
1735 
1736 			// never adjust
1737 		case WP_KNIFE:
1738 		case WP_GRENADE_LAUNCHER:
1739 		case WP_GRENADE_PINEAPPLE:
1740 			break;
1741 
1742 			// adjust when leaning right (in case of reload)
1743 		default:
1744 			if ( cg.predictedPlayerState.leanf > 0 ) {
1745 				leanscale = 1.3f;
1746 			}
1747 			break;
1748 		}
1749 
1750 		// reverse the roll on the weapon so it stays relatively level
1751 		angles[ROLL] -= cg.predictedPlayerState.leanf / ( leanscale * 2.0f );
1752 		AngleVectors( angles, NULL, right, up );
1753 		VectorMA( origin, angles[ROLL], right, origin );
1754 
1755 		// pitch the gun down a bit to show that firing is not allowed when leaning
1756 		angles[PITCH] += ( fabs( cg.predictedPlayerState.leanf ) / 2.0f );
1757 
1758 		// this gives you some impression that the weapon stays in relatively the same
1759 		// position while you lean, so you appear to 'peek' over the weapon
1760 		AngleVectors( cg.refdefViewAngles, NULL, right, NULL );
1761 		VectorMA( origin, -cg.predictedPlayerState.leanf / 4.0f, right, origin );
1762 	}
1763 
1764 
1765 	// on odd legs, invert some angles
1766 	if ( cg.bobcycle & 1 ) {
1767 		scale = -cg.xyspeed;
1768 	} else {
1769 		scale = cg.xyspeed;
1770 	}
1771 
1772 	// gun angles from bobbing
1773 
1774 	angles[ROLL] += scale * cg.bobfracsin * 0.005;
1775 	angles[YAW] += scale * cg.bobfracsin * 0.01;
1776 	angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
1777 
1778 	// drop the weapon when landing
1779 	delta = cg.time - cg.landTime;
1780 	if ( delta < LAND_DEFLECT_TIME ) {
1781 		origin[2] += cg.landChange * 0.25 * delta / LAND_DEFLECT_TIME;
1782 	} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
1783 		origin[2] += cg.landChange * 0.25 *
1784 					 ( LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta ) / LAND_RETURN_TIME;
1785 	}
1786 
1787 #if 0
1788 	// drop the weapon when stair climbing
1789 	delta = cg.time - cg.stepTime;
1790 	if ( delta < STEP_TIME / 2 ) {
1791 		origin[2] -= cg.stepChange * 0.25 * delta / ( STEP_TIME / 2 );
1792 	} else if ( delta < STEP_TIME ) {
1793 		origin[2] -= cg.stepChange * 0.25 * ( STEP_TIME - delta ) / ( STEP_TIME / 2 );
1794 	}
1795 #endif
1796 
1797 	// idle drift
1798 //----(SA) adjustment for MAX KAUFMAN
1799 //	scale = cg.xyspeed + 40;
1800 	scale = 80;
1801 //----(SA)	end
1802 	fracsin = sin( cg.time * 0.001 );
1803 	angles[ROLL] += scale * fracsin * 0.01;
1804 	angles[YAW] += scale * fracsin * 0.01;
1805 	angles[PITCH] += scale * fracsin * 0.01;
1806 
1807 	// RF, subtract the kickAngles
1808 	VectorMA( angles, -1.0, cg.kickAngles, angles );
1809 
1810 }
1811 
1812 
1813 // Ridah
1814 /*
1815 ===============
1816 CG_FlamethrowerFlame
1817 ===============
1818 */
CG_FlamethrowerFlame(centity_t * cent,vec3_t origin)1819 static void CG_FlamethrowerFlame( centity_t *cent, vec3_t origin ) {
1820 
1821 	if ( cent->currentState.weapon != WP_FLAMETHROWER ) {
1822 		return;
1823 	}
1824 
1825 	if ( cent->currentState.number == cg.snap->ps.clientNum ) {
1826 		if ( cg.snap->ps.weapon != WP_FLAMETHROWER || ( cg.snap->ps.weaponstate != WEAPON_FIRING && cg.snap->ps.weaponstate != WEAPON_READY ) ) {
1827 			return;
1828 		}
1829 	}
1830 
1831 	CG_FireFlameChunks( cent, origin, cent->lerpAngles, 1.0, qtrue, 0 );
1832 
1833 	return;
1834 }
1835 // done.
1836 
1837 /*
1838 ======================
1839 CG_MachinegunSpinAngle
1840 ======================
1841 */
1842 /*
1843 //#define		SPIN_SPEED	0.9
1844 //#define		COAST_TIME	1000
1845 #define		SPIN_SPEED	1
1846 #define		COAST_TIME	2000
1847 
1848 static float	CG_MachinegunSpinAngle( centity_t *cent ) {
1849 	int		delta;
1850 	float	angle;
1851 	float	speed;
1852 
1853 	delta = cg.time - cent->pe.barrelTime;
1854 	if ( cent->pe.barrelSpinning ) {
1855 		angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
1856 	} else {
1857 		if ( delta > COAST_TIME ) {
1858 			delta = COAST_TIME;
1859 		}
1860 
1861 		speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
1862 		angle = cent->pe.barrelAngle + delta * speed;
1863 	}
1864 
1865 	if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
1866 		cent->pe.barrelTime = cg.time;
1867 		cent->pe.barrelAngle = AngleMod( angle );
1868 		cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
1869 	}
1870 
1871 	return angle;
1872 }
1873 */
1874 
1875 /*
1876 ==============
1877 CG_TeslaSpinAngle
1878 ==============
1879 */
1880 //#define TESLA_SPINSPEED .2
1881 //#define TESLA_COASTTIME	2000
1882 #define TESLA_SPINSPEED .05
1883 #define TESLA_IDLESPEED .15
1884 #define TESLA_COASTTIME 1000
1885 
CG_TeslaSpinAngle(centity_t * cent)1886 static float CG_TeslaSpinAngle( centity_t *cent ) {
1887 	int	delta;
1888 	float	angle;
1889 //	float	speed;
1890 
1891 	delta = cg.time - cent->pe.barrelTime;
1892 
1893 	angle = cent->pe.barrelAngle;
1894 
1895 	if ( cent->currentState.eFlags & EF_FIRING ) {
1896 		angle += delta * TESLA_SPINSPEED;
1897 	} else {
1898 		angle += delta * TESLA_IDLESPEED;
1899 	}
1900 
1901 	cent->pe.barrelAngle = AngleMod( angle );
1902 
1903 	cent->pe.barrelTime = cg.time;
1904 
1905 	return AngleMod( angle );
1906 
1907 //----(SA)	trying new tesla effect scheme for MK
1908 //	angle = -(cent->pe.barrelAngle + delta * TESLA_SPINSPEED);
1909 //	cent->pe.barrelAngle = AngleMod( angle );
1910 
1911 //	if(cent->currentState.eFlags & EF_FIRING)
1912 //		cent->pe.barrelAngle += delta * TESLA_SPINSPEED;
1913 //	else
1914 //		cent->pe.barrelAngle += delta * TESLA_IDLESPEED;
1915 
1916 #if 0
1917 	return AngleMod( cent->pe.barrelAngle );
1918 
1919 	return angle;
1920 
1921 
1922 	if ( cent->pe.barrelSpinning ) {
1923 		angle = -( cent->pe.barrelAngle + delta * TESLA_SPINSPEED );
1924 	} else {
1925 		if ( delta > TESLA_COASTTIME ) {
1926 			delta = TESLA_COASTTIME;
1927 		}
1928 
1929 		speed = 0.5 * ( TESLA_SPINSPEED + (float)( TESLA_COASTTIME - delta ) / TESLA_COASTTIME );
1930 		angle = -( cent->pe.barrelAngle + delta * speed );
1931 	}
1932 
1933 	if ( cent->pe.barrelSpinning == !( cent->currentState.eFlags & EF_FIRING ) ) {
1934 		cent->pe.barrelTime = cg.time;
1935 		cent->pe.barrelAngle = AngleMod( angle );
1936 		cent->pe.barrelSpinning = !!( cent->currentState.eFlags & EF_FIRING );
1937 	}
1938 
1939 	return angle;
1940 #endif
1941 }
1942 
1943 //----(SA)	added
1944 
1945 /*
1946 ======================
1947 CG_VenomSpinAngle
1948 ======================
1949 */
1950 
1951 #define     VENOM_LOADTIME 2000
1952 #define     VENOM_DELTATIME ( VENOM_LOADTIME / 10 )     // as there are 10 shots to be loaded
1953 
1954 #define     SPIN_SPEED  1
1955 #define     COAST_TIME  2000
1956 
CG_VenomSpinAngle(centity_t * cent)1957 static float CG_VenomSpinAngle( centity_t *cent ) {
1958 	int delta;
1959 	float angle;
1960 	float speed;
1961 	qboolean firing;
1962 
1963 	firing = (qboolean)( cent->currentState.eFlags & EF_FIRING );
1964 
1965 	if ( cg.snap->ps.weaponstate != WEAPON_FIRING ) { // (SA) this seems better
1966 		firing = qfalse;
1967 	}
1968 
1969 	delta = cg.time - cent->pe.barrelTime;
1970 	if ( cent->pe.barrelSpinning ) {
1971 		angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
1972 	} else {
1973 		if ( delta > COAST_TIME ) {
1974 			delta = COAST_TIME;
1975 		}
1976 
1977 		speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
1978 		angle = cent->pe.barrelAngle + delta * speed;
1979 	}
1980 
1981 	if ( cent->pe.barrelSpinning == !firing ) {
1982 		cent->pe.barrelTime = cg.time;
1983 		cent->pe.barrelAngle = AngleMod( angle );
1984 		cent->pe.barrelSpinning = !!firing;
1985 
1986 		// just switching between not spinning and spinning, play the appropriate weapon sound
1987 		if ( cent->pe.barrelSpinning ) {
1988 			if ( cg_weapons[WP_VENOM].spinupSound ) {
1989 				trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cg_weapons[WP_VENOM].spinupSound );
1990 			}
1991 		} else {
1992 			if ( cg_weapons[WP_VENOM].spindownSound ) {
1993 				trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cg_weapons[WP_VENOM].spindownSound );
1994 			}
1995 		}
1996 
1997 	}
1998 
1999 	return angle;
2000 }
2001 
2002 
2003 
2004 
2005 /*
2006 ==============
2007 CG_DrawRealWeapons
2008 ==============
2009 */
CG_DrawRealWeapons(centity_t * cent)2010 qboolean CG_DrawRealWeapons( centity_t *cent ) {
2011 
2012 	switch ( cent->currentState.aiChar ) {
2013 	case AICHAR_LOPER:
2014 	case AICHAR_SUPERSOLDIER:       //----(SA)	added
2015 	case AICHAR_PROTOSOLDIER:
2016 	case AICHAR_ZOMBIE:
2017 	case AICHAR_HELGA:      //----(SA)	added	// boss1 is now helga-blob
2018 	case AICHAR_WARZOMBIE:
2019 		return qfalse;
2020 	}
2021 
2022 	return qtrue;
2023 }
2024 
2025 
2026 /*
2027 ========================
2028 CG_AddWeaponWithPowerups
2029 ========================
2030 */
CG_AddWeaponWithPowerups(refEntity_t * gun,int powerups,playerState_t * ps,centity_t * cent)2031 static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups, playerState_t *ps, centity_t *cent ) {
2032 
2033 	// add powerup effects
2034 	if ( powerups & ( 1 << PW_INVIS ) ) {
2035 		gun->customShader = cgs.media.invisShader;
2036 		trap_R_AddRefEntityToScene( gun );
2037 	} else {
2038 		trap_R_AddRefEntityToScene( gun );
2039 
2040 		if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
2041 			gun->customShader = cgs.media.battleWeaponShader;
2042 			trap_R_AddRefEntityToScene( gun );
2043 		}
2044 		if ( powerups & ( 1 << PW_QUAD ) ) {
2045 			gun->customShader = cgs.media.quadWeaponShader;
2046 			trap_R_AddRefEntityToScene( gun );
2047 		}
2048 	}
2049 /*
2050 	if (ps && ps->clientNum == cg.snap->ps.clientNum) {
2051 		float	alpha, adjust;
2052 		weaponInfo_t	*weapon;
2053 
2054 		weapon = &cg_weapons[ps->weapon];
2055 		//if (gun->hModel == weapon->handsModel)
2056 //		if (cg.snap->ps.onFireStart)
2057 		{
2058 
2059 			// add the flames if on fire
2060 //			alpha = 2.0 * (float)(FIRE_FLASH_TIME - (cg.time - cg.snap->ps.onFireStart))/FIRE_FLASH_TIME;
2061 alpha = 1;
2062 			if (alpha > 0) {
2063 				if (alpha >= 1.0) {
2064 					alpha = 1.0;
2065 				}
2066 				gun->shaderRGBA[3] = (unsigned char)(255.0*alpha);
2067 				// calc the fireRiseDir from the velocity
2068 				VectorNegate( cg.snap->ps.velocity, gun->fireRiseDir );
2069 				VectorNormalize( gun->fireRiseDir );
2070 				gun->fireRiseDir[2] += 1;
2071 				if (VectorNormalize( gun->fireRiseDir ) < 1) {
2072 					VectorClear( gun->fireRiseDir );
2073 					gun->fireRiseDir[2] = 1;
2074 				}
2075 				// now move towards the newDir
2076 				adjust = 5.0*(0.001*cg.frametime);
2077 				VectorMA( cg.v_fireRiseDir, adjust, gun->fireRiseDir, cg.v_fireRiseDir );
2078 				if (VectorNormalize( cg.v_fireRiseDir ) <= 0.1) {
2079 					VectorCopy( gun->fireRiseDir, cg.v_fireRiseDir );
2080 				}
2081 				VectorCopy( cg.v_fireRiseDir, gun->fireRiseDir );
2082 
2083 //				gun->reFlags |= REFLAG_ONLYHAND;
2084 gun->customShader = cgs.media.dripWetShader2;
2085 //				gun->customShader = cgs.media.onFireShader;
2086 				trap_R_AddRefEntityToScene( gun );
2087 //				gun->shaderTime = 500;
2088 //				trap_R_AddRefEntityToScene( gun );
2089 gun->customShader = cgs.media.dripWetShader;
2090 //				gun->customShader = cgs.media.onFireShader2;
2091 				trap_R_AddRefEntityToScene( gun );
2092 //				gun->reFlags &= ~REFLAG_ONLYHAND;
2093 			}
2094 		}
2095 	}
2096 */
2097 }
2098 
2099 /*
2100 ==============
2101 CG_PlayerTeslaCoilFire
2102 
2103   TODO: this needs to be fixed for multiplay. entities being hurt need to be sent
2104   by server to all clients, so they draw the correct effects.
2105 ==============
2106 */
CG_PlayerTeslaCoilFire(centity_t * cent,vec3_t flashorigin)2107 void CG_PlayerTeslaCoilFire( centity_t *cent, vec3_t flashorigin ) {
2108 
2109 #define	TESLA_LIGHTNING_POINT_TIMEOUT	3000
2110 #define	TESLA_LIGHTNING_MAX_DIST	( cent->currentState.aiChar == AICHAR_SUPERSOLDIER ? TESLA_SUPERSOLDIER_RANGE : TESLA_RANGE )     // use these to perhaps vary the distance according to aiming
2111 #define	TESLA_LIGHTNING_NORMAL_DIST	( TESLA_RANGE / 2.0 )
2112 #define	TESLA_MAX_POINT_TESTS			10
2113 #define	TESLA_MAX_POINT_TESTS_PERFRAME	20
2114 
2115 	int i, j, pointTests = 0;
2116 	vec3_t testPos, tagPos, vec;
2117 	trace_t tr;
2118 	float maxDist;
2119 	int numPoints;
2120 	vec3_t viewAngles, viewDir;
2121 	int visEnemies[16];
2122 	float visDists[16];
2123 	int visEnemiesSorted[MAX_TESLA_BOLTS];
2124 	int numEnemies, numSorted = 0, best;
2125 	float bestDist;
2126 	centity_t *ctrav;
2127 	vec3_t traceOrg;
2128 	int playerTeam;
2129 
2130 	if ( cent->currentState.weapon != WP_TESLA ) {
2131 		return;
2132 	}
2133 
2134 // JPW NERVE no tesla in multiplayer
2135 	if ( cg_gameType.integer != GT_SINGLE_PLAYER ) {
2136 		return;
2137 	}
2138 
2139 	//if (cent->currentState.number == cg.snap->ps.clientNum)
2140 	//	VectorCopy( cg.snap->ps.viewangles, viewAngles );
2141 	//else
2142 		VectorCopy( cent->lerpAngles, viewAngles );
2143 
2144 	AngleVectors( viewAngles, viewDir, NULL, NULL );
2145 
2146 	if ( cent->currentState.number == cg.snap->ps.clientNum ) {
2147 		VectorCopy( cg.snap->ps.origin, traceOrg );
2148 		playerTeam = cg.snap->ps.teamNum;
2149 	} else {
2150 		VectorCopy( cent->lerpOrigin, traceOrg );
2151 		playerTeam = cent->currentState.teamNum;
2152 	}
2153 
2154 	maxDist = TESLA_LIGHTNING_MAX_DIST;
2155 	numPoints = MAX_TESLA_BOLTS;
2156 
2157 	VectorCopy( flashorigin, tagPos );
2158 
2159 	// first, build a list of visible enemies that can be hurt by this tesla, then filter by distance
2160 	if ( !cent->pe.teslaDamageApplyTime || cent->pe.teslaDamageApplyTime < cg.time - 200 ) {
2161 		numEnemies = 0;
2162 		// check the local playing client
2163 		VectorSubtract( cg.snap->ps.origin, traceOrg, vec );
2164 		VectorNormalize( vec );
2165 		if ( ( cent != &cg_entities[cg.snap->ps.clientNum] ) &&
2166 			 ( cg.snap->ps.teamNum != playerTeam ) &&
2167 			 ( Distance( tagPos, cg.snap->ps.origin ) < TESLA_LIGHTNING_MAX_DIST ) &&
2168 			 ( DotProduct( viewDir, vec ) > 0.8 ) ) {
2169 			CG_Trace( &tr, traceOrg, NULL, NULL, cg.snap->ps.origin, cg.snap->ps.clientNum, MASK_SHOT & ~( CONTENTS_BODY ) );
2170 			if ( tr.fraction == 1 || tr.entityNum == cg.snap->ps.clientNum ) {
2171 				visDists[numEnemies] = Distance( tagPos, cg.snap->ps.origin );
2172 				visEnemies[numEnemies++] = cg.snap->ps.clientNum;
2173 			} else {    // try head
2174 				VectorCopy( cg.snap->ps.origin, vec );
2175 				vec[2] += cg.snap->ps.viewheight;
2176 				CG_Trace( &tr, tagPos, NULL, NULL, vec, cg.snap->ps.clientNum, MASK_SHOT & ~( CONTENTS_BODY ) );
2177 				if ( tr.fraction == 1 || tr.entityNum == cg.snap->ps.clientNum ) {
2178 					visDists[numEnemies] = Distance( tagPos, cg.snap->ps.origin );
2179 					visEnemies[numEnemies++] = cg.snap->ps.clientNum;
2180 				} else {    // try body, from tag
2181 					VectorCopy( cg.snap->ps.origin, vec );
2182 					CG_Trace( &tr, tagPos, NULL, NULL, vec, cg.snap->ps.clientNum, MASK_SHOT & ~( CONTENTS_BODY ) );
2183 					if ( tr.fraction == 1 || tr.entityNum == cg.snap->ps.clientNum ) {
2184 						visDists[numEnemies] = Distance( tagPos, cg.snap->ps.origin );
2185 						visEnemies[numEnemies++] = cg.snap->ps.clientNum;
2186 					}
2187 				}
2188 			}
2189 		}
2190 
2191 		if ( cgs.localServer && cgs.gametype == GT_SINGLE_PLAYER ) {
2192 			// check for AI's getting hurt (TODO: bot support?)
2193 			for ( ctrav = cg_entities, i = 0; i < cgs.maxclients && numEnemies < 16; ctrav++, i++ ) {
2194 				// RF, proto and supersoldier are invulnerable to tesla
2195 /*				switch ( ctrav->currentState.aiChar ) {
2196 				case AICHAR_SUPERSOLDIER:
2197 				case AICHAR_PROTOSOLDIER:
2198 					continue;
2199 				}
2200 */                                                                                                                              //
2201 				if ( ctrav->currentState.aiChar &&
2202 					 ( ctrav != cent ) &&
2203 					 ( ctrav->currentState.teamNum != playerTeam ) &&
2204 					 !( ctrav->currentState.eFlags & EF_DEAD ) &&
2205 					 ctrav->currentValid && // is in the visible frame
2206 					 ( Distance( tagPos, ctrav->lerpOrigin ) < TESLA_LIGHTNING_MAX_DIST ) ) {
2207 					VectorSubtract( ctrav->lerpOrigin, traceOrg, vec );
2208 					VectorNormalize( vec );
2209 
2210 					if ( DotProduct( viewDir, vec ) > 0.8 ) {
2211 						CG_Trace( &tr, traceOrg, NULL, NULL, ctrav->lerpOrigin, ctrav->currentState.number, MASK_SHOT & ~CONTENTS_BODY );
2212 						if ( tr.fraction == 1 || tr.entityNum == ctrav->currentState.number ) {
2213 							visDists[numEnemies] = Distance( tagPos, ctrav->lerpOrigin );
2214 							visEnemies[numEnemies++] = ctrav->currentState.number;
2215 						}
2216 					}
2217 				}
2218 			}
2219 		}
2220 
2221 		// now sort by distance
2222 		for ( j = 0; j < MAX_TESLA_BOLTS; j++ ) {
2223 			visEnemiesSorted[j] = -1;
2224 
2225 			bestDist = 99999;
2226 			best = -1;
2227 			for ( i = 0; i < numEnemies; i++ ) {
2228 				if ( visEnemies[i] < 0 ) {
2229 					continue;
2230 				}
2231 				if ( visDists[i] < bestDist ) {
2232 					bestDist = visDists[i];
2233 					visEnemiesSorted[j] = visEnemies[i];
2234 					best = i;
2235 				}
2236 			}
2237 
2238 			if ( best >= 0 ) {
2239 				visEnemies[best] = -1;
2240 				numSorted = j + 1;
2241 			}
2242 		}
2243 
2244 		// now fill in the teslaEnemy[]'s
2245 		for ( i = 0; i < MAX_TESLA_BOLTS; i++ ) {
2246 			if ( numSorted && i / numSorted < 1 /*( MAX_TESLA_BOLTS / 3 )*/ ) {  // bolts per enemy
2247 				j = i % numSorted;
2248 				cent->pe.teslaEnemy[i] = visEnemiesSorted[j];
2249 				// apply damage
2250 				CG_ClientDamage( visEnemiesSorted[j], cent->currentState.number, CLDMG_TESLA );
2251 				// show the effect
2252 				cg_entities[ visEnemiesSorted[j] ].pe.teslaDamagedTime = cg.time;
2253 			} else {
2254 				if ( cent->pe.teslaEnemy[i] >= 0 ) {
2255 					cent->pe.teslaEndPointTimes[i] = 0; // make sure we find a new spot
2256 				}
2257 				cent->pe.teslaEnemy[i] = -1;
2258 			}
2259 		}
2260 		cent->pe.teslaDamageApplyTime = cg.time;
2261 	}
2262 
2263 	for ( i = 0; i < numPoints; i++ ) {
2264 
2265 		//if (!(rand()%3))
2266 		//	continue;
2267 
2268 		VectorSubtract( cent->pe.teslaEndPoints[i], tagPos, vec );
2269 		VectorNormalize( vec );
2270 
2271 		// if this point has timed out, find a new spot
2272 		if ( cent->pe.teslaEnemy[i] >= 0 ) {
2273 			// attacking the player
2274 			VectorSet( testPos, 6 * crandom(),
2275 					   6 * crandom(),
2276 					   20 * crandom() - 8 );
2277 			//VectorClear( testPos );
2278 			if ( cent->pe.teslaEnemy[i] != cg.snap->ps.clientNum ) {
2279 				VectorAdd( testPos, cg_entities[cent->pe.teslaEnemy[i]].lerpOrigin, testPos );
2280 			} else {
2281 				VectorAdd( testPos, cg.snap->ps.origin, testPos );
2282 			}
2283 			cent->pe.teslaEndPointTimes[i] = cg.time; // - rand()%(TESLA_LIGHTNING_POINT_TIMEOUT/2);
2284 			VectorCopy( testPos, cent->pe.teslaEndPoints[i] );
2285 		} else if ( ( !cent->pe.teslaEndPointTimes[i] ) ||
2286 					( cent->pe.teslaEndPointTimes[i] > cg.time ) ||
2287 					( cent->pe.teslaEndPointTimes[i] < cg.time - TESLA_LIGHTNING_POINT_TIMEOUT ) ||
2288 					( VectorDistance( tagPos, cent->pe.teslaEndPoints[i] ) > maxDist ) ||
2289 					( DotProduct( viewDir, vec ) < 0.7 ) ) {
2290 
2291 			//if (cent->currentState.groundEntityNum == ENTITYNUM_NONE)
2292 			//	continue;	// must be on the ground
2293 
2294 			// find a new spot
2295 			for ( j = 0; j < TESLA_MAX_POINT_TESTS; j++ ) {
2296 				VectorSet( testPos, cg.refdef.fov_y * crandom() * 0.5,
2297 						   cg.refdef.fov_x * crandom() * 0.5,
2298 						   0 );
2299 				VectorAdd( viewAngles, testPos, testPos );
2300 				AngleVectors( testPos, vec, NULL, NULL );
2301 				VectorMA( tagPos, TESLA_LIGHTNING_NORMAL_DIST, vec, testPos );
2302 				// try a trace to find a world collision
2303 				CG_Trace( &tr, tagPos, NULL, NULL, testPos, cent->currentState.number, MASK_SHOT & ~CONTENTS_BODY );
2304 				if ( tr.fraction < 1 && tr.entityNum == ENTITYNUM_WORLD && !( tr.surfaceFlags & ( SURF_NOIMPACT | SURF_SKY ) ) ) {
2305 					// found a valid spot!
2306 					cent->pe.teslaEndPointTimes[i] = cg.time - rand() % ( TESLA_LIGHTNING_POINT_TIMEOUT / 2 );
2307 					VectorCopy( tr.endpos, cent->pe.teslaEndPoints[i] );
2308 					break;
2309 				}
2310 				if ( pointTests++ > TESLA_MAX_POINT_TESTS_PERFRAME ) {
2311 					j = TESLA_MAX_POINT_TESTS;
2312 					continue;
2313 				}
2314 			}
2315 			if ( j == TESLA_MAX_POINT_TESTS ) {
2316 				continue;   // just don't draw this point
2317 			}
2318 
2319 			// add an impact mark on the wall
2320 			VectorSubtract( cent->pe.teslaEndPoints[i], tagPos, vec );
2321 			VectorNormalize( vec );
2322 			VectorInverse( vec );
2323 			CG_ImpactMark( cgs.media.lightningHitWallShader, cent->pe.teslaEndPoints[i], vec, random() * 360, 0.2, 0.2, 0.2, 1.0, qtrue, 4, qfalse, 300 );
2324 		}
2325 		//
2326 		// we have a valid lightning point, so draw it
2327 		// sanity check though to make sure it's valid
2328 		if ( VectorDistance( tagPos, cent->pe.teslaEndPoints[i] ) <= maxDist ) {
2329 			CG_DynamicLightningBolt( cgs.media.lightningBoltShader, tagPos, cent->pe.teslaEndPoints[i], 1 + ( ( cg.time % ( ( i + 2 ) * ( i + 3 ) ) ) + i ) % 2, 20 + (float)( i % 3 ) * 5 + 6.0 * random(), ( cent->pe.teslaEnemy[i] < 0 ), 1.0, 0, i * i * 3 );
2330 
2331 			// play a zap sound
2332 			if ( cent->pe.lightningSoundTime < cg.time - 200 ) {
2333 				CG_SoundPlayIndexedScript( cgs.media.teslaZapScript, cent->pe.teslaEndPoints[i], ENTITYNUM_WORLD );
2334 				CG_SoundPlayIndexedScript( cgs.media.teslaZapScript, cent->lerpOrigin, ENTITYNUM_WORLD );
2335 				//trap_S_StartSound( cent->pe.teslaEndPoints[i], ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.lightningSounds[rand()%3] );
2336 				cent->pe.lightningSoundTime = cg.time + rand() % 200;
2337 			}
2338 		}
2339 	}
2340 
2341 	if ( cg.time % 3 ) {  // break it up a bit
2342 		// add the looping sound
2343 		CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.teslaLoopSound, 255 );
2344 	}
2345 
2346 	// drop a dynamic light out infront of us
2347 	AngleVectors( viewAngles, vec, NULL, NULL );
2348 	VectorMA( tagPos, 300, vec, testPos );
2349 	// try a trace to find a world collision
2350 	CG_Trace( &tr, tagPos, NULL, NULL, testPos, cent->currentState.number, MASK_SOLID );
2351 
2352 	if ( ( cg.time / 50 ) % ( 4 + ( cg.time % 4 ) ) == 0 ) {
2353 		// alt light
2354 		trap_R_AddLightToScene( tr.endpos, 256 + 600 * tr.fraction, 0.2, 0.6, 1, 1 );
2355 	} else if ( ( cg.time / 50 ) % ( 4 + ( cg.time % 4 ) ) == 1 ) {
2356 		// no light
2357 		//trap_R_AddLightToScene( tr.endpos, 128 + 500*tr.fraction, 1, 1, 1, 10 );
2358 	} else {
2359 		// blue light
2360 		trap_R_AddLightToScene( tr.endpos, 256 + 600 * tr.fraction, 0.2, 0.6, 1, 0 );
2361 	}
2362 
2363 	// shake the camera a bit
2364 	CG_StartShakeCamera( 0.05, 200, cent->lerpOrigin, 100 );
2365 }
2366 
2367 //----(SA)
2368 /*
2369 ==============
2370 CG_AddProtoWeapons
2371 ==============
2372 */
CG_AddProtoWeapons(refEntity_t * parent,playerState_t * ps,centity_t * cent)2373 void CG_AddProtoWeapons( refEntity_t *parent, playerState_t *ps, centity_t *cent ) {
2374 #if 0
2375 	refEntity_t gun;
2376 
2377 	if ( !( cent->currentState.aiChar == AICHAR_PROTOSOLDIER ) ) {
2378 		return;
2379 	}
2380 	memset( &gun, 0, sizeof( gun ) );
2381 	VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
2382 //	gun.hModel		= cgs.media.protoWeapon;
2383 	gun.shadowPlane = parent->shadowPlane;
2384 	gun.renderfx    = parent->renderfx;
2385 	CG_PositionEntityOnTag( &gun, parent, "tag_armright", 0, NULL );
2386 	CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups, ps, cent );
2387 #endif
2388 }
2389 //----(SA)	end
2390 
2391 
2392 
2393 // Ridah
2394 /*
2395 ==============
2396 CG_MonsterUsingWeapon
2397 ==============
2398 */
CG_MonsterUsingWeapon(centity_t * cent,int aiChar,int weaponNum)2399 qboolean CG_MonsterUsingWeapon( centity_t *cent, int aiChar, int weaponNum ) {
2400 	return ( cent->currentState.aiChar == aiChar ) && ( cent->currentState.weapon == weaponNum );
2401 }
2402 
2403 /*
2404 =============
2405 CG_AddPlayerWeapon
2406 
2407 Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL)
2408 The main player will have this called for BOTH cases, so effects like light and
2409 sound should only be done on the world model case.
2410 =============
2411 */
2412 static qboolean debuggingweapon = qfalse;
2413 
CG_AddPlayerWeapon(refEntity_t * parent,playerState_t * ps,centity_t * cent)2414 void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent ) {
2415 
2416 	refEntity_t gun;
2417 	refEntity_t barrel;
2418 	refEntity_t flash;
2419 	vec3_t angles;
2420 	weapon_t weaponNum, weapSelect;
2421 	weaponInfo_t    *weapon;
2422 	centity_t   *nonPredictedCent;
2423 	qboolean firing;    // Ridah
2424 
2425 	qboolean akimboFire = qfalse;       //----(SA)	added
2426 
2427 	qboolean playerScaled;
2428 	qboolean drawpart, drawrealweap;
2429 	int i;
2430 	qboolean isPlayer;
2431 
2432 	// (SA) might as well have this check consistant throughout the routine
2433 	isPlayer = (qboolean)( cent->currentState.clientNum == cg.snap->ps.clientNum );
2434 
2435 	weaponNum = cent->currentState.weapon;
2436 	weapSelect = cg.weaponSelect;
2437 
2438 	if ( ps && cg.cameraMode ) {
2439 		return;
2440 	}
2441 
2442 	// don't draw any weapons when the binocs are up
2443 	if ( cent->currentState.eFlags & EF_ZOOMING ) {
2444 		if ( isPlayer ) {
2445 			if ( !cg.renderingThirdPerson ) {
2446 				return;
2447 			}
2448 		} else {
2449 			return;
2450 		}
2451 	}
2452 
2453 	// don't draw weapon stuff when looking through a scope
2454 	if ( weaponNum == WP_SNOOPERSCOPE || weaponNum == WP_SNIPERRIFLE || weaponNum == WP_FG42SCOPE ||
2455 		 weapSelect == WP_SNOOPERSCOPE || weapSelect == WP_SNIPERRIFLE || weapSelect == WP_FG42SCOPE ) {
2456 		if ( isPlayer && !cg.renderingThirdPerson ) {
2457 			return;
2458 		}
2459 	}
2460 
2461 	// no weapon when on mg_42
2462 	if ( cent->currentState.eFlags & EF_MG42_ACTIVE ) {
2463 		return;
2464 	}
2465 
2466 	// DHM - Nerve :: Special case for WP_CLASS_SPECIAL
2467 	if ( cgs.gametype == GT_WOLF && weaponNum == WP_CLASS_SPECIAL ) {
2468 		switch ( cent->currentState.teamNum ) {
2469 		case PC_ENGINEER:
2470 			CG_RegisterWeapon( WP_CLASS_SPECIAL );
2471 			weapon = &cg_weapons[WP_CLASS_SPECIAL];
2472 			break;
2473 		case PC_MEDIC:
2474 			CG_RegisterWeapon( WP_MEDIC_HEAL );
2475 			weapon = &cg_weapons[WP_MEDIC_HEAL];
2476 			break;
2477 		case PC_LT:
2478 			CG_RegisterWeapon( WP_GRENADE_SMOKE );
2479 			weapon = &cg_weapons[WP_GRENADE_SMOKE];
2480 			break;
2481 		default:
2482 			CG_RegisterWeapon( weaponNum );
2483 			weapon = &cg_weapons[weaponNum];
2484 			break;
2485 		}
2486 	} else {
2487 		CG_RegisterWeapon( weaponNum );
2488 		weapon = &cg_weapons[weaponNum];
2489 	}
2490 	// dhm - end
2491 
2492 
2493 	if ( isPlayer ) {
2494 		akimboFire = BG_AkimboFireSequence( weaponNum, cg.predictedPlayerState.ammoclip[WP_AKIMBO], cg.predictedPlayerState.ammoclip[WP_COLT] );
2495 	} else if ( ps ) {
2496 		akimboFire = BG_AkimboFireSequence( weaponNum, ps->ammoclip[WP_AKIMBO], ps->ammoclip[WP_AKIMBO] );
2497 	}
2498 
2499 	// add the weapon
2500 	memset( &gun, 0, sizeof( gun ) );
2501 	VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
2502 	gun.shadowPlane = parent->shadowPlane;
2503 	gun.renderfx = parent->renderfx;
2504 
2505 	// set custom shading for railgun refire rate
2506 	if ( ps ) {
2507 		gun.shaderRGBA[0] = 255;
2508 		gun.shaderRGBA[1] = 255;
2509 		gun.shaderRGBA[2] = 255;
2510 		gun.shaderRGBA[3] = 255;
2511 	}
2512 
2513 	if ( ps ) {
2514 		gun.hModel = weapon->weaponModel[W_FP_MODEL];
2515 	} else {
2516 		CG_AddProtoWeapons( parent, ps, cent );
2517 		// skeletal guys use a different third person weapon (for different tag business)
2518 		if ( cgs.clientinfo[ cent->currentState.clientNum ].isSkeletal && weapon->weaponModel[W_SKTP_MODEL] ) {
2519 			gun.hModel = weapon->weaponModel[W_SKTP_MODEL];
2520 		} else {
2521 			gun.hModel = weapon->weaponModel[W_TP_MODEL];
2522 		}
2523 	}
2524 
2525 	if ( !gun.hModel ) {
2526 		if ( debuggingweapon ) {
2527 			CG_Printf( "returning due to: !gun.hModel\n" );
2528 		}
2529 		return;
2530 	}
2531 
2532 	if ( weaponNum == WP_GAUNTLET ) {  // (SA) this is the 'knife'.  no model yet, so we can give it to the zombie and have him visually 'unarmed'
2533 		if ( debuggingweapon ) {
2534 			CG_Printf( "returning due to: weaponNum == WP_GAUNTLET\n" );
2535 		}
2536 		return;
2537 	}
2538 
2539 	if ( !ps && cg.snap->ps.pm_flags & PMF_LADDER && isPlayer ) {      //----(SA) player on ladder
2540 		if ( debuggingweapon ) {
2541 			CG_Printf( "returning due to: !ps && cg.snap->ps.pm_flags & PMF_LADDER\n" );
2542 		}
2543 		return;
2544 	}
2545 
2546 	if ( !ps ) {
2547 		// add weapon ready sound
2548 		cent->pe.lightningFiring = qfalse;
2549 		if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) {
2550 			// lightning gun and guantlet make a different sound when fire is held down
2551 			CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound, 255 );
2552 			cent->pe.lightningFiring = qtrue;
2553 		} else if ( weapon->readySound ) {
2554 			CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound, 255 );
2555 		}
2556 	}
2557 
2558 
2559 	// Ridah
2560 	firing = ( ( cent->currentState.eFlags & EF_FIRING ) != 0 );
2561 
2562 	CG_PositionEntityOnTag( &gun, parent, "tag_weapon", 0, NULL );
2563 
2564 	playerScaled = (qboolean)( cgs.clientinfo[ cent->currentState.clientNum ].playermodelScale[0] != 0 );
2565 	if ( !ps && playerScaled ) {   // don't "un-scale" weap up in 1st person
2566 		for ( i = 0; i < 3; i++ ) {  // scale weapon back up so it doesn't pick up the adjusted scale of the character models.
2567 									 // this will affect any parts attached to the gun as well (barrel/bolt/flash/brass/etc.)
2568 			VectorScale( gun.axis[i], 1.0 / ( cgs.clientinfo[ cent->currentState.clientNum ].playermodelScale[i] ), gun.axis[i] );
2569 		}
2570 	}
2571 
2572 	// characters that draw their own special weapon model will not draw the standard ones
2573 	if ( CG_DrawRealWeapons( cent ) ) {
2574 		drawrealweap = qtrue;
2575 	} else {
2576 		drawrealweap = qfalse;
2577 	}
2578 
2579 	if ( ps ) {
2580 		drawpart = CG_GetPartFramesFromWeap( cent, &gun, parent, W_MAX_PARTS, weapon );   // W_MAX_PARTS specifies this as the primary view model
2581 	} else {
2582 		drawpart = qtrue;
2583 	}
2584 
2585 	if ( drawpart && drawrealweap ) {
2586 		CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups, ps, cent );
2587 	}
2588 
2589 	if ( isPlayer ) {
2590 		refEntity_t brass;
2591 
2592 		// opposite tag in akimbo, since at this point the weapon
2593 		// has fired and the fire seq has switched over
2594 		if ( weaponNum == WP_AKIMBO && akimboFire ) {
2595 			CG_PositionRotatedEntityOnTag( &brass, &gun, "tag_brass2" );
2596 		} else {
2597 			CG_PositionRotatedEntityOnTag( &brass, &gun, "tag_brass" );
2598 		}
2599 
2600 		VectorCopy( brass.origin, ejectBrassCasingOrigin );
2601 	}
2602 
2603 	memset( &barrel, 0, sizeof( barrel ) );
2604 	VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
2605 	barrel.shadowPlane = parent->shadowPlane;
2606 	barrel.renderfx = parent->renderfx;
2607 
2608 	// add barrels
2609 	// attach generic weapon parts to the first person weapon.
2610 	// if a barrel should be attached for third person, add it in the (!ps) section below
2611 	angles[YAW] = angles[PITCH] = 0;
2612 
2613 	if ( ps ) {
2614 		qboolean spunpart;
2615 
2616 		for ( i = W_PART_1; i < W_MAX_PARTS; i++ ) {
2617 
2618 			spunpart = qfalse;
2619 			barrel.hModel = weapon->wpPartModels[W_FP_MODEL][i];
2620 
2621 			// check for spinning
2622 			if ( weaponNum == WP_VENOM ) {
2623 				if ( i == W_PART_1 ) {
2624 					angles[ROLL] = CG_VenomSpinAngle( cent );
2625 					spunpart = qtrue;
2626 				} else if ( i == W_PART_2 )    {
2627 					angles[ROLL] = -CG_VenomSpinAngle( cent );
2628 					spunpart = qtrue;
2629 				}
2630 				// 'blurry' barel when firing
2631 				// (SA) not right now.  at the moment, just spin the belt when firing, no swapout
2632 				else if ( i == W_PART_3 ) {
2633 					if ( ( cent->pe.weap.animationNumber & ~ANIM_TOGGLEBIT ) == WEAP_ATTACK1 ) {
2634 						barrel.hModel = weapon->wpPartModels[W_FP_MODEL][i];
2635 						angles[ROLL] = -CG_VenomSpinAngle( cent );
2636 						angles[ROLL] = -( angles[ROLL] / 8.0f );
2637 					} else {
2638 						angles[ROLL] = 0;
2639 					}
2640 					spunpart = qtrue;
2641 				}
2642 			} else if ( weaponNum == WP_TESLA ) {
2643 				if ( i == W_PART_1 || i == W_PART_2 ) {
2644 					angles[ROLL] = CG_TeslaSpinAngle( cent );
2645 					spunpart = qtrue;
2646 				}
2647 			}
2648 
2649 			if ( spunpart ) {
2650 				AnglesToAxis( angles, barrel.axis );
2651 			}
2652 			// end spinning
2653 
2654 
2655 			if ( barrel.hModel ) {
2656 				if ( i == W_PART_1 ) {
2657 					if ( spunpart ) {
2658 						CG_PositionRotatedEntityOnTag( &barrel, parent, "tag_barrel" );
2659 					} else { CG_PositionEntityOnTag( &barrel, parent, "tag_barrel", 0, NULL );}
2660 				} else {
2661 					if ( spunpart ) {
2662 						CG_PositionRotatedEntityOnTag( &barrel, parent, va( "tag_barrel%d", i + 1 ) );
2663 					} else { CG_PositionEntityOnTag( &barrel, parent, va( "tag_barrel%d", i + 1 ), 0, NULL );}
2664 				}
2665 
2666 				drawpart = CG_GetPartFramesFromWeap( cent, &barrel, parent, i, weapon );
2667 
2668 				if ( drawpart && drawrealweap ) {
2669 					CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent );
2670 				}
2671 			}
2672 		}
2673 	} else {    // weapons with barrels drawn in third person
2674 		if ( drawrealweap ) {
2675 			if ( weaponNum == WP_VENOM ) {
2676 
2677 				angles[ROLL] = CG_VenomSpinAngle( cent );
2678 				AnglesToAxis( angles, barrel.axis );
2679 
2680 				barrel.hModel = weapon->wpPartModels[W_TP_MODEL][W_PART_1];
2681 				CG_PositionRotatedEntityOnTag( &barrel, &gun, "tag_barrel" );
2682 				CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent );
2683 			}
2684 		}
2685 	}
2686 
2687 	// add the scope model to the rifle if you've got it
2688 	if ( isPlayer && !cg.renderingThirdPerson ) {      // (SA) for now just do it on the first person weapons
2689 		if ( weaponNum == WP_MAUSER ) {
2690 			if ( COM_BitCheck( cg.predictedPlayerState.weapons, WP_SNIPERRIFLE ) ) {
2691 				barrel.hModel = weapon->modModel[0];
2692 				if ( barrel.hModel ) {
2693 					CG_PositionEntityOnTag( &barrel, &gun, "tag_scope", 0, NULL );
2694 					CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent );
2695 				}
2696 			}
2697 		}
2698 	}
2699 
2700 
2701 	// make sure we aren't looking at cg.predictedPlayerEntity for LG
2702 	nonPredictedCent = &cg_entities[cent->currentState.clientNum];
2703 
2704 	// if the index of the nonPredictedCent is not the same as the clientNum
2705 	// then this is a fake player (like on the single player podiums), so
2706 	// go ahead and use the cent
2707 	if ( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) {
2708 		nonPredictedCent = cent;
2709 	}
2710 
2711 
2712 	// add the flash
2713 	memset( &flash, 0, sizeof( flash ) );
2714 	VectorCopy( parent->lightingOrigin, flash.lightingOrigin );
2715 	flash.shadowPlane = parent->shadowPlane;
2716 	flash.renderfx = parent->renderfx;
2717 
2718 	if ( ps ) {
2719 		flash.hModel = weapon->flashModel[W_FP_MODEL];
2720 	} else {
2721 		flash.hModel = weapon->flashModel[W_TP_MODEL];
2722 	}
2723 
2724 	angles[YAW]     = 0;
2725 	angles[PITCH]   = 0;
2726 	angles[ROLL]    = crandom() * 10;
2727 	AnglesToAxis( angles, flash.axis );
2728 
2729 	CG_PositionRotatedEntityOnTag( &flash, &gun, "tag_flash" );
2730 
2731 	// store this position for other cgame elements to access
2732 	cent->pe.gunRefEnt = gun;
2733 	cent->pe.gunRefEntFrame = cg.clientFrame;
2734 
2735 	if ( ( weaponNum == WP_FLAMETHROWER || weaponNum == WP_TESLA ) && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) {
2736 		// continuous flash
2737 
2738 	} else {
2739 
2740 		// continuous smoke after firing
2741 #define BARREL_SMOKE_TIME 1000
2742 
2743 		if ( ps || cg.renderingThirdPerson || !isPlayer ) {
2744 			if ( weaponNum == WP_VENOM || weaponNum == WP_STEN ) {
2745 				if ( !cg_paused.integer ) {    // don't add while paused
2746 					// hot smoking gun
2747 					if ( cg.time - cent->overheatTime < 3000 ) {
2748 						if ( !( rand() % 3 ) ) {
2749 							float alpha;
2750 							alpha = 1.0f - ( (float)( cg.time - cent->overheatTime ) / 3000.0f );
2751 							alpha *= 0.25f;     // .25 max alpha
2752 							if ( weaponNum == WP_VENOM ) { // silly thing that makes the smoke off the venom swirlier since it's spinning real fast
2753 								CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, tv( 0,0,1 ), 8, 1000, 8, 20, 70, alpha );
2754 							} else {
2755 								CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, tv( 0,0,1 ), 8, 1000, 8, 20, 30, alpha );
2756 							}
2757 						}
2758 					}
2759 				}
2760 
2761 			} else if ( weaponNum == WP_PANZERFAUST ) {
2762 				if ( !cg_paused.integer ) {    // don't add while paused
2763 					if ( cg.time - cent->muzzleFlashTime < BARREL_SMOKE_TIME ) {
2764 						if ( !( rand() % 5 ) ) {
2765 							float alpha;
2766 							alpha = 1.0f - ( (float)( cg.time - cent->muzzleFlashTime ) / (float)BARREL_SMOKE_TIME ); // what fraction of BARREL_SMOKE_TIME are we at
2767 							alpha *= 0.25f;     // .25 max alpha
2768 							CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, tv( 0,0,1 ), 8, 1000, 8, 20, 30, alpha );
2769 						}
2770 					}
2771 				}
2772 			}
2773 		}
2774 
2775 		// impulse flash
2776 		if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME ) {
2777 			// Ridah, blue ignition flame if not firing flamer
2778 			if ( weaponNum != WP_FLAMETHROWER && weaponNum != WP_TESLA ) {
2779 				return;
2780 			}
2781 		}
2782 
2783 	}
2784 
2785 	// weapons that don't need to go any further as they have no flash or light
2786 	if ( weaponNum == WP_GRENADE_LAUNCHER ||
2787 		 weaponNum == WP_GRENADE_PINEAPPLE ||
2788 		 weaponNum == WP_KNIFE ||
2789 		 weaponNum == WP_DYNAMITE ) {
2790 		return;
2791 	}
2792 
2793 	if ( weaponNum == WP_STEN ) {  // sten has no muzzleflash
2794 		flash.hModel = 0;
2795 	}
2796 
2797 	// weaps with barrel smoke
2798 	if ( ps || cg.renderingThirdPerson || !isPlayer ) {
2799 		if ( !cg_paused.integer ) {    // don't add while paused
2800 			if ( weaponNum == WP_STEN || weaponNum == WP_VENOM ) {
2801 				if ( cg.time - cent->muzzleFlashTime < 100 ) {
2802 //					CG_ParticleImpactSmokePuff (cgs.media.smokeParticleShader, flash.origin);
2803 					CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, tv( 0,0,1 ), 8, 500, 8, 20, 30, 0.25f );
2804 				}
2805 			}
2806 		}
2807 	}
2808 
2809 	if ( isPlayer ) {
2810 		if ( weaponNum == WP_AKIMBO ) {
2811 			if ( !cent->akimboFire ) {
2812 				CG_PositionRotatedEntityOnTag( &flash, &gun, "tag_flash2" );
2813 			}
2814 		}
2815 	}
2816 
2817 
2818 	if ( flash.hModel ) {
2819 		if ( weaponNum != WP_FLAMETHROWER && weaponNum != WP_TESLA ) {    //Ridah, hide the flash also for now
2820 			// RF, changed this so the muzzle flash stays onscreen for long enough to be seen
2821 			if ( cg.time - cent->muzzleFlashTime < MUZZLE_FLASH_TIME ) {
2822 //				if ( firing )	// Ridah
2823 					trap_R_AddRefEntityToScene( &flash );
2824 			}
2825 		}
2826 	}
2827 
2828 	// Ridah, zombie fires from his head
2829 	//if (CG_MonsterUsingWeapon( cent, AICHAR_ZOMBIE, WP_MONSTER_ATTACK1 )) {
2830 	//	CG_PositionEntityOnTag( &flash, parent, parent->hModel, "tag_head", NULL);
2831 	//}
2832 
2833 	if ( ps || cg.renderingThirdPerson || !isPlayer ) {
2834 
2835 		if ( firing ) {
2836 			// Ridah, Flamethrower effect
2837 			CG_FlamethrowerFlame( cent, flash.origin );
2838 
2839 			// RF, Tesla coil
2840 			CG_PlayerTeslaCoilFire( cent, flash.origin );
2841 
2842 			// make a dlight for the flash
2843 			if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) {
2844 				trap_R_AddLightToScene( flash.origin, 200 + ( rand() & 31 ), weapon->flashDlightColor[0],
2845 										weapon->flashDlightColor[1], weapon->flashDlightColor[2], 0 );
2846 			}
2847 		} else {
2848 			if ( weaponNum == WP_FLAMETHROWER ) {
2849 				vec3_t angles;
2850 				AxisToAngles( flash.axis, angles );
2851 				CG_FireFlameChunks( cent, flash.origin, angles, 1.0, qfalse, 0 );
2852 			}
2853 		}
2854 	}
2855 }
2856 
CG_AddPlayerFoot(refEntity_t * parent,playerState_t * ps,centity_t * cent)2857 void CG_AddPlayerFoot( refEntity_t *parent, playerState_t *ps, centity_t *cent ) {
2858 	refEntity_t wolfkick;
2859 	vec3_t kickangle;
2860 	int frame;
2861 
2862 	if ( !( cg.snap->ps.persistant[PERS_WOLFKICK] ) ) {
2863 		return;
2864 	}
2865 
2866 	memset( &wolfkick, 0, sizeof( wolfkick ) );
2867 
2868 	VectorCopy( parent->lightingOrigin, wolfkick.lightingOrigin );
2869 	wolfkick.shadowPlane = parent->shadowPlane;
2870 
2871 	// note to self we want this to lerp and advance frame
2872 	wolfkick.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;;
2873 	wolfkick.hModel = wolfkickModel;
2874 
2875 	VectorCopy( cg.refdef.vieworg, wolfkick.origin );
2876 	//----(SA)	allow offsets for testing boot model
2877 	if ( cg_gun_x.value ) {
2878 		VectorMA( wolfkick.origin, cg_gun_x.value,  cg.refdef.viewaxis[0], wolfkick.origin );
2879 	}
2880 	if ( cg_gun_y.value ) {
2881 		VectorMA( wolfkick.origin, cg_gun_y.value,  cg.refdef.viewaxis[1], wolfkick.origin );
2882 	}
2883 	if ( cg_gun_z.value ) {
2884 		VectorMA( wolfkick.origin, cg_gun_z.value,  cg.refdef.viewaxis[2], wolfkick.origin );
2885 	}
2886 	//----(SA)	end
2887 
2888 
2889 	VectorCopy( cg.refdefViewAngles, kickangle );
2890 	if ( kickangle[0] < 0 ) {
2891 		kickangle[0] = 0;                       //----(SA)	avoid "Rockette" syndrome :)
2892 	}
2893 	AnglesToAxis( kickangle, wolfkick.axis );
2894 
2895 
2896 	frame = cg.snap->ps.persistant[PERS_WOLFKICK];
2897 
2898 //	CG_Printf("frame: %d\n", frame);
2899 
2900 	wolfkick.frame = frame;
2901 	wolfkick.oldframe = frame - 1;
2902 	wolfkick.backlerp = 1 - cg.frameInterpolation;
2903 	trap_R_AddRefEntityToScene( &wolfkick );
2904 
2905 }
2906 
2907 /*
2908 ==============
2909 CG_AddViewWeapon
2910 
2911 Add the weapon, and flash for the player's view
2912 ==============
2913 */
CG_AddViewWeapon(playerState_t * ps)2914 void CG_AddViewWeapon( playerState_t *ps ) {
2915 	refEntity_t hand;
2916 	vec3_t fovOffset;
2917 	vec3_t angles;
2918 	vec3_t gunoff;
2919 	weaponInfo_t    *weapon;
2920 
2921 	if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
2922 		return;
2923 	}
2924 
2925 	if ( ps->pm_type == PM_INTERMISSION ) {
2926 		return;
2927 	}
2928 
2929 	// no gun if in third person view
2930 	if ( cg.renderingThirdPerson ) {
2931 		return;
2932 	}
2933 
2934 	// allow the gun to be completely removed
2935 	if ( !cg_drawGun.integer ) {
2936 /*
2937 		vec3_t origin;
2938 
2939 		if ( cg.predictedPlayerState.eFlags & EF_FIRING ) {
2940 			// special hack for lightning gun...
2941 			VectorCopy( cg.refdef.vieworg, origin );
2942 			VectorMA( origin, -8, cg.refdef.viewaxis[2], origin );
2943 			CG_LightningBolt( &cg_entities[ps->clientNum], origin );
2944 		}
2945 */
2946 		return;
2947 	}
2948 
2949 	// don't draw if testing a gun model
2950 	if ( cg.testGun ) {
2951 		return;
2952 	}
2953 
2954 	if ( ps->eFlags & EF_MG42_ACTIVE ) {
2955 		return;
2956 	}
2957 
2958 	VectorClear(fovOffset);
2959 
2960 	if ( cg_fixedAspect.integer ) {
2961 		fovOffset[2] = 0;
2962 	} else if ( cg.fov > 90 ) {
2963 		// drop gun lower at higher fov
2964 		fovOffset[2] = -0.2 * ( cg.fov - 90 ) * cg.refdef.fov_x / cg.fov;
2965 	} else if ( cg.fov < 90 ) {
2966 		// move gun forward at lower fov
2967 		fovOffset[0] = -0.2 * ( cg.fov - 90 ) * cg.refdef.fov_x / cg.fov;
2968 	} else if ( cg_fov.integer > 90 ) {
2969 		// old auto adjust
2970 		fovOffset[2] = -0.2 * ( cg_fov.integer - 90 );
2971  	}
2972 
2973 	memset( &hand, 0, sizeof( hand ) );
2974 
2975 	if ( ps->weapon > WP_NONE ) {
2976 		// DHM - Nerve :: handle WP_CLASS_SPECIAL for different classes
2977 		if ( cgs.gametype == GT_WOLF && ps->weapon == WP_CLASS_SPECIAL ) {
2978 			switch ( ps->stats[ STAT_PLAYER_CLASS ] ) {
2979 			case PC_ENGINEER:
2980 				CG_RegisterWeapon( WP_CLASS_SPECIAL );
2981 				weapon = &cg_weapons[ WP_CLASS_SPECIAL ];
2982 				break;
2983 			case PC_MEDIC:
2984 				CG_RegisterWeapon( WP_MEDIC_HEAL );
2985 				weapon = &cg_weapons[ WP_MEDIC_HEAL ];
2986 				break;
2987 			case PC_LT:
2988 				CG_RegisterWeapon( WP_GRENADE_SMOKE );
2989 				weapon = &cg_weapons[ WP_GRENADE_SMOKE ];
2990 				break;
2991 			default:
2992 				CG_RegisterWeapon( ps->weapon );
2993 				weapon = &cg_weapons[ ps->weapon ];
2994 				break;
2995 			}
2996 		} else {
2997 			CG_RegisterWeapon( ps->weapon );
2998 			weapon = &cg_weapons[ ps->weapon ];
2999 		}
3000 		// dhm - end
3001 
3002 		// set up gun position
3003 		CG_CalculateWeaponPosition( hand.origin, angles );
3004 
3005 		gunoff[0] = cg_gun_x.value;
3006 		gunoff[1] = cg_gun_y.value;
3007 		gunoff[2] = cg_gun_z.value;
3008 
3009 //----(SA)	removed
3010 
3011 		VectorMA( hand.origin, ( gunoff[0] + fovOffset[0] ), cg.refdef.viewaxis[0], hand.origin );
3012 		VectorMA( hand.origin, ( gunoff[1] + fovOffset[1] ), cg.refdef.viewaxis[1], hand.origin );
3013 		VectorMA( hand.origin, ( gunoff[2] + fovOffset[2] ), cg.refdef.viewaxis[2], hand.origin );
3014 
3015 		AnglesToAxis( angles, hand.axis );
3016 
3017 		if ( cg_gun_frame.integer ) {
3018 			hand.frame = hand.oldframe = cg_gun_frame.integer;
3019 			hand.backlerp = 0;
3020 		} else {  // get the animation state
3021 			CG_WeaponAnimation( ps, weapon, &hand.oldframe, &hand.frame, &hand.backlerp );   //----(SA)	changed
3022 		}
3023 
3024 		VectorCopy( hand.origin, hand.lightingOrigin );
3025 
3026 		hand.hModel = weapon->handsModel;
3027 		hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT;   //----(SA)
3028 
3029 		// add everything onto the hand
3030 		CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity );
3031 		// Ridah
3032 
3033 	}   // end  "if ( ps->weapon > WP_NONE)"
3034 
3035 	// Rafael
3036 	// add the foot
3037 	CG_AddPlayerFoot( &hand, ps, &cg.predictedPlayerEntity );
3038 
3039 	cg.predictedPlayerEntity.lastWeaponClientFrame = cg.clientFrame;
3040 }
3041 
3042 /*
3043 ==============================================================================
3044 
3045 WEAPON SELECTION
3046 
3047 ==============================================================================
3048 */
3049 
3050 #define WP_ICON_X       38  // new sizes per MK
3051 #define WP_ICON_X_WIDE  72  // new sizes per MK
3052 #define WP_ICON_Y       38
3053 #define WP_ICON_SPACE_Y 10
3054 #define WP_DRAW_X       640 - WP_ICON_X - 4 // 4 is 'selected' border width
3055 #define WP_DRAW_X_WIDE  640 - WP_ICON_X_WIDE - 4
3056 #define WP_DRAW_Y       4
3057 
3058 // secondary fire icons
3059 #define WP_ICON_SEC_X   18  // new sizes per MK
3060 #define WP_ICON_SEC_Y   18
3061 
3062 
3063 /*
3064 ===================
3065 CG_DrawWeaponSelect
3066 ===================
3067 */
CG_DrawWeaponSelect(void)3068 void CG_DrawWeaponSelect( void ) {
3069 	int i;
3070 	int x, y;
3071 	int curweap, curweapbank = 0, curweapcycle = 0, drawweap;
3072 	int realweap;               // DHM - Nerve
3073 	int bits[MAX_WEAPONS / ( sizeof( int ) * 8 )];
3074 	float       *color;
3075 
3076 	// don't display if dead
3077 	if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
3078 		return;
3079 	}
3080 
3081 	if ( !cg.weaponSelect ) {
3082 		return;
3083 	}
3084 
3085 	color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME );
3086 	if ( !color ) {
3087 		return;
3088 	}
3089 	trap_R_SetColor( color );
3090 
3091 	if ( cg_fixedAspect.integer == 2 ) {
3092 		CG_SetScreenPlacement(PLACE_RIGHT, PLACE_TOP);
3093 	}
3094 
3095 //----(SA)	neither of these overlap the weapon selection area anymore, so let them stay
3096 	// showing weapon select clears pickup item display, but not the blend blob
3097 //	cg.itemPickupTime = 0;
3098 
3099 	// also clear holdable list
3100 //	cg.holdableSelectTime = 0;
3101 //----(SA)	end
3102 
3103 	// count the number of weapons owned
3104 	memcpy( bits, cg.snap->ps.weapons, sizeof( bits ) );
3105 
3106 	curweap = cg.weaponSelect;
3107 
3108 	// get bank/cycle of current weapon
3109 	if ( !CG_WeaponIndex( curweap, &curweapbank, &curweapcycle ) ) {
3110 
3111 		// weapon selected isn't a primary weapon, so draw the alternates bank
3112 		CG_WeaponIndex( getAltWeapon( curweap ), &curweapbank, &curweapcycle );
3113 
3114 	}
3115 
3116 	y = WP_DRAW_Y;
3117 
3118 	for ( i = 0; i < maxWeapsInBank; i++ ) {
3119 
3120 		qboolean wideweap; // is the icon one of the double width ones
3121 
3122 		// primary fire
3123 // JPW NERVE
3124 		if ( cg_gameType.integer == GT_WOLF ) {
3125 			drawweap = weapBanksMultiPlayer[curweapbank][i];
3126 		} else {
3127 // jpw
3128 			drawweap = weapBanks[curweapbank][i];
3129 		}
3130 
3131 		realweap = drawweap;        // DHM - Nerve
3132 
3133 		switch ( drawweap ) {
3134 		case WP_THOMPSON:
3135 		case WP_MP40:
3136 		case WP_STEN:
3137 		case WP_MAUSER:
3138 		case WP_GARAND:
3139 		case WP_VENOM:
3140 		case WP_TESLA:
3141 		case WP_PANZERFAUST:
3142 		case WP_FLAMETHROWER:
3143 		case WP_FG42:
3144 		case WP_FG42SCOPE:
3145 			wideweap = qtrue;
3146 			break;
3147 		default:
3148 			wideweap = qfalse;
3149 			break;
3150 		}
3151 
3152 		if ( wideweap ) {
3153 			x = WP_DRAW_X_WIDE;
3154 		} else {
3155 			x = WP_DRAW_X;
3156 		}
3157 
3158 		if ( drawweap && ( bits[0] & ( 1 << drawweap ) ) ) {
3159 			// you've got it, draw it
3160 
3161 			// DHM - Nerve :: Special case for WP_CLASS_SPECIAL
3162 			if ( cgs.gametype == GT_WOLF && drawweap == WP_CLASS_SPECIAL ) {
3163 				switch ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] ) {
3164 				case PC_ENGINEER:
3165 					drawweap = WP_CLASS_SPECIAL;
3166 					break;
3167 				case PC_MEDIC:
3168 					drawweap = WP_MEDIC_HEAL;
3169 					break;
3170 				case PC_LT:
3171 					drawweap = WP_GRENADE_SMOKE;
3172 					break;
3173 				default:
3174 					break;
3175 				}
3176 			}
3177 			// dhm - end
3178 
3179 			CG_RegisterWeapon( drawweap );
3180 
3181 			if ( wideweap ) {
3182 				// weapon icon
3183 				if ( realweap == curweap ) {
3184 					CG_DrawPic( x, y, WP_ICON_X_WIDE, WP_ICON_Y, cg_weapons[drawweap].weaponIcon[1] );
3185 				} else {
3186 					CG_DrawPic( x, y, WP_ICON_X_WIDE, WP_ICON_Y, cg_weapons[drawweap].weaponIcon[0] );
3187 				}
3188 
3189 				// no ammo cross
3190 				if ( !CG_WeaponHasAmmo( realweap ) ) {   // DHM - Nerve
3191 					CG_DrawPic( x, y, WP_ICON_X_WIDE, WP_ICON_Y, cgs.media.noammoShader );
3192 				}
3193 			} else {
3194 				// weapon icon
3195 				if ( realweap == curweap ) {
3196 					CG_DrawPic( x, y, WP_ICON_X, WP_ICON_Y, cg_weapons[drawweap].weaponIcon[1] );
3197 				} else {
3198 					CG_DrawPic( x, y, WP_ICON_X, WP_ICON_Y, cg_weapons[drawweap].weaponIcon[0] );
3199 				}
3200 
3201 				// no ammo cross
3202 				if ( !CG_WeaponHasAmmo( realweap ) ) {   // DHM - Nerve
3203 					CG_DrawPic( x, y, WP_ICON_X, WP_ICON_Y, cgs.media.noammoShader );
3204 				}
3205 			}
3206 
3207 		} else {
3208 			continue;
3209 		}
3210 
3211 		// secondary fire
3212 		if ( wideweap ) {
3213 			x = WP_DRAW_X_WIDE - WP_ICON_SEC_X - 4;
3214 		} else {
3215 			x = WP_DRAW_X - WP_ICON_SEC_X - 4;
3216 		}
3217 
3218 // JPW NERVE
3219 		if ( cg_gameType.integer == GT_WOLF ) {
3220 			drawweap = getAltWeapon( weapBanksMultiPlayer[curweapbank][i] );
3221 		} else {
3222 // jpw
3223 			drawweap = getAltWeapon( weapBanks[curweapbank][i] );
3224 		}
3225 
3226 		// clear drawweap if getaltweap() returns the same weap as passed in. (no secondary available)
3227 // JPW NERVE
3228 		if ( cg_gameType.integer == GT_WOLF ) {
3229 			if ( drawweap == weapBanksMultiPlayer[curweapbank][i] ) {
3230 				drawweap = 0;
3231 			}
3232 		} else {
3233 // jpw
3234 			if ( drawweap == weapBanks[curweapbank][i] ) {
3235 				drawweap = 0;
3236 			}
3237 		}
3238 
3239 		realweap = drawweap;        // DHM - Nerve
3240 
3241 		if ( drawweap && ( bits[0] & ( 1 << drawweap ) ) ) {
3242 			// you've got it, draw it
3243 			// DHM - Nerve :: Special case for WP_CLASS_SPECIAL
3244 			if ( cgs.gametype == GT_WOLF && drawweap == WP_CLASS_SPECIAL ) {
3245 				switch ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] ) {
3246 				case PC_ENGINEER:
3247 					drawweap = WP_CLASS_SPECIAL;
3248 					break;
3249 				case PC_MEDIC:
3250 					drawweap = WP_MEDIC_HEAL;
3251 					break;
3252 				case PC_LT:
3253 					drawweap = WP_GRENADE_SMOKE;
3254 					break;
3255 				default:
3256 					break;
3257 				}
3258 			}
3259 			// dhm - end
3260 
3261 			CG_RegisterWeapon( drawweap );
3262 
3263 			// weapon icon
3264 			if ( realweap == cg.weaponSelect ) {
3265 				CG_DrawPic( x, y, WP_ICON_SEC_X, WP_ICON_SEC_Y, cg_weapons[drawweap].weaponIcon[1] );
3266 			} else {
3267 				CG_DrawPic( x, y, WP_ICON_SEC_X, WP_ICON_SEC_Y, cg_weapons[drawweap].weaponIcon[0] );
3268 			}
3269 
3270 			// no ammo cross
3271 			if ( !CG_WeaponHasAmmo( realweap ) ) {
3272 				CG_DrawPic( x, y, WP_ICON_SEC_X, WP_ICON_SEC_Y, cgs.media.noammoShader );
3273 			}
3274 		}
3275 
3276 
3277 		y += ( WP_ICON_Y + WP_ICON_SPACE_Y );
3278 	}
3279 }
3280 
3281 
3282 
3283 
3284 /*
3285 ==============
3286 CG_WeaponHasAmmo
3287 	check for ammo
3288 ==============
3289 */
CG_WeaponHasAmmo(int i)3290 static qboolean CG_WeaponHasAmmo( int i ) {
3291 	if ( !( cg.predictedPlayerState.ammo[BG_FindAmmoForWeapon( i )] ) &&
3292 		 !( cg.predictedPlayerState.ammoclip[BG_FindClipForWeapon( i )] ) ) {
3293 		return qfalse;
3294 	}
3295 
3296 	return qtrue;
3297 }
3298 
3299 
3300 /*
3301 ===============
3302 CG_WeaponSelectable
3303 ===============
3304 */
CG_WeaponSelectable(int i)3305 static qboolean CG_WeaponSelectable( int i ) {
3306 
3307 	// allow the player to unselect all weapons
3308 //	if(i == WP_NONE)
3309 //		return qtrue;
3310 
3311 	// if holding a melee weapon (chair/shield/etc.) only allow single-handed weapons
3312 	if ( cg.snap->ps.eFlags & EF_MELEE_ACTIVE ) {
3313 		if ( !( WEAPS_ONE_HANDED & ( 1 << i ) ) ) {
3314 			return qfalse;
3315 		}
3316 	}
3317 
3318 	// allow switch out of scope for weapons where you fired the last shot while scoped
3319 	// and we left you in that view to see the result of the shot
3320 	switch ( cg.weaponSelect ) {
3321 	case WP_SNOOPERSCOPE:
3322 		if ( i == WP_GARAND ) {
3323 			return qtrue;
3324 		}
3325 		break;
3326 	case WP_SNIPERRIFLE:
3327 		if ( i == WP_MAUSER ) {
3328 			return qtrue;
3329 		}
3330 		break;
3331 	case WP_FG42SCOPE:
3332 		if ( i == WP_FG42 ) {
3333 			return qtrue;
3334 		}
3335 		break;
3336 	default:
3337 		break;
3338 	}
3339 
3340 
3341 	// check for weapon
3342 	if ( !( COM_BitCheck( cg.predictedPlayerState.weapons, i ) ) ) {
3343 		return qfalse;
3344 	}
3345 
3346 	if ( !CG_WeaponHasAmmo( i ) ) {
3347 		return qfalse;
3348 	}
3349 
3350 	return qtrue;
3351 }
3352 
3353 
3354 
3355 
3356 /*
3357 ==============
3358 CG_WeaponIndex
3359 ==============
3360 */
CG_WeaponIndex(int weapnum,int * bank,int * cycle)3361 int CG_WeaponIndex( int weapnum, int *bank, int *cycle ) {
3362 	static int bnk, cyc;
3363 
3364 	if ( weapnum <= 0 || weapnum >= WP_NUM_WEAPONS ) {
3365 		if ( bank ) {
3366 			*bank = 0;
3367 		}
3368 		if ( cycle ) {
3369 			*cycle = 0;
3370 		}
3371 		return 0;
3372 	}
3373 
3374 	for ( bnk = 0; bnk < maxWeapBanks; bnk++ ) {
3375 		for ( cyc = 0; cyc < maxWeapsInBank; cyc++ ) {
3376 
3377 			// end of cycle, go to next bank
3378 			if ( cg_gameType.integer != GT_WOLF ) { // JPW NERVE
3379 				if ( !weapBanks[bnk][cyc] ) {
3380 					break;
3381 				}
3382 
3383 				// found the current weapon
3384 				if ( weapBanks[bnk][cyc] == weapnum ) {
3385 					if ( bank ) {
3386 						*bank = bnk;
3387 					}
3388 					if ( cycle ) {
3389 						*cycle = cyc;
3390 					}
3391 					return 1;
3392 				}
3393 			}
3394 // JPW NERVE
3395 			else {
3396 				if ( !weapBanksMultiPlayer[bnk][cyc] ) {
3397 					break;
3398 				}
3399 
3400 				// found the current weapon
3401 				if ( weapBanksMultiPlayer[bnk][cyc] == weapnum ) {
3402 					if ( bank ) {
3403 						*bank = bnk;
3404 					}
3405 					if ( cycle ) {
3406 						*cycle = cyc;
3407 					}
3408 					return 1;
3409 				}
3410 			}
3411 // jpw
3412 		}
3413 	}
3414 
3415 	// failed to find the weapon in the table
3416 	// probably an alternate
3417 
3418 	return 0;
3419 }
3420 
3421 
3422 
3423 /*
3424 ==============
3425 getNextWeapInBank
3426 	Pass in a bank and cycle and this will return the next valid weapon higher in the cycle.
3427 	if the weap passed in is above highest in a cycle (maxWeapsInBank), this will safely loop around
3428 ==============
3429 */
getNextWeapInBank(int bank,int cycle)3430 static int getNextWeapInBank( int bank, int cycle ) {
3431 
3432 	cycle++;
3433 
3434 	cycle = cycle % maxWeapsInBank;
3435 
3436 	if ( cg_gameType.integer != GT_WOLF ) { // JPW NERVE
3437 		if ( weapBanks[bank][cycle] ) {    // return next weapon in bank if there is one
3438 			return weapBanks[bank][cycle];
3439 		} else {                            // return first in bank
3440 			return weapBanks[bank][0];
3441 		}
3442 	}
3443 // JPW NERVE
3444 	else {
3445 		if ( weapBanksMultiPlayer[bank][cycle] ) {     // return next weapon in bank if there is one
3446 			return weapBanksMultiPlayer[bank][cycle];
3447 		} else {                            // return first in bank
3448 			return weapBanksMultiPlayer[bank][0];
3449 		}
3450 	}
3451 // jpw
3452 }
3453 
getNextWeapInBankBynum(int weapnum)3454 static int getNextWeapInBankBynum( int weapnum ) {
3455 	int bank, cycle;
3456 
3457 	if ( !CG_WeaponIndex( weapnum, &bank, &cycle ) ) {
3458 		return weapnum;
3459 	}
3460 
3461 	return getNextWeapInBank( bank, cycle );
3462 }
3463 
3464 
3465 /*
3466 ==============
3467 getPrevWeapInBank
3468 	Pass in a bank and cycle and this will return the next valid weapon lower in the cycle.
3469 	if the weap passed in is the lowest in a cycle (0), this will loop around to the
3470 	top (maxWeapsInBank-1) and start down from there looking for a valid weapon position
3471 ==============
3472 */
getPrevWeapInBank(int bank,int cycle)3473 static int getPrevWeapInBank( int bank, int cycle ) {
3474 	cycle--;
3475 	if ( cycle < 0 ) {
3476 		cycle = maxWeapsInBank - 1;
3477 	}
3478 
3479 
3480 	if ( cg_gameType.integer != GT_WOLF ) {
3481 		while ( !weapBanks[bank][cycle] ) {
3482 			cycle--;
3483 
3484 			if ( cycle < 0 ) {
3485 				cycle = maxWeapsInBank - 1;
3486 			}
3487 		}
3488 		return weapBanks[bank][cycle];
3489 	} else {
3490 		while ( !weapBanksMultiPlayer[bank][cycle] ) {
3491 			cycle--;
3492 
3493 			if ( cycle < 0 ) {
3494 				cycle = maxWeapsInBank - 1;
3495 			}
3496 		}
3497 		return weapBanksMultiPlayer[bank][cycle];
3498 	}
3499 }
3500 
3501 
getPrevWeapInBankBynum(int weapnum)3502 static int getPrevWeapInBankBynum( int weapnum ) {
3503 	int bank, cycle;
3504 
3505 	if ( !CG_WeaponIndex( weapnum, &bank, &cycle ) ) {
3506 		return weapnum;
3507 	}
3508 
3509 	return getPrevWeapInBank( bank, cycle );
3510 }
3511 
3512 
3513 
3514 /*
3515 ==============
3516 getNextBankWeap
3517 	Pass in a bank and cycle and this will return the next valid weapon in a higher bank.
3518 	sameBankPosition: if there's a weapon in the next bank at the same cycle,
3519 	return that	(colt returns thompson for example) rather than the lowest weapon
3520 ==============
3521 */
getNextBankWeap(int bank,int cycle,qboolean sameBankPosition)3522 static int getNextBankWeap( int bank, int cycle, qboolean sameBankPosition ) {
3523 	bank++;
3524 
3525 	bank = bank % maxWeapBanks;
3526 
3527 	if ( cg_gameType.integer != GT_WOLF ) { // JPW NERVE
3528 		if ( sameBankPosition && weapBanks[bank][cycle] ) {
3529 			return weapBanks[bank][cycle];
3530 		} else {
3531 			return weapBanks[bank][0];
3532 		}
3533 	}
3534 // JPW NERVE
3535 	else {
3536 		if ( sameBankPosition && weapBanksMultiPlayer[bank][cycle] ) {
3537 			return weapBanksMultiPlayer[bank][cycle];
3538 		} else {
3539 			return weapBanksMultiPlayer[bank][0];
3540 		}
3541 	}
3542 // jpw
3543 }
3544 
3545 /*
3546 ==============
3547 getPrevBankWeap
3548 	Pass in a bank and cycle and this will return the next valid weapon in a lower bank.
3549 	sameBankPosition: if there's a weapon in the prev bank at the same cycle,
3550 	return that	(thompson returns colt for example) rather than the highest weapon
3551 ==============
3552 */
getPrevBankWeap(int bank,int cycle,qboolean sameBankPosition)3553 static int getPrevBankWeap( int bank, int cycle, qboolean sameBankPosition ) {
3554 	int i;
3555 
3556 	bank--;
3557 
3558 	if ( bank < 0 ) {    // don't go below 0, cycle up to top
3559 		bank += maxWeapBanks;
3560 	}
3561 	bank = bank % maxWeapBanks;
3562 
3563 	if ( cg_gameType.integer != GT_WOLF ) { // JPW NERVE
3564 		if ( sameBankPosition && weapBanks[bank][cycle] ) {
3565 			return weapBanks[bank][cycle];
3566 		} else
3567 		{   // find highest weap in bank
3568 			for ( i = maxWeapsInBank - 1; i >= 0; i-- ) {
3569 				if ( weapBanks[bank][i] ) {
3570 					return weapBanks[bank][i];
3571 				}
3572 			}
3573 
3574 			// if it gets to here, no valid weaps in this bank, go down another bank
3575 			return getPrevBankWeap( bank, cycle, sameBankPosition );
3576 		}
3577 	}
3578 // JPW NERVE
3579 	else {
3580 		if ( sameBankPosition && weapBanksMultiPlayer[bank][cycle] ) {
3581 			return weapBanksMultiPlayer[bank][cycle];
3582 		} else
3583 		{   // find highest weap in bank
3584 			for ( i = maxWeapsInBank - 1; i >= 0; i-- ) {
3585 				if ( weapBanksMultiPlayer[bank][i] ) {
3586 					return weapBanksMultiPlayer[bank][i];
3587 				}
3588 			}
3589 
3590 			// if it gets to here, no valid weaps in this bank, go down another bank
3591 			return getPrevBankWeap( bank, cycle, sameBankPosition );
3592 		}
3593 	}
3594 // jpw
3595 }
3596 
3597 /*
3598 ==============
3599 getAltWeapon
3600 ==============
3601 */
getAltWeapon(int weapnum)3602 static int getAltWeapon( int weapnum ) {
3603 	if ( weapnum > MAX_WEAP_ALTS ) {
3604 		return weapnum;
3605 	}
3606 
3607 	if ( weapAlts[weapnum] ) {
3608 		return weapAlts[weapnum];
3609 	}
3610 
3611 	return weapnum;
3612 }
3613 
3614 /*
3615 ==============
3616 getEquivWeapon
3617 	return the id of the opposite team's weapon.
3618 	Passing the weapnum of the mp40 returns the id of the thompson, and likewise
3619 	passing the weapnum of the thompson returns the id of the mp40.
3620 	No equivalent available will return the weapnum passed in.
3621 ==============
3622 */
getEquivWeapon(int weapnum)3623 int getEquivWeapon( int weapnum ) {
3624 	int num = weapnum;
3625 
3626 	switch ( weapnum ) {
3627 		// going from german to american
3628 	case WP_LUGER:              num = WP_COLT;              break;
3629 	case WP_MAUSER:             num = WP_GARAND;            break;
3630 	case WP_MP40:               num = WP_THOMPSON;          break;
3631 	case WP_GRENADE_LAUNCHER:   num = WP_GRENADE_PINEAPPLE; break;
3632 
3633 		// going from american to german
3634 	case WP_COLT:               num = WP_LUGER;             break;
3635 	case WP_GARAND:             num = WP_MAUSER;            break;
3636 	case WP_THOMPSON:           num = WP_MP40;              break;
3637 	case WP_GRENADE_PINEAPPLE:  num = WP_GRENADE_LAUNCHER;  break;
3638 	}
3639 	return num;
3640 }
3641 
3642 
3643 
3644 /*
3645 ==============
3646 CG_WeaponSuggest
3647 ==============
3648 */
CG_WeaponSuggest(int weap)3649 void CG_WeaponSuggest( int weap ) {
3650 #if 0 // not currently supported
3651 	int bank, cycle;
3652 
3653 	if ( !cg_useSuggestedWeapons.integer ) {
3654 		return;
3655 	}
3656 
3657 	cg.weaponSelectTime = cg.time;
3658 
3659 	CG_WeaponIndex( weap, &bank, &cycle );    // get location of this weap
3660 
3661 	cg.lastWeapSelInBank[bank] = weap;      // make this weap first priority in that bank
3662 #endif
3663 }
3664 
3665 
3666 /*
3667 ==============
3668 CG_SetSniperZoom
3669 ==============
3670 */
3671 
CG_SetSniperZoom(int lastweap,int newweap)3672 void CG_SetSniperZoom( int lastweap, int newweap ) {
3673 	int zoomindex;
3674 
3675 	if ( lastweap == newweap ) {
3676 		return;
3677 	}
3678 
3679 	cg.zoomval      = 0;
3680 	cg.zoomedScope  = 0;
3681 
3682 	// check for fade-outs
3683 	switch ( lastweap ) {
3684 	case WP_SNIPERRIFLE:
3685 //			cg.zoomedScope	= 500;	// TODO: add to zoomTable
3686 //			cg.zoomTime		= cg.time;
3687 		break;
3688 	case WP_SNOOPERSCOPE:
3689 //			cg.zoomedScope	= 500;	// TODO: add to zoomTable
3690 //			cg.zoomTime		= cg.time;
3691 		break;
3692 	case WP_FG42SCOPE:
3693 //			cg.zoomedScope	= 1;	// TODO: add to zoomTable
3694 //			cg.zoomTime		= cg.time;
3695 		break;
3696 	}
3697 
3698 	switch ( newweap ) {
3699 
3700 	default:
3701 		return;     // no sniper zoom, get out.
3702 
3703 	case WP_SNIPERRIFLE:
3704 		cg.zoomval = cg_zoomDefaultSniper.value;
3705 		cg.zoomedScope  = 900;      // TODO: add to zoomTable
3706 		zoomindex = ZOOM_SNIPER;
3707 		break;
3708 	case WP_SNOOPERSCOPE:
3709 		cg.zoomval = cg_zoomDefaultSnooper.value;
3710 		cg.zoomedScope  = 800;      // TODO: add to zoomTable
3711 		zoomindex = ZOOM_SNOOPER;
3712 		break;
3713 	case WP_FG42SCOPE:
3714 		cg.zoomval = cg_zoomDefaultFG.value;
3715 		cg.zoomedScope  = 1;        // TODO: add to zoomTable
3716 		zoomindex = ZOOM_FG42SCOPE;
3717 		break;
3718 	}
3719 
3720 	// constrain user preferred fov to weapon limitations
3721 	if ( cg.zoomval > zoomTable[zoomindex][ZOOM_OUT] ) {
3722 		cg.zoomval = zoomTable[zoomindex][ZOOM_OUT];
3723 	}
3724 	if ( cg.zoomval < zoomTable[zoomindex][ZOOM_IN] ) {
3725 		cg.zoomval = zoomTable[zoomindex][ZOOM_IN];
3726 	}
3727 
3728 	cg.zoomTime     = cg.time;
3729 }
3730 
3731 
3732 /*
3733 ==============
3734 CG_PlaySwitchSound
3735 	Get special switching sounds if they're there
3736 ==============
3737 */
CG_PlaySwitchSound(int lastweap,int newweap)3738 void CG_PlaySwitchSound( int lastweap, int newweap ) {
3739 //	weaponInfo_t	*weap;
3740 //	weap = &cg_weapons[ ent->weapon ];
3741 	sfxHandle_t switchsound;
3742 
3743 	switchsound = cgs.media.selectSound;
3744 
3745 	if ( getAltWeapon( lastweap ) == newweap ) { // alt switch
3746 		switch ( newweap ) {
3747 		case WP_SILENCER:
3748 		case WP_LUGER:
3749 			switchsound = cg_weapons[newweap].switchSound[0];
3750 			break;
3751 		default:
3752 			break;
3753 		}
3754 	}
3755 
3756 	trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_WEAPON, switchsound );
3757 }
3758 
3759 
3760 /*
3761 ==============
3762 CG_FinishWeaponChange
3763 ==============
3764 */
CG_FinishWeaponChange(int lastweap,int newweap)3765 void CG_FinishWeaponChange( int lastweap, int newweap ) {
3766 	int newbank;
3767 
3768 	cg.weaponSelectTime = cg.time;  // flash the weapon icon
3769 
3770 	// remember which weapon in this bank was last selected so when cycling back
3771 	// to this bank, that weap will be highlighted first
3772 	if ( CG_WeaponIndex( newweap, &newbank, NULL ) ) {
3773 		cg.lastWeapSelInBank[newbank] = newweap;
3774 	}
3775 
3776 	if ( lastweap == newweap ) {   // no need to do any more than flash the icon
3777 		return;
3778 	}
3779 
3780 	CG_PlaySwitchSound( lastweap, newweap );  //----(SA)	added
3781 
3782 	CG_SetSniperZoom( lastweap, newweap );
3783 
3784 	// setup for a user call to CG_LastWeaponUsed_f()
3785 	if ( lastweap == cg.lastFiredWeapon ) {
3786 		// don't set switchback for some weaps...
3787 		switch ( lastweap ) {
3788 		case WP_SNIPERRIFLE:
3789 		case WP_SNOOPERSCOPE:
3790 		case WP_FG42SCOPE:
3791 			break;
3792 		default:
3793 			cg.switchbackWeapon = lastweap;
3794 			break;
3795 		}
3796 	} else {
3797 		// if this ended up having the switchback be the same
3798 		// as the new weapon, set the switchback to the prev
3799 		// selected weapon will become the switchback
3800 		if ( cg.switchbackWeapon == newweap ) {
3801 			cg.switchbackWeapon = lastweap;
3802 		}
3803 	}
3804 
3805 	cg.weaponSelect     = newweap;
3806 }
3807 
3808 /*
3809 ==============
3810 CG_AltfireWeapon_f
3811 	for example, switching between WP_MAUSER and WP_SNIPERRIFLE
3812 ==============
3813 */
CG_AltWeapon_f(void)3814 void CG_AltWeapon_f( void ) {
3815 	int original, num;
3816 
3817 	if ( !cg.snap ) {
3818 		return;
3819 	}
3820 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
3821 		return;
3822 	}
3823 
3824 	if ( cg.snap->ps.eFlags & EF_MG42_ACTIVE ) { // no alt-switching when on mg42
3825 		return;
3826 	}
3827 
3828 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
3829 		return; // force pause so holding it down won't go too fast
3830 
3831 	}
3832 	// Don't try to switch when in the middle of reloading.
3833 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
3834 		return;
3835 	}
3836 
3837 	original = cg.weaponSelect;
3838 
3839 	num = getAltWeapon( original );
3840 
3841 	if ( CG_WeaponSelectable( num ) ) {   // new weapon is valid
3842 
3843 //----(SA)	testing mod functionality for the silencer on the luger
3844 		// (SA) this way, if you switch away from the silenced luger,
3845 		//		the silencer will still be attached when you switch back
3846 		//		(until you remove it)
3847 		// TODO: will need to make sure the table gets initialized properly on restart/death/whatever.
3848 		//		 I still think I'm going to make the weapon banks stored in the config, so this will
3849 		//		just be a matter of resetting the banks to what's in the config.
3850 		switch ( original ) {
3851 		case WP_LUGER:
3852 			if ( cg.snap->ps.eFlags & EF_MELEE_ACTIVE ) {   // if you're holding a chair, you can't screw on the silencer
3853 				return;
3854 			}
3855 			weapBanks[2][0] = WP_SILENCER;
3856 			break;
3857 		case WP_SILENCER:
3858 			if ( cg.snap->ps.eFlags & EF_MELEE_ACTIVE ) {   // if you're holding a chair, you can't remove the silencer
3859 				return;
3860 			}
3861 			weapBanks[2][0] = WP_LUGER;
3862 			break;
3863 
3864 		case WP_AKIMBO:
3865 			weapBanks[2][1] = WP_COLT;
3866 			break;
3867 		case WP_COLT:
3868 			weapBanks[2][1] = WP_AKIMBO;
3869 			break;
3870 		}
3871 
3872 //----(SA)	end
3873 		CG_FinishWeaponChange( original, num );
3874 	}
3875 }
3876 
3877 
3878 /*
3879 ==============
3880 CG_NextWeap
3881 
3882   switchBanks - curweap is the last in a bank, 'qtrue' means go to the next available bank, 'qfalse' means loop to the head of the bank
3883 ==============
3884 */
CG_NextWeap(qboolean switchBanks)3885 void CG_NextWeap( qboolean switchBanks ) {
3886 	int bank = 0, cycle = 0, newbank = 0, newcycle = 0;
3887 	int num, curweap;
3888 	qboolean nextbank = qfalse;     // need to switch to the next bank of weapons?
3889 	int i, j;
3890 
3891 	num = curweap = cg.weaponSelect;
3892 
3893 	CG_WeaponIndex( curweap, &bank, &cycle );     // get bank/cycle of current weapon
3894 
3895 	// if you're using an alt mode weapon, try switching back to the parent first
3896 	if ( curweap >= WP_BEGINSECONDARY && curweap <= WP_LASTSECONDARY ) {
3897 		num = getAltWeapon( curweap );    // base any further changes on the parent
3898 		if ( CG_WeaponSelectable( num ) ) {  // the parent was selectable, drop back to that
3899 			CG_FinishWeaponChange( curweap, num );
3900 			return;
3901 		}
3902 	}
3903 
3904 
3905 //	if ( cg_cycleAllWeaps.integer || !switchBanks ) {
3906 	if ( 1 ) {
3907 		for ( i = 0; i < maxWeapsInBank; i++ ) {
3908 			num = getNextWeapInBankBynum( num );
3909 
3910 			CG_WeaponIndex( num, NULL, &newcycle );       // get cycle of new weapon.  if it's lower than the original, then it cycled around
3911 
3912 			if ( switchBanks ) {
3913 				if ( newcycle <= cycle ) {
3914 					nextbank = qtrue;
3915 					break;
3916 				}
3917 			} else {    // don't switch banks if you get to the end
3918 
3919 				if ( num == curweap ) {    // back to start, just leave it where it is
3920 					return;
3921 				}
3922 			}
3923 
3924 			if ( CG_WeaponSelectable( num ) ) {
3925 				break;
3926 			}
3927 		}
3928 	} else {
3929 		nextbank = qtrue;
3930 	}
3931 
3932 	if ( nextbank ) {
3933 		for ( i = 0; i < maxWeapBanks; i++ ) {
3934 //			if ( cg_cycleAllWeaps.integer ) {
3935 			if ( 1 ) {
3936 				num = getNextBankWeap( bank + i, cycle, qfalse );   // cycling all weaps always starts the next bank at the bottom
3937 			} else {
3938 				if ( cg.lastWeapSelInBank[bank + i + 1] ) {
3939 					num = cg.lastWeapSelInBank[bank + i + 1];
3940 				} else {
3941 					num = getNextBankWeap( bank + i, cycle, qtrue );
3942 				}
3943 			}
3944 
3945 			if ( num == 0 ) {
3946 				continue;
3947 			}
3948 
3949 			if ( CG_WeaponSelectable( num ) ) {  // first entry in bank was selectable, no need to scan the bank
3950 				break;
3951 			}
3952 
3953 			CG_WeaponIndex( num, &newbank, &newcycle );   // get the bank of the new weap
3954 
3955 			for ( j = newcycle; j < maxWeapsInBank; j++ ) {
3956 				num = getNextWeapInBank( newbank, j );
3957 
3958 				if ( CG_WeaponSelectable( num ) ) {  // found selectable weapon
3959 					break;
3960 				}
3961 
3962 				num = 0;
3963 			}
3964 
3965 			if ( num ) {   // a selectable weapon was found in the current bank
3966 				break;
3967 			}
3968 		}
3969 	}
3970 
3971 	CG_FinishWeaponChange( curweap, num ); //----(SA)
3972 }
3973 
3974 /*
3975 ==============
3976 CG_PrevWeap
3977 
3978   switchBanks - curweap is the last in a bank
3979 		'qtrue'  - go to the next available bank
3980 		'qfalse' - loop to the head of the bank
3981 ==============
3982 */
CG_PrevWeap(qboolean switchBanks)3983 void CG_PrevWeap( qboolean switchBanks ) {
3984 	int bank = 0, cycle = 0, newbank = 0, newcycle = 0;
3985 	int num, curweap;
3986 	qboolean prevbank = qfalse;     // need to switch to the next bank of weapons?
3987 	int i, j;
3988 
3989 	num = curweap = cg.weaponSelect;
3990 
3991 	CG_WeaponIndex( curweap, &bank, &cycle );     // get bank/cycle of current weapon
3992 
3993 	// if you're using an alt mode weapon, try switching back to the parent first
3994 	if ( curweap >= WP_BEGINSECONDARY && curweap <= WP_LASTSECONDARY ) {
3995 		num = getAltWeapon( curweap );    // base any further changes on the parent
3996 		if ( CG_WeaponSelectable( num ) ) {  // the parent was selectable, drop back to that
3997 			CG_FinishWeaponChange( curweap, num );
3998 			return;
3999 		}
4000 	}
4001 
4002 	// initially, just try to find a lower weapon in the current bank
4003 //	if ( cg_cycleAllWeaps.integer || !switchBanks ) {
4004 	if ( 1 ) {
4005 
4006 //		if(cycle == 0) {		// already at bottom of list
4007 //			prevbank = qtrue;
4008 //		} else {
4009 		for ( i = cycle; i >= 0; i-- ) {
4010 //				num = getPrevWeapInBank(bank, i);
4011 			num = getPrevWeapInBankBynum( num );
4012 
4013 			CG_WeaponIndex( num, NULL, &newcycle );         // get cycle of new weapon.  if it's greater than the original, then it cycled around
4014 
4015 			if ( switchBanks ) {
4016 				if ( newcycle > ( cycle - 1 ) ) {
4017 					prevbank = qtrue;
4018 					break;
4019 				}
4020 			} else {        // don't switch banks if you get to the end
4021 				if ( num == curweap ) {     // back to start, just leave it where it is
4022 					return;
4023 				}
4024 			}
4025 
4026 			if ( CG_WeaponSelectable( num ) ) {
4027 				break;
4028 			}
4029 		}
4030 //		}
4031 	} else {
4032 		prevbank = qtrue;
4033 	}
4034 
4035 	// cycle to previous bank.
4036 	//	if cycleAllWeaps: find highest weapon in bank
4037 	//		else: try to find weap in bank that matches cycle position
4038 	//			else: use base weap in bank
4039 
4040 	if ( prevbank ) {
4041 		for ( i = 0; i < maxWeapBanks; i++ ) {
4042 //			if ( cg_cycleAllWeaps.integer ) {
4043 			if ( 1 ) {
4044 				num = getPrevBankWeap( bank - i, cycle, qfalse );   // cycling all weaps always starts the next bank at the bottom
4045 			} else {
4046 				num = getPrevBankWeap( bank - i, cycle, qtrue );
4047 			}
4048 
4049 			if ( num == 0 ) {
4050 				continue;
4051 			}
4052 
4053 			if ( CG_WeaponSelectable( num ) ) {  // first entry in bank was selectable, no need to scan the bank
4054 				break;
4055 			}
4056 
4057 			CG_WeaponIndex( num, &newbank, &newcycle );   // get the bank of the new weap
4058 
4059 			for ( j = maxWeapsInBank; j > 0; j-- ) {
4060 				num = getPrevWeapInBank( newbank, j );
4061 
4062 				if ( CG_WeaponSelectable( num ) ) {  // found selectable weapon
4063 					break;
4064 				}
4065 
4066 				num = 0;
4067 			}
4068 
4069 			if ( num ) {   // a selectable weapon was found in the current bank
4070 				break;
4071 			}
4072 		}
4073 	}
4074 
4075 	CG_FinishWeaponChange( curweap, num ); //----(SA)
4076 }
4077 
4078 
4079 /*
4080 ==============
4081 CG_LastWeaponUsed_f
4082 ==============
4083 */
CG_LastWeaponUsed_f(void)4084 void CG_LastWeaponUsed_f( void ) {
4085 
4086 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4087 		return; // force pause so holding it down won't go too fast
4088 
4089 	}
4090 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4091 
4092 	// don't switchback if reloading (it nullifies the reload)
4093 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
4094 		return;
4095 	}
4096 
4097 	if ( !cg.switchbackWeapon ) {
4098 		cg.switchbackWeapon = cg.weaponSelect;
4099 		return;
4100 	}
4101 
4102 	if ( CG_WeaponSelectable( cg.switchbackWeapon ) ) {
4103 		CG_FinishWeaponChange( cg.weaponSelect, cg.switchbackWeapon );
4104 	} else {    // switchback no longer selectable, reset cycle
4105 		cg.switchbackWeapon = 0;
4106 	}
4107 
4108 }
4109 
4110 /*
4111 ==============
4112 CG_NextWeaponInBank_f
4113 ==============
4114 */
CG_NextWeaponInBank_f(void)4115 void CG_NextWeaponInBank_f( void ) {
4116 
4117 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4118 		return; // force pause so holding it down won't go too fast
4119 
4120 	}
4121 	// this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel)
4122 	// for zooming (binocs/snooper/sniper/etc.)
4123 	if ( cg.zoomval ) {
4124 		if ( cg_useWeapsForZoom.integer == 1 ) {
4125 			CG_ZoomIn_f();
4126 			return;
4127 		} else if ( cg_useWeapsForZoom.integer == 2 ) {
4128 			CG_ZoomOut_f();
4129 			return;
4130 		}
4131 	}
4132 
4133 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4134 
4135 	CG_NextWeap( qfalse );
4136 }
4137 
4138 /*
4139 ==============
4140 CG_PrevWeaponInBank_f
4141 ==============
4142 */
CG_PrevWeaponInBank_f(void)4143 void CG_PrevWeaponInBank_f( void ) {
4144 
4145 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4146 		return; // force pause so holding it down won't go too fast
4147 
4148 	}
4149 	// this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel)
4150 	// for zooming (binocs/snooper/sniper/etc.)
4151 	if ( cg.zoomval ) {
4152 		if ( cg_useWeapsForZoom.integer == 2 ) {
4153 			CG_ZoomIn_f();
4154 			return;
4155 		} else if ( cg_useWeapsForZoom.integer == 1 ) {
4156 			CG_ZoomOut_f();
4157 			return;
4158 		}
4159 	}
4160 
4161 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4162 
4163 	CG_PrevWeap( qfalse );
4164 }
4165 
4166 
4167 /*
4168 ==============
4169 CG_NextWeapon_f
4170 ==============
4171 */
CG_NextWeapon_f(void)4172 void CG_NextWeapon_f( void ) {
4173 
4174 	if ( !cg.snap ) {
4175 		return;
4176 	}
4177 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
4178 		return;
4179 	}
4180 
4181 	// this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel)
4182 	// for zooming (binocs/snooper/sniper/etc.)
4183 	if ( cg.zoomval ) {
4184 		if ( cg_useWeapsForZoom.integer == 1 ) {
4185 			CG_ZoomIn_f();
4186 			return;
4187 		} else if ( cg_useWeapsForZoom.integer == 2 ) {
4188 			CG_ZoomOut_f();
4189 			return;
4190 		}
4191 	}
4192 
4193 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4194 		return; // force pause so holding it down won't go too fast
4195 
4196 	}
4197 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4198 
4199 	// Don't try to switch when in the middle of reloading.
4200 	// cheatinfo:	The server actually would let you switch if this check were not
4201 	//				present, but would discard the reload.  So the when you switched
4202 	//				back you'd have to start the reload over.  This seems bad, however
4203 	//				the delay for the current reload is already in effect, so you'd lose
4204 	//				the reload time twice.  (the first pause for the current weapon reload,
4205 	//				and the pause when you have to reload again 'cause you canceled this one)
4206 
4207 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
4208 		return;
4209 	}
4210 
4211 	CG_NextWeap( qtrue );
4212 }
4213 
4214 
4215 /*
4216 ==============
4217 CG_PrevWeapon_f
4218 ==============
4219 */
CG_PrevWeapon_f(void)4220 void CG_PrevWeapon_f( void ) {
4221 	if ( !cg.snap ) {
4222 		return;
4223 	}
4224 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
4225 		return;
4226 	}
4227 
4228 	// this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel)
4229 	// for zooming (binocs/snooper/sniper/etc.)
4230 	if ( cg.zoomval ) {
4231 		if ( cg_useWeapsForZoom.integer == 1 ) {
4232 			CG_ZoomOut_f();
4233 			return;
4234 		} else if ( cg_useWeapsForZoom.integer == 2 ) {
4235 			CG_ZoomIn_f();
4236 			return;
4237 		}
4238 	}
4239 
4240 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4241 		return; // force pause so holding it down won't go too fast
4242 
4243 	}
4244 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4245 
4246 	// Don't try to switch when in the middle of reloading.
4247 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
4248 		return;
4249 	}
4250 
4251 	CG_PrevWeap( qtrue );
4252 }
4253 
4254 
4255 /*
4256 ==============
4257 CG_WeaponBank_f
4258 	weapon keys are not generally bound directly('bind 1 weapon 1'),
4259 	rather the key is bound to a given bank ('bind 1 weaponbank 1')
4260 ==============
4261 */
CG_WeaponBank_f(void)4262 void CG_WeaponBank_f( void ) {
4263 	int num, i, curweap;
4264 	int curbank = 0, curcycle = 0, bank = 0, cycle = 0;
4265 
4266 	if ( !cg.snap ) {
4267 		return;
4268 	}
4269 
4270 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
4271 		return;
4272 	}
4273 
4274 	if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) {
4275 		return; // force pause so holding it down won't go too fast
4276 
4277 	}
4278 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4279 
4280 	// Don't try to switch when in the middle of reloading.
4281 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
4282 		return;
4283 	}
4284 
4285 	bank = atoi( CG_Argv( 1 ) );
4286 
4287 	if ( bank <= 0 || bank > maxWeapBanks ) {
4288 		return;
4289 	}
4290 
4291 	curweap = cg.weaponSelect;
4292 	CG_WeaponIndex( curweap, &curbank, &curcycle );       // get bank/cycle of current weapon
4293 
4294 	if ( !cg.lastWeapSelInBank[bank] ) {
4295 		if ( cg_gameType.integer != GT_WOLF ) { // JPW NERVE
4296 			num = weapBanks[bank][0];
4297 		}
4298 // JPW NERVE
4299 		else {
4300 			num = weapBanksMultiPlayer[bank][0];
4301 		}
4302 // jpw
4303 		cycle -= 1;   // cycle up to first weap
4304 	} else {
4305 		num = cg.lastWeapSelInBank[bank];
4306 		CG_WeaponIndex( num, &bank, &cycle );
4307 		if ( bank != curbank ) {
4308 			cycle -= 1;
4309 		}
4310 
4311 	}
4312 
4313 	for ( i = 0; i < maxWeapsInBank; i++ ) {
4314 		num = getNextWeapInBank( bank, cycle + i );
4315 
4316 		if ( CG_WeaponSelectable( num ) ) {
4317 			break;
4318 		}
4319 	}
4320 
4321 	if ( i == maxWeapsInBank ) {
4322 		return;
4323 	}
4324 
4325 	CG_FinishWeaponChange( curweap, num );
4326 
4327 }
4328 
4329 /*
4330 ===============
4331 CG_Weapon_f
4332 ===============
4333 */
CG_Weapon_f(void)4334 void CG_Weapon_f( void ) {
4335 	int num, i, curweap;
4336 	int bank = 0, cycle = 0, newbank = 0, newcycle = 0;
4337 	qboolean banked = qfalse;
4338 
4339 	if ( !cg.snap ) {
4340 		return;
4341 	}
4342 
4343 	if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
4344 		return;
4345 	}
4346 
4347 	num = atoi( CG_Argv( 1 ) );
4348 
4349 // JPW NERVE
4350 // weapon bind should execute weaponbank instead -- for splitting out class weapons, per Id request
4351 	if ( cg_gameType.integer == GT_WOLF ) {
4352 		if ( num < maxWeapBanks ) {
4353 			CG_WeaponBank_f();
4354 		}
4355 		return;
4356 	}
4357 // jpw
4358 
4359 	cg.weaponSelectTime = cg.time;  // flash the current weapon icon
4360 
4361 	// Don't try to switch when in the middle of reloading.
4362 	if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) {
4363 		return;
4364 	}
4365 
4366 
4367 	if ( num <= WP_NONE || num > WP_NUM_WEAPONS ) {
4368 		return;
4369 	}
4370 
4371 	curweap = cg.weaponSelect;
4372 
4373 	CG_WeaponIndex( curweap, &bank, &cycle );     // get bank/cycle of current weapon
4374 	banked = CG_WeaponIndex( num, &newbank, &newcycle );      // get bank/cycle of requested weapon
4375 
4376 	// the new weapon was not found in the reglar banks
4377 	// assume the player want's to go directly to it if possible
4378 	if ( !banked ) {
4379 		if ( CG_WeaponSelectable( num ) ) {
4380 			CG_FinishWeaponChange( curweap, num );
4381 			return;
4382 		}
4383 	}
4384 
4385 	if ( bank != newbank ) {
4386 		cycle = newcycle - 1;   //	drop down one from the requested weap's cycle so it will
4387 	}
4388 	//	try to initially cycle up to the requested weapon
4389 
4390 	for ( i = 0; i < maxWeapsInBank; i++ ) {
4391 		num = getNextWeapInBank( newbank, cycle + i );
4392 
4393 		if ( num == curweap ) { // no other weapons in bank
4394 			return;
4395 		}
4396 
4397 		if ( CG_WeaponSelectable( num ) ) {
4398 			break;
4399 		}
4400 	}
4401 
4402 	if ( i == maxWeapsInBank ) {
4403 		return;
4404 	}
4405 
4406 	CG_FinishWeaponChange( curweap, num );
4407 }
4408 
4409 /*
4410 ===================
4411 CG_OutOfAmmoChange
4412 
4413 The current weapon has just run out of ammo
4414 ===================
4415 */
CG_OutOfAmmoChange(void)4416 void CG_OutOfAmmoChange( void ) {
4417 	int i;
4418 	int bank = 0, cycle = 0;
4419 	int equiv = WP_NONE;
4420 
4421 	//
4422 	// trivial switching
4423 	//
4424 
4425 	// if you're using an alt mode weapon, try switching back to the parent
4426 	// otherwise, switch to the equivalent if you've got it
4427 	if ( cg.weaponSelect >= WP_BEGINSECONDARY && cg.weaponSelect <= WP_LASTSECONDARY ) {
4428 		cg.weaponSelect = equiv = getAltWeapon( cg.weaponSelect );    // base any further changes on the parent
4429 		if ( CG_WeaponSelectable( equiv ) ) {    // the parent was selectable, drop back to that
4430 			CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA)
4431 			return;
4432 		}
4433 	}
4434 
4435 
4436 	// now try the opposite team's equivalent weap
4437 	equiv = getEquivWeapon( cg.weaponSelect );
4438 
4439 	if ( equiv != cg.weaponSelect && CG_WeaponSelectable( equiv ) ) {
4440 		cg.weaponSelect = equiv;
4441 		CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA)
4442 		return;
4443 	}
4444 
4445 	//
4446 	// more complicated selection
4447 	//
4448 
4449 	// didn't have available alternative or equivalent, try another weap in the bank
4450 	CG_WeaponIndex( cg.weaponSelect, &bank, &cycle );     // get bank/cycle of current weapon
4451 
4452 	for ( i = cycle; i < maxWeapsInBank; i++ ) {
4453 		equiv = getNextWeapInBank( bank, i );
4454 		if ( CG_WeaponSelectable( equiv ) ) {  // found a reasonable replacement
4455 			cg.weaponSelect = equiv;
4456 			CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA)
4457 			return;
4458 		}
4459 	}
4460 
4461 
4462 	// still nothing available, just go to the next
4463 	// available weap using the regular selection scheme
4464 	CG_NextWeap( qtrue );
4465 
4466 }
4467 
4468 /*
4469 ===================================================================================================
4470 
4471 WEAPON EVENTS
4472 
4473 ===================================================================================================
4474 */
4475 
4476 // Note to self this is dead code
CG_MG42EFX(centity_t * cent)4477 void CG_MG42EFX( centity_t *cent ) {
4478 	vec3_t forward;
4479 	vec3_t point;
4480 	refEntity_t flash;
4481 
4482 //	trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, hWeaponSnd );
4483 
4484 	VectorCopy( cent->currentState.origin, point );
4485 	AngleVectors( cent->currentState.angles, forward, NULL, NULL );
4486 	VectorMA( point, 40, forward, point );
4487 	trap_R_AddLightToScene( point, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
4488 
4489 	memset( &flash, 0, sizeof( flash ) );
4490 	flash.renderfx = RF_LIGHTING_ORIGIN;
4491 	flash.hModel = cgs.media.mg42muzzleflash;
4492 
4493 	VectorCopy( point, flash.origin );
4494 	AnglesToAxis( cg.refdefViewAngles, flash.axis );
4495 
4496 	trap_R_AddRefEntityToScene( &flash );
4497 }
4498 
CG_FLAKEFX(centity_t * cent,int whichgun)4499 void CG_FLAKEFX( centity_t *cent, int whichgun ) {
4500 	entityState_t *ent;
4501 	vec3_t forward, right, up;
4502 	vec3_t point;
4503 	refEntity_t flash;
4504 
4505 	ent = &cent->currentState;
4506 
4507 	VectorCopy( cent->currentState.pos.trBase, point );
4508 	AngleVectors( cent->currentState.apos.trBase, forward, right, up );
4509 
4510 	// gun 1 and 2 were switched
4511 	if ( whichgun == 2 ) {
4512 		VectorMA( point, 136, forward, point );
4513 		VectorMA( point, 31, up, point );
4514 		VectorMA( point, 22, right, point );
4515 	} else if ( whichgun == 1 )     {
4516 		VectorMA( point, 136, forward, point );
4517 		VectorMA( point, 31, up, point );
4518 		VectorMA( point, -22, right, point );
4519 	} else if ( whichgun == 3 )     {
4520 		VectorMA( point, 136, forward, point );
4521 		VectorMA( point, 10, up, point );
4522 		VectorMA( point, 22, right, point );
4523 	} else if ( whichgun == 4 )     {
4524 		VectorMA( point, 136, forward, point );
4525 		VectorMA( point, 10, up, point );
4526 		VectorMA( point, -22, right, point );
4527 	}
4528 
4529 	trap_R_AddLightToScene( point, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
4530 
4531 	memset( &flash, 0, sizeof( flash ) );
4532 	flash.renderfx = RF_LIGHTING_ORIGIN;
4533 	flash.hModel = cgs.media.mg42muzzleflash;
4534 
4535 	VectorCopy( point, flash.origin );
4536 	AnglesToAxis( cg.refdefViewAngles, flash.axis );
4537 
4538 	trap_R_AddRefEntityToScene( &flash );
4539 
4540 	trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, hflakWeaponSnd );
4541 }
4542 
4543 
4544 //----(SA)
4545 /*
4546 ==============
4547 CG_MortarEFX
4548 	Right now mostly copied directly from Raf's MG42 FX, but with the optional addtion of smoke
4549 ==============
4550 */
CG_MortarEFX(centity_t * cent)4551 void CG_MortarEFX( centity_t *cent ) {
4552 	refEntity_t flash;
4553 
4554 	if ( cent->currentState.density & 1 ) {
4555 		// smoke
4556 		CG_ParticleImpactSmokePuff( cgs.media.smokePuffShader, cent->currentState.origin );
4557 	}
4558 
4559 	if ( cent->currentState.density & 2 ) {
4560 		// light
4561 		trap_R_AddLightToScene( cent->currentState.origin, 200 + ( rand() & 31 ), 1.0, 1.0, 1.0, 0 );
4562 
4563 		// muzzle flash
4564 		memset( &flash, 0, sizeof( flash ) );
4565 		flash.renderfx = RF_LIGHTING_ORIGIN;
4566 		flash.hModel = cgs.media.mg42muzzleflash;
4567 		VectorCopy( cent->currentState.origin, flash.origin );
4568 		AnglesToAxis( cg.refdefViewAngles, flash.axis );
4569 		trap_R_AddRefEntityToScene( &flash );
4570 	}
4571 }
4572 
4573 //----(SA)	end
4574 
4575 
4576 // RF
4577 /*
4578 ==============
4579 CG_WeaponFireRecoil
4580 ==============
4581 */
CG_WeaponFireRecoil(int weapon)4582 void CG_WeaponFireRecoil( int weapon ) {
4583 //	const	vec3_t maxKickAngles = {25, 30, 25};
4584 	float pitchRecoilAdd, pitchAdd;
4585 	float yawRandom;
4586 	vec3_t recoil;
4587 	//
4588 	pitchRecoilAdd = 0;
4589 	pitchAdd = 0;
4590 	yawRandom = 0;
4591 	//
4592 	switch ( weapon ) {
4593 	case WP_LUGER:
4594 	case WP_SILENCER:
4595 	case WP_COLT:
4596 	case WP_AKIMBO: //----(SA)	added
4597 		//pitchAdd = 2+rand()%3;
4598 		//yawRandom = 2;
4599 		break;
4600 	case WP_MAUSER:
4601 	case WP_GARAND:
4602 		//pitchAdd = 4+rand()%3;
4603 		//yawRandom = 4;
4604 		pitchAdd = 2;   //----(SA)	for DM
4605 		yawRandom = 1;  //----(SA)	for DM
4606 		break;
4607 	case WP_SNIPERRIFLE:
4608 	case WP_SNOOPERSCOPE:
4609 		pitchAdd = 0.6;
4610 		break;
4611 	case WP_FG42SCOPE:
4612 	case WP_FG42:
4613 	case WP_MP40:
4614 	case WP_THOMPSON:
4615 	case WP_STEN:
4616 		//pitchRecoilAdd = 1;
4617 		pitchAdd = 1 + rand() % 3;
4618 		yawRandom = 2;
4619 
4620 
4621 		pitchAdd *= 0.3;
4622 		yawRandom *= 0.3;
4623 		break;
4624 	case WP_PANZERFAUST:
4625 		//pitchAdd = 12+rand()%3;
4626 		//yawRandom = 6;
4627 
4628 		CG_StartShakeCamera( 0.05, 700, cg.snap->ps.origin, 100 );
4629 
4630 		// push the player back instead
4631 		break;
4632 	case WP_VENOM:
4633 		pitchRecoilAdd = pow( random(),8 ) * ( 10 + VectorLength( cg.snap->ps.velocity ) / 5 );
4634 		pitchAdd = ( rand() % 5 ) - 2;
4635 		yawRandom = 2;
4636 
4637 
4638 		pitchRecoilAdd *= 0.5;
4639 		pitchAdd *= 0.5;
4640 		yawRandom *= 0.5;
4641 		break;
4642 	default:
4643 		return;
4644 	}
4645 	// calc the recoil
4646 	recoil[YAW] = crandom() * yawRandom;
4647 	recoil[ROLL] = -recoil[YAW];    // why not
4648 	recoil[PITCH] = -pitchAdd;
4649 	// scale it up a bit (easier to modify this while tweaking)
4650 	VectorScale( recoil, 30, recoil );
4651 	// set the recoil
4652 	VectorCopy( recoil, cg.kickAVel );
4653 	// set the recoil
4654 	cg.recoilPitch -= pitchRecoilAdd;
4655 }
4656 
4657 
4658 /*
4659 ================
4660 CG_FireWeapon
4661 
4662 Caused by an EV_FIRE_WEAPON event
4663 
4664 ================
4665 */
CG_FireWeapon(centity_t * cent)4666 void CG_FireWeapon( centity_t *cent ) {
4667 	entityState_t *ent;
4668 	int c;
4669 	weaponInfo_t    *weap;
4670 	sfxHandle_t     *firesound;
4671 	sfxHandle_t     *fireEchosound;
4672 
4673 	ent = &cent->currentState;
4674 
4675 	// Rafael - mg42
4676 	if ( ( cent->currentState.clientNum == cg.snap->ps.clientNum && cg.snap->ps.persistant[PERS_HWEAPON_USE] ) ||
4677 		 ( cent->currentState.clientNum != cg.snap->ps.clientNum && ( cent->currentState.eFlags & EF_MG42_ACTIVE ) ) ) {
4678 		if ( cg.snap->ps.gunfx ) {
4679 			return;
4680 		}
4681 
4682 		trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, hWeaponSnd );
4683 		//trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, hWeaponSnd );
4684 		if ( cg_brassTime.integer > 0 ) {
4685 			CG_MachineGunEjectBrass( cent );
4686 		}
4687 
4688 		//	CG_MG42EFX (cent);
4689 
4690 		return;
4691 	}
4692 
4693 	if ( ent->weapon == WP_NONE ) {
4694 		return;
4695 	}
4696 	if ( ent->weapon >= WP_NUM_WEAPONS ) {
4697 		CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
4698 		return;
4699 	}
4700 	weap = &cg_weapons[ ent->weapon ];
4701 
4702 	cg.lastFiredWeapon = ent->weapon;   //----(SA)	added
4703 
4704 	// mark the entity as muzzle flashing, so when it is added it will
4705 	// append the flash to the weapon model
4706 	cent->muzzleFlashTime = cg.time;
4707 
4708 	// RF, kick angles
4709 	if ( ent->number == cg.snap->ps.clientNum ) {
4710 		CG_WeaponFireRecoil( ent->weapon );
4711 	}
4712 
4713 	// lightning gun only does this this on initial press
4714 	if ( ent->weapon == WP_FLAMETHROWER ) {
4715 		if ( cent->pe.lightningFiring ) {
4716 			return;
4717 		}
4718 	} else if (   ent->weapon == WP_GRENADE_LAUNCHER ||
4719 				  ent->weapon == WP_GRENADE_PINEAPPLE ||
4720 				  ent->weapon == WP_DYNAMITE ||
4721 				  ent->weapon == WP_GRENADE_SMOKE ) { // JPW NERVE
4722 		if ( ent->weapon == WP_GRENADE_SMOKE ) {
4723 			CG_Printf( "smoke grenade!\n" );
4724 		}
4725 		if ( ent->apos.trBase[0] > 0 ) { // underhand
4726 			return;
4727 		}
4728 	}
4729 
4730 	// play quad sound if needed
4731 	if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
4732 		trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound );
4733 	}
4734 
4735 	if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == EV_FIRE_WEAPON_LASTSHOT ) {
4736 		firesound = &weap->lastShotSound[0];
4737 		fireEchosound = &weap->flashEchoSound[0];
4738 
4739 		// try to use the lastShotSound, but don't assume it's there.
4740 		// if a weapon without the sound calls it, drop back to regular fire sound
4741 
4742 		for ( c = 0; c < 4; c++ ) {
4743 			if ( !firesound[c] ) {
4744 				break;
4745 			}
4746 		}
4747 		if ( !c ) {
4748 			firesound = &weap->flashSound[0];
4749 			fireEchosound = &weap->flashEchoSound[0];
4750 		}
4751 	} else {
4752 		firesound = &weap->flashSound[0];
4753 		fireEchosound = &weap->flashEchoSound[0];
4754 	}
4755 
4756 
4757 	// play a sound
4758 	for ( c = 0 ; c < 4 ; c++ ) {
4759 		if ( !firesound[c] ) {
4760 			break;
4761 		}
4762 	}
4763 	if ( c > 0 ) {
4764 		c = rand() % c;
4765 		if ( firesound[c] ) {
4766 			trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, firesound[c] );
4767 
4768 			if ( fireEchosound && fireEchosound[c] ) { // check for echo
4769 				centity_t   *cent;
4770 				vec3_t porg, gorg, norm;    // player/gun origin
4771 				float gdist;
4772 
4773 				cent = &cg_entities[ent->number];
4774 				VectorCopy( cent->currentState.pos.trBase, gorg );
4775 				VectorCopy( cg.refdef.vieworg, porg );
4776 				VectorSubtract( gorg, porg, norm );
4777 				gdist = VectorNormalize( norm );
4778 				if ( gdist > 512 && gdist < 4096 ) {   // temp dist.  TODO: use numbers that are weapon specific
4779 					// use gorg as the new sound origin
4780 					VectorMA( cg.refdef.vieworg, 64, norm, gorg );    // sound-on-a-stick
4781 					trap_S_StartSound( gorg, ent->number, CHAN_WEAPON, fireEchosound[c] );
4782 				}
4783 			}
4784 		}
4785 	}
4786 
4787 	// do brass ejection
4788 	if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
4789 		weap->ejectBrassFunc( cent );
4790 	}
4791 }
4792 
4793 
4794 // Ridah
4795 /*
4796 =================
4797 CG_AddSparks
4798 =================
4799 */
CG_AddSparks(vec3_t origin,vec3_t dir,int speed,int duration,int count,float randScale)4800 void CG_AddSparks( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ) {
4801 	localEntity_t   *le;
4802 	refEntity_t     *re;
4803 	vec3_t velocity;
4804 	int i;
4805 
4806 	for ( i = 0; i < count; i++ ) {
4807 		le = CG_AllocLocalEntity();
4808 		re = &le->refEntity;
4809 
4810 		VectorSet( velocity, dir[0] + crandom() * randScale, dir[1] + crandom() * randScale, dir[2] + crandom() * randScale );
4811 		VectorScale( velocity, (float)speed, velocity );
4812 
4813 		le->leType = LE_SPARK;
4814 		le->startTime = cg.time;
4815 		le->endTime = le->startTime + duration - (int)( 0.5 * random() * duration );
4816 		le->lastTrailTime = cg.time;
4817 
4818 		VectorCopy( origin, re->origin );
4819 		AxisCopy( axisDefault, re->axis );
4820 
4821 		le->pos.trType = TR_GRAVITY_LOW;
4822 		VectorCopy( origin, le->pos.trBase );
4823 		VectorMA( le->pos.trBase, 2 + random() * 4, dir, le->pos.trBase );
4824 		VectorCopy( velocity, le->pos.trDelta );
4825 		le->pos.trTime = cg.time;
4826 
4827 		le->refEntity.customShader = cgs.media.sparkParticleShader;
4828 
4829 		le->bounceFactor = 0.9;
4830 
4831 //		le->leBounceSoundType = LEBS_BLOOD;
4832 //		le->leMarkType = LEMT_BLOOD;
4833 	}
4834 }
4835 /*
4836 =================
4837 CG_AddBulletParticles
4838 =================
4839 */
CG_AddBulletParticles(vec3_t origin,vec3_t dir,int speed,int duration,int count,float randScale)4840 void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ) {
4841 //	localEntity_t	*le;
4842 //	refEntity_t		*re;
4843 	vec3_t velocity, pos;
4844 	int i;
4845 /*
4846 	// add the falling streaks
4847 	for (i=0; i<count/3; i++) {
4848 		le = CG_AllocLocalEntity();
4849 		re = &le->refEntity;
4850 
4851 		VectorSet( velocity, dir[0] + crandom()*randScale, dir[1] + crandom()*randScale, dir[2] + crandom()*randScale );
4852 		VectorScale( velocity, (float)speed*3, velocity );
4853 
4854 		le->leType = LE_SPARK;
4855 		le->startTime = cg.time;
4856 		le->endTime = le->startTime + duration - (int)(0.5 * random() * duration);
4857 		le->lastTrailTime = cg.time;
4858 
4859 		VectorCopy( origin, re->origin );
4860 		AxisCopy( axisDefault, re->axis );
4861 
4862 		le->pos.trType = TR_GRAVITY;
4863 		VectorCopy( origin, le->pos.trBase );
4864 		VectorMA( le->pos.trBase, 2 + random()*4, dir, le->pos.trBase );
4865 		VectorCopy( velocity, le->pos.trDelta );
4866 		le->pos.trTime = cg.time;
4867 
4868 		le->refEntity.customShader = cgs.media.bulletParticleTrailShader;
4869 //		le->refEntity.customShader = cgs.media.sparkParticleShader;
4870 
4871 		le->bounceFactor = 0.9;
4872 
4873 //		le->leBounceSoundType = LEBS_BLOOD;
4874 //		le->leMarkType = LEMT_BLOOD;
4875 	}
4876 */
4877 	// add the falling particles
4878 	for ( i = 0; i < count; i++ ) {
4879 
4880 		VectorSet( velocity, dir[0] + crandom() * randScale, dir[1] + crandom() * randScale, dir[2] + crandom() * randScale );
4881 		VectorScale( velocity, (float)speed, velocity );
4882 
4883 		VectorCopy( origin, pos );
4884 		VectorMA( pos, 2 + random() * 4, dir, pos );
4885 
4886 		CG_ParticleBulletDebris( pos, velocity, 300 + rand() % 300 );
4887 
4888 	}
4889 }
4890 
4891 //----(SA)	from MP
4892 /*
4893 =================
4894 CG_AddDirtBulletParticles
4895 =================
4896 */
CG_AddDirtBulletParticles(vec3_t origin,vec3_t dir,int speed,int duration,int count,float randScale,float width,float height,float alpha,char * shadername)4897 void CG_AddDirtBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale,
4898 								float width, float height, float alpha, char *shadername ) { // JPW NERVE
4899 	vec3_t velocity, pos;
4900 	int i;
4901 
4902 	// add the big falling particle
4903 	VectorSet( velocity, 0, 0, (float)speed );
4904 
4905 	VectorCopy( origin, pos );
4906 
4907 // JPW NERVE
4908 	CG_ParticleDirtBulletDebris_Core( pos, velocity, duration, width,height, alpha, shadername ); //600 + rand()%300 ); // keep central one
4909 	for ( i = 0; i < count; i++ ) {
4910 		VectorSet( velocity, dir[0] * crandom() * speed * randScale,
4911 				   dir[1] * crandom() * speed * randScale, dir[2] * random() * speed );
4912 		CG_ParticleDirtBulletDebris_Core( pos, velocity, duration + ( rand() % ( duration >> 1 ) ), // dur * 0.5, but int
4913 										  width,height, alpha, shadername );
4914 	}
4915 }
4916 //----(SA)	end
4917 
4918 /*
4919 =================
4920 CG_AddDebris
4921 =================
4922 */
CG_AddDebris(vec3_t origin,vec3_t dir,int speed,int duration,int count)4923 void CG_AddDebris( vec3_t origin, vec3_t dir, int speed, int duration, int count ) {
4924 	localEntity_t   *le;
4925 	refEntity_t     *re;
4926 	vec3_t velocity, unitvel;
4927 	float timeAdd;
4928 	int i;
4929 
4930 	for ( i = 0; i < count; i++ ) {
4931 		le = CG_AllocLocalEntity();
4932 		re = &le->refEntity;
4933 
4934 		VectorSet( unitvel, dir[0] + crandom() * 0.9, dir[1] + crandom() * 0.9, fabs( dir[2] ) > 0.5 ? dir[2] * ( 0.2 + 0.8 * random() ) : random() * 0.6 );
4935 		VectorScale( unitvel, (float)speed + (float)speed * 0.5 * crandom(), velocity );
4936 
4937 		le->leType = LE_DEBRIS;
4938 		le->startTime = cg.time;
4939 		le->endTime = le->startTime + duration + (int)( (float)duration * 0.8 * crandom() );
4940 		le->lastTrailTime = cg.time;
4941 
4942 		VectorCopy( origin, re->origin );
4943 		AxisCopy( axisDefault, re->axis );
4944 
4945 		le->pos.trType = TR_GRAVITY_LOW;
4946 		VectorCopy( origin, le->pos.trBase );
4947 		VectorCopy( velocity, le->pos.trDelta );
4948 		le->pos.trTime = cg.time;
4949 
4950 		timeAdd = 10.0 + random() * 40.0;
4951 		BG_EvaluateTrajectory( &le->pos, cg.time + (int)timeAdd, le->pos.trBase );
4952 
4953 		le->bounceFactor = 0.5;
4954 
4955 //		if (!rand()%2)
4956 //			le->effectWidth = 0;	// no flame
4957 //		else
4958 		le->effectWidth = 5 + random() * 5;
4959 
4960 //		if (rand()%3)
4961 		le->effectFlags |= 1;       // smoke trail
4962 
4963 
4964 //		le->leBounceSoundType = LEBS_BLOOD;
4965 //		le->leMarkType = LEMT_BLOOD;
4966 	}
4967 }
4968 // done.
4969 
4970 
4971 /*
4972 ==============
4973 CG_WaterRipple
4974 ==============
4975 */
CG_WaterRipple(qhandle_t shader,vec3_t loc,vec3_t dir,int size,int lifetime)4976 void CG_WaterRipple( qhandle_t shader, vec3_t loc, vec3_t dir, int size, int lifetime ) {
4977 	localEntity_t   *le;
4978 	refEntity_t     *re;
4979 
4980 	le = CG_AllocLocalEntity();
4981 	le->leType          = LE_SCALE_FADE;
4982 	le->leFlags         = LEF_PUFF_DONT_SCALE;
4983 
4984 	le->startTime       = cg.time;
4985 	le->endTime         = cg.time + lifetime;
4986 	le->lifeRate        = 1.0 / ( le->endTime - le->startTime );
4987 
4988 	re = &le->refEntity;
4989 	VectorCopy( loc, re->origin );
4990 	re->shaderTime      = cg.time / 1000.0f;
4991 	re->reType          = RT_SPLASH;
4992 	re->radius          = size;
4993 	re->customShader    = shader;
4994 	re->shaderRGBA[0]   = 0xff;
4995 	re->shaderRGBA[1]   = 0xff;
4996 	re->shaderRGBA[2]   = 0xff;
4997 	re->shaderRGBA[3]   = 0xff;
4998 	le->color[3]        = 1.0;
4999 }
5000 
5001 /*
5002 =================
5003 CG_MissileHitWall
5004 
5005 Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
5006 
5007 ClientNum is a dummy field used to define what sort of effect to spawn
5008 =================
5009 */
CG_MissileHitWall(int weapon,int clientNum,vec3_t origin,vec3_t dir,int surfFlags)5010 void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, int surfFlags ) { //	(SA) modified to send missilehitwall surface parameters
5011 	qhandle_t mod;
5012 	qhandle_t mark;
5013 	qhandle_t shader;
5014 	sfxHandle_t sfx, sfx2;
5015 	float radius;
5016 	float light;
5017 	vec3_t lightColor;
5018 	localEntity_t   *le;
5019 	int r;
5020 	qboolean alphaFade = qfalse;
5021 	qboolean isSprite;
5022 	int duration;
5023 	// Ridah
5024 	int lightOverdraw;
5025 	vec3_t sprOrg;
5026 	vec3_t sprVel = { 0 };
5027 	int i,j;
5028 
5029 //----(SA)	added
5030 	float shakeAmt;
5031 	int shakeDur, shakeRad;
5032 	shakeAmt = 0;
5033 	shakeDur = shakeRad = 0;
5034 //----(SA)	end
5035 
5036 	mark = 0;
5037 	radius = 32;
5038 	sfx = 0;
5039 	sfx2 = 0;
5040 	mod = 0;
5041 	shader = 0;
5042 	light = 0;
5043 	lightColor[0] = 1;
5044 	lightColor[1] = 1;
5045 	lightColor[2] = 0;
5046 	// Ridah
5047 	lightOverdraw = 0;
5048 
5049 	// set defaults
5050 	isSprite = qfalse;
5051 	duration = 600;
5052 
5053 	if ( surfFlags & SURF_SKY ) {
5054 		return;
5055 	}
5056 
5057 	switch ( weapon ) {
5058 	case WP_KNIFE:
5059 		sfx     = cgs.media.sfx_knifehit[4];    // different values for different types (stone/metal/wood/etc.)
5060 		mark    = cgs.media.bulletMarkShader;
5061 		radius  = 1 + rand() % 2;
5062 
5063 		CG_AddBulletParticles( origin, dir,
5064 							   20,      // speed
5065 							   800,     // duration
5066 							   3 + rand() % 6, // count
5067 							   1.0 );   // rand scale
5068 		break;
5069 
5070 	case WP_LUGER:
5071 	case WP_AKIMBO: //----(SA)	added
5072 	case WP_COLT:
5073 	case WP_MAUSER:
5074 	case WP_GARAND:
5075 	case WP_SNIPERRIFLE:
5076 	case WP_SNOOPERSCOPE:
5077 	case WP_MP40:
5078 	case WP_FG42:
5079 	case WP_FG42SCOPE:
5080 	case WP_THOMPSON:
5081 	case WP_STEN:
5082 	case WP_SILENCER:
5083 	case WP_VENOM:
5084 
5085 		// actually yeah.  meant that.  very rare.
5086 		if ( cg_gameType.integer == GT_SINGLE_PLAYER ) { // JPW NERVE
5087 			r = rand() & 31;
5088 		} else {
5089 			r = ( rand() & 3 ) + 1; // JPW NERVE increased spark frequency so players can tell where rounds are coming from in MP
5090 
5091 		}
5092 		if ( r == 3 ) {
5093 			sfx = cgs.media.sfx_ric1;
5094 		} else if ( r == 2 ) {
5095 			sfx = cgs.media.sfx_ric2;
5096 		} else if ( r == 1 ) {
5097 			sfx = cgs.media.sfx_ric3;
5098 		}
5099 
5100 		// clientNum is a dummy field used to define what sort of effect to spawn
5101 
5102 		if ( !clientNum ) {
5103 			// RF, why is this here? we need sparks if clientNum = 0, used for warzombie
5104 			// if ( sfx )
5105 			CG_AddSparks( origin, dir,
5106 						  350,      // speed
5107 						  200,      // duration
5108 						  15 + rand() % 7,  // count
5109 						  0.2 );    // rand scale
5110 		} else if ( clientNum == 1 )      { // just do a little smoke puff
5111 			vec3_t d, o;
5112 			VectorMA( origin, 12, dir, o );
5113 			VectorScale( dir, 7, d );
5114 			d[2] += 16;
5115 
5116 			// just snow
5117 			if ( surfFlags & SURF_SNOW ) {
5118 				CG_AddDirtBulletParticles( origin, dir,
5119 										   120, // speed
5120 										   900, // duration
5121 										   3, // count
5122 										   0.6f,
5123 										   20,
5124 										   4,
5125 										   0.3f,
5126 										   "water_splash" ); // rand scale
5127 			}
5128 
5129 			// grass/gravel
5130 			if ( surfFlags & ( SURF_GRASS | SURF_GRAVEL ) ) {
5131 				CG_AddDirtBulletParticles( origin, dir,
5132 										   190, // speed
5133 										   800, // duration
5134 										   3, // count
5135 										   0.1,
5136 										   60,
5137 										   10,
5138 										   0.5,
5139 										   "dirt_splash" ); // rand scale
5140 			} else {
5141 				CG_ParticleImpactSmokePuff( cgs.media.smokeParticleShader, o );
5142 
5143 				// some debris particles
5144 				CG_AddBulletParticles( origin, dir,
5145 									   20, // speed
5146 									   800, // duration
5147 									   3 + rand() % 6, // count
5148 									   1.0 ); // rand scale
5149 
5150 				// just do a little one
5151 				if ( sfx && ( rand() % 3 == 0 ) ) {
5152 					CG_AddSparks( origin, dir,
5153 								  450,      // speed
5154 								  300,      // duration
5155 								  3 + rand() % 3,   // count
5156 								  0.5 );    // rand scale
5157 				}
5158 			}
5159 		} else if ( clientNum == 2 )      {
5160 			sfx = 0;
5161 			mark = 0;
5162 
5163 			// (SA) needed to do the CG_WaterRipple using a localent since I needed the timer reset on the shader for each shot
5164 			CG_WaterRipple( cgs.media.wakeMarkShaderAnim, origin, tv( 0, 0, 1 ), 32, 1000 );
5165 
5166 			CG_AddDirtBulletParticles( origin, dir,
5167 									   190, // speed
5168 									   900, // duration
5169 									   5, // count
5170 									   0.5, // rand scale
5171 									   50, // w
5172 									   12, // h
5173 									   0.125, // alpha
5174 									   "water_splash" );
5175 
5176 			break;  // (SA) testing
5177 
5178 #if 0
5179 			// play a water splash
5180 			mod = cgs.media.waterSplashModel;
5181 			shader = cgs.media.waterSplashShader;
5182 			duration = 250;
5183 #endif
5184 		}
5185 
5186 		// Ridah, optimization, only spawn the bullet hole if we are close
5187 		// enough to see it, this way we can leave other marks around a lot
5188 		// longer, since most of the time we can't actually see the bullet holes
5189 // (SA) small modification.  only do this for non-rifles (so you can see your shots hitting when you're zooming with a rifle scope)
5190 		if ( weapon == WP_FG42SCOPE || weapon == WP_SNIPERRIFLE || weapon == WP_SNOOPERSCOPE || ( Distance( cg.refdef.vieworg, origin ) < 384 ) ) {
5191 
5192 			if ( clientNum ) {
5193 
5194 				// mark and sound can potentially use the surface for override values
5195 
5196 				mark = cgs.media.bulletMarkShader;  // default
5197 				alphaFade = qtrue;      // max made the bullet mark alpha (he'll make everything in the game out of 1024 textures, all with alpha blend funcs yet...)
5198 				radius = 1.5f + rand() % 2;   // slightly larger for DM
5199 
5200 				if ( surfFlags & SURF_METAL ) {
5201 					sfx = cgs.media.sfx_bullet_metalhit[rand() % 3];
5202 					mark = cgs.media.bulletMarkShaderMetal;
5203 					alphaFade = qtrue;
5204 				} else if ( surfFlags & SURF_WOOD ) {
5205 					sfx = cgs.media.sfx_bullet_woodhit[rand() % 3];
5206 					mark = cgs.media.bulletMarkShaderWood;
5207 					alphaFade = qtrue;
5208 					radius += 1;    // experimenting with different mark sizes per surface
5209 
5210 /*
5211 					if (rand()%100 > 75)
5212 					{
5213 						gentity_t	*sfx;
5214 						vec3_t		start;
5215 						vec3_t		dir;
5216 
5217 						sfx = G_Spawn ();
5218 
5219 						sfx->s.density = type;
5220 
5221 						VectorCopy (tr.endpos, start);
5222 
5223 						VectorCopy (muzzleTrace, dir);
5224 						VectorNegate (dir, dir);
5225 
5226 						G_SetOrigin (sfx, start);
5227 						G_SetAngle (sfx, dir);
5228 
5229 						G_AddEvent( sfx, EV_SHARD, DirToByte( dir ));
5230 
5231 						sfx->think = G_FreeEntity;
5232 						sfx->nextthink = level.time + 1000;
5233 
5234 						sfx->s.frame = 3 + (rand()%3) ;
5235 
5236 						trap_LinkEntity (sfx);
5237 
5238 
5239 void CG_Shard(centity_t *cent, vec3_t origin, vec3_t dir)
5240 						CG_Shard
5241 
5242 					}
5243 
5244 */
5245 
5246 
5247 				} else if ( surfFlags & SURF_CERAMIC ) {
5248 					sfx = cgs.media.sfx_bullet_ceramichit[rand() % 3];
5249 					mark = cgs.media.bulletMarkShaderCeramic;
5250 					alphaFade = qtrue;
5251 					radius += 2;    // experimenting with different mark sizes per surface
5252 
5253 				} else if ( surfFlags & SURF_GLASS ) {
5254 					sfx = cgs.media.sfx_bullet_glasshit[rand() % 3];
5255 					mark = cgs.media.bulletMarkShaderGlass;
5256 					alphaFade = qtrue;
5257 				} else if ( surfFlags & SURF_GRASS ) {
5258 
5259 				} else if ( surfFlags & SURF_GRAVEL ) {
5260 
5261 				} else if ( surfFlags & SURF_SNOW ) {
5262 
5263 				} else if ( surfFlags & SURF_ROOF ) {
5264 
5265 				} else if ( surfFlags & SURF_CARPET ) {
5266 
5267 				}
5268 
5269 			}
5270 		}
5271 		break;
5272 
5273 
5274 	case WP_MORTAR:
5275 		sfx = cgs.media.sfx_rockexp;
5276 		mark = cgs.media.burnMarkShader;
5277 		radius = 64;
5278 		light = 300;
5279 		isSprite = qtrue;
5280 		duration = 1000;
5281 		lightColor[0] = 0.75;
5282 		lightColor[1] = 0.5;
5283 		lightColor[2] = 0.1;
5284 
5285 		shakeAmt = 0.15f;
5286 		shakeDur = 600;
5287 		shakeRad = 700;
5288 
5289 		VectorScale( dir, 16, sprVel );
5290 		for ( i = 0; i < 5; i++ ) {
5291 			for ( j = 0; j < 3; j++ )
5292 				sprOrg[j] = origin[j] + 64 * dir[j] + 24 * crandom();
5293 			sprVel[2] += rand() % 50;
5294 //			CG_ParticleExplosion( 2, sprOrg, sprVel, 1000+rand()%250, 20, 40+rand()%60 );
5295 			CG_ParticleExplosion( "blacksmokeanimb", sprOrg, sprVel, 3500 + rand() % 250, 10, 250 + rand() % 60 );
5296 		}
5297 
5298 		VectorMA( origin, 24, dir, sprOrg );
5299 		VectorScale( dir, 64, sprVel );
5300 		// RF, I like this new animation, feel free to revert
5301 		CG_ParticleExplosion( "expblue", sprOrg, sprVel, 1000, 20, 300 );
5302 //		CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1200, 9, 300 );
5303 		break;
5304 
5305 	case WP_DYNAMITE:
5306 		shader = cgs.media.rocketExplosionShader;
5307 		sfx = cgs.media.sfx_dynamiteexp;
5308 		sfx2 = cgs.media.sfx_dynamiteexpDist;
5309 		mark = cgs.media.burnMarkShader;
5310 		radius = 64;
5311 		light = 300;
5312 		isSprite = qtrue;
5313 		duration = 1000;
5314 		lightColor[0] = 0.75;
5315 		lightColor[1] = 0.5;
5316 		lightColor[2] = 0.1;
5317 
5318 		shakeAmt = 0.25f;
5319 		shakeDur = 2800;
5320 		shakeRad = 8192;
5321 
5322 		if ( cg_gameType.integer == GT_SINGLE_PLAYER ) { // JPW NERVE
5323 			for ( i = 0; i < 5; i++ ) {
5324 				for ( j = 0; j < 3; j++ )
5325 					sprOrg[j] = origin[j] + 64 * dir[j] + 24 * crandom();
5326 				sprVel[2] += rand() % 50;
5327 				CG_ParticleExplosion( "blacksmokeanimb", sprOrg, sprVel,
5328 									  3500 + rand() % 250,          // duration
5329 									  10,                           // startsize
5330 									  250 + rand() % 60 );          // endsize
5331 			}
5332 
5333 			VectorMA( origin, 16, dir, sprOrg );
5334 			VectorScale( dir, 100, sprVel );
5335 
5336 			// trying this one just for now just for variety
5337 			CG_ParticleExplosion( "explode1", sprOrg, sprVel,
5338 								  1200,         // duration
5339 								  9,            // startsize
5340 								  300 );        // endsize
5341 
5342 			CG_AddDebris( origin, dir,
5343 						  280,              // speed
5344 						  1400,             // duration
5345 						  7 + rand() % 2 ); // count
5346 		}
5347 // JPW NERVE
5348 		else {
5349 			for ( i = 0; i < 5; i++ ) {
5350 				for ( j = 0; j < 2; j++ )
5351 					sprOrg[j] = origin[j] + 96 * crandom();
5352 				sprOrg[2] = origin[2] + 32 * random();
5353 				sprVel[2] += rand() % 75;
5354 				CG_ParticleExplosion( "blacksmokeanimb", sprOrg, sprVel,
5355 									  3500 + rand() % 250,      // duration
5356 									  10,                       // startsize
5357 									  250 + rand() % 400 );     // endsize
5358 			}
5359 
5360 			VectorMA( origin, 16, dir, sprOrg );
5361 			VectorScale( dir, 100, sprVel );
5362 
5363 			// trying this one just for now just for variety
5364 			CG_ParticleExplosion( "explode1", sprOrg, sprVel,
5365 								  1200,         // duration
5366 								  9,            // startsize
5367 								  250 + rand() % 400 );     // endsize
5368 
5369 			CG_AddDebris( origin, dir,
5370 						  400 + random() * 300,         // speed
5371 						  1400,             // duration
5372 						  7 + rand() % 12 );    // count
5373 		}
5374 // jpw
5375 		break;
5376 
5377 	case WP_GRENADE_SMOKE: // JPW NERVE
5378 	case WP_GRENADE_LAUNCHER:
5379 	case WP_GRENADE_PINEAPPLE:
5380 //		mod = cgs.media.dishFlashModel;
5381 //		shader = cgs.media.grenadeExplosionShader;
5382 		shader = cgs.media.rocketExplosionShader;       // copied from RL
5383 		sfx = cgs.media.sfx_rockexp;
5384 		mark = cgs.media.burnMarkShader;
5385 		radius = 64;
5386 		light = 300;
5387 		isSprite = qtrue;
5388 		duration = 1000;
5389 		lightColor[0] = 0.75;
5390 		lightColor[1] = 0.5;
5391 		lightColor[2] = 0.1;
5392 
5393 		if ( weapon != WP_GRENADE_SMOKE ) {
5394 			shakeAmt = 0.15f;
5395 			shakeDur = 1000;
5396 			shakeRad = 600;
5397 		}
5398 
5399 		// Ridah, explosion sprite animation
5400 		VectorMA( origin, 16, dir, sprOrg );
5401 		VectorScale( dir, 100, sprVel );
5402 
5403 		// RF, testing new explosion animation
5404 		CG_ParticleExplosion( "expblue", sprOrg, sprVel, 700, 20, 160 );
5405 		//CG_ParticleExplosion( "twiltb", sprOrg, sprVel, 600, 9, 100 );
5406 		//CG_ParticleExplosion( 3, sprOrg, sprVel, 900, 9, 250 );
5407 /*
5408 		r = 2 + rand()%3;
5409 		for (i=0; i<3; i++) {
5410 			for (j=0;j<3;j++) sprOrg[j] = origin[j] + 14*dir[j] + 14*crandom();
5411 			CG_ParticleExplosion( 3, sprOrg, sprVel, 800+rand()%250, 9, 60+rand()%200 );
5412 		}
5413 */
5414 		// Ridah, throw some debris
5415 		CG_AddDebris( origin, dir,
5416 					  280,      // speed
5417 					  1400,     // duration
5418 					  // 15 + rand()%5 );	// count
5419 					  7 + rand() % 2 ); // count
5420 
5421 		break;
5422 	case VERYBIGEXPLOSION:
5423 	case WP_PANZERFAUST:
5424 //		mod = cgs.media.dishFlashModel;
5425 //		shader = cgs.media.rocketExplosionShader;
5426 		sfx = cgs.media.sfx_rockexp;
5427 		mark = cgs.media.burnMarkShader;
5428 		radius = 64;
5429 		light = 600;
5430 		isSprite = qtrue;
5431 		duration = 1000;
5432 		// Ridah, changed to flamethrower colors
5433 //		lightColor[0] = 1;
5434 //		lightColor[1] = 1;//0.75;
5435 //		lightColor[2] = 0.6;//0.0;
5436 		lightColor[0] = 0.75;
5437 		lightColor[1] = 0.5;
5438 		lightColor[2] = 0.1;
5439 
5440 		// explosion sprite animation
5441 		VectorMA( origin, 24, dir, sprOrg );
5442 		VectorScale( dir, 64, sprVel );
5443 
5444 		// cam shake
5445 		if ( weapon == VERYBIGEXPLOSION ) {
5446 			shakeAmt = 0.2f;
5447 			shakeDur = 1000;
5448 			shakeRad = 1200;
5449 		} else {
5450 			shakeAmt = 0.15f;
5451 			shakeDur = 800;
5452 			shakeRad = 1000;
5453 		}
5454 
5455 // JPW NERVE
5456 		if ( cg_gameType.integer == GT_SINGLE_PLAYER ) {
5457 			if ( weapon == VERYBIGEXPLOSION ) {
5458 				CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1200, 20, 300 );
5459 			} else {
5460 				CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 40, 70 );
5461 			}
5462 
5463 			// NOTE: these must all have the same duration, so that we are less likely to use a wider range of images per scene
5464 			for ( i = 0; i < 4; i++ ) {
5465 				if ( weapon == VERYBIGEXPLOSION ) {
5466 					for ( j = 0; j < 3; j++ ) sprOrg[j] = origin[j] + 32 * dir[j] + 32 * crandom();
5467 					CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1200, 40, 160 + rand() % 120 );
5468 				} else if ( i < 2 ) {
5469 					for ( j = 0; j < 3; j++ ) sprOrg[j] = origin[j] + 24 * dir[j] + 16 * crandom();
5470 					CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 15, 40 + rand() % 30 );
5471 				}
5472 			}
5473 
5474 			// Ridah, throw some debris
5475 			CG_AddDebris( origin, dir,
5476 						  120,      // speed
5477 						  2000,  //350,	// duration
5478 						  // 15 + rand()%5 );	// count
5479 						  7 + rand() % 2 ); // count
5480 		}
5481 // JPW NERVE -- multiplayer explosions over the top due to large damage radiusesesizes
5482 		else {
5483 			CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1600, 20, 200 + random() * 400 );
5484 
5485 			// NOTE: these must all have the same duration, so that we are less likely to use a wider range of images per scene
5486 			for ( i = 0; i < 4; i++ ) {
5487 				for ( j = 0; j < 3; j++ ) sprOrg[j] = origin[j] + 160 * crandom();
5488 				CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1600, 40, 260 + rand() % 120 );
5489 			}
5490 
5491 			CG_AddDebris( origin, dir,
5492 						  400 + random() * 200, // speed
5493 						  rand() % 2000 + 1000, //350,	// duration
5494 						  // 15 + rand()%5 );	// count
5495 						  5 + rand() % 5 ); // count
5496 
5497 		}
5498 // jpw
5499 /*
5500 		// some sparks
5501 		CG_AddSparks( origin, dir,
5502 						200,	// speed
5503 						800,	// duration
5504 						5 + rand()%10,	// count
5505 						0.8 );	// rand scale
5506 */
5507 		// done.
5508 
5509 		break;
5510 
5511 	default:
5512 	case WP_FLAMETHROWER:
5513 		// no explosion at LG impact, it is added with the beam
5514 		return;
5515 
5516 		break;
5517 
5518 	}
5519 	// done.
5520 
5521 	if ( sfx ) {
5522 		trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
5523 	}
5524 
5525 //----(SA)	added
5526 	if ( sfx2 ) {  // distant sounds for weapons with a broadcast fire sound (so you /always/ hear dynamite explosions)
5527 		trap_S_StartLocalSound( sfx2, CHAN_AUTO );
5528 	}
5529 //----(SA)	end
5530 
5531 
5532 	//
5533 	// camera shake
5534 	//
5535 	if ( shakeAmt ) {
5536 		CG_StartShakeCamera( shakeAmt, shakeDur, origin, shakeRad );
5537 	}
5538 
5539 
5540 	//
5541 	// create the explosion
5542 	//
5543 	if ( mod ) {
5544 		le = CG_MakeExplosion( origin, dir,
5545 							   mod, shader,
5546 							   duration, isSprite );
5547 		le->light = light;
5548 		// Ridah
5549 		le->lightOverdraw = lightOverdraw;
5550 		VectorCopy( lightColor, le->lightColor );
5551 	}
5552 
5553 	//
5554 	// impact mark
5555 	//
5556 	if ( mark ) {
5557 		CG_ImpactMark( mark, origin, dir, random() * 360, 1,1,1,1, alphaFade, radius, qfalse, -1 );
5558 	}
5559 }
5560 
5561 /*
5562 ==============
5563 CG_MissileHitWallSmall
5564 ==============
5565 */
CG_MissileHitWallSmall(int weapon,int clientNum,vec3_t origin,vec3_t dir)5566 void CG_MissileHitWallSmall( int weapon, int clientNum, vec3_t origin, vec3_t dir ) {
5567 	qhandle_t mod;
5568 	qhandle_t mark;
5569 	qhandle_t shader;
5570 	sfxHandle_t sfx;
5571 	float radius;
5572 	float light;
5573 	vec3_t lightColor;
5574 	localEntity_t   *le;
5575 //	int				r;
5576 	qboolean alphaFade;
5577 	qboolean isSprite;
5578 	int duration;
5579 	// Ridah
5580 	int lightOverdraw;
5581 	vec3_t sprOrg;
5582 	vec3_t sprVel;
5583 //	int				i,j;
5584 
5585 	mod = 0;
5586 	lightColor[0] = 1;
5587 	lightColor[1] = 1;
5588 	lightColor[2] = 0;
5589 	// Ridah
5590 	lightOverdraw = 0;
5591 
5592 	// set defaults
5593 	shader = cgs.media.rocketExplosionShader;       // copied from RL
5594 	sfx = cgs.media.sfx_rockexp;
5595 	mark = cgs.media.burnMarkShader;
5596 	radius = 64;
5597 	light = 300;
5598 	isSprite = qtrue;
5599 	duration = 1000;
5600 	lightColor[0] = 0.75;
5601 	lightColor[1] = 0.5;
5602 	lightColor[2] = 0.1;
5603 
5604 	// Ridah, explosion sprite animation
5605 	VectorMA( origin, 16, dir, sprOrg );
5606 	VectorScale( dir, 64, sprVel );
5607 
5608 	CG_ParticleExplosion( "explode1", sprOrg, sprVel, 600, 6, 50 );
5609 
5610 	// Ridah, throw some debris
5611 	CG_AddDebris( origin, dir,
5612 				  280,          // speed
5613 				  1400,         // duration
5614 				  // 15 + rand()%5 );	// count
5615 				  7 + rand() % 2 );     // count
5616 
5617 	if ( sfx ) {
5618 		trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
5619 	}
5620 
5621 	//
5622 	// create the explosion
5623 	//
5624 	if ( mod ) {
5625 		le = CG_MakeExplosion( origin, dir,
5626 							   mod, shader,
5627 							   duration, isSprite );
5628 		le->light = light;
5629 		// Ridah
5630 		le->lightOverdraw = lightOverdraw;
5631 		VectorCopy( lightColor, le->lightColor );
5632 	}
5633 
5634 	//
5635 	// impact mark
5636 	//
5637 	alphaFade = ( mark == cgs.media.energyMarkShader );   // plasma fades alpha, all others fade color
5638 	// CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse, 60000 );
5639 	CG_ImpactMark( mark, origin, dir, random() * 360, 1,1,1,1, alphaFade, radius, qfalse, INT_MAX );
5640 
5641 	CG_StartShakeCamera( 0.05, 300, origin, 300 );
5642 }
5643 
5644 /*
5645 =================
5646 CG_MissileHitPlayer
5647 =================
5648 */
CG_MissileHitPlayer(centity_t * cent,int weapon,vec3_t origin,vec3_t dir,int entityNum)5649 void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, int entityNum ) {
5650 	int i;
5651 
5652 	CG_Bleed( origin, entityNum );
5653 
5654 	// some weapons will make an explosion with the blood, while
5655 	// others will just make the blood
5656 	switch ( weapon ) {
5657 		// knives just make the flesh hit sound.  no other effects
5658 	case WP_KNIFE:
5659 		i = rand() % 4;
5660 		if ( cgs.media.sfx_knifehit[i] ) {
5661 			trap_S_StartSound( origin, cent->currentState.number, CHAN_WEAPON, cgs.media.sfx_knifehit[i] );
5662 		}
5663 
5664 		if ( cent->currentState.number == cg.snap->ps.clientNum ) {
5665 			CG_StartShakeCamera( 0.03, 500, origin, 100 );
5666 		}
5667 		break;
5668 
5669 	case WP_GRENADE_LAUNCHER:
5670 	case WP_PANZERFAUST:
5671 		// this shake is /on top/ of the shake from the impact (done in CG_MissileHitWall)
5672 		CG_StartShakeCamera( 0.1, 500, origin, 100 );
5673 		CG_MissileHitWall( weapon, 0, origin, dir, 0 );     //	(SA) modified to send missilehitwall surface parameters
5674 		break;
5675 	default:
5676 		break;
5677 	}
5678 }
5679 
5680 
5681 
5682 /*
5683 ============================================================================
5684 
5685 VENOM GUN TRACING
5686 
5687 ============================================================================
5688 */
5689 
5690 /*
5691 ================
5692 CG_VenomPellet
5693 ================
5694 */
CG_VenomPellet(vec3_t start,vec3_t end,int skipNum)5695 static void CG_VenomPellet( vec3_t start, vec3_t end, int skipNum ) {
5696 }
5697 
5698 
5699 //----(SA)	all changes to venom below should be mine
5700 #define DEFAULT_VENOM_COUNT 10
5701 //#define DEFAULT_VENOM_SPREAD 20
5702 //#define DEFAULT_VENOM_SPREAD 400
5703 #define DEFAULT_VENOM_SPREAD 700
5704 
5705 /*
5706 ================
5707 CG_VenomPattern
5708 
5709 Perform the same traces the server did to locate the
5710 hit splashes (FIXME: random seed isn't synced anymore)
5711 
5712   (SA)	right now this is random like a shotgun.  I want to make it more
5713 		organized so that the pattern is more of a circle (with some degree of randomness)
5714 ================
5715 */
CG_VenomPattern(vec3_t origin,vec3_t origin2,int otherEntNum)5716 static void CG_VenomPattern( vec3_t origin, vec3_t origin2, int otherEntNum ) {
5717 	int i;
5718 	float r, u;
5719 	vec3_t end;
5720 	vec3_t forward, right, up;
5721 
5722 	// derive the right and up vectors from the forward vector, because
5723 	// the client won't have any other information
5724 	VectorNormalize2( origin2, forward );
5725 	PerpendicularVector( right, forward );
5726 	CrossProduct( forward, right, up );
5727 
5728 	// generate the "random" spread pattern
5729 	for ( i = 0 ; i < DEFAULT_VENOM_COUNT ; i++ ) {
5730 		r = crandom() * DEFAULT_VENOM_SPREAD;
5731 		u = crandom() * DEFAULT_VENOM_SPREAD;
5732 		VectorMA( origin, 8192, forward, end );
5733 		VectorMA( end, r, right, end );
5734 		VectorMA( end, u, up, end );
5735 
5736 		CG_VenomPellet( origin, end, otherEntNum );
5737 	}
5738 }
5739 
5740 /*
5741 ==============
5742 CG_VenomFire
5743 ==============
5744 */
CG_VenomFire(entityState_t * es,qboolean fullmode)5745 void CG_VenomFire( entityState_t *es, qboolean fullmode ) {
5746 	vec3_t v;
5747 	int contents;
5748 
5749 	VectorSubtract( es->origin2, es->pos.trBase, v );
5750 	VectorNormalize( v );
5751 	VectorScale( v, 32, v );
5752 	VectorAdd( es->pos.trBase, v, v );
5753 	if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) {
5754 		// ragepro can't alpha fade, so don't even bother with smoke
5755 		vec3_t up;
5756 
5757 		contents = CG_PointContents( es->pos.trBase, 0 );
5758 		if ( !( contents & CONTENTS_WATER ) ) {
5759 			VectorSet( up, 0, 0, 32 );
5760 			if ( fullmode ) {
5761 				CG_SmokePuff( v, up, 24, 1, 1, 1, 0.33, 1200, cg.time, 0, 0, cgs.media.shotgunSmokePuffShader );    // LEF_PUFF_DONT_SCALE
5762 			}
5763 //----(SA)	for the time being don't do the single shot smoke as it's position is funky
5764 //			else
5765 //				CG_SmokePuff( v, up, 4, 1, 1, 1, 0.33, 700, cg.time, 0, cgs.media.shotgunSmokePuffShader );
5766 		}
5767 	}
5768 	if ( fullmode ) {
5769 		CG_VenomPattern( es->pos.trBase, es->origin2, es->otherEntityNum );
5770 	}
5771 }
5772 
5773 /*
5774 ============================================================================
5775 
5776 BULLETS
5777 
5778 ============================================================================
5779 */
5780 
5781 /*
5782 ===============
5783 CG_SpawnTracer
5784 ===============
5785 */
CG_SpawnTracer(int sourceEnt,vec3_t pstart,vec3_t pend)5786 void CG_SpawnTracer( int sourceEnt, vec3_t pstart, vec3_t pend ) {
5787 	localEntity_t   *le;
5788 	float dist;
5789 	vec3_t dir, ofs;
5790 	orientation_t or;
5791 	vec3_t start, end;
5792 
5793 	VectorCopy( pstart, start );
5794 	VectorCopy( pend, end );
5795 
5796 	VectorSubtract( end, start, dir );
5797 	dist = VectorNormalize( dir );
5798 
5799 	if ( dist < 2.0 * cg_tracerLength.value ) {
5800 		return; // segment isnt long enough, dont bother
5801 
5802 	}
5803 	if ( sourceEnt < cgs.maxclients ) {
5804 		// for visual purposes, find the actual tag_weapon for this client
5805 		// and offset the start and end accordingly
5806 		if ( cg_entities[sourceEnt].currentState.eFlags & EF_MG42_ACTIVE ) {   // mounted
5807 			start[2] -= 32; // (SA) hack to get the tracer down below the barrel FIXME: do properly
5808 		} else {
5809 			if ( CG_GetWeaponTag( sourceEnt, "tag_flash", &or ) ) {
5810 				VectorSubtract( or.origin, start, ofs );
5811 				if ( VectorLength( ofs ) < 64 ) {
5812 					VectorAdd( start, ofs, start );
5813 				}
5814 			}
5815 		}
5816 	}
5817 
5818 	// subtract the length of the tracer from the end point, so we dont go through the end point
5819 	VectorMA( end, -cg_tracerLength.value, dir, end );
5820 	dist = VectorDistance( start, end );
5821 
5822 	le = CG_AllocLocalEntity();
5823 	le->leType = LE_MOVING_TRACER;
5824 	le->startTime = cg.time - ( cg.frametime ? ( rand() % cg.frametime ) / 2 : 0 );
5825 	le->endTime = le->startTime + 1000.0 * dist / cg_tracerSpeed.value;
5826 
5827 	le->pos.trType = TR_LINEAR;
5828 	le->pos.trTime = le->startTime;
5829 	VectorCopy( start, le->pos.trBase );
5830 	VectorScale( dir, cg_tracerSpeed.value, le->pos.trDelta );
5831 }
5832 
5833 /*
5834 ===============
5835 CG_DrawTracer
5836 ===============
5837 */
CG_DrawTracer(vec3_t start,vec3_t finish)5838 void CG_DrawTracer( vec3_t start, vec3_t finish ) {
5839 	vec3_t forward, right;
5840 	polyVert_t verts[4];
5841 	vec3_t line;
5842 
5843 	VectorSubtract( finish, start, forward );
5844 
5845 	line[0] = DotProduct( forward, cg.refdef.viewaxis[1] );
5846 	line[1] = DotProduct( forward, cg.refdef.viewaxis[2] );
5847 
5848 	VectorScale( cg.refdef.viewaxis[1], line[1], right );
5849 	VectorMA( right, -line[0], cg.refdef.viewaxis[2], right );
5850 	VectorNormalize( right );
5851 
5852 	VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz );
5853 	verts[0].st[0] = 1;
5854 	verts[0].st[1] = 1;
5855 	verts[0].modulate[0] = 255;
5856 	verts[0].modulate[1] = 255;
5857 	verts[0].modulate[2] = 255;
5858 	verts[0].modulate[3] = 255;
5859 
5860 	VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz );
5861 	verts[1].st[0] = 1;
5862 	verts[1].st[1] = 0;
5863 	verts[1].modulate[0] = 255;
5864 	verts[1].modulate[1] = 255;
5865 	verts[1].modulate[2] = 255;
5866 	verts[1].modulate[3] = 255;
5867 
5868 	VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz );
5869 	verts[2].st[0] = 0;
5870 	verts[2].st[1] = 0;
5871 	verts[2].modulate[0] = 255;
5872 	verts[2].modulate[1] = 255;
5873 	verts[2].modulate[2] = 255;
5874 	verts[2].modulate[3] = 255;
5875 
5876 	VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz );
5877 	verts[3].st[0] = 0;
5878 	verts[3].st[1] = 1;
5879 	verts[3].modulate[0] = 255;
5880 	verts[3].modulate[1] = 255;
5881 	verts[3].modulate[2] = 255;
5882 	verts[3].modulate[3] = 255;
5883 
5884 	trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts );
5885 }
5886 
5887 /*
5888 ===============
5889 CG_Tracer
5890 ===============
5891 */
CG_Tracer(vec3_t source,vec3_t dest,int sparks)5892 void CG_Tracer( vec3_t source, vec3_t dest, int sparks ) {
5893 	float len, begin, end;
5894 	vec3_t start, finish;
5895 	vec3_t forward;
5896 
5897 	// tracer
5898 	VectorSubtract( dest, source, forward );
5899 	len = VectorNormalize( forward );
5900 
5901 	// start at least a little ways from the muzzle
5902 	if ( len < 100 && !sparks ) {
5903 		return;
5904 	}
5905 	begin = 50 + random() * ( len - 60 );
5906 	end = begin + cg_tracerLength.value;
5907 	if ( end > len ) {
5908 		end = len;
5909 	}
5910 	VectorMA( source, begin, forward, start );
5911 	VectorMA( source, end, forward, finish );
5912 
5913 	CG_DrawTracer( start, finish );
5914 }
5915 
5916 
5917 /*
5918 ======================
5919 CG_CalcMuzzlePoint
5920 ======================
5921 */
CG_CalcMuzzlePoint(int entityNum,vec3_t muzzle)5922 static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) {
5923 	vec3_t forward, right, up;
5924 	centity_t   *cent;
5925 
5926 	if ( entityNum == cg.snap->ps.clientNum ) {
5927 		VectorCopy( cg.snap->ps.origin, muzzle );
5928 		muzzle[2] += cg.snap->ps.viewheight;
5929 		AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL );
5930 		VectorMA( muzzle, 14, forward, muzzle );
5931 		return qtrue;
5932 	}
5933 
5934 	cent = &cg_entities[entityNum];
5935 
5936 	VectorCopy( cent->currentState.pos.trBase, muzzle );
5937 
5938 	AngleVectors( cent->currentState.apos.trBase, forward, right, up );
5939 	muzzle[2] += DEFAULT_VIEWHEIGHT;
5940 
5941 	VectorMA( muzzle, 14, forward, muzzle );
5942 
5943 	return qtrue;
5944 }
5945 
5946 /*
5947 ======================
5948 CG_Bullet
5949 
5950 Renders bullet effects.
5951 ======================
5952 */
CG_Bullet(vec3_t end,int sourceEntityNum,vec3_t normal,qboolean flesh,int fleshEntityNum,qboolean wolfkick,int otherEntNum2)5953 void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum, qboolean wolfkick, int otherEntNum2 ) {
5954 	trace_t trace;
5955 	int sourceContentType = 0, destContentType = 0; // TTimo: init
5956 	vec3_t dir;
5957 	vec3_t start, trend, tmp;      // JPW
5958 	static int lastBloodSpat;
5959 
5960 	// if the shooter is currently valid, calc a source point and possibly
5961 	// do trail effects
5962 	if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) {
5963 		if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) {
5964 			sourceContentType = CG_PointContents( start, 0 );
5965 			destContentType = CG_PointContents( end, 0 );
5966 
5967 			// do a complete bubble trail if necessary
5968 			if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) {
5969 				CG_BubbleTrail( start, end, .5, 8 );
5970 			}
5971 			// bubble trail from water into air
5972 			else if ( ( sourceContentType & CONTENTS_WATER ) ) {
5973 				trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
5974 				CG_BubbleTrail( start, trace.endpos, .5, 8 );
5975 			}
5976 			// bubble trail from air into water
5977 			else if ( ( destContentType & CONTENTS_WATER ) ) {
5978 				// only add bubbles if effect is close to viewer
5979 				if ( Distance( cg.snap->ps.origin, end ) < 1024 ) {
5980 					trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
5981 					CG_BubbleTrail( end, trace.endpos, .5, 8 );
5982 				}
5983 			}
5984 
5985 			// if not flesh, then do a moving tracer
5986 			if ( flesh ) {
5987 				// draw a tracer
5988 				if ( !wolfkick && random() < cg_tracerChance.value ) {
5989 					CG_Tracer( start, end, 0 );
5990 				}
5991 			} else {    // (not flesh)
5992 				if ( otherEntNum2 >= 0 && otherEntNum2 != ENTITYNUM_NONE ) {
5993 					CG_SpawnTracer( otherEntNum2, start, end );
5994 				} else {
5995 					CG_SpawnTracer( sourceEntityNum, start, end );
5996 				}
5997 			}
5998 		}
5999 	}
6000 
6001 	// impact splash and mark
6002 	if ( flesh ) {
6003 		vec3_t origin;
6004 		int aiType;
6005 
6006 		aiType = cg_entities[fleshEntityNum].currentState.aiChar;
6007 
6008 		if ( fleshEntityNum < MAX_CLIENTS ) {
6009 			CG_Bleed( end, fleshEntityNum );
6010 		}
6011 
6012 		// play the bullet hit flesh sound
6013 		// HACK, if this is not us getting hit, make it quieter
6014 		if ( fleshEntityNum == cg.snap->ps.clientNum ) {
6015 
6016 			// (SA) TODO: for metal guys, make metal a flag rather than an aitype check?
6017 			if ( aiType == AICHAR_PROTOSOLDIER ||
6018 				 aiType == AICHAR_SUPERSOLDIER ) {
6019 				CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshMetalScript, NULL, fleshEntityNum );
6020 			} else {
6021 				CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshScript, NULL, fleshEntityNum );
6022 			}
6023 		} else {
6024 			VectorSubtract( cg_entities[fleshEntityNum].lerpOrigin, cg.snap->ps.origin, origin );
6025 			VectorMA( cg.snap->ps.origin, 3, origin, origin );
6026 			if ( aiType == AICHAR_PROTOSOLDIER ||
6027 				 aiType == AICHAR_SUPERSOLDIER ) {
6028 				CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshMetalScript, origin, ENTITYNUM_WORLD );
6029 			} else {
6030 				CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshScript, origin, ENTITYNUM_WORLD );
6031 			}
6032 		}
6033 
6034 		// if we haven't dropped a blood spat in a while, check if this is a good scenario
6035 		if ( lastBloodSpat > cg.time || lastBloodSpat < cg.time - 500 ) {
6036 			if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) {
6037 				VectorSubtract( end, start, dir );
6038 				VectorNormalize( dir );
6039 				VectorMA( end, 128, dir, trend );
6040 				trap_CM_BoxTrace( &trace, end, trend, NULL, NULL, 0, MASK_SHOT & ~CONTENTS_BODY );
6041 
6042 				if ( trace.fraction < 1 ) {
6043 					CG_ImpactMark( cgs.media.bloodDotShaders[rand() % 5], trace.endpos, trace.plane.normal, random() * 360,
6044 								   1,1,1,1, qtrue, 15 + random() * 20, qfalse, cg_bloodTime.integer * 1000 );
6045 					lastBloodSpat = cg.time;
6046 				} else if ( lastBloodSpat < cg.time - 1000 ) {
6047 					// drop one on the ground?
6048 					VectorCopy( end, trend );
6049 					trend[2] -= 64;
6050 					trap_CM_BoxTrace( &trace, end, trend, NULL, NULL, 0, MASK_SHOT & ~CONTENTS_BODY );
6051 
6052 					if ( trace.fraction < 1 ) {
6053 						CG_ImpactMark( cgs.media.bloodDotShaders[rand() % 5], trace.endpos, trace.plane.normal, random() * 360,
6054 									   1,1,1,1, qtrue, 15 + random() * 10, qfalse, cg_bloodTime.integer * 1000 );
6055 						lastBloodSpat = cg.time;
6056 					}
6057 				}
6058 			}
6059 		}
6060 
6061 	} else {    // (not flesh)
6062 		int fromweap;
6063 		fromweap = cg_entities[sourceEntityNum].currentState.weapon;
6064 
6065 		if ( !fromweap || cg_entities[sourceEntityNum].currentState.eFlags & EF_MG42_ACTIVE ) { // mounted
6066 			fromweap = WP_MP40;
6067 		}
6068 
6069 		// TODO: not sure what kind of effect were going to do
6070 		if ( wolfkick ) {
6071 			return;
6072 		}
6073 
6074 		if ( CG_CalcMuzzlePoint( sourceEntityNum, start )
6075 			 || cg.snap->ps.persistant[PERS_HWEAPON_USE] ) {
6076 			vec3_t start2;
6077 			VectorSubtract( end, start, dir );
6078 			VectorNormalize( dir );
6079 			VectorMA( end, -4, dir, start2 );   // back off a little so it doesn't start in solid
6080 			VectorMA( end, 64, dir, dir );
6081 			trap_CM_BoxTrace( &trace, start2, dir, NULL, NULL, 0, MASK_SHOT );
6082 
6083 			if ( ( trace.surfaceFlags & SURF_METAL ) || !( rand() % 10 ) || ( otherEntNum2 != ENTITYNUM_NONE ) ) {
6084 				// JPW NERVE compute new spark direction from normal & dir (rotate -dir 180 degrees about normal)
6085 
6086 				// (SA) NOTE: isn't this done by the server and sent along in the 'normal' that's passed into this routine? (1107 g_weapon.c)
6087 
6088 				VectorScale( dir,-1.0f,tmp );
6089 				RotatePointAroundVector( tmp,normal,tmp,180.0f );
6090 				CG_MissileHitWall( fromweap, 0, end, tmp, trace.surfaceFlags ); // sparks	//	(SA) modified to send missilehitwall surface parameters
6091 // jpw
6092 //				CG_MissileHitWall( fromweap, 0, end, normal, trace.surfaceFlags );	// sparks	//	(SA) modified to send missilehitwall surface parameters
6093 			}
6094 			if ( !( sourceContentType & CONTENTS_WATER ) && ( destContentType & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {   // only when shooting /into/ water
6095 				trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, MASK_WATER );
6096 //				CG_Trace(&trace, start, NULL, NULL, end, -1, MASK_WATER);
6097 //				if (!(trace.surfaceFlags & SURF_NOMARKS)) {	// check to see if the surface should draw splashes
6098 				CG_MissileHitWall( fromweap, 2, trace.endpos, trace.plane.normal, trace.surfaceFlags );
6099 //				}
6100 			} else {
6101 				CG_MissileHitWall( fromweap, 1, end, normal, trace.surfaceFlags );   // smoke puff	//	(SA) modified to send missilehitwall surface parameters
6102 
6103 				if ( 0 ) {
6104 					localEntity_t   *le;
6105 					le = CG_AllocLocalEntity();
6106 					le->leType = LE_EMITTER;
6107 					le->startTime = cg.time;
6108 					le->endTime = le->startTime + 20000;
6109 					le->pos.trType = TR_STATIONARY;
6110 					VectorCopy( end, le->pos.trBase );
6111 					VectorCopy( normal, le->angles.trBase );
6112 					le->ownerNum = 0;
6113 				}
6114 			}
6115 		}
6116 	}
6117 }
6118 
6119 /*
6120 ============
6121 CG_ClientDamage
6122 ============
6123 */
CG_ClientDamage(int entnum,int enemynum,int id)6124 void CG_ClientDamage( int entnum, int enemynum, int id ) {
6125 	if ( id > CLDMG_MAX ) {
6126 		CG_Error( "CG_ClientDamage: unknown damage type: %i\n", id );
6127 	}
6128 
6129 	// NERVE - SMF - clientDamage commands are now sent through usercmds for multiplayer
6130 	if ( cgs.gametype == GT_WOLF ) {
6131 		if ( entnum == cg.snap->ps.clientNum ) {
6132 			// NOTE: MAX_CLIENTS currently only needs 7 bits, the rest is for id tag
6133 			cg.cld = id << 7;
6134 			cg.cld |= enemynum;
6135 		}
6136 	}
6137 	// -NERVE - SMF
6138 	else {
6139 		trap_SendClientCommand( va( "cld %i %i %i", entnum, enemynum, id ) );
6140 	}
6141 }
6142