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