1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 // bg_misc.c -- both games misc functions, all completely stateless
25 
26 #include "qcommon/q_shared.h"
27 #include "bg_public.h"
28 
29 #if defined(_GAME)
30 	#include "g_local.h"
31 #elif defined(_CGAME)
32 	#include "cgame/cg_local.h"
33 #elif defined(UI_BUILD)
34 	#include "ui/ui_local.h"
35 #endif
36 
37 const char *bgToggleableSurfaces[BG_NUM_TOGGLEABLE_SURFACES] =
38 {
39 	"l_arm_key",					//0
40 	"torso_canister1",
41 	"torso_canister2",
42 	"torso_canister3",
43 	"torso_tube1",
44 	"torso_tube2",					//5
45 	"torso_tube3",
46 	"torso_tube4",
47 	"torso_tube5",
48 	"torso_tube6",
49 	"r_arm",						//10
50 	"l_arm",
51 	"torso_shield",
52 	"torso_galaktorso",
53 	"torso_collar",
54 //	"torso_eyes_mouth",				//15
55 //	"torso_galakhead",
56 //	"torso_galakface",
57 //	"torso_antenna_base_cap",
58 //	"torso_antenna",
59 //	"l_arm_augment",				//20
60 //	"l_arm_middle",
61 //	"l_arm_wrist",
62 //	"r_arm_middle", //yeah.. galak's surf stuff is no longer auto, sorry! need the space for vehicle surfs.
63 	"r_wing1",						//15
64 	"r_wing2",
65 	"l_wing1",
66 	"l_wing2",
67 	"r_gear",
68 	"l_gear",						//20
69 	"nose",
70 	"blah4",
71 	"blah5",
72 	"l_hand",
73 	"r_hand",						//25
74 	"helmet",
75 	"head",
76 	"head_concussion_charger",
77 	"head_light_blaster_cann",		//29
78 	NULL
79 };
80 
81 const int bgToggleableSurfaceDebris[BG_NUM_TOGGLEABLE_SURFACES] =
82 {
83 	0,					//0
84 	0,
85 	0,
86 	0,
87 	0,
88 	0,					//5
89 	0,
90 	0,
91 	0,
92 	0,
93 	0,					//10
94 	0,
95 	0,
96 	0,
97 	0, //>= 2 means it should create a flame trail when destroyed (for vehicles)
98 	3,					//15
99 	5, //rwing2
100 	4,
101 	6, //lwing2
102 	0, //rgear
103 	0, //lgear			//20
104 	7, //nose
105 	0, //blah
106 	0, //blah
107 	0,
108 	0,					//25
109 	0,
110 	0,
111 	0,
112 	0,					//29
113 	-1
114 };
115 
116 const char	*bg_customSiegeSoundNames[MAX_CUSTOM_SIEGE_SOUNDS] =
117 {
118 	"*att_attack",
119 	"*att_primary",
120 	"*att_second",
121 	"*def_guns",
122 	"*def_position",
123 	"*def_primary",
124 	"*def_second",
125 	"*reply_coming",
126 	"*reply_go",
127 	"*reply_no",
128 	"*reply_stay",
129 	"*reply_yes",
130 	"*req_assist",
131 	"*req_demo",
132 	"*req_hvy",
133 	"*req_medic",
134 	"*req_sup",
135 	"*req_tech",
136 	"*spot_air",
137 	"*spot_defenses",
138 	"*spot_emplaced",
139 	"*spot_sniper",
140 	"*spot_troops",
141 	"*tac_cover",
142 	"*tac_fallback",
143 	"*tac_follow",
144 	"*tac_hold",
145 	"*tac_split",
146 	"*tac_together",
147 	NULL
148 };
149 
150 //rww - not putting @ in front of these because
151 //we don't need them in a cgame StringEd lookup.
152 //Let me know if this causes problems, pat.
153 char *forceMasteryLevels[NUM_FORCE_MASTERY_LEVELS] =
154 {
155 	"MASTERY0",	//"Uninitiated",	// FORCE_MASTERY_UNINITIATED,
156 	"MASTERY1",	//"Initiate",		// FORCE_MASTERY_INITIATE,
157 	"MASTERY2",	//"Padawan",		// FORCE_MASTERY_PADAWAN,
158 	"MASTERY3",	//"Jedi",			// FORCE_MASTERY_JEDI,
159 	"MASTERY4",	//"Jedi Adept",		// FORCE_MASTERY_JEDI_GUARDIAN,
160 	"MASTERY5",	//"Jedi Guardian",	// FORCE_MASTERY_JEDI_ADEPT,
161 	"MASTERY6",	//"Jedi Knight",	// FORCE_MASTERY_JEDI_KNIGHT,
162 	"MASTERY7",	//"Jedi Master"		// FORCE_MASTERY_JEDI_MASTER,
163 };
164 
165 int forceMasteryPoints[NUM_FORCE_MASTERY_LEVELS] =
166 {
167 	0,		// FORCE_MASTERY_UNINITIATED,
168 	5,		// FORCE_MASTERY_INITIATE,
169 	10,		// FORCE_MASTERY_PADAWAN,
170 	20,		// FORCE_MASTERY_JEDI,
171 	30,		// FORCE_MASTERY_JEDI_GUARDIAN,
172 	50,		// FORCE_MASTERY_JEDI_ADEPT,
173 	75,		// FORCE_MASTERY_JEDI_KNIGHT,
174 	100		// FORCE_MASTERY_JEDI_MASTER,
175 };
176 
177 int bgForcePowerCost[NUM_FORCE_POWERS][NUM_FORCE_POWER_LEVELS] = //0 == neutral
178 {
179 	{	0,	2,	4,	6	},	// Heal			// FP_HEAL
180 	{	0,	0,	2,	6	},	// Jump			//FP_LEVITATION,//hold/duration
181 	{	0,	2,	4,	6	},	// Speed		//FP_SPEED,//duration
182 	{	0,	1,	3,	6	},	// Push			//FP_PUSH,//hold/duration
183 	{	0,	1,	3,	6	},	// Pull			//FP_PULL,//hold/duration
184 	{	0,	4,	6,	8	},	// Mind Trick	//FP_TELEPATHY,//instant
185 	{	0,	1,	3,	6	},	// Grip			//FP_GRIP,//hold/duration
186 	{	0,	2,	5,	8	},	// Lightning	//FP_LIGHTNING,//hold/duration
187 	{	0,	4,	6,	8	},	// Dark Rage	//FP_RAGE,//duration
188 	{	0,	2,	5,	8	},	// Protection	//FP_PROTECT,//duration
189 	{	0,	1,	3,	6	},	// Absorb		//FP_ABSORB,//duration
190 	{	0,	1,	3,	6	},	// Team Heal	//FP_TEAM_HEAL,//instant
191 	{	0,	1,	3,	6	},	// Team Force	//FP_TEAM_FORCE,//instant
192 	{	0,	2,	4,	6	},	// Drain		//FP_DRAIN,//hold/duration
193 	{	0,	2,	5,	8	},	// Sight		//FP_SEE,//duration
194 	{	0,	1,	5,	8	},	// Saber Attack	//FP_SABER_OFFENSE,
195 	{	0,	1,	5,	8	},	// Saber Defend	//FP_SABER_DEFENSE,
196 	{	0,	4,	6,	8	}	// Saber Throw	//FP_SABERTHROW,
197 	//NUM_FORCE_POWERS
198 };
199 
200 int forcePowerSorted[NUM_FORCE_POWERS] =
201 { //rww - always use this order when drawing force powers for any reason
202 	FP_TELEPATHY,
203 	FP_HEAL,
204 	FP_ABSORB,
205 	FP_PROTECT,
206 	FP_TEAM_HEAL,
207 	FP_LEVITATION,
208 	FP_SPEED,
209 	FP_PUSH,
210 	FP_PULL,
211 	FP_SEE,
212 	FP_LIGHTNING,
213 	FP_DRAIN,
214 	FP_RAGE,
215 	FP_GRIP,
216 	FP_TEAM_FORCE,
217 	FP_SABER_OFFENSE,
218 	FP_SABER_DEFENSE,
219 	FP_SABERTHROW
220 };
221 
222 int forcePowerDarkLight[NUM_FORCE_POWERS] = //0 == neutral
223 { //nothing should be usable at rank 0..
224 	FORCE_LIGHTSIDE,//FP_HEAL,//instant
225 	0,//FP_LEVITATION,//hold/duration
226 	0,//FP_SPEED,//duration
227 	0,//FP_PUSH,//hold/duration
228 	0,//FP_PULL,//hold/duration
229 	FORCE_LIGHTSIDE,//FP_TELEPATHY,//instant
230 	FORCE_DARKSIDE,//FP_GRIP,//hold/duration
231 	FORCE_DARKSIDE,//FP_LIGHTNING,//hold/duration
232 	FORCE_DARKSIDE,//FP_RAGE,//duration
233 	FORCE_LIGHTSIDE,//FP_PROTECT,//duration
234 	FORCE_LIGHTSIDE,//FP_ABSORB,//duration
235 	FORCE_LIGHTSIDE,//FP_TEAM_HEAL,//instant
236 	FORCE_DARKSIDE,//FP_TEAM_FORCE,//instant
237 	FORCE_DARKSIDE,//FP_DRAIN,//hold/duration
238 	0,//FP_SEE,//duration
239 	0,//FP_SABER_OFFENSE,
240 	0,//FP_SABER_DEFENSE,
241 	0//FP_SABERTHROW,
242 		//NUM_FORCE_POWERS
243 };
244 
245 int WeaponReadyAnim[WP_NUM_WEAPONS] =
246 {
247 	TORSO_DROPWEAP1,//WP_NONE,
248 
249 	TORSO_WEAPONREADY3,//WP_STUN_BATON,
250 	TORSO_WEAPONREADY3,//WP_MELEE,
251 	BOTH_STAND2,//WP_SABER,
252 	TORSO_WEAPONREADY2,//WP_BRYAR_PISTOL,
253 	TORSO_WEAPONREADY3,//WP_BLASTER,
254 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY4,//WP_DISRUPTOR,
255 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY5,//WP_BOWCASTER,
256 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY6,//WP_REPEATER,
257 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY7,//WP_DEMP2,
258 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY8,//WP_FLECHETTE,
259 	TORSO_WEAPONREADY3,//TORSO_WEAPONREADY9,//WP_ROCKET_LAUNCHER,
260 	TORSO_WEAPONREADY10,//WP_THERMAL,
261 	TORSO_WEAPONREADY10,//TORSO_WEAPONREADY11,//WP_TRIP_MINE,
262 	TORSO_WEAPONREADY10,//TORSO_WEAPONREADY12,//WP_DET_PACK,
263 	TORSO_WEAPONREADY3,//WP_CONCUSSION
264 	TORSO_WEAPONREADY2,//WP_BRYAR_OLD,
265 
266 	//NOT VALID (e.g. should never really be used):
267 	BOTH_STAND1,//WP_EMPLACED_GUN,
268 	TORSO_WEAPONREADY1//WP_TURRET,
269 };
270 
271 int WeaponReadyLegsAnim[WP_NUM_WEAPONS] =
272 {
273 	BOTH_STAND1,//WP_NONE,
274 
275 	BOTH_STAND1,//WP_STUN_BATON,
276 	BOTH_STAND1,//WP_MELEE,
277 	BOTH_STAND2,//WP_SABER,
278 	BOTH_STAND1,//WP_BRYAR_PISTOL,
279 	BOTH_STAND1,//WP_BLASTER,
280 	BOTH_STAND1,//TORSO_WEAPONREADY4,//WP_DISRUPTOR,
281 	BOTH_STAND1,//TORSO_WEAPONREADY5,//WP_BOWCASTER,
282 	BOTH_STAND1,//TORSO_WEAPONREADY6,//WP_REPEATER,
283 	BOTH_STAND1,//TORSO_WEAPONREADY7,//WP_DEMP2,
284 	BOTH_STAND1,//TORSO_WEAPONREADY8,//WP_FLECHETTE,
285 	BOTH_STAND1,//TORSO_WEAPONREADY9,//WP_ROCKET_LAUNCHER,
286 	BOTH_STAND1,//WP_THERMAL,
287 	BOTH_STAND1,//TORSO_WEAPONREADY11,//WP_TRIP_MINE,
288 	BOTH_STAND1,//TORSO_WEAPONREADY12,//WP_DET_PACK,
289 	BOTH_STAND1,//WP_CONCUSSION
290 	BOTH_STAND1,//WP_BRYAR_OLD,
291 
292 	//NOT VALID (e.g. should never really be used):
293 	BOTH_STAND1,//WP_EMPLACED_GUN,
294 	BOTH_STAND1//WP_TURRET,
295 };
296 
297 int WeaponAttackAnim[WP_NUM_WEAPONS] =
298 {
299 	BOTH_ATTACK1,//WP_NONE, //(shouldn't happen)
300 
301 	BOTH_ATTACK3,//WP_STUN_BATON,
302 	BOTH_ATTACK3,//WP_MELEE,
303 	BOTH_STAND2,//WP_SABER, //(has its own handling)
304 	BOTH_ATTACK2,//WP_BRYAR_PISTOL,
305 	BOTH_ATTACK3,//WP_BLASTER,
306 	BOTH_ATTACK3,//BOTH_ATTACK4,//WP_DISRUPTOR,
307 	BOTH_ATTACK3,//BOTH_ATTACK5,//WP_BOWCASTER,
308 	BOTH_ATTACK3,//BOTH_ATTACK6,//WP_REPEATER,
309 	BOTH_ATTACK3,//BOTH_ATTACK7,//WP_DEMP2,
310 	BOTH_ATTACK3,//BOTH_ATTACK8,//WP_FLECHETTE,
311 	BOTH_ATTACK3,//BOTH_ATTACK9,//WP_ROCKET_LAUNCHER,
312 	BOTH_THERMAL_THROW,//WP_THERMAL,
313 	BOTH_ATTACK3,//BOTH_ATTACK11,//WP_TRIP_MINE,
314 	BOTH_ATTACK3,//BOTH_ATTACK12,//WP_DET_PACK,
315 	#ifndef BASE_COMPAT
316 		BOTH_ATTACK3,//WP_CONCUSSION,
317 	#endif // BASE_COMPAT
318 	BOTH_ATTACK2,//WP_BRYAR_OLD,
319 
320 	//NOT VALID (e.g. should never really be used):
321 	BOTH_STAND1,//WP_EMPLACED_GUN,
322 	BOTH_ATTACK1//WP_TURRET,
323 };
324 
BG_FileExists(const char * fileName)325 qboolean BG_FileExists( const char *fileName ) {
326 	if ( fileName && fileName[0] ) {
327 		fileHandle_t f = NULL_FILE;
328 		trap->FS_Open( fileName, &f, FS_READ );
329 		if ( f > 0 ) {
330 			trap->FS_Close( f );
331 			return qtrue;
332 		}
333 	}
334 	return qfalse;
335 }
336 
337 // given a boltmatrix, return in vec a normalised vector for the axis requested in flags
BG_GiveMeVectorFromMatrix(mdxaBone_t * boltMatrix,int flags,vec3_t vec)338 void BG_GiveMeVectorFromMatrix(mdxaBone_t *boltMatrix, int flags, vec3_t vec)
339 {
340 	switch (flags)
341 	{
342 	case ORIGIN:
343 		vec[0] = boltMatrix->matrix[0][3];
344 		vec[1] = boltMatrix->matrix[1][3];
345 		vec[2] = boltMatrix->matrix[2][3];
346 		break;
347 	case POSITIVE_Y:
348 		vec[0] = boltMatrix->matrix[0][1];
349 		vec[1] = boltMatrix->matrix[1][1];
350 		vec[2] = boltMatrix->matrix[2][1];
351  		break;
352 	case POSITIVE_X:
353 		vec[0] = boltMatrix->matrix[0][0];
354 		vec[1] = boltMatrix->matrix[1][0];
355 		vec[2] = boltMatrix->matrix[2][0];
356 		break;
357 	case POSITIVE_Z:
358 		vec[0] = boltMatrix->matrix[0][2];
359 		vec[1] = boltMatrix->matrix[1][2];
360 		vec[2] = boltMatrix->matrix[2][2];
361 		break;
362 	case NEGATIVE_Y:
363 		vec[0] = -boltMatrix->matrix[0][1];
364 		vec[1] = -boltMatrix->matrix[1][1];
365 		vec[2] = -boltMatrix->matrix[2][1];
366 		break;
367 	case NEGATIVE_X:
368 		vec[0] = -boltMatrix->matrix[0][0];
369 		vec[1] = -boltMatrix->matrix[1][0];
370 		vec[2] = -boltMatrix->matrix[2][0];
371 		break;
372 	case NEGATIVE_Z:
373 		vec[0] = -boltMatrix->matrix[0][2];
374 		vec[1] = -boltMatrix->matrix[1][2];
375 		vec[2] = -boltMatrix->matrix[2][2];
376 		break;
377 	}
378 }
379 
380 /*
381 ================
382 BG_LegalizedForcePowers
383 
384 The magical function to end all functions.
385 This will take the force power string in powerOut and parse through it, then legalize
386 it based on the supposed rank and spit it into powerOut, returning true if it was legal
387 to begin with and false if not.
388 fpDisabled is actually only expected (needed) from the server, because the ui disables
389 force power selection anyway when force powers are disabled on the server.
390 ================
391 */
BG_LegalizedForcePowers(char * powerOut,size_t powerOutSize,int maxRank,qboolean freeSaber,int teamForce,int gametype,int fpDisabled)392 qboolean BG_LegalizedForcePowers(char *powerOut, size_t powerOutSize, int maxRank, qboolean freeSaber, int teamForce, int gametype, int fpDisabled)
393 {
394 	char powerBuf[128];
395 	char readBuf[128];
396 	qboolean maintainsValidity = qtrue;
397 	int powerLen = strlen(powerOut);
398 	int i = 0;
399 	int c = 0;
400 	int allowedPoints = 0;
401 	int usedPoints = 0;
402 	int countDown = 0;
403 
404 	int final_Side;
405 	int final_Powers[NUM_FORCE_POWERS] = {0};
406 
407 	if ( powerLen >= 128 )
408 	{ //This should not happen. If it does, this is obviously a bogus string.
409 		//They can have this string. Because I said so.
410 		Q_strncpyz( powerBuf, DEFAULT_FORCEPOWERS, sizeof( powerBuf ) );
411 		maintainsValidity = qfalse;
412 	}
413 	else
414 		Q_strncpyz( powerBuf, powerOut, sizeof( powerBuf ) ); //copy it as the original
415 
416 	//first of all, print the max rank into the string as the rank
417 	Q_strncpyz( powerOut, va( "%i-", maxRank ), powerOutSize );
418 
419 	while (i < sizeof( powerBuf ) && powerBuf[i] && powerBuf[i] != '-')
420 	{
421 		i++;
422 	}
423 	i++;
424 	while (i < sizeof( powerBuf ) && powerBuf[i] && powerBuf[i] != '-')
425 	{
426 		readBuf[c] = powerBuf[i];
427 		c++;
428 		i++;
429 	}
430 	readBuf[c] = 0;
431 	i++;
432 	//at this point, readBuf contains the intended side
433 	final_Side = atoi(readBuf);
434 
435 	if (final_Side != FORCE_LIGHTSIDE &&
436 		final_Side != FORCE_DARKSIDE)
437 	{ //Not a valid side. You will be dark. Because I said so. (this is something that should never actually happen unless you purposely feed in an invalid config)
438 		final_Side = FORCE_DARKSIDE;
439 		maintainsValidity = qfalse;
440 	}
441 
442 	if (teamForce)
443 	{ //If we are under force-aligned teams, make sure we're on the right side.
444 		if (final_Side != teamForce)
445 		{
446 			final_Side = teamForce;
447 			//maintainsValidity = qfalse;
448 			//Not doing this, for now. Let them join the team with their filtered powers.
449 		}
450 	}
451 
452 	//Now we have established a valid rank, and a valid side.
453 	//Read the force powers in, and cut them down based on the various rules supplied.
454 	c = 0;
455 	while (i < sizeof( powerBuf ) && powerBuf[i] && powerBuf[i] != '\n' && powerBuf[i] != '\r'
456 		&& powerBuf[i] >= '0' && powerBuf[i] <= '3' && c < NUM_FORCE_POWERS)
457 	{
458 		readBuf[0] = powerBuf[i];
459 		readBuf[1] = 0;
460 		final_Powers[c] = atoi(readBuf);
461 		c++;
462 		i++;
463 	}
464 
465 	//final_Powers now contains all the stuff from the string
466 	//Set the maximum allowed points used based on the max rank level, and count the points actually used.
467 	allowedPoints = forceMasteryPoints[maxRank];
468 
469 	i = 0;
470 	while (i < NUM_FORCE_POWERS)
471 	{ //if this power doesn't match the side we're on, then 0 it now.
472 		if (final_Powers[i] &&
473 			forcePowerDarkLight[i] &&
474 			forcePowerDarkLight[i] != final_Side)
475 		{
476 			final_Powers[i] = 0;
477 			//This is only likely to happen with g_forceBasedTeams. Let it slide.
478 		}
479 
480 		if ( final_Powers[i] &&
481 			(fpDisabled & (1 << i)) )
482 		{ //if this power is disabled on the server via said server option, then we don't get it.
483 			final_Powers[i] = 0;
484 		}
485 
486 		i++;
487 	}
488 
489 	if (gametype < GT_TEAM)
490 	{ //don't bother with team powers then
491 		final_Powers[FP_TEAM_HEAL] = 0;
492 		final_Powers[FP_TEAM_FORCE] = 0;
493 	}
494 
495 	usedPoints = 0;
496 	i = 0;
497 	while (i < NUM_FORCE_POWERS)
498 	{
499 		countDown = Com_Clampi( 0, NUM_FORCE_POWER_LEVELS, final_Powers[i] );
500 
501 		while (countDown > 0)
502 		{
503 			usedPoints += bgForcePowerCost[i][countDown]; //[fp index][fp level]
504 			//if this is jump, or we have a free saber and it's offense or defense, take the level back down on level 1
505 			if ( countDown == 1 &&
506 				((i == FP_LEVITATION) ||
507 				 (i == FP_SABER_OFFENSE && freeSaber) ||
508 				 (i == FP_SABER_DEFENSE && freeSaber)) )
509 			{
510 				usedPoints -= bgForcePowerCost[i][countDown];
511 			}
512 			countDown--;
513 		}
514 
515 		i++;
516 	}
517 
518 	if (usedPoints > allowedPoints)
519 	{ //Time to do the fancy stuff. (meaning, slowly cut parts off while taking a guess at what is most or least important in the config)
520 		int attemptedCycles = 0;
521 		int powerCycle = 2;
522 		int minPow = 0;
523 
524 		if (freeSaber)
525 		{
526 			minPow = 1;
527 		}
528 
529 		maintainsValidity = qfalse;
530 
531 		while (usedPoints > allowedPoints)
532 		{
533 			c = 0;
534 
535 			while (c < NUM_FORCE_POWERS && usedPoints > allowedPoints)
536 			{
537 				if (final_Powers[c] && final_Powers[c] < powerCycle)
538 				{ //kill in order of lowest powers, because the higher powers are probably more important
539 					if (c == FP_SABER_OFFENSE &&
540 						(final_Powers[FP_SABER_DEFENSE] > minPow || final_Powers[FP_SABERTHROW] > 0))
541 					{ //if we're on saber attack, only suck it down if we have no def or throw either
542 						int whichOne = FP_SABERTHROW; //first try throw
543 
544 						if (!final_Powers[whichOne])
545 						{
546 							whichOne = FP_SABER_DEFENSE; //if no throw, drain defense
547 						}
548 
549 						while (final_Powers[whichOne] > 0 && usedPoints > allowedPoints)
550 						{
551 							if ( final_Powers[whichOne] > 1 ||
552 								( (whichOne != FP_SABER_OFFENSE || !freeSaber) &&
553 								  (whichOne != FP_SABER_DEFENSE || !freeSaber) ) )
554 							{ //don't take attack or defend down on level 1 still, if it's free
555 								usedPoints -= bgForcePowerCost[whichOne][final_Powers[whichOne]];
556 								final_Powers[whichOne]--;
557 							}
558 							else
559 							{
560 								break;
561 							}
562 						}
563 					}
564 					else
565 					{
566 						while (final_Powers[c] > 0 && usedPoints > allowedPoints)
567 						{
568 							if ( final_Powers[c] > 1 ||
569 								((c != FP_LEVITATION) &&
570 								(c != FP_SABER_OFFENSE || !freeSaber) &&
571 								(c != FP_SABER_DEFENSE || !freeSaber)) )
572 							{
573 								usedPoints -= bgForcePowerCost[c][final_Powers[c]];
574 								final_Powers[c]--;
575 							}
576 							else
577 							{
578 								break;
579 							}
580 						}
581 					}
582 				}
583 
584 				c++;
585 			}
586 
587 			powerCycle++;
588 			attemptedCycles++;
589 
590 			if (attemptedCycles > NUM_FORCE_POWERS)
591 			{ //I think this should be impossible. But just in case.
592 				break;
593 			}
594 		}
595 
596 		if (usedPoints > allowedPoints)
597 		{ //Still? Fine then.. we will kill all of your powers, except the freebies.
598 			i = 0;
599 
600 			while (i < NUM_FORCE_POWERS)
601 			{
602 				final_Powers[i] = 0;
603 				if (i == FP_LEVITATION ||
604 					(i == FP_SABER_OFFENSE && freeSaber) ||
605 					(i == FP_SABER_DEFENSE && freeSaber))
606 				{
607 					final_Powers[i] = 1;
608 				}
609 				i++;
610 			}
611 			usedPoints = 0;
612 		}
613 	}
614 
615 	if (freeSaber)
616 	{
617 		if (final_Powers[FP_SABER_OFFENSE] < 1)
618 			final_Powers[FP_SABER_OFFENSE] = 1;
619 		if (final_Powers[FP_SABER_DEFENSE] < 1)
620 			final_Powers[FP_SABER_DEFENSE] = 1;
621 	}
622 	if (final_Powers[FP_LEVITATION] < 1)
623 		final_Powers[FP_LEVITATION] = 1;
624 
625 	i = 0;
626 	while (i < NUM_FORCE_POWERS)
627 	{
628 		if (final_Powers[i] > FORCE_LEVEL_3)
629 			final_Powers[i] = FORCE_LEVEL_3;
630 		i++;
631 	}
632 
633 	if (fpDisabled)
634 	{ //If we specifically have attack or def disabled, force them up to level 3. It's the way
635 	  //things work for the case of all powers disabled.
636 	  //If jump is disabled, down-cap it to level 1. Otherwise don't do a thing.
637 		if (fpDisabled & (1 << FP_LEVITATION))
638 			final_Powers[FP_LEVITATION] = 1;
639 		if (fpDisabled & (1 << FP_SABER_OFFENSE))
640 			final_Powers[FP_SABER_OFFENSE] = 3;
641 		if (fpDisabled & (1 << FP_SABER_DEFENSE))
642 			final_Powers[FP_SABER_DEFENSE] = 3;
643 	}
644 
645 	if (final_Powers[FP_SABER_OFFENSE] < 1)
646 	{
647 		final_Powers[FP_SABER_DEFENSE] = 0;
648 		final_Powers[FP_SABERTHROW] = 0;
649 	}
650 
651 	//We finally have all the force powers legalized and stored locally.
652 	//Put them all into the string and return the result. We already have
653 	//the rank there, so print the side and the powers now.
654 	Q_strcat(powerOut, powerOutSize, va("%i-", final_Side));
655 
656 	i = strlen(powerOut);
657 	c = 0;
658 	while (c < NUM_FORCE_POWERS)
659 	{
660 		Q_strncpyz(readBuf, va( "%i", final_Powers[c] ), sizeof( readBuf ) );
661 		powerOut[i] = readBuf[0];
662 		c++;
663 		i++;
664 	}
665 	powerOut[i] = 0;
666 
667 	return maintainsValidity;
668 }
669 
670 /*QUAKED item_***** ( 0 0 0 ) (-16 -16 -16) (16 16 16) suspended
671 DO NOT USE THIS CLASS, IT JUST HOLDS GENERAL INFORMATION.
672 The suspended flag will allow items to hang in the air, otherwise they are dropped to the next surface.
673 
674 If an item is the target of another entity, it will not spawn in until fired.
675 
676 An item fires all of its targets when it is picked up.  If the toucher can't carry it, the targets won't be fired.
677 
678 "notfree" if set to 1, don't spawn in free for all games
679 "notteam" if set to 1, don't spawn in team games
680 "notsingle" if set to 1, don't spawn in single player games
681 "wait"	override the default wait before respawning.  -1 = never respawn automatically, which can be used with targeted spawning.
682 "random" random number of plus or minus seconds varied from the respawn time
683 "count" override quantity or duration on most items.
684 */
685 
686 gitem_t	bg_itemlist[] =
687 {
688 	{
689 		NULL,				// classname
690 		NULL,				// pickup_sound
691 		{	NULL,			// world_model[0]
692 			NULL,			// world_model[1]
693 			0, 0} ,			// world_model[2],[3]
694 		NULL,				// view_model
695 /* icon */		NULL,		// icon
696 /* pickup */	//NULL,		// pickup_name
697 		0,					// quantity
698 		IT_BAD,				// giType (IT_*)
699 		0,					// giTag
700 /* precache */ "",			// precaches
701 /* sounds */ "",			// sounds
702 		""					// description
703 	},	// leave index 0 alone
704 
705 	//
706 	// Pickups
707 	//
708 
709 /*QUAKED item_shield_sm_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
710 Instant shield pickup, restores 25
711 */
712 	{
713 		"item_shield_sm_instant",
714 		"sound/player/pickupshield.wav",
715         { "models/map_objects/mp/psd_sm.md3",
716 		0, 0, 0},
717 /* view */		NULL,
718 /* icon */		"gfx/mp/small_shield",
719 /* pickup *///	"Shield Small",
720 		25,
721 		IT_ARMOR,
722 		1, //special for shield - max on pickup is maxhealth*tag, thus small shield goes up to 100 shield
723 /* precache */ "",
724 /* sounds */ ""
725 		""					// description
726 	},
727 
728 /*QUAKED item_shield_lrg_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
729 Instant shield pickup, restores 100
730 */
731 	{
732 		"item_shield_lrg_instant",
733 		"sound/player/pickupshield.wav",
734         { "models/map_objects/mp/psd.md3",
735 		0, 0, 0},
736 /* view */		NULL,
737 /* icon */		"gfx/mp/large_shield",
738 /* pickup *///	"Shield Large",
739 		100,
740 		IT_ARMOR,
741 		2, //special for shield - max on pickup is maxhealth*tag, thus large shield goes up to 200 shield
742 /* precache */ "",
743 /* sounds */ "",
744 		""					// description
745 	},
746 
747 /*QUAKED item_medpak_instant (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
748 Instant medpack pickup, heals 25
749 */
750 	{
751 		"item_medpak_instant",
752 		"sound/player/pickuphealth.wav",
753         { "models/map_objects/mp/medpac.md3",
754 		0, 0, 0 },
755 /* view */		NULL,
756 /* icon */		"gfx/hud/i_icon_medkit",
757 /* pickup *///	"Medpack",
758 		25,
759 		IT_HEALTH,
760 		0,
761 /* precache */ "",
762 /* sounds */ "",
763 		""					// description
764 	},
765 
766 
767 	//
768 	// ITEMS
769 	//
770 
771 /*QUAKED item_seeker (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
772 30 seconds of seeker drone
773 */
774 	{
775 		"item_seeker",
776 		"sound/weapons/w_pkup.wav",
777 		{ "models/items/remote.md3",
778 		0, 0, 0} ,
779 /* view */		NULL,
780 /* icon */		"gfx/hud/i_icon_seeker",
781 /* pickup *///	"Seeker Drone",
782 		120,
783 		IT_HOLDABLE,
784 		HI_SEEKER,
785 /* precache */ "",
786 /* sounds */ "",
787 		"@MENUS_AN_ATTACK_DRONE_SIMILAR"					// description
788 	},
789 
790 /*QUAKED item_shield (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
791 Portable shield
792 */
793 	{
794 		"item_shield",
795 		"sound/weapons/w_pkup.wav",
796 		{ "models/map_objects/mp/shield.md3",
797 		0, 0, 0} ,
798 /* view */		NULL,
799 /* icon */		"gfx/hud/i_icon_shieldwall",
800 /* pickup *///	"Forcefield",
801 		120,
802 		IT_HOLDABLE,
803 		HI_SHIELD,
804 /* precache */ "",
805 /* sounds */ "sound/weapons/detpack/stick.wav sound/movers/doors/forcefield_on.wav sound/movers/doors/forcefield_off.wav sound/movers/doors/forcefield_lp.wav sound/effects/bumpfield.wav",
806 		"@MENUS_THIS_STATIONARY_ENERGY"					// description
807 	},
808 
809 /*QUAKED item_medpac (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
810 Bacta canister pickup, heals 25 on use
811 */
812 	{
813 		"item_medpac",	//should be item_bacta
814 		"sound/weapons/w_pkup.wav",
815 		{ "models/map_objects/mp/bacta.md3",
816 		0, 0, 0} ,
817 /* view */		NULL,
818 /* icon */		"gfx/hud/i_icon_bacta",
819 /* pickup *///	"Bacta Canister",
820 		25,
821 		IT_HOLDABLE,
822 		HI_MEDPAC,
823 /* precache */ "",
824 /* sounds */ "",
825 		"@SP_INGAME_BACTA_DESC"					// description
826 	},
827 
828 /*QUAKED item_medpac_big (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
829 Big bacta canister pickup, heals 50 on use
830 */
831 	{
832 		"item_medpac_big",	//should be item_bacta
833 		"sound/weapons/w_pkup.wav",
834 		{ "models/items/big_bacta.md3",
835 		0, 0, 0} ,
836 /* view */		NULL,
837 /* icon */		"gfx/hud/i_icon_big_bacta",
838 /* pickup *///	"Bacta Canister",
839 		25,
840 		IT_HOLDABLE,
841 		HI_MEDPAC_BIG,
842 /* precache */ "",
843 /* sounds */ "",
844 		"@SP_INGAME_BACTA_DESC"					// description
845 	},
846 
847 /*QUAKED item_binoculars (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
848 These will be standard equipment on the player - DO NOT PLACE
849 */
850 	{
851 		"item_binoculars",
852 		"sound/weapons/w_pkup.wav",
853 		{ "models/items/binoculars.md3",
854 		0, 0, 0} ,
855 /* view */		NULL,
856 /* icon */		"gfx/hud/i_icon_zoom",
857 /* pickup *///	"Binoculars",
858 		60,
859 		IT_HOLDABLE,
860 		HI_BINOCULARS,
861 /* precache */ "",
862 /* sounds */ "",
863 		"@SP_INGAME_LA_GOGGLES_DESC"					// description
864 	},
865 
866 /*QUAKED item_sentry_gun (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
867 Sentry gun inventory pickup.
868 */
869 	{
870 		"item_sentry_gun",
871 		"sound/weapons/w_pkup.wav",
872 		{ "models/items/psgun.glm",
873 		0, 0, 0} ,
874 /* view */		NULL,
875 /* icon */		"gfx/hud/i_icon_sentrygun",
876 /* pickup *///	"Sentry Gun",
877 		120,
878 		IT_HOLDABLE,
879 		HI_SENTRY_GUN,
880 /* precache */ "",
881 /* sounds */ "",
882 		"@MENUS_THIS_DEADLY_WEAPON_IS"					// description
883 	},
884 
885 /*QUAKED item_jetpack (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
886 Do not place.
887 */
888 	{
889 		"item_jetpack",
890 		"sound/weapons/w_pkup.wav",
891 		{ "models/items/psgun.glm", //FIXME: no model
892 		0, 0, 0} ,
893 /* view */		NULL,
894 /* icon */		"gfx/hud/i_icon_jetpack",
895 /* pickup *///	"Sentry Gun",
896 		120,
897 		IT_HOLDABLE,
898 		HI_JETPACK,
899 /* precache */ "effects/boba/jet.efx",
900 /* sounds */ "sound/chars/boba/JETON.wav sound/chars/boba/JETHOVER.wav sound/effects/fire_lp.wav",
901 		"@MENUS_JETPACK_DESC"					// description
902 	},
903 
904 /*QUAKED item_healthdisp (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
905 Do not place. For siege classes ONLY.
906 */
907 	{
908 		"item_healthdisp",
909 		"sound/weapons/w_pkup.wav",
910 		{ "models/map_objects/mp/bacta.md3", //replace me
911 		0, 0, 0} ,
912 /* view */		NULL,
913 /* icon */		"gfx/hud/i_icon_healthdisp",
914 /* pickup *///	"Sentry Gun",
915 		120,
916 		IT_HOLDABLE,
917 		HI_HEALTHDISP,
918 /* precache */ "",
919 /* sounds */ "",
920 		""					// description
921 	},
922 
923 /*QUAKED item_ammodisp (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
924 Do not place. For siege classes ONLY.
925 */
926 	{
927 		"item_ammodisp",
928 		"sound/weapons/w_pkup.wav",
929 		{ "models/map_objects/mp/bacta.md3", //replace me
930 		0, 0, 0} ,
931 /* view */		NULL,
932 /* icon */		"gfx/hud/i_icon_ammodisp",
933 /* pickup *///	"Sentry Gun",
934 		120,
935 		IT_HOLDABLE,
936 		HI_AMMODISP,
937 /* precache */ "",
938 /* sounds */ "",
939 		""					// description
940 	},
941 
942 /*QUAKED item_eweb_holdable (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
943 Do not place. For siege classes ONLY.
944 */
945 	{
946 		"item_eweb_holdable",
947 		"sound/interface/shieldcon_empty",
948 		{ "models/map_objects/hoth/eweb_model.glm",
949 		0, 0, 0} ,
950 /* view */		NULL,
951 /* icon */		"gfx/hud/i_icon_eweb",
952 /* pickup *///	"Sentry Gun",
953 		120,
954 		IT_HOLDABLE,
955 		HI_EWEB,
956 /* precache */ "",
957 /* sounds */ "",
958 		"@MENUS_EWEB_DESC"					// description
959 	},
960 
961 /*QUAKED item_seeker (.3 .3 1) (-8 -8 -0) (8 8 16) suspended
962 30 seconds of seeker drone
963 */
964 	{
965 		"item_cloak",
966 		"sound/weapons/w_pkup.wav",
967 		{ "models/items/psgun.glm", //FIXME: no model
968 		0, 0, 0} ,
969 /* view */		NULL,
970 /* icon */		"gfx/hud/i_icon_cloak",
971 /* pickup *///	"Seeker Drone",
972 		120,
973 		IT_HOLDABLE,
974 		HI_CLOAK,
975 /* precache */ "",
976 /* sounds */ "",
977 		"@MENUS_CLOAK_DESC"					// description
978 	},
979 
980 /*QUAKED item_force_enlighten_light (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
981 Adds one rank to all Force powers temporarily. Only light jedi can use.
982 */
983 	{
984 		"item_force_enlighten_light",
985 		"sound/player/enlightenment.wav",
986 		{ "models/map_objects/mp/jedi_enlightenment.md3",
987 		0, 0, 0} ,
988 /* view */		NULL,
989 /* icon */		"gfx/hud/mpi_jlight",
990 /* pickup *///	"Light Force Enlightenment",
991 		25,
992 		IT_POWERUP,
993 		PW_FORCE_ENLIGHTENED_LIGHT,
994 /* precache */ "",
995 /* sounds */ "",
996 		""					// description
997 	},
998 
999 /*QUAKED item_force_enlighten_dark (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1000 Adds one rank to all Force powers temporarily. Only dark jedi can use.
1001 */
1002 	{
1003 		"item_force_enlighten_dark",
1004 		"sound/player/enlightenment.wav",
1005 		{ "models/map_objects/mp/dk_enlightenment.md3",
1006 		0, 0, 0} ,
1007 /* view */		NULL,
1008 /* icon */		"gfx/hud/mpi_dklight",
1009 /* pickup *///	"Dark Force Enlightenment",
1010 		25,
1011 		IT_POWERUP,
1012 		PW_FORCE_ENLIGHTENED_DARK,
1013 /* precache */ "",
1014 /* sounds */ "",
1015 		""					// description
1016 	},
1017 
1018 /*QUAKED item_force_boon (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1019 Unlimited Force Pool for a short time.
1020 */
1021 	{
1022 		"item_force_boon",
1023 		"sound/player/boon.wav",
1024 		{ "models/map_objects/mp/force_boon.md3",
1025 		0, 0, 0} ,
1026 /* view */		NULL,
1027 /* icon */		"gfx/hud/mpi_fboon",
1028 /* pickup *///	"Force Boon",
1029 		25,
1030 		IT_POWERUP,
1031 		PW_FORCE_BOON,
1032 /* precache */ "",
1033 /* sounds */ "",
1034 		""					// description
1035 	},
1036 
1037 /*QUAKED item_ysalimari (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1038 A small lizard carried on the player, which prevents the possessor from using any Force power.  However, he is unaffected by any Force power.
1039 */
1040 	{
1041 		"item_ysalimari",
1042 		"sound/player/ysalimari.wav",
1043 		{ "models/map_objects/mp/ysalimari.md3",
1044 		0, 0, 0} ,
1045 /* view */		NULL,
1046 /* icon */		"gfx/hud/mpi_ysamari",
1047 /* pickup *///	"Ysalamiri",
1048 		25,
1049 		IT_POWERUP,
1050 		PW_YSALAMIRI,
1051 /* precache */ "",
1052 /* sounds */ "",
1053 		""					// description
1054 	},
1055 
1056 	//
1057 	// WEAPONS
1058 	//
1059 
1060 /*QUAKED weapon_stun_baton (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1061 Don't place this
1062 */
1063 	{
1064 		"weapon_stun_baton",
1065 		"sound/weapons/w_pkup.wav",
1066         { "models/weapons2/stun_baton/baton_w.glm",
1067 		0, 0, 0},
1068 /* view */		"models/weapons2/stun_baton/baton.md3",
1069 /* icon */		"gfx/hud/w_icon_stunbaton",
1070 /* pickup *///	"Stun Baton",
1071 		100,
1072 		IT_WEAPON,
1073 		WP_STUN_BATON,
1074 /* precache */ "",
1075 /* sounds */ "",
1076 		""					// description
1077 	},
1078 
1079 /*QUAKED weapon_melee (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1080 Don't place this
1081 */
1082 	{
1083 		"weapon_melee",
1084 		"sound/weapons/w_pkup.wav",
1085         { "models/weapons2/stun_baton/baton_w.glm",
1086 		0, 0, 0},
1087 /* view */		"models/weapons2/stun_baton/baton.md3",
1088 /* icon */		"gfx/hud/w_icon_melee",
1089 /* pickup *///	"Stun Baton",
1090 		100,
1091 		IT_WEAPON,
1092 		WP_MELEE,
1093 /* precache */ "",
1094 /* sounds */ "",
1095 		"@MENUS_MELEE_DESC"					// description
1096 	},
1097 
1098 /*QUAKED weapon_saber (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1099 Don't place this
1100 */
1101 	{
1102 		"weapon_saber",
1103 		"sound/weapons/w_pkup.wav",
1104         { DEFAULT_SABER_MODEL,
1105 		0, 0, 0},
1106 /* view */		"models/weapons2/saber/saber_w.md3",
1107 /* icon */		"gfx/hud/w_icon_lightsaber",
1108 /* pickup *///	"Lightsaber",
1109 		100,
1110 		IT_WEAPON,
1111 		WP_SABER,
1112 /* precache */ "",
1113 /* sounds */ "",
1114 		"@MENUS_AN_ELEGANT_WEAPON_FOR"				// description
1115 	},
1116 
1117 /*QUAKED weapon_bryar_pistol (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1118 Don't place this
1119 */
1120 	{
1121 		//"weapon_bryar_pistol",
1122 		"weapon_blaster_pistol",
1123 		"sound/weapons/w_pkup.wav",
1124         { "models/weapons2/blaster_pistol/blaster_pistol_w.glm",//"models/weapons2/briar_pistol/briar_pistol_w.glm",
1125 		0, 0, 0},
1126 /* view */		"models/weapons2/blaster_pistol/blaster_pistol.md3",//"models/weapons2/briar_pistol/briar_pistol.md3",
1127 /* icon */		"gfx/hud/w_icon_blaster_pistol",//"gfx/hud/w_icon_rifle",
1128 /* pickup *///	"Bryar Pistol",
1129 		100,
1130 		IT_WEAPON,
1131 		WP_BRYAR_PISTOL,
1132 /* precache */ "",
1133 /* sounds */ "",
1134 		"@MENUS_BLASTER_PISTOL_DESC"					// description
1135 	},
1136 
1137 /*QUAKED weapon_concussion_rifle (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1138 */
1139 	{
1140 		"weapon_concussion_rifle",
1141 		"sound/weapons/w_pkup.wav",
1142         { "models/weapons2/concussion/c_rifle_w.glm",
1143 		0, 0, 0},
1144 /* view */		"models/weapons2/concussion/c_rifle.md3",
1145 /* icon */		"gfx/hud/w_icon_c_rifle",//"gfx/hud/w_icon_rifle",
1146 /* pickup *///	"Concussion Rifle",
1147 		50,
1148 		IT_WEAPON,
1149 		WP_CONCUSSION,
1150 /* precache */ "",
1151 /* sounds */ "",
1152 		"@MENUS_CONC_RIFLE_DESC"					// description
1153 	},
1154 
1155 /*QUAKED weapon_bryar_pistol_old (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1156 Don't place this
1157 */
1158 	{
1159 		"weapon_bryar_pistol",
1160 		"sound/weapons/w_pkup.wav",
1161         { "models/weapons2/briar_pistol/briar_pistol_w.glm",
1162 		0, 0, 0},
1163 /* view */		"models/weapons2/briar_pistol/briar_pistol.md3",
1164 /* icon */		"gfx/hud/w_icon_briar",//"gfx/hud/w_icon_rifle",
1165 /* pickup *///	"Bryar Pistol",
1166 		100,
1167 		IT_WEAPON,
1168 		WP_BRYAR_OLD,
1169 /* precache */ "",
1170 /* sounds */ "",
1171 		"@SP_INGAME_BLASTER_PISTOL"					// description
1172 	},
1173 
1174 /*QUAKED weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1175 */
1176 	{
1177 		"weapon_blaster",
1178 		"sound/weapons/w_pkup.wav",
1179         { "models/weapons2/blaster_r/blaster_w.glm",
1180 		0, 0, 0},
1181 /* view */		"models/weapons2/blaster_r/blaster.md3",
1182 /* icon */		"gfx/hud/w_icon_blaster",
1183 /* pickup *///	"E11 Blaster Rifle",
1184 		100,
1185 		IT_WEAPON,
1186 		WP_BLASTER,
1187 /* precache */ "",
1188 /* sounds */ "",
1189 		"@MENUS_THE_PRIMARY_WEAPON_OF"				// description
1190 	},
1191 
1192 /*QUAKED weapon_disruptor (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1193 */
1194 	{
1195 		"weapon_disruptor",
1196 		"sound/weapons/w_pkup.wav",
1197         { "models/weapons2/disruptor/disruptor_w.glm",
1198 		0, 0, 0},
1199 /* view */		"models/weapons2/disruptor/disruptor.md3",
1200 /* icon */		"gfx/hud/w_icon_disruptor",
1201 /* pickup *///	"Tenloss Disruptor Rifle",
1202 		100,
1203 		IT_WEAPON,
1204 		WP_DISRUPTOR,
1205 /* precache */ "",
1206 /* sounds */ "",
1207 		"@MENUS_THIS_NEFARIOUS_WEAPON"					// description
1208 	},
1209 
1210 /*QUAKED weapon_bowcaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1211 */
1212 	{
1213 		"weapon_bowcaster",
1214 		"sound/weapons/w_pkup.wav",
1215         { "models/weapons2/bowcaster/bowcaster_w.glm",
1216 		0, 0, 0},
1217 /* view */		"models/weapons2/bowcaster/bowcaster.md3",
1218 /* icon */		"gfx/hud/w_icon_bowcaster",
1219 /* pickup *///	"Wookiee Bowcaster",
1220 		100,
1221 		IT_WEAPON,
1222 		WP_BOWCASTER,
1223 /* precache */ "",
1224 /* sounds */ "",
1225 		"@MENUS_THIS_ARCHAIC_LOOKING"					// description
1226 	},
1227 
1228 /*QUAKED weapon_repeater (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1229 */
1230 	{
1231 		"weapon_repeater",
1232 		"sound/weapons/w_pkup.wav",
1233         { "models/weapons2/heavy_repeater/heavy_repeater_w.glm",
1234 		0, 0, 0},
1235 /* view */		"models/weapons2/heavy_repeater/heavy_repeater.md3",
1236 /* icon */		"gfx/hud/w_icon_repeater",
1237 /* pickup *///	"Imperial Heavy Repeater",
1238 		100,
1239 		IT_WEAPON,
1240 		WP_REPEATER,
1241 /* precache */ "",
1242 /* sounds */ "",
1243 		"@MENUS_THIS_DESTRUCTIVE_PROJECTILE"					// description
1244 	},
1245 
1246 /*QUAKED weapon_demp2 (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1247 NOTENOTE This weapon is not yet complete.  Don't place it.
1248 */
1249 	{
1250 		"weapon_demp2",
1251 		"sound/weapons/w_pkup.wav",
1252         { "models/weapons2/demp2/demp2_w.glm",
1253 		0, 0, 0},
1254 /* view */		"models/weapons2/demp2/demp2.md3",
1255 /* icon */		"gfx/hud/w_icon_demp2",
1256 /* pickup *///	"DEMP2",
1257 		100,
1258 		IT_WEAPON,
1259 		WP_DEMP2,
1260 /* precache */ "",
1261 /* sounds */ "",
1262 		"@MENUS_COMMONLY_REFERRED_TO"					// description
1263 	},
1264 
1265 /*QUAKED weapon_flechette (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1266 */
1267 	{
1268 		"weapon_flechette",
1269 		"sound/weapons/w_pkup.wav",
1270         { "models/weapons2/golan_arms/golan_arms_w.glm",
1271 		0, 0, 0},
1272 /* view */		"models/weapons2/golan_arms/golan_arms.md3",
1273 /* icon */		"gfx/hud/w_icon_flechette",
1274 /* pickup *///	"Golan Arms Flechette",
1275 		100,
1276 		IT_WEAPON,
1277 		WP_FLECHETTE,
1278 /* precache */ "",
1279 /* sounds */ "",
1280 		"@MENUS_WIDELY_USED_BY_THE_CORPORATE"					// description
1281 	},
1282 
1283 /*QUAKED weapon_rocket_launcher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1284 */
1285 	{
1286 		"weapon_rocket_launcher",
1287 		"sound/weapons/w_pkup.wav",
1288         { "models/weapons2/merr_sonn/merr_sonn_w.glm",
1289 		0, 0, 0},
1290 /* view */		"models/weapons2/merr_sonn/merr_sonn.md3",
1291 /* icon */		"gfx/hud/w_icon_merrsonn",
1292 /* pickup *///	"Merr-Sonn Missile System",
1293 		3,
1294 		IT_WEAPON,
1295 		WP_ROCKET_LAUNCHER,
1296 /* precache */ "",
1297 /* sounds */ "",
1298 		"@MENUS_THE_PLX_2M_IS_AN_EXTREMELY"					// description
1299 	},
1300 
1301 /*QUAKED ammo_thermal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1302 */
1303 	{
1304 		"ammo_thermal",
1305 		"sound/weapons/w_pkup.wav",
1306         { "models/weapons2/thermal/thermal_pu.md3",
1307 		"models/weapons2/thermal/thermal_w.glm", 0, 0},
1308 /* view */		"models/weapons2/thermal/thermal.md3",
1309 /* icon */		"gfx/hud/w_icon_thermal",
1310 /* pickup *///	"Thermal Detonators",
1311 		4,
1312 		IT_AMMO,
1313 		AMMO_THERMAL,
1314 /* precache */ "",
1315 /* sounds */ "",
1316 		"@MENUS_THE_THERMAL_DETONATOR"					// description
1317 	},
1318 
1319 /*QUAKED ammo_tripmine (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1320 */
1321 	{
1322 		"ammo_tripmine",
1323 		"sound/weapons/w_pkup.wav",
1324         { "models/weapons2/laser_trap/laser_trap_pu.md3",
1325 		"models/weapons2/laser_trap/laser_trap_w.glm", 0, 0},
1326 /* view */		"models/weapons2/laser_trap/laser_trap.md3",
1327 /* icon */		"gfx/hud/w_icon_tripmine",
1328 /* pickup *///	"Trip Mines",
1329 		3,
1330 		IT_AMMO,
1331 		AMMO_TRIPMINE,
1332 /* precache */ "",
1333 /* sounds */ "",
1334 		"@MENUS_TRIP_MINES_CONSIST_OF"					// description
1335 	},
1336 
1337 /*QUAKED ammo_detpack (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1338 */
1339 	{
1340 		"ammo_detpack",
1341 		"sound/weapons/w_pkup.wav",
1342         { "models/weapons2/detpack/det_pack_pu.md3", "models/weapons2/detpack/det_pack_proj.glm", "models/weapons2/detpack/det_pack_w.glm", 0},
1343 /* view */		"models/weapons2/detpack/det_pack.md3",
1344 /* icon */		"gfx/hud/w_icon_detpack",
1345 /* pickup *///	"Det Packs",
1346 		3,
1347 		IT_AMMO,
1348 		AMMO_DETPACK,
1349 /* precache */ "",
1350 /* sounds */ "",
1351 		"@MENUS_A_DETONATION_PACK_IS"					// description
1352 	},
1353 
1354 /*QUAKED weapon_thermal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1355 */
1356 	{
1357 		"weapon_thermal",
1358 		"sound/weapons/w_pkup.wav",
1359         { "models/weapons2/thermal/thermal_w.glm", "models/weapons2/thermal/thermal_pu.md3",
1360 		0, 0 },
1361 /* view */		"models/weapons2/thermal/thermal.md3",
1362 /* icon */		"gfx/hud/w_icon_thermal",
1363 /* pickup *///	"Thermal Detonator",
1364 		4,
1365 		IT_WEAPON,
1366 		WP_THERMAL,
1367 /* precache */ "",
1368 /* sounds */ "",
1369 		"@MENUS_THE_THERMAL_DETONATOR"					// description
1370 	},
1371 
1372 /*QUAKED weapon_trip_mine (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1373 */
1374 	{
1375 		"weapon_trip_mine",
1376 		"sound/weapons/w_pkup.wav",
1377         { "models/weapons2/laser_trap/laser_trap_w.glm", "models/weapons2/laser_trap/laser_trap_pu.md3",
1378 		0, 0},
1379 /* view */		"models/weapons2/laser_trap/laser_trap.md3",
1380 /* icon */		"gfx/hud/w_icon_tripmine",
1381 /* pickup *///	"Trip Mine",
1382 		3,
1383 		IT_WEAPON,
1384 		WP_TRIP_MINE,
1385 /* precache */ "",
1386 /* sounds */ "",
1387 		"@MENUS_TRIP_MINES_CONSIST_OF"					// description
1388 	},
1389 
1390 /*QUAKED weapon_det_pack (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1391 */
1392 	{
1393 		"weapon_det_pack",
1394 		"sound/weapons/w_pkup.wav",
1395         { "models/weapons2/detpack/det_pack_proj.glm", "models/weapons2/detpack/det_pack_pu.md3", "models/weapons2/detpack/det_pack_w.glm", 0},
1396 /* view */		"models/weapons2/detpack/det_pack.md3",
1397 /* icon */		"gfx/hud/w_icon_detpack",
1398 /* pickup *///	"Det Pack",
1399 		3,
1400 		IT_WEAPON,
1401 		WP_DET_PACK,
1402 /* precache */ "",
1403 /* sounds */ "",
1404 		"@MENUS_A_DETONATION_PACK_IS"					// description
1405 	},
1406 
1407 /*QUAKED weapon_emplaced (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1408 */
1409 	{
1410 		"weapon_emplaced",
1411 		"sound/weapons/w_pkup.wav",
1412         { "models/weapons2/blaster_r/blaster_w.glm",
1413 		0, 0, 0},
1414 /* view */		"models/weapons2/blaster_r/blaster.md3",
1415 /* icon */		"gfx/hud/w_icon_blaster",
1416 /* pickup *///	"Emplaced Gun",
1417 		50,
1418 		IT_WEAPON,
1419 		WP_EMPLACED_GUN,
1420 /* precache */ "",
1421 /* sounds */ "",
1422 		""					// description
1423 	},
1424 
1425 
1426 //NOTE: This is to keep things from messing up because the turret weapon type isn't real
1427 	{
1428 		"weapon_turretwp",
1429 		"sound/weapons/w_pkup.wav",
1430         { "models/weapons2/blaster_r/blaster_w.glm",
1431 		0, 0, 0},
1432 /* view */		"models/weapons2/blaster_r/blaster.md3",
1433 /* icon */		"gfx/hud/w_icon_blaster",
1434 /* pickup *///	"Turret Gun",
1435 		50,
1436 		IT_WEAPON,
1437 		WP_TURRET,
1438 /* precache */ "",
1439 /* sounds */ "",
1440 		""					// description
1441 	},
1442 
1443 	//
1444 	// AMMO ITEMS
1445 	//
1446 
1447 /*QUAKED ammo_force (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1448 Don't place this
1449 */
1450 	{
1451 		"ammo_force",
1452 		"sound/player/pickupenergy.wav",
1453         { "models/items/energy_cell.md3",
1454 		0, 0, 0},
1455 /* view */		NULL,
1456 /* icon */		"gfx/hud/w_icon_blaster",
1457 /* pickup *///	"Force??",
1458 		100,
1459 		IT_AMMO,
1460 		AMMO_FORCE,
1461 /* precache */ "",
1462 /* sounds */ "",
1463 		""					// description
1464 	},
1465 
1466 /*QUAKED ammo_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1467 Ammo for the Bryar and Blaster pistols.
1468 */
1469 	{
1470 		"ammo_blaster",
1471 		"sound/player/pickupenergy.wav",
1472         { "models/items/energy_cell.md3",
1473 		0, 0, 0},
1474 /* view */		NULL,
1475 /* icon */		"gfx/hud/i_icon_battery",
1476 /* pickup *///	"Blaster Pack",
1477 		100,
1478 		IT_AMMO,
1479 		AMMO_BLASTER,
1480 /* precache */ "",
1481 /* sounds */ "",
1482 		""					// description
1483 	},
1484 
1485 /*QUAKED ammo_powercell (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1486 Ammo for Tenloss Disruptor, Wookie Bowcaster, and the Destructive Electro Magnetic Pulse (demp2 ) guns
1487 */
1488 	{
1489 		"ammo_powercell",
1490 		"sound/player/pickupenergy.wav",
1491         { "models/items/power_cell.md3",
1492 		0, 0, 0},
1493 /* view */		NULL,
1494 /* icon */		"gfx/mp/ammo_power_cell",
1495 /* pickup *///	"Power Cell",
1496 		100,
1497 		IT_AMMO,
1498 		AMMO_POWERCELL,
1499 /* precache */ "",
1500 /* sounds */ "",
1501 		""					// description
1502 	},
1503 
1504 /*QUAKED ammo_metallic_bolts (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1505 Ammo for Imperial Heavy Repeater and the Golan Arms Flechette
1506 */
1507 	{
1508 		"ammo_metallic_bolts",
1509 		"sound/player/pickupenergy.wav",
1510         { "models/items/metallic_bolts.md3",
1511 		0, 0, 0},
1512 /* view */		NULL,
1513 /* icon */		"gfx/mp/ammo_metallic_bolts",
1514 /* pickup *///	"Metallic Bolts",
1515 		100,
1516 		IT_AMMO,
1517 		AMMO_METAL_BOLTS,
1518 /* precache */ "",
1519 /* sounds */ "",
1520 		""					// description
1521 	},
1522 
1523 /*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1524 Ammo for Merr-Sonn portable missile launcher
1525 */
1526 	{
1527 		"ammo_rockets",
1528 		"sound/player/pickupenergy.wav",
1529         { "models/items/rockets.md3",
1530 		0, 0, 0},
1531 /* view */		NULL,
1532 /* icon */		"gfx/mp/ammo_rockets",
1533 /* pickup *///	"Rockets",
1534 		3,
1535 		IT_AMMO,
1536 		AMMO_ROCKETS,
1537 /* precache */ "",
1538 /* sounds */ "",
1539 		""					// description
1540 	},
1541 
1542 /*QUAKED ammo_all (.3 .3 1) (-16 -16 -16) (16 16 16) suspended
1543 DO NOT PLACE in a map, this is only for siege classes that have ammo
1544 dispensing ability
1545 */
1546 	{
1547 		"ammo_all",
1548 		"sound/player/pickupenergy.wav",
1549         { "models/items/battery.md3",  //replace me
1550 		0, 0, 0},
1551 /* view */		NULL,
1552 /* icon */		"gfx/mp/ammo_rockets", //replace me
1553 /* pickup *///	"Rockets",
1554 		0,
1555 		IT_AMMO,
1556 		-1,
1557 /* precache */ "",
1558 /* sounds */ "",
1559 		""					// description
1560 	},
1561 
1562 	//
1563 	// POWERUP ITEMS
1564 	//
1565 /*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16)
1566 Only in CTF games
1567 */
1568 	{
1569 		"team_CTF_redflag",
1570 		NULL,
1571         { "models/flags/r_flag.md3",
1572 		"models/flags/r_flag_ysal.md3", 0, 0 },
1573 /* view */		NULL,
1574 /* icon */		"gfx/hud/mpi_rflag",
1575 /* pickup *///	"Red Flag",
1576 		0,
1577 		IT_TEAM,
1578 		PW_REDFLAG,
1579 /* precache */ "",
1580 /* sounds */ "",
1581 		""					// description
1582 	},
1583 
1584 /*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16)
1585 Only in CTF games
1586 */
1587 	{
1588 		"team_CTF_blueflag",
1589 		NULL,
1590         { "models/flags/b_flag.md3",
1591 		"models/flags/b_flag_ysal.md3", 0, 0 },
1592 /* view */		NULL,
1593 /* icon */		"gfx/hud/mpi_bflag",
1594 /* pickup *///	"Blue Flag",
1595 		0,
1596 		IT_TEAM,
1597 		PW_BLUEFLAG,
1598 /* precache */ "",
1599 /* sounds */ "",
1600 		""					// description
1601 	},
1602 
1603 	//
1604 	// PERSISTANT POWERUP ITEMS
1605 	//
1606 
1607 	/*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16)
1608 Only in One Flag CTF games
1609 */
1610 	{
1611 		"team_CTF_neutralflag",
1612 		NULL,
1613         { "models/flags/n_flag.md3",
1614 		0, 0, 0 },
1615 /* view */		NULL,
1616 /* icon */		"icons/iconf_neutral1",
1617 /* pickup *///	"Neutral Flag",
1618 		0,
1619 		IT_TEAM,
1620 		PW_NEUTRALFLAG,
1621 /* precache */ "",
1622 /* sounds */ "",
1623 		""					// description
1624 	},
1625 
1626 	{
1627 		"item_redcube",
1628 		"sound/player/pickupenergy.wav",
1629         { "models/powerups/orb/r_orb.md3",
1630 		0, 0, 0 },
1631 /* view */		NULL,
1632 /* icon */		"icons/iconh_rorb",
1633 /* pickup *///	"Red Cube",
1634 		0,
1635 		IT_TEAM,
1636 		0,
1637 /* precache */ "",
1638 /* sounds */ "",
1639 		""					// description
1640 	},
1641 
1642 	{
1643 		"item_bluecube",
1644 		"sound/player/pickupenergy.wav",
1645         { "models/powerups/orb/b_orb.md3",
1646 		0, 0, 0 },
1647 /* view */		NULL,
1648 /* icon */		"icons/iconh_borb",
1649 /* pickup *///	"Blue Cube",
1650 		0,
1651 		IT_TEAM,
1652 		0,
1653 /* precache */ "",
1654 /* sounds */ "",
1655 		""					// description
1656 	},
1657 
1658 	// end of list marker
1659 	{NULL}
1660 };
1661 
1662 int		bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1;
1663 
vectoyaw(const vec3_t vec)1664 float vectoyaw( const vec3_t vec ) {
1665 	float	yaw;
1666 
1667 	if (vec[YAW] == 0 && vec[PITCH] == 0) {
1668 		yaw = 0;
1669 	} else {
1670 		if (vec[PITCH]) {
1671 			yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
1672 		} else if (vec[YAW] > 0) {
1673 			yaw = 90;
1674 		} else {
1675 			yaw = 270;
1676 		}
1677 		if (yaw < 0) {
1678 			yaw += 360;
1679 		}
1680 	}
1681 
1682 	return yaw;
1683 }
1684 
BG_HasYsalamiri(int gametype,playerState_t * ps)1685 qboolean BG_HasYsalamiri(int gametype, playerState_t *ps)
1686 {
1687 	if (gametype == GT_CTY &&
1688 		(ps->powerups[PW_REDFLAG] || ps->powerups[PW_BLUEFLAG]))
1689 	{
1690 		return qtrue;
1691 	}
1692 
1693 	if (ps->powerups[PW_YSALAMIRI])
1694 	{
1695 		return qtrue;
1696 	}
1697 
1698 	return qfalse;
1699 }
1700 
BG_CanUseFPNow(int gametype,playerState_t * ps,int time,forcePowers_t power)1701 qboolean BG_CanUseFPNow(int gametype, playerState_t *ps, int time, forcePowers_t power)
1702 {
1703 	if (BG_HasYsalamiri(gametype, ps))
1704 	{
1705 		return qfalse;
1706 	}
1707 
1708 	if ( ps->forceRestricted || ps->trueNonJedi )
1709 	{
1710 		return qfalse;
1711 	}
1712 
1713 	if (ps->weapon == WP_EMPLACED_GUN)
1714 	{ //can't use any of your powers while on an emplaced weapon
1715 		return qfalse;
1716 	}
1717 
1718 	if (ps->m_iVehicleNum)
1719 	{ //can't use powers while riding a vehicle (this may change, I don't know)
1720 		return qfalse;
1721 	}
1722 
1723 	if (ps->duelInProgress)
1724 	{
1725 		if (power != FP_SABER_OFFENSE && power != FP_SABER_DEFENSE && /*power != FP_SABERTHROW &&*/
1726 			power != FP_LEVITATION)
1727 		{
1728 			if (!ps->saberLockFrame || power != FP_PUSH)
1729 			{
1730 				return qfalse;
1731 			}
1732 		}
1733 	}
1734 
1735 	if (ps->saberLockFrame || ps->saberLockTime > time)
1736 	{
1737 		if (power != FP_PUSH)
1738 		{
1739 			return qfalse;
1740 		}
1741 	}
1742 
1743 	if (ps->fallingToDeath)
1744 	{
1745 		return qfalse;
1746 	}
1747 
1748 	if ((ps->brokenLimbs & (1 << BROKENLIMB_RARM)) ||
1749 		(ps->brokenLimbs & (1 << BROKENLIMB_LARM)))
1750 	{ //powers we can't use with a broken arm
1751         switch (power)
1752 		{
1753 		case FP_PUSH:
1754 		case FP_PULL:
1755 		case FP_GRIP:
1756 		case FP_LIGHTNING:
1757 		case FP_DRAIN:
1758 			return qfalse;
1759 		default:
1760 			break;
1761 		}
1762 	}
1763 
1764 	return qtrue;
1765 }
1766 
1767 /*
1768 ==============
1769 BG_FindItemForPowerup
1770 ==============
1771 */
BG_FindItemForPowerup(powerup_t pw)1772 gitem_t	*BG_FindItemForPowerup( powerup_t pw ) {
1773 	int		i;
1774 
1775 	for ( i = 0 ; i < bg_numItems ; i++ ) {
1776 		if ( (bg_itemlist[i].giType == IT_POWERUP ||
1777 					bg_itemlist[i].giType == IT_TEAM) &&
1778 			bg_itemlist[i].giTag == pw ) {
1779 			return &bg_itemlist[i];
1780 		}
1781 	}
1782 
1783 	return NULL;
1784 }
1785 
1786 
1787 /*
1788 ==============
1789 BG_FindItemForHoldable
1790 ==============
1791 */
BG_FindItemForHoldable(holdable_t pw)1792 gitem_t	*BG_FindItemForHoldable( holdable_t pw ) {
1793 	int		i;
1794 
1795 	for ( i = 0 ; i < bg_numItems ; i++ ) {
1796 		if ( bg_itemlist[i].giType == IT_HOLDABLE && bg_itemlist[i].giTag == pw ) {
1797 			return &bg_itemlist[i];
1798 		}
1799 	}
1800 
1801 	Com_Error( ERR_DROP, "HoldableItem not found" );
1802 
1803 	return NULL;
1804 }
1805 
1806 
1807 /*
1808 ===============
1809 BG_FindItemForWeapon
1810 
1811 ===============
1812 */
BG_FindItemForWeapon(weapon_t weapon)1813 gitem_t	*BG_FindItemForWeapon( weapon_t weapon ) {
1814 	gitem_t	*it;
1815 
1816 	for ( it = bg_itemlist + 1 ; it->classname ; it++) {
1817 		if ( it->giType == IT_WEAPON && it->giTag == weapon ) {
1818 			return it;
1819 		}
1820 	}
1821 
1822 	Com_Error( ERR_DROP, "Couldn't find item for weapon %i", weapon);
1823 	return NULL;
1824 }
1825 
1826 /*
1827 ===============
1828 BG_FindItemForAmmo
1829 
1830 ===============
1831 */
BG_FindItemForAmmo(ammo_t ammo)1832 gitem_t	*BG_FindItemForAmmo( ammo_t ammo ) {
1833 	gitem_t	*it;
1834 
1835 	for ( it = bg_itemlist + 1 ; it->classname ; it++) {
1836 		if ( it->giType == IT_AMMO && it->giTag == ammo ) {
1837 			return it;
1838 		}
1839 	}
1840 
1841 	Com_Error( ERR_DROP, "Couldn't find item for ammo %i", ammo);
1842 	return NULL;
1843 }
1844 
1845 /*
1846 ===============
1847 BG_FindItem
1848 
1849 ===============
1850 */
BG_FindItem(const char * classname)1851 gitem_t	*BG_FindItem( const char *classname ) {
1852 	gitem_t	*it;
1853 
1854 	for ( it = bg_itemlist + 1 ; it->classname ; it++ ) {
1855 		if ( !Q_stricmp( it->classname, classname) )
1856 			return it;
1857 	}
1858 
1859 	return NULL;
1860 }
1861 
1862 /*
1863 ============
1864 BG_PlayerTouchesItem
1865 
1866 Items can be picked up without actually touching their physical bounds to make
1867 grabbing them easier
1868 ============
1869 */
BG_PlayerTouchesItem(playerState_t * ps,entityState_t * item,int atTime)1870 qboolean	BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) {
1871 	vec3_t		origin;
1872 
1873 	BG_EvaluateTrajectory( &item->pos, atTime, origin );
1874 
1875 	// we are ignoring ducked differences here
1876 	if ( ps->origin[0] - origin[0] > 44
1877 		|| ps->origin[0] - origin[0] < -50
1878 		|| ps->origin[1] - origin[1] > 36
1879 		|| ps->origin[1] - origin[1] < -36
1880 		|| ps->origin[2] - origin[2] > 36
1881 		|| ps->origin[2] - origin[2] < -36 ) {
1882 		return qfalse;
1883 	}
1884 
1885 	return qtrue;
1886 }
1887 
BG_ProperForceIndex(int power)1888 int BG_ProperForceIndex( int power ) {
1889 	int i;
1890 
1891 	for ( i=0; i<NUM_FORCE_POWERS; i++ ) {
1892 		if ( forcePowerSorted[i] == power )
1893 			return i;
1894 	}
1895 
1896 	return -1;
1897 }
1898 
BG_CycleForce(playerState_t * ps,int direction)1899 void BG_CycleForce( playerState_t *ps, int direction ) {
1900 	int i, x, presel;
1901 	int foundnext = -1;
1902 
1903 	presel = x = i = ps->fd.forcePowerSelected;
1904 
1905 	// no valid force powers
1906 	if ( x >= NUM_FORCE_POWERS || x == -1 )
1907 		return;
1908 
1909 	presel = x = BG_ProperForceIndex( x );
1910 
1911 	// get the next/prev power and handle overflow
1912 	if ( direction == 1 )	x++;
1913 	else					x--;
1914 	if ( x >= NUM_FORCE_POWERS )	x = 0;
1915 	if ( x < 0 )					x = NUM_FORCE_POWERS-1;
1916 
1917 	i = forcePowerSorted[x]; //the "sorted" value of this power
1918 
1919 	while ( x != presel ) {
1920 		// loop around to the current force power
1921 		if ( ps->fd.forcePowersKnown & (1 << i) && i != (signed)ps->fd.forcePowerSelected ) {
1922 			// we have this power
1923 			if ( i != FP_LEVITATION && i != FP_SABER_OFFENSE && i != FP_SABER_DEFENSE && i != FP_SABERTHROW ) {
1924 				// it's selectable
1925 				foundnext = i;
1926 				break;
1927 			}
1928 		}
1929 
1930 		// get the next/prev power and handle overflow
1931 		if ( direction == 1 )	x++;
1932 		else					x--;
1933 		if ( x >= NUM_FORCE_POWERS )	x = 0;
1934 		if ( x < 0 )					x = NUM_FORCE_POWERS-1;
1935 
1936 		i = forcePowerSorted[x]; //set to the sorted value again
1937 	}
1938 
1939 	// if we found one, select it
1940 	if ( foundnext != -1 )
1941 		ps->fd.forcePowerSelected = foundnext;
1942 }
1943 
BG_GetItemIndexByTag(int tag,int type)1944 int BG_GetItemIndexByTag(int tag, int type)
1945 { //Get the itemlist index from the tag and type
1946 	int i = 0;
1947 
1948 	while (i < bg_numItems)
1949 	{
1950 		if (bg_itemlist[i].giTag == tag &&
1951 			bg_itemlist[i].giType == type)
1952 		{
1953 			return i;
1954 		}
1955 
1956 		i++;
1957 	}
1958 
1959 	return 0;
1960 }
1961 
1962 //yeah..
BG_IsItemSelectable(playerState_t * ps,int item)1963 qboolean BG_IsItemSelectable(playerState_t *ps, int item)
1964 {
1965 	if (item == HI_HEALTHDISP || item == HI_AMMODISP ||
1966 		item == HI_JETPACK)
1967 	{
1968 		return qfalse;
1969 	}
1970 	return qtrue;
1971 }
1972 
BG_CycleInven(playerState_t * ps,int direction)1973 void BG_CycleInven(playerState_t *ps, int direction)
1974 {
1975 	int i;
1976 	int dontFreeze = 0;
1977 	int original;
1978 
1979 	i = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
1980 	original = i;
1981 
1982 	if (direction == 1)
1983 	{ //next
1984 		i++;
1985 		if (i == HI_NUM_HOLDABLE)
1986 		{
1987 			i = 1;
1988 		}
1989 	}
1990 	else
1991 	{ //previous
1992 		i--;
1993 		if (i == 0)
1994 		{
1995 			i = HI_NUM_HOLDABLE-1;
1996 		}
1997 	}
1998 
1999 	while (i != original)
2000 	{ //go in a full loop until hitting something, if hit nothing then select nothing
2001 		if (ps->stats[STAT_HOLDABLE_ITEMS] & (1 << i))
2002 		{ //we have it, select it.
2003 			if (BG_IsItemSelectable(ps, i))
2004 			{
2005 				ps->stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(i, IT_HOLDABLE);
2006 				break;
2007 			}
2008 		}
2009 
2010 		if (direction == 1)
2011 		{ //next
2012 			i++;
2013 		}
2014 		else
2015 		{ //previous
2016 			i--;
2017 		}
2018 
2019 		if (i <= 0)
2020 		{ //wrap around to the last
2021 			i = HI_NUM_HOLDABLE-1;
2022 		}
2023 		else if (i >= HI_NUM_HOLDABLE)
2024 		{ //wrap around to the first
2025 			i = 1;
2026 		}
2027 
2028 		dontFreeze++;
2029 		if (dontFreeze >= 32)
2030 		{ //yeah, sure, whatever (it's 2 am and I'm paranoid and can't frickin think)
2031 			break;
2032 		}
2033 	}
2034 }
2035 
2036 /*
2037 ================
2038 BG_CanItemBeGrabbed
2039 
2040 Returns false if the item should not be picked up.
2041 This needs to be the same for client side prediction and server use.
2042 ================
2043 */
BG_CanItemBeGrabbed(int gametype,const entityState_t * ent,const playerState_t * ps)2044 qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ) {
2045 	gitem_t	*item;
2046 
2047 	if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) {
2048 		Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" );
2049 	}
2050 
2051 	item = &bg_itemlist[ent->modelindex];
2052 
2053 	if ( ps )
2054 	{
2055 		if ( ps->trueJedi )
2056 		{//force powers and saber only
2057 			if ( item->giType != IT_TEAM //not a flag
2058 				&& item->giType != IT_ARMOR//not shields
2059 				&& (item->giType != IT_WEAPON
2060 									|| item->giTag != WP_SABER)//not a saber
2061 				&& (item->giType != IT_HOLDABLE || item->giTag != HI_SEEKER)//not a seeker
2062 				&& (item->giType != IT_POWERUP || item->giTag == PW_YSALAMIRI) )//not a force pick-up
2063 			{
2064 				return qfalse;
2065 			}
2066 		}
2067 		else if ( ps->trueNonJedi )
2068 		{//can't pick up force powerups
2069 			if ( (item->giType == IT_POWERUP && item->giTag != PW_YSALAMIRI) //if a powerup, can only can pick up ysalamiri
2070 				|| (item->giType == IT_HOLDABLE && item->giTag == HI_SEEKER)//if holdable, cannot pick up seeker
2071 				|| (item->giType == IT_WEAPON && item->giTag == WP_SABER ) )//or if it's a saber
2072 			{
2073 				return qfalse;
2074 			}
2075 		}
2076 		if ( ps->isJediMaster && item && (item->giType == IT_WEAPON || item->giType == IT_AMMO))
2077 		{//jedi master cannot pick up weapons
2078 			return qfalse;
2079 		}
2080 		if ( ps->duelInProgress )
2081 		{ //no picking stuff up while in a duel, no matter what the type is
2082 			return qfalse;
2083 		}
2084 	}
2085 	else
2086 	{//safety return since below code assumes a non-null ps
2087 		return qfalse;
2088 	}
2089 
2090 	switch( item->giType ) {
2091 	case IT_WEAPON:
2092 		if (ent->generic1 == ps->clientNum && ent->powerups)
2093 		{
2094 			return qfalse;
2095 		}
2096 		if (!(ent->eFlags & EF_DROPPEDWEAPON) && (ps->stats[STAT_WEAPONS] & (1 << item->giTag)) &&
2097 			item->giTag != WP_THERMAL && item->giTag != WP_TRIP_MINE && item->giTag != WP_DET_PACK)
2098 		{ //weaponstay stuff.. if this isn't dropped, and you already have it, you don't get it.
2099 			return qfalse;
2100 		}
2101 		if (item->giTag == WP_THERMAL || item->giTag == WP_TRIP_MINE || item->giTag == WP_DET_PACK)
2102 		{ //check to see if full on ammo for this, if so, then..
2103 			int ammoIndex = weaponData[item->giTag].ammoIndex;
2104 			if (ps->ammo[ammoIndex] >= ammoData[ammoIndex].max)
2105 			{ //don't need it
2106 				return qfalse;
2107 			}
2108 		}
2109 		return qtrue;	// weapons are always picked up
2110 
2111 	case IT_AMMO:
2112 		if (item->giTag == -1)
2113 		{ //special case for "all ammo" packs
2114 			return qtrue;
2115 		}
2116 		if ( ps->ammo[item->giTag] >= ammoData[item->giTag].max) {
2117 			return qfalse;		// can't hold any more
2118 		}
2119 		return qtrue;
2120 
2121 	case IT_ARMOR:
2122 		if ( ps->stats[STAT_ARMOR] >= ps->stats[STAT_MAX_HEALTH]/* * item->giTag*/ ) {
2123 			return qfalse;
2124 		}
2125 		return qtrue;
2126 
2127 	case IT_HEALTH:
2128 		// small and mega healths will go over the max, otherwise
2129 		// don't pick up if already at max
2130 		if ((ps->fd.forcePowersActive & (1 << FP_RAGE)))
2131 		{
2132 			return qfalse;
2133 		}
2134 
2135 		if ( item->quantity == 5 || item->quantity == 100 ) {
2136 			if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) {
2137 				return qfalse;
2138 			}
2139 			return qtrue;
2140 		}
2141 
2142 		if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] ) {
2143 			return qfalse;
2144 		}
2145 		return qtrue;
2146 
2147 	case IT_POWERUP:
2148 		if (ps && (ps->powerups[PW_YSALAMIRI]))
2149 		{
2150 			if (item->giTag != PW_YSALAMIRI)
2151 			{
2152 				return qfalse;
2153 			}
2154 		}
2155 		return qtrue;	// powerups are always picked up
2156 
2157 	case IT_TEAM: // team items, such as flags
2158 		if( gametype == GT_CTF || gametype == GT_CTY ) {
2159 			// ent->modelindex2 is non-zero on items if they are dropped
2160 			// we need to know this because we can pick up our dropped flag (and return it)
2161 			// but we can't pick up our flag at base
2162 			if (ps->persistant[PERS_TEAM] == TEAM_RED) {
2163 				if (item->giTag == PW_BLUEFLAG ||
2164 					(item->giTag == PW_REDFLAG && ent->modelindex2) ||
2165 					(item->giTag == PW_REDFLAG && ps->powerups[PW_BLUEFLAG]) )
2166 					return qtrue;
2167 			} else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) {
2168 				if (item->giTag == PW_REDFLAG ||
2169 					(item->giTag == PW_BLUEFLAG && ent->modelindex2) ||
2170 					(item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) )
2171 					return qtrue;
2172 			}
2173 		}
2174 
2175 		return qfalse;
2176 
2177 	case IT_HOLDABLE:
2178 		if ( ps->stats[STAT_HOLDABLE_ITEMS] & (1 << item->giTag))
2179 		{
2180 			return qfalse;
2181 		}
2182 		return qtrue;
2183 
2184         case IT_BAD:
2185             Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" );
2186         default:
2187 #ifndef NDEBUG // bk0001204
2188           Com_Printf("BG_CanItemBeGrabbed: unknown enum %d\n", item->giType );
2189 #endif
2190          break;
2191 	}
2192 
2193 	return qfalse;
2194 }
2195 
2196 //======================================================================
2197 
2198 /*
2199 ================
2200 BG_EvaluateTrajectory
2201 
2202 ================
2203 */
BG_EvaluateTrajectory(const trajectory_t * tr,int atTime,vec3_t result)2204 void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) {
2205 	float		deltaTime;
2206 	float		phase;
2207 
2208 	switch( tr->trType ) {
2209 	case TR_STATIONARY:
2210 	case TR_INTERPOLATE:
2211 		VectorCopy( tr->trBase, result );
2212 		break;
2213 	case TR_LINEAR:
2214 		deltaTime = ( atTime - tr->trTime ) * 0.001;	// milliseconds to seconds
2215 		VectorMA( tr->trBase, deltaTime, tr->trDelta, result );
2216 		break;
2217 	case TR_SINE:
2218 		deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration;
2219 		phase = sin( deltaTime * M_PI * 2 );
2220 		VectorMA( tr->trBase, phase, tr->trDelta, result );
2221 		break;
2222 	case TR_LINEAR_STOP:
2223 		if ( atTime > tr->trTime + tr->trDuration ) {
2224 			atTime = tr->trTime + tr->trDuration;
2225 		}
2226 		deltaTime = ( atTime - tr->trTime ) * 0.001;	// milliseconds to seconds
2227 		if ( deltaTime < 0 ) {
2228 			deltaTime = 0;
2229 		}
2230 		VectorMA( tr->trBase, deltaTime, tr->trDelta, result );
2231 		break;
2232 	case TR_NONLINEAR_STOP:
2233 		if ( atTime > tr->trTime + tr->trDuration )
2234 		{
2235 			atTime = tr->trTime + tr->trDuration;
2236 		}
2237 		//new slow-down at end
2238 		if ( atTime - tr->trTime > tr->trDuration || atTime - tr->trTime <= 0  )
2239 		{
2240 			deltaTime = 0;
2241 		}
2242 		else
2243 		{//FIXME: maybe scale this somehow?  So that it starts out faster and stops faster?
2244 			deltaTime = tr->trDuration*0.001f*((float)cos( DEG2RAD(90.0f - (90.0f*((float)(atTime-tr->trTime))/(float)tr->trDuration)) ));
2245 		}
2246 		VectorMA( tr->trBase, deltaTime, tr->trDelta, result );
2247 		break;
2248 	case TR_GRAVITY:
2249 		deltaTime = ( atTime - tr->trTime ) * 0.001;	// milliseconds to seconds
2250 		VectorMA( tr->trBase, deltaTime, tr->trDelta, result );
2251 		result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime;		// FIXME: local gravity...
2252 		break;
2253 	default:
2254 #ifdef _GAME
2255 		Com_Error( ERR_DROP, "BG_EvaluateTrajectory: [ GAME] unknown trType: %i", tr->trType );
2256 #else
2257 		Com_Error( ERR_DROP, "BG_EvaluateTrajectory: [CGAME] unknown trType: %i", tr->trType );
2258 #endif
2259 		break;
2260 	}
2261 }
2262 
2263 /*
2264 ================
2265 BG_EvaluateTrajectoryDelta
2266 
2267 For determining velocity at a given time
2268 ================
2269 */
BG_EvaluateTrajectoryDelta(const trajectory_t * tr,int atTime,vec3_t result)2270 void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) {
2271 	float	deltaTime;
2272 	float	phase;
2273 
2274 	switch( tr->trType ) {
2275 	case TR_STATIONARY:
2276 	case TR_INTERPOLATE:
2277 		VectorClear( result );
2278 		break;
2279 	case TR_LINEAR:
2280 		VectorCopy( tr->trDelta, result );
2281 		break;
2282 	case TR_SINE:
2283 		deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration;
2284 		phase = cos( deltaTime * M_PI * 2 );	// derivative of sin = cos
2285 		phase *= 0.5;
2286 		VectorScale( tr->trDelta, phase, result );
2287 		break;
2288 	case TR_LINEAR_STOP:
2289 		if ( atTime > tr->trTime + tr->trDuration ) {
2290 			VectorClear( result );
2291 			return;
2292 		}
2293 		VectorCopy( tr->trDelta, result );
2294 		break;
2295 	case TR_NONLINEAR_STOP:
2296 		if ( atTime - tr->trTime > tr->trDuration || atTime - tr->trTime <= 0  )
2297 		{
2298 			VectorClear( result );
2299 			return;
2300 		}
2301 		deltaTime = tr->trDuration*0.001f*((float)cos( DEG2RAD(90.0f - (90.0f*((float)(atTime-tr->trTime))/(float)tr->trDuration)) ));
2302 		VectorScale( tr->trDelta, deltaTime, result );
2303 		break;
2304 	case TR_GRAVITY:
2305 		deltaTime = ( atTime - tr->trTime ) * 0.001;	// milliseconds to seconds
2306 		VectorCopy( tr->trDelta, result );
2307 		result[2] -= DEFAULT_GRAVITY * deltaTime;		// FIXME: local gravity...
2308 		break;
2309 	default:
2310 #ifdef _GAME
2311 		Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: [ GAME] unknown trType: %i", tr->trType );
2312 #else
2313 		Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: [CGAME] unknown trType: %i", tr->trType );
2314 #endif
2315 		break;
2316 	}
2317 }
2318 
2319 const char *eventnames[EV_NUM_ENTITY_EVENTS] = {
2320 	"EV_NONE",
2321 
2322 	"EV_CLIENTJOIN",
2323 
2324 	"EV_FOOTSTEP",
2325 	"EV_FOOTSTEP_METAL",
2326 	"EV_FOOTSPLASH",
2327 	"EV_FOOTWADE",
2328 	"EV_SWIM",
2329 
2330 	"EV_STEP_4",
2331 	"EV_STEP_8",
2332 	"EV_STEP_12",
2333 	"EV_STEP_16",
2334 
2335 	"EV_FALL",
2336 
2337 	"EV_JUMP_PAD",			// boing sound at origin", jump sound on player
2338 
2339 	"EV_GHOUL2_MARK",			//create a projectile impact mark on something with a client-side g2 instance.
2340 
2341 	"EV_GLOBAL_DUEL",
2342 	"EV_PRIVATE_DUEL",
2343 
2344 	"EV_JUMP",
2345 	"EV_ROLL",
2346 	"EV_WATER_TOUCH",	// foot touches
2347 	"EV_WATER_LEAVE",	// foot leaves
2348 	"EV_WATER_UNDER",	// head touches
2349 	"EV_WATER_CLEAR",	// head leaves
2350 
2351 	"EV_ITEM_PICKUP",			// normal item pickups are predictable
2352 	"EV_GLOBAL_ITEM_PICKUP",	// powerup / team sounds are broadcast to everyone
2353 
2354 	"EV_VEH_FIRE",
2355 
2356 	"EV_NOAMMO",
2357 	"EV_CHANGE_WEAPON",
2358 	"EV_FIRE_WEAPON",
2359 	"EV_ALT_FIRE",
2360 	"EV_SABER_ATTACK",
2361 	"EV_SABER_HIT",
2362 	"EV_SABER_BLOCK",
2363 	"EV_SABER_CLASHFLARE",
2364 	"EV_SABER_UNHOLSTER",
2365 	"EV_BECOME_JEDIMASTER",
2366 	"EV_DISRUPTOR_MAIN_SHOT",
2367 	"EV_DISRUPTOR_SNIPER_SHOT",
2368 	"EV_DISRUPTOR_SNIPER_MISS",
2369 	"EV_DISRUPTOR_HIT",
2370 	"EV_DISRUPTOR_ZOOMSOUND",
2371 
2372 	"EV_PREDEFSOUND",
2373 
2374 	"EV_TEAM_POWER",
2375 
2376 	"EV_SCREENSHAKE",
2377 
2378 	"EV_LOCALTIMER",
2379 
2380 	"EV_USE",			// +Use key
2381 
2382 	"EV_USE_ITEM0",
2383 	"EV_USE_ITEM1",
2384 	"EV_USE_ITEM2",
2385 	"EV_USE_ITEM3",
2386 	"EV_USE_ITEM4",
2387 	"EV_USE_ITEM5",
2388 	"EV_USE_ITEM6",
2389 	"EV_USE_ITEM7",
2390 	"EV_USE_ITEM8",
2391 	"EV_USE_ITEM9",
2392 	"EV_USE_ITEM10",
2393 	"EV_USE_ITEM11",
2394 	"EV_USE_ITEM12",
2395 	"EV_USE_ITEM13",
2396 	"EV_USE_ITEM14",
2397 	"EV_USE_ITEM15",
2398 
2399 	"EV_ITEMUSEFAIL",
2400 
2401 	"EV_ITEM_RESPAWN",
2402 	"EV_ITEM_POP",
2403 	"EV_PLAYER_TELEPORT_IN",
2404 	"EV_PLAYER_TELEPORT_OUT",
2405 
2406 	"EV_GRENADE_BOUNCE",		// eventParm will be the soundindex
2407 	"EV_MISSILE_STICK",
2408 
2409 	"EV_PLAY_EFFECT",
2410 	"EV_PLAY_EFFECT_ID", //finally gave in and added it..
2411 	"EV_PLAY_PORTAL_EFFECT_ID",
2412 
2413 	"EV_PLAYDOORSOUND",
2414 	"EV_PLAYDOORLOOPSOUND",
2415 	"EV_BMODEL_SOUND",
2416 
2417 	"EV_MUTE_SOUND",
2418 	"EV_VOICECMD_SOUND",
2419 	"EV_GENERAL_SOUND",
2420 	"EV_GLOBAL_SOUND",		// no attenuation
2421 	"EV_GLOBAL_TEAM_SOUND",
2422 	"EV_ENTITY_SOUND",
2423 
2424 	"EV_PLAY_ROFF",
2425 
2426 	"EV_GLASS_SHATTER",
2427 	"EV_DEBRIS",
2428 	"EV_MISC_MODEL_EXP",
2429 
2430 	"EV_CONC_ALT_IMPACT",
2431 
2432 	"EV_MISSILE_HIT",
2433 	"EV_MISSILE_MISS",
2434 	"EV_MISSILE_MISS_METAL",
2435 	"EV_BULLET",				// otherEntity is the shooter
2436 
2437 	"EV_PAIN",
2438 	"EV_DEATH1",
2439 	"EV_DEATH2",
2440 	"EV_DEATH3",
2441 	"EV_OBITUARY",
2442 
2443 	#ifdef BASE_COMPAT
2444 		"EV_POWERUP_QUAD",
2445 		"EV_POWERUP_BATTLESUIT",
2446 	#endif // BASE_COMPAT
2447 	//"EV_POWERUP_REGEN",
2448 
2449 	"EV_FORCE_DRAINED",
2450 
2451 	"EV_GIB_PLAYER",			// gib a previously living player
2452 	"EV_SCOREPLUM",			// score plum
2453 
2454 	"EV_CTFMESSAGE",
2455 
2456 	"EV_BODYFADE",
2457 
2458 	"EV_SIEGE_ROUNDOVER",
2459 	"EV_SIEGE_OBJECTIVECOMPLETE",
2460 
2461 	"EV_DESTROY_GHOUL2_INSTANCE",
2462 
2463 	"EV_DESTROY_WEAPON_MODEL",
2464 
2465 	"EV_GIVE_NEW_RANK",
2466 	"EV_SET_FREE_SABER",
2467 	"EV_SET_FORCE_DISABLE",
2468 
2469 	"EV_WEAPON_CHARGE",
2470 	"EV_WEAPON_CHARGE_ALT",
2471 
2472 	"EV_SHIELD_HIT",
2473 
2474 	"EV_DEBUG_LINE",
2475 	"EV_TESTLINE",
2476 	"EV_STOPLOOPINGSOUND",
2477 	"EV_STARTLOOPINGSOUND",
2478 	"EV_TAUNT",
2479 //fixme, added a bunch that aren't here!
2480 };
2481 
2482 /*
2483 ===============
2484 BG_AddPredictableEventToPlayerstate
2485 
2486 Handles the sequence numbers
2487 ===============
2488 */
2489 
BG_AddPredictableEventToPlayerstate(int newEvent,int eventParm,playerState_t * ps)2490 void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) {
2491 
2492 #ifdef _DEBUG
2493 	{
2494 		static vmCvar_t		showEvents;
2495 		static qboolean		isRegistered = qfalse;
2496 
2497 		if (!isRegistered)
2498 		{
2499 			trap->Cvar_Register(&showEvents, "showevents", "0", 0);
2500 			isRegistered = qtrue;
2501 		}
2502 
2503 		if ( showEvents.integer != 0 ) {
2504 #ifdef _GAME
2505 			Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm);
2506 #else
2507 			Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm);
2508 #endif
2509 		}
2510 	}
2511 #endif
2512 	ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent;
2513 	ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm;
2514 	ps->eventSequence++;
2515 }
2516 
2517 /*
2518 ========================
2519 BG_TouchJumpPad
2520 ========================
2521 */
BG_TouchJumpPad(playerState_t * ps,entityState_t * jumppad)2522 void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) {
2523 	// spectators don't use jump pads
2524 	if ( ps->pm_type != PM_NORMAL && ps->pm_type != PM_JETPACK && ps->pm_type != PM_FLOAT ) {
2525 		return;
2526 	}
2527 
2528 	// if we didn't hit this same jumppad the previous frame
2529 	// then don't play the event sound again if we are in a fat trigger
2530 	/*
2531 	if ( ps->jumppad_ent != jumppad->number ) {
2532 		vec3_t angles;
2533 		float p;
2534 
2535 		vectoangles( jumppad->origin2, angles);
2536 		p = fabs( AngleNormalize180( angles[PITCH] ) );
2537 		effectNum =  (p<45) ? 0 : 1;
2538 	}
2539 	*/
2540 
2541 	// remember hitting this jumppad this frame
2542 	ps->jumppad_ent = jumppad->number;
2543 	ps->jumppad_frame = ps->pmove_framecount;
2544 	// give the player the velocity from the jumppad
2545 	VectorCopy( jumppad->origin2, ps->velocity );
2546 	// fix: no more force draining after bouncing the jumppad
2547 	ps->fd.forcePowersActive &= ~(1<<FP_LEVITATION);
2548 }
2549 
2550 /*
2551 =================
2552 BG_EmplacedView
2553 
2554 Shared code for emplaced angle gun constriction
2555 =================
2556 */
BG_EmplacedView(vec3_t baseAngles,vec3_t angles,float * newYaw,float constraint)2557 int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint)
2558 {
2559 	float dif = AngleSubtract(baseAngles[YAW], angles[YAW]);
2560 
2561 	if (dif > constraint ||
2562 		dif < -constraint)
2563 	{
2564 		float amt;
2565 
2566 		if (dif > constraint)
2567 		{
2568 			amt = (dif-constraint);
2569 			dif = constraint;
2570 		}
2571 		else if (dif < -constraint)
2572 		{
2573 			amt = (dif+constraint);
2574 			dif = -constraint;
2575 		}
2576 		else
2577 		{
2578 			amt = 0.0f;
2579 		}
2580 
2581 		*newYaw = AngleSubtract(angles[YAW], -dif);
2582 
2583 		if (amt > 1.0f || amt < -1.0f)
2584 		{ //significant, force the view
2585 			return 2;
2586 		}
2587 		else
2588 		{ //just a little out of range
2589 			return 1;
2590 		}
2591 	}
2592 
2593 	return 0;
2594 }
2595 
2596 //To see if the client is trying to use one of the included skins not meant for MP.
2597 //I don't much care for hardcoded strings, but this seems the best way to go.
BG_IsValidCharacterModel(const char * modelName,const char * skinName)2598 qboolean BG_IsValidCharacterModel(const char *modelName, const char *skinName)
2599 {
2600 	if (!Q_stricmp(skinName, "menu"))
2601 	{
2602 		return qfalse;
2603 	}
2604 	else if (!Q_stricmp(modelName, "kyle"))
2605 	{
2606 		if (!Q_stricmp(skinName, "fpls"))
2607 		{
2608 			return qfalse;
2609 		}
2610 		else if (!Q_stricmp(skinName, "fpls2"))
2611 		{
2612 			return qfalse;
2613 		}
2614 		else if (!Q_stricmp(skinName, "fpls3"))
2615 		{
2616 			return qfalse;
2617 		}
2618 	}
2619 	return qtrue;
2620 }
2621 
BG_ValidateSkinForTeam(const char * modelName,char * skinName,int team,float * colors)2622 qboolean BG_ValidateSkinForTeam( const char *modelName, char *skinName, int team, float *colors )
2623 {
2624 	if (strlen (modelName) > 5 && Q_stricmpn (modelName, "jedi_", 5) == 0)
2625 	{ //argh, it's a custom player skin!
2626 		if (team == TEAM_RED && colors)
2627 		{
2628 			colors[0] = 1.0f;
2629 			colors[1] = 0.0f;
2630 			colors[2] = 0.0f;
2631 		}
2632 		else if (team == TEAM_BLUE && colors)
2633 		{
2634 			colors[0] = 0.0f;
2635 			colors[1] = 0.0f;
2636 			colors[2] = 1.0f;
2637 		}
2638 		return qtrue;
2639 	}
2640 
2641 	if (team == TEAM_RED)
2642 	{
2643 		if ( Q_stricmp( "red", skinName ) != 0 )
2644 		{//not "red"
2645 			if ( Q_stricmp( "blue", skinName ) == 0
2646 				|| Q_stricmp( "default", skinName ) == 0
2647 				|| strchr(skinName, '|')//a multi-skin playerModel
2648 				|| !BG_IsValidCharacterModel(modelName, skinName) )
2649 			{
2650 				Q_strncpyz(skinName, "red", MAX_QPATH);
2651 				return qfalse;
2652 			}
2653 			else
2654 			{//need to set it to red
2655 				int len = strlen( skinName );
2656 				if ( len < 3 )
2657 				{//too short to be "red"
2658 					Q_strcat(skinName, MAX_QPATH, "_red");
2659 				}
2660 				else
2661 				{
2662 					char	*start = &skinName[len-3];
2663 					if ( Q_strncmp( "red", start, 3 ) != 0 )
2664 					{//doesn't already end in "red"
2665 						if ( len+4 >= MAX_QPATH )
2666 						{//too big to append "_red"
2667 							Q_strncpyz(skinName, "red", MAX_QPATH);
2668 							return qfalse;
2669 						}
2670 						else
2671 						{
2672 							Q_strcat(skinName, MAX_QPATH, "_red");
2673 						}
2674 					}
2675 				}
2676 				//if file does not exist, set to "red"
2677 				if ( !BG_FileExists( va( "models/players/%s/model_%s.skin", modelName, skinName ) ) )
2678 				{
2679 					Q_strncpyz(skinName, "red", MAX_QPATH);
2680 				}
2681 				return qfalse;
2682 			}
2683 		}
2684 
2685 	}
2686 	else if (team == TEAM_BLUE)
2687 	{
2688 		if ( Q_stricmp( "blue", skinName ) != 0 )
2689 		{
2690 			if ( Q_stricmp( "red", skinName ) == 0
2691 				|| Q_stricmp( "default", skinName ) == 0
2692 				|| strchr(skinName, '|')//a multi-skin playerModel
2693 				|| !BG_IsValidCharacterModel(modelName, skinName) )
2694 			{
2695 				Q_strncpyz(skinName, "blue", MAX_QPATH);
2696 				return qfalse;
2697 			}
2698 			else
2699 			{//need to set it to blue
2700 				int len = strlen( skinName );
2701 				if ( len < 4 )
2702 				{//too short to be "blue"
2703 					Q_strcat(skinName, MAX_QPATH, "_blue");
2704 				}
2705 				else
2706 				{
2707 					char	*start = &skinName[len-4];
2708 					if ( Q_strncmp( "blue", start, 4 ) != 0 )
2709 					{//doesn't already end in "blue"
2710 						if ( len+5 >= MAX_QPATH )
2711 						{//too big to append "_blue"
2712 							Q_strncpyz(skinName, "blue", MAX_QPATH);
2713 							return qfalse;
2714 						}
2715 						else
2716 						{
2717 							Q_strcat(skinName, MAX_QPATH, "_blue");
2718 						}
2719 					}
2720 				}
2721 				//if file does not exist, set to "blue"
2722 				if ( !BG_FileExists( va( "models/players/%s/model_%s.skin", modelName, skinName ) ) )
2723 				{
2724 					Q_strncpyz(skinName, "blue", MAX_QPATH);
2725 				}
2726 				return qfalse;
2727 			}
2728 		}
2729 	}
2730 	return qtrue;
2731 }
2732 
2733 /*
2734 ========================
2735 BG_PlayerStateToEntityState
2736 
2737 This is done after each set of usercmd_t on the server,
2738 and after local prediction on the client
2739 ========================
2740 */
BG_PlayerStateToEntityState(playerState_t * ps,entityState_t * s,qboolean snap)2741 void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ) {
2742 	int		i;
2743 
2744 	if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) {
2745 		s->eType = ET_INVISIBLE;
2746 	} else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) {
2747 		s->eType = ET_INVISIBLE;
2748 	} else {
2749 		s->eType = ET_PLAYER;
2750 	}
2751 
2752 	s->number = ps->clientNum;
2753 
2754 	s->pos.trType = TR_INTERPOLATE;
2755 	VectorCopy( ps->origin, s->pos.trBase );
2756 	if ( snap ) {
2757 		SnapVector( s->pos.trBase );
2758 	}
2759 	// set the trDelta for flag direction
2760 	VectorCopy( ps->velocity, s->pos.trDelta );
2761 
2762 	s->apos.trType = TR_INTERPOLATE;
2763 	VectorCopy( ps->viewangles, s->apos.trBase );
2764 	if ( snap ) {
2765 		SnapVector( s->apos.trBase );
2766 	}
2767 
2768 	s->trickedentindex = ps->fd.forceMindtrickTargetIndex;
2769 	s->trickedentindex2 = ps->fd.forceMindtrickTargetIndex2;
2770 	s->trickedentindex3 = ps->fd.forceMindtrickTargetIndex3;
2771 	s->trickedentindex4 = ps->fd.forceMindtrickTargetIndex4;
2772 
2773 	s->forceFrame = ps->saberLockFrame;
2774 
2775 	s->emplacedOwner = ps->electrifyTime;
2776 
2777 	s->speed = ps->speed;
2778 
2779 	s->genericenemyindex = ps->genericEnemyIndex;
2780 
2781 	s->activeForcePass = ps->activeForcePass;
2782 
2783 	s->angles2[YAW] = ps->movementDir;
2784 	s->legsAnim = ps->legsAnim;
2785 	s->torsoAnim = ps->torsoAnim;
2786 
2787 	s->legsFlip = ps->legsFlip;
2788 	s->torsoFlip = ps->torsoFlip;
2789 
2790 	s->clientNum = ps->clientNum;		// ET_PLAYER looks here instead of at number
2791 										// so corpses can also reference the proper config
2792 	s->eFlags = ps->eFlags;
2793 	s->eFlags2 = ps->eFlags2;
2794 
2795 	s->saberInFlight = ps->saberInFlight;
2796 	s->saberEntityNum = ps->saberEntityNum;
2797 	s->saberMove = ps->saberMove;
2798 	s->forcePowersActive = ps->fd.forcePowersActive;
2799 
2800 	if (ps->duelInProgress)
2801 	{
2802 		s->bolt1 = 1;
2803 	}
2804 	else
2805 	{
2806 		s->bolt1 = 0;
2807 	}
2808 
2809 	s->otherEntityNum2 = ps->emplacedIndex;
2810 
2811 	s->saberHolstered = ps->saberHolstered;
2812 
2813 	if (ps->genericEnemyIndex != -1)
2814 	{
2815 		s->eFlags |= EF_SEEKERDRONE;
2816 	}
2817 
2818 	if ( ps->stats[STAT_HEALTH] <= 0 ) {
2819 		s->eFlags |= EF_DEAD;
2820 	} else {
2821 		s->eFlags &= ~EF_DEAD;
2822 	}
2823 
2824 	if ( ps->externalEvent ) {
2825 		s->event = ps->externalEvent;
2826 		s->eventParm = ps->externalEventParm;
2827 	} else if ( ps->entityEventSequence < ps->eventSequence ) {
2828 		int		seq;
2829 
2830 		if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) {
2831 			ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS;
2832 		}
2833 		seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
2834 		s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
2835 		s->eventParm = ps->eventParms[ seq ];
2836 		ps->entityEventSequence++;
2837 	}
2838 
2839 
2840 	s->weapon = ps->weapon;
2841 	s->groundEntityNum = ps->groundEntityNum;
2842 
2843 	s->powerups = 0;
2844 	for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
2845 		if ( ps->powerups[ i ] ) {
2846 			s->powerups |= 1 << i;
2847 		}
2848 	}
2849 
2850 	s->loopSound = ps->loopSound;
2851 	s->generic1 = ps->generic1;
2852 
2853 	//NOT INCLUDED IN ENTITYSTATETOPLAYERSTATE:
2854 	s->modelindex2 = ps->weaponstate;
2855 	s->constantLight = ps->weaponChargeTime;
2856 
2857 	VectorCopy(ps->lastHitLoc, s->origin2);
2858 
2859 	s->isJediMaster = ps->isJediMaster;
2860 
2861 	s->time2 = ps->holocronBits;
2862 
2863 	s->fireflag = ps->fd.saberAnimLevel;
2864 
2865 	s->heldByClient = ps->heldByClient;
2866 	s->ragAttach = ps->ragAttach;
2867 
2868 	s->iModelScale = ps->iModelScale;
2869 
2870 	s->brokenLimbs = ps->brokenLimbs;
2871 
2872 	s->hasLookTarget = ps->hasLookTarget;
2873 	s->lookTarget = ps->lookTarget;
2874 
2875 	s->customRGBA[0] = ps->customRGBA[0];
2876 	s->customRGBA[1] = ps->customRGBA[1];
2877 	s->customRGBA[2] = ps->customRGBA[2];
2878 	s->customRGBA[3] = ps->customRGBA[3];
2879 
2880 	s->m_iVehicleNum = ps->m_iVehicleNum;
2881 }
2882 
2883 /*
2884 ========================
2885 BG_PlayerStateToEntityStateExtraPolate
2886 
2887 This is done after each set of usercmd_t on the server,
2888 and after local prediction on the client
2889 ========================
2890 */
BG_PlayerStateToEntityStateExtraPolate(playerState_t * ps,entityState_t * s,int time,qboolean snap)2891 void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) {
2892 	int		i;
2893 
2894 	if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) {
2895 		s->eType = ET_INVISIBLE;
2896 	} else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) {
2897 		s->eType = ET_INVISIBLE;
2898 	} else {
2899 		s->eType = ET_PLAYER;
2900 	}
2901 
2902 	s->number = ps->clientNum;
2903 
2904 	s->pos.trType = TR_LINEAR_STOP;
2905 	VectorCopy( ps->origin, s->pos.trBase );
2906 	if ( snap ) {
2907 		SnapVector( s->pos.trBase );
2908 	}
2909 	// set the trDelta for flag direction and linear prediction
2910 	VectorCopy( ps->velocity, s->pos.trDelta );
2911 	// set the time for linear prediction
2912 	s->pos.trTime = time;
2913 	// set maximum extra polation time
2914 	s->pos.trDuration = 50; // 1000 / sv_fps (default = 20)
2915 
2916 	s->apos.trType = TR_INTERPOLATE;
2917 	VectorCopy( ps->viewangles, s->apos.trBase );
2918 	if ( snap ) {
2919 		SnapVector( s->apos.trBase );
2920 	}
2921 
2922 	s->trickedentindex = ps->fd.forceMindtrickTargetIndex;
2923 	s->trickedentindex2 = ps->fd.forceMindtrickTargetIndex2;
2924 	s->trickedentindex3 = ps->fd.forceMindtrickTargetIndex3;
2925 	s->trickedentindex4 = ps->fd.forceMindtrickTargetIndex4;
2926 
2927 	s->forceFrame = ps->saberLockFrame;
2928 
2929 	s->emplacedOwner = ps->electrifyTime;
2930 
2931 	s->speed = ps->speed;
2932 
2933 	s->genericenemyindex = ps->genericEnemyIndex;
2934 
2935 	s->activeForcePass = ps->activeForcePass;
2936 
2937 	s->angles2[YAW] = ps->movementDir;
2938 	s->legsAnim = ps->legsAnim;
2939 	s->torsoAnim = ps->torsoAnim;
2940 
2941 	s->legsFlip = ps->legsFlip;
2942 	s->torsoFlip = ps->torsoFlip;
2943 
2944 	s->clientNum = ps->clientNum;		// ET_PLAYER looks here instead of at number
2945 										// so corpses can also reference the proper config
2946 	s->eFlags = ps->eFlags;
2947 	s->eFlags2 = ps->eFlags2;
2948 
2949 	s->saberInFlight = ps->saberInFlight;
2950 	s->saberEntityNum = ps->saberEntityNum;
2951 	s->saberMove = ps->saberMove;
2952 	s->forcePowersActive = ps->fd.forcePowersActive;
2953 
2954 	if (ps->duelInProgress)
2955 	{
2956 		s->bolt1 = 1;
2957 	}
2958 	else
2959 	{
2960 		s->bolt1 = 0;
2961 	}
2962 
2963 	s->otherEntityNum2 = ps->emplacedIndex;
2964 
2965 	s->saberHolstered = ps->saberHolstered;
2966 
2967 	if (ps->genericEnemyIndex != -1)
2968 	{
2969 		s->eFlags |= EF_SEEKERDRONE;
2970 	}
2971 
2972 	if ( ps->stats[STAT_HEALTH] <= 0 ) {
2973 		s->eFlags |= EF_DEAD;
2974 	} else {
2975 		s->eFlags &= ~EF_DEAD;
2976 	}
2977 
2978 	if ( ps->externalEvent ) {
2979 		s->event = ps->externalEvent;
2980 		s->eventParm = ps->externalEventParm;
2981 	} else if ( ps->entityEventSequence < ps->eventSequence ) {
2982 		int		seq;
2983 
2984 		if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) {
2985 			ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS;
2986 		}
2987 		seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
2988 		s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
2989 		s->eventParm = ps->eventParms[ seq ];
2990 		ps->entityEventSequence++;
2991 	}
2992 	s->weapon = ps->weapon;
2993 	s->groundEntityNum = ps->groundEntityNum;
2994 
2995 	s->powerups = 0;
2996 	for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
2997 		if ( ps->powerups[ i ] ) {
2998 			s->powerups |= 1 << i;
2999 		}
3000 	}
3001 
3002 	s->loopSound = ps->loopSound;
3003 	s->generic1 = ps->generic1;
3004 
3005 	//NOT INCLUDED IN ENTITYSTATETOPLAYERSTATE:
3006 	s->modelindex2 = ps->weaponstate;
3007 	s->constantLight = ps->weaponChargeTime;
3008 
3009 	VectorCopy(ps->lastHitLoc, s->origin2);
3010 
3011 	s->isJediMaster = ps->isJediMaster;
3012 
3013 	s->time2 = ps->holocronBits;
3014 
3015 	s->fireflag = ps->fd.saberAnimLevel;
3016 
3017 	s->heldByClient = ps->heldByClient;
3018 	s->ragAttach = ps->ragAttach;
3019 
3020 	s->iModelScale = ps->iModelScale;
3021 
3022 	s->brokenLimbs = ps->brokenLimbs;
3023 
3024 	s->hasLookTarget = ps->hasLookTarget;
3025 	s->lookTarget = ps->lookTarget;
3026 
3027 	s->customRGBA[0] = ps->customRGBA[0];
3028 	s->customRGBA[1] = ps->customRGBA[1];
3029 	s->customRGBA[2] = ps->customRGBA[2];
3030 	s->customRGBA[3] = ps->customRGBA[3];
3031 
3032 	s->m_iVehicleNum = ps->m_iVehicleNum;
3033 }
3034 
3035 /*
3036 =============================================================================
3037 
3038 PLAYER ANGLES
3039 
3040 =============================================================================
3041 */
3042 
BG_ModelCache(const char * modelName,const char * skinName)3043 int BG_ModelCache(const char *modelName, const char *skinName)
3044 {
3045 	#ifdef _GAME
3046 		void *g2 = NULL;
3047 
3048 		if ( VALIDSTRING( skinName ) )
3049 			trap->R_RegisterSkin( skinName );
3050 
3051 		//I could hook up a precache ghoul2 function, but oh well, this works
3052 		trap->G2API_InitGhoul2Model( &g2, modelName, 0, 0, 0, 0, 0 );
3053 		//now get rid of it
3054 		if ( g2 )
3055 			trap->G2API_CleanGhoul2Models( &g2 );
3056 
3057 		return 0;
3058 	#else // !_GAME
3059 		if ( VALIDSTRING( skinName ) )
3060 		{
3061 			#ifdef _CGAME
3062 				trap->R_RegisterSkin( skinName );
3063 			#else // !_CGAME
3064 				trap->R_RegisterSkin( skinName );
3065 			#endif // _CGAME
3066 		}
3067 		#ifdef _CGAME
3068 			return trap->R_RegisterModel( modelName );
3069 		#else // !_CGAME
3070 			return trap->R_RegisterModel( modelName );
3071 		#endif // _CGAME
3072 	#endif // _GAME
3073 }
3074 
3075 #if defined(_GAME)
3076 	#define MAX_POOL_SIZE	3000000 //1024000
3077 #elif defined(_CGAME) //don't need as much for cgame stuff. 2mb will be fine.
3078 	#define MAX_POOL_SIZE	2048000
3079 #elif defined(UI_BUILD) //And for the ui the only thing we'll be using this for anyway is allocating anim data for g2 menu models
3080 	#define MAX_POOL_SIZE	512000
3081 #endif
3082 
3083 //I am using this for all the stuff like NPC client structures on server/client and
3084 //non-humanoid animations as well until/if I can get dynamic memory working properly
3085 //with casted datatypes, which is why it is so large.
3086 
3087 
3088 static char		bg_pool[MAX_POOL_SIZE];
3089 static int		bg_poolSize = 0;
3090 static int		bg_poolTail = MAX_POOL_SIZE;
3091 
BG_Alloc(int size)3092 void *BG_Alloc ( int size )
3093 {
3094 	bg_poolSize = ((bg_poolSize + 0x00000003) & 0xfffffffc);
3095 
3096 	if (bg_poolSize + size > bg_poolTail)
3097 	{
3098 		Com_Error( ERR_DROP, "BG_Alloc: buffer exceeded tail (%d > %d)", bg_poolSize + size, bg_poolTail);
3099 		return 0;
3100 	}
3101 
3102 	bg_poolSize += size;
3103 
3104 	return &bg_pool[bg_poolSize-size];
3105 }
3106 
BG_AllocUnaligned(int size)3107 void *BG_AllocUnaligned ( int size )
3108 {
3109 	if (bg_poolSize + size > bg_poolTail)
3110 	{
3111 		Com_Error( ERR_DROP, "BG_AllocUnaligned: buffer exceeded tail (%d > %d)", bg_poolSize + size, bg_poolTail);
3112 		return 0;
3113 	}
3114 
3115 	bg_poolSize += size;
3116 
3117 	return &bg_pool[bg_poolSize-size];
3118 }
3119 
BG_TempAlloc(int size)3120 void *BG_TempAlloc( int size )
3121 {
3122 	size = ((size + 0x00000003) & 0xfffffffc);
3123 
3124 	if (bg_poolTail - size < bg_poolSize)
3125 	{
3126 		Com_Error( ERR_DROP, "BG_TempAlloc: buffer exceeded head (%d > %d)", bg_poolTail - size, bg_poolSize);
3127 		return 0;
3128 	}
3129 
3130 	bg_poolTail -= size;
3131 
3132 	return &bg_pool[bg_poolTail];
3133 }
3134 
BG_TempFree(int size)3135 void BG_TempFree( int size )
3136 {
3137 	size = ((size + 0x00000003) & 0xfffffffc);
3138 
3139 	if (bg_poolTail+size > MAX_POOL_SIZE)
3140 	{
3141 		Com_Error( ERR_DROP, "BG_TempFree: tail greater than size (%d > %d)", bg_poolTail+size, MAX_POOL_SIZE );
3142 	}
3143 
3144 	bg_poolTail += size;
3145 }
3146 
BG_StringAlloc(const char * source)3147 char *BG_StringAlloc ( const char *source )
3148 {
3149 	char *dest = (char*)BG_Alloc( strlen ( source ) + 1 );
3150 	strcpy( dest, source );
3151 	return dest;
3152 }
3153 
BG_OutOfMemory(void)3154 qboolean BG_OutOfMemory ( void )
3155 {
3156 	return bg_poolSize >= MAX_POOL_SIZE;
3157 }
3158 
3159 const char *gametypeStringShort[GT_MAX_GAME_TYPE] = {
3160 	"FFA",
3161 	"HOLO",
3162 	"JM",
3163 	"1v1",
3164 	"2v1",
3165 	"SP",
3166 	"TDM",
3167 	"SAGA",
3168 	"CTF",
3169 	"CTY"
3170 };
3171 
BG_GetGametypeString(int gametype)3172 const char *BG_GetGametypeString( int gametype )
3173 {
3174 	switch ( gametype )
3175 	{
3176 	case GT_FFA:
3177 		return "Free For All";
3178 	case GT_HOLOCRON:
3179 		return "Holocron";
3180 	case GT_JEDIMASTER:
3181 		return "Jedi Master";
3182 	case GT_DUEL:
3183 		return "Duel";
3184 	case GT_POWERDUEL:
3185 		return "Power Duel";
3186 	case GT_SINGLE_PLAYER:
3187 		return "Cooperative";
3188 
3189 	case GT_TEAM:
3190 		return "Team Deathmatch";
3191 	case GT_SIEGE:
3192 		return "Siege";
3193 	case GT_CTF:
3194 		return "Capture The Flag";
3195 	case GT_CTY:
3196 		return "Capture The Ysalimiri";
3197 
3198 	default:
3199 		return "Unknown Gametype";
3200 	}
3201 }
3202 
BG_GetGametypeForString(const char * gametype)3203 int BG_GetGametypeForString( const char *gametype )
3204 {
3205 		 if ( !Q_stricmp( gametype, "ffa" )
3206 			||!Q_stricmp( gametype, "dm" ) )			return GT_FFA;
3207 	else if ( !Q_stricmp( gametype, "holocron" ) )		return GT_HOLOCRON;
3208 	else if ( !Q_stricmp( gametype, "jm" ) )			return GT_JEDIMASTER;
3209 	else if ( !Q_stricmp( gametype, "duel" ) )			return GT_DUEL;
3210 	else if ( !Q_stricmp( gametype, "powerduel" ) )		return GT_POWERDUEL;
3211 	else if ( !Q_stricmp( gametype, "sp" )
3212 			||!Q_stricmp( gametype, "coop" ) )			return GT_SINGLE_PLAYER;
3213 	else if ( !Q_stricmp( gametype, "tdm" )
3214 			||!Q_stricmp( gametype, "tffa" )
3215 			||!Q_stricmp( gametype, "team" ) )			return GT_TEAM;
3216 	else if ( !Q_stricmp( gametype, "siege" ) )			return GT_SIEGE;
3217 	else if ( !Q_stricmp( gametype, "ctf" ) )			return GT_CTF;
3218 	else if ( !Q_stricmp( gametype, "cty" ) )			return GT_CTY;
3219 	else												return -1;
3220 }
3221