PageRenderTime 108ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 2ms

/codemp/cgame/cg_draw.c

https://github.com/Stoiss/jaMME
C | 8534 lines | 6389 code | 1213 blank | 932 comment | 1635 complexity | ae3b1e5de7cae0e92a46aa7392141981 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_draw.c -- draw all of the graphical elements during
  4. // active (after loading) gameplay
  5. #include "cg_local.h"
  6. #include "game/bg_saga.h"
  7. #include "ui/ui_shared.h"
  8. #include "ui/ui_public.h"
  9. extern float CG_RadiusForCent( centity_t *cent );
  10. qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y);
  11. qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle );
  12. static void CG_DrawSiegeTimer(int timeRemaining, qboolean isMyTeam);
  13. static void CG_DrawSiegeDeathTimer( int timeRemaining );
  14. // nmckenzie: DUEL_HEALTH
  15. void CG_DrawDuelistHealth ( float x, float y, float w, float h, int duelist );
  16. // used for scoreboard
  17. extern displayContextDef_t cgDC;
  18. menuDef_t *menuScoreboard = NULL;
  19. int sortedTeamPlayers[TEAM_MAXOVERLAY];
  20. int numSortedTeamPlayers;
  21. float lastvalidlockdif;
  22. extern float zoomFov; //this has to be global client-side
  23. char systemChat[256];
  24. char teamChat1[256];
  25. char teamChat2[256];
  26. // The time at which you died and the time it will take for you to rejoin game.
  27. int cg_siegeDeathTime = 0;
  28. #define MAX_HUD_TICS 4
  29. const char *armorTicName[MAX_HUD_TICS] =
  30. {
  31. "armor_tic1",
  32. "armor_tic2",
  33. "armor_tic3",
  34. "armor_tic4",
  35. };
  36. const char *healthTicName[MAX_HUD_TICS] =
  37. {
  38. "health_tic1",
  39. "health_tic2",
  40. "health_tic3",
  41. "health_tic4",
  42. };
  43. const char *forceTicName[MAX_HUD_TICS] =
  44. {
  45. "force_tic1",
  46. "force_tic2",
  47. "force_tic3",
  48. "force_tic4",
  49. };
  50. const char *ammoTicName[MAX_HUD_TICS] =
  51. {
  52. "ammo_tic1",
  53. "ammo_tic2",
  54. "ammo_tic3",
  55. "ammo_tic4",
  56. };
  57. char *showPowersName[] =
  58. {
  59. "HEAL2",//FP_HEAL
  60. "JUMP2",//FP_LEVITATION
  61. "SPEED2",//FP_SPEED
  62. "PUSH2",//FP_PUSH
  63. "PULL2",//FP_PULL
  64. "MINDTRICK2",//FP_TELEPTAHY
  65. "GRIP2",//FP_GRIP
  66. "LIGHTNING2",//FP_LIGHTNING
  67. "DARK_RAGE2",//FP_RAGE
  68. "PROTECT2",//FP_PROTECT
  69. "ABSORB2",//FP_ABSORB
  70. "TEAM_HEAL2",//FP_TEAM_HEAL
  71. "TEAM_REPLENISH2",//FP_TEAM_FORCE
  72. "DRAIN2",//FP_DRAIN
  73. "SEEING2",//FP_SEE
  74. "SABER_OFFENSE2",//FP_SABER_OFFENSE
  75. "SABER_DEFENSE2",//FP_SABER_DEFENSE
  76. "SABER_THROW2",//FP_SABERTHROW
  77. NULL
  78. };
  79. //Called from UI shared code. For now we'll just redirect to the normal anim load function.
  80. int UI_ParseAnimationFile(const char *filename, animation_t *animset, qboolean isHumanoid)
  81. {
  82. return BG_ParseAnimationFile(filename, animset, isHumanoid);
  83. }
  84. int MenuFontToHandle(int iMenuFont)
  85. {
  86. switch (iMenuFont)
  87. {
  88. case FONT_SMALL: return cgDC.Assets.qhSmallFont;
  89. case FONT_SMALL2: return cgDC.Assets.qhSmall2Font;
  90. case FONT_MEDIUM: return cgDC.Assets.qhMediumFont;
  91. case FONT_LARGE: return cgDC.Assets.qhMediumFont;//cgDC.Assets.qhBigFont;
  92. //fixme? Big fonr isn't registered...?
  93. }
  94. return cgDC.Assets.qhMediumFont;
  95. }
  96. int CG_Text_Width(const char *text, float scale, int iMenuFont)
  97. {
  98. int iFontIndex = MenuFontToHandle(iMenuFont);
  99. return trap_R_Font_StrLenPixels(text, iFontIndex, scale);
  100. }
  101. int CG_Text_Height(const char *text, float scale, int iMenuFont)
  102. {
  103. int iFontIndex = MenuFontToHandle(iMenuFont);
  104. return trap_R_Font_HeightPixels(iFontIndex, scale);
  105. }
  106. #include "qcommon/qfiles.h" // for STYLE_BLINK etc
  107. void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style, int iMenuFont)
  108. {
  109. int iStyleOR = 0;
  110. int iFontIndex = MenuFontToHandle(iMenuFont);
  111. switch (style)
  112. {
  113. case ITEM_TEXTSTYLE_NORMAL: iStyleOR = 0;break; // JK2 normal text
  114. case ITEM_TEXTSTYLE_BLINK: iStyleOR = STYLE_BLINK;break; // JK2 fast blinking
  115. case ITEM_TEXTSTYLE_PULSE: iStyleOR = STYLE_BLINK;break; // JK2 slow pulsing
  116. case ITEM_TEXTSTYLE_SHADOWED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
  117. case ITEM_TEXTSTYLE_OUTLINED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
  118. case ITEM_TEXTSTYLE_OUTLINESHADOWED: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
  119. case ITEM_TEXTSTYLE_SHADOWEDMORE: iStyleOR = (int)STYLE_DROPSHADOW;break; // JK2 drop shadow ( need a color for this )
  120. }
  121. trap_R_Font_DrawString( x, // int ox
  122. y, // int oy
  123. text, // const char *text
  124. color, // paletteRGBA_c c
  125. iStyleOR | iFontIndex, // const int iFontHandle
  126. !limit?-1:limit, // iCharLimit (-1 = none)
  127. scale // const float scale = 1.0f
  128. );
  129. }
  130. /*
  131. qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y)
  132. Take any world coord and convert it to a 2D virtual 640x480 screen coord
  133. */
  134. /*
  135. qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y)
  136. {
  137. int xcenter, ycenter;
  138. vec3_t local, transformed;
  139. // xcenter = cg.refdef.width / 2;//gives screen coords adjusted for resolution
  140. // ycenter = cg.refdef.height / 2;//gives screen coords adjusted for resolution
  141. //NOTE: did it this way because most draw functions expect virtual 640x480 coords
  142. // and adjust them for current resolution
  143. xcenter = 640 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
  144. ycenter = 480 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
  145. VectorSubtract (worldCoord, cg.refdef.vieworg, local);
  146. transformed[0] = DotProduct(local,vright);
  147. transformed[1] = DotProduct(local,vup);
  148. transformed[2] = DotProduct(local,vfwd);
  149. // Make sure Z is not negative.
  150. if(transformed[2] < 0.01)
  151. {
  152. return qfalse;
  153. }
  154. // Simple convert to screen coords.
  155. float xzi = xcenter / transformed[2] * (90.0/cg.refdef.fov_x);
  156. float yzi = ycenter / transformed[2] * (90.0/cg.refdef.fov_y);
  157. *x = xcenter + xzi * transformed[0];
  158. *y = ycenter - yzi * transformed[1];
  159. return qtrue;
  160. }
  161. qboolean CG_WorldCoordToScreenCoord( vec3_t worldCoord, int *x, int *y )
  162. {
  163. float xF, yF;
  164. qboolean retVal = CG_WorldCoordToScreenCoordFloat( worldCoord, &xF, &yF );
  165. *x = (int)xF;
  166. *y = (int)yF;
  167. return retVal;
  168. }
  169. */
  170. /*
  171. ================
  172. CG_DrawZoomMask
  173. ================
  174. */
  175. static void CG_DrawZoomMask( void )
  176. {
  177. vec4_t color1;
  178. float level;
  179. static qboolean flip = qtrue;
  180. // int ammo = cg_entities[0].gent->client->ps.ammo[weaponData[cent->currentState.weapon].ammoIndex];
  181. float cx, cy;
  182. // int val[5];
  183. float max, fi;
  184. // Check for Binocular specific zooming since we'll want to render different bits in each case
  185. if ( cg.predictedPlayerState.zoomMode == 2 )
  186. {
  187. int val, i;
  188. float off;
  189. // zoom level
  190. level = (float)(80.0f - cg.predictedPlayerState.zoomFov) / 80.0f;
  191. // ...so we'll clamp it
  192. if ( level < 0.0f )
  193. {
  194. level = 0.0f;
  195. }
  196. else if ( level > 1.0f )
  197. {
  198. level = 1.0f;
  199. }
  200. // Using a magic number to convert the zoom level to scale amount
  201. level *= 162.0f;
  202. // draw blue tinted distortion mask, trying to make it as small as is necessary to fill in the viewable area
  203. trap_R_SetColor( colorTable[CT_WHITE] );
  204. CG_DrawPic( 34, 48, 570, 362, cgs.media.binocularStatic );
  205. // Black out the area behind the numbers
  206. trap_R_SetColor( colorTable[CT_BLACK]);
  207. CG_DrawPic( 212, 367, 200, 40, cgs.media.whiteShader );
  208. // Numbers should be kind of greenish
  209. color1[0] = 0.2f;
  210. color1[1] = 0.4f;
  211. color1[2] = 0.2f;
  212. color1[3] = 0.3f;
  213. trap_R_SetColor( color1 );
  214. // Draw scrolling numbers, use intervals 10 units apart--sorry, this section of code is just kind of hacked
  215. // up with a bunch of magic numbers.....
  216. val = ((int)((cg.refdef.viewangles[YAW] + 180) / 10)) * 10;
  217. off = (cg.refdef.viewangles[YAW] + 180) - val;
  218. for ( i = -10; i < 30; i += 10 )
  219. {
  220. val -= 10;
  221. if ( val < 0 )
  222. {
  223. val += 360;
  224. }
  225. // we only want to draw the very far left one some of the time, if it's too far to the left it will
  226. // poke outside the mask.
  227. if (( off > 3.0f && i == -10 ) || i > -10 )
  228. {
  229. // draw the value, but add 200 just to bump the range up...arbitrary, so change it if you like
  230. CG_DrawNumField( 155 + i * 10 + off * 10, 374, 3, val + 200, 24, 14, NUM_FONT_CHUNKY, qtrue );
  231. CG_DrawPic( 245 + (i-1) * 10 + off * 10, 376, 6, 6, cgs.media.whiteShader );
  232. }
  233. }
  234. CG_DrawPic( 212, 367, 200, 28, cgs.media.binocularOverlay );
  235. color1[0] = sin( cg.time * 0.01 + cg.timeFraction * 0.01 ) * 0.5f + 0.5f;
  236. color1[0] = color1[0] * color1[0];
  237. color1[1] = color1[0];
  238. color1[2] = color1[0];
  239. color1[3] = 1.0f;
  240. trap_R_SetColor( color1 );
  241. CG_DrawPic( 82, 94, 16, 16, cgs.media.binocularCircle );
  242. // Flickery color
  243. color1[0] = 0.7f + crandom() * 0.1f;
  244. color1[1] = 0.8f + crandom() * 0.1f;
  245. color1[2] = 0.7f + crandom() * 0.1f;
  246. color1[3] = 1.0f;
  247. trap_R_SetColor( color1 );
  248. CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, cgs.media.binocularMask );
  249. CG_DrawPic( 4, 282 - level, 16, 16, cgs.media.binocularArrow );
  250. // The top triangle bit randomly flips
  251. if ( flip )
  252. {
  253. CG_DrawPic( 330, 60, -26, -30, cgs.media.binocularTri );
  254. }
  255. else
  256. {
  257. CG_DrawPic( 307, 40, 26, 30, cgs.media.binocularTri );
  258. }
  259. if ( random() > 0.98f && ( cg.time & 1024 ))
  260. {
  261. flip = !flip;
  262. }
  263. }
  264. else if (cg.zoomMode)
  265. {
  266. // disruptor zoom mode
  267. level = (float)(50.0f - zoomFov) / 50.0f;//(float)(80.0f - zoomFov) / 80.0f;
  268. // ...so we'll clamp it
  269. if ( level < 0.0f )
  270. {
  271. level = 0.0f;
  272. }
  273. else if ( level > 1.0f )
  274. {
  275. level = 1.0f;
  276. }
  277. if (!cg.playerPredicted)
  278. level = 0.46f;
  279. // Using a magic number to convert the zoom level to a rotation amount that correlates more or less with the zoom artwork.
  280. level *= 103.0f;
  281. // Draw target mask
  282. trap_R_SetColor( colorTable[CT_WHITE] );
  283. CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, cgs.media.disruptorMask );
  284. // apparently 99.0f is the full zoom level
  285. if ( level >= 99 )
  286. {
  287. // Fully zoomed, so make the rotating insert pulse
  288. color1[0] = 1.0f;
  289. color1[1] = 1.0f;
  290. color1[2] = 1.0f;
  291. color1[3] = 0.7f + sin( cg.time * 0.01 + cg.timeFraction * 0.01 ) * 0.3f;
  292. trap_R_SetColor( color1 );
  293. }
  294. // Draw rotating insert
  295. CG_DrawRotatePic2( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT, -level, cgs.media.disruptorInsert );
  296. // Increase the light levels under the center of the target
  297. // CG_DrawPic( 198, 118, 246, 246, cgs.media.disruptorLight );
  298. // weirdness.....converting ammo to a base five number scale just to be geeky.
  299. /* val[0] = ammo % 5;
  300. val[1] = (ammo / 5) % 5;
  301. val[2] = (ammo / 25) % 5;
  302. val[3] = (ammo / 125) % 5;
  303. val[4] = (ammo / 625) % 5;
  304. color1[0] = 0.2f;
  305. color1[1] = 0.55f + crandom() * 0.1f;
  306. color1[2] = 0.5f + crandom() * 0.1f;
  307. color1[3] = 1.0f;
  308. trap_R_SetColor( color1 );
  309. for ( int t = 0; t < 5; t++ )
  310. {
  311. cx = 320 + sin( (t*10+45)/57.296f ) * 192;
  312. cy = 240 + cos( (t*10+45)/57.296f ) * 192;
  313. CG_DrawRotatePic2( cx, cy, 24, 38, 45 - t * 10, trap_R_RegisterShader( va("gfx/2d/char%d",val[4-t] )));
  314. }
  315. */
  316. //max = ( cg_entities[0].gent->health / 100.0f );
  317. if (cg.playerPredicted) {
  318. if ( (cg.snap->ps.eFlags & EF_DOUBLE_AMMO) )
  319. max = cg.snap->ps.ammo[weaponData[WP_DISRUPTOR].ammoIndex] / ((float)ammoData[weaponData[WP_DISRUPTOR].ammoIndex].max*2.0f);
  320. else
  321. max = cg.snap->ps.ammo[weaponData[WP_DISRUPTOR].ammoIndex] / (float)ammoData[weaponData[WP_DISRUPTOR].ammoIndex].max;
  322. if ( max > 1.0f )
  323. max = 1.0f;
  324. } else {
  325. max = 0.5f; // let it be half
  326. }
  327. color1[0] = (1.0f - max) * 2.0f;
  328. color1[1] = max * 1.5f;
  329. color1[2] = 0.0f;
  330. color1[3] = 1.0f;
  331. // If we are low on health, make us flash
  332. if ( max < 0.15f && ( cg.time & 512 ))
  333. {
  334. VectorClear( color1 );
  335. }
  336. if ( color1[0] > 1.0f )
  337. {
  338. color1[0] = 1.0f;
  339. }
  340. if ( color1[1] > 1.0f )
  341. {
  342. color1[1] = 1.0f;
  343. }
  344. trap_R_SetColor( color1 );
  345. max *= 58.0f;
  346. for (fi = 18.5f; fi <= 18.5f + max; fi+= 3 ) // going from 15 to 45 degrees, with 5 degree increments
  347. {
  348. cx = SCREEN_WIDTH / 2 + sin( (fi+90.0f)/57.296f ) * 190;
  349. cy = SCREEN_HEIGHT / 2 + cos( (fi+90.0f)/57.296f ) * 190;
  350. CG_DrawRotatePic2( cx, cy, 12, 24, 90 - fi, cgs.media.disruptorInsertTick );
  351. }
  352. if ((cg.playerPredicted && cg.predictedPlayerState.weaponstate == WEAPON_CHARGING_ALT)
  353. || (!cg.playerPredicted && cg.charging && cg.chargeTime && cg.time > cg.chargeTime))
  354. {
  355. trap_R_SetColor( colorTable[CT_WHITE] );
  356. // draw the charge level
  357. if (cg.playerPredicted)
  358. max = ((cg.time - cg.predictedPlayerState.weaponChargeTime) + cg.timeFraction) / (50.0f * 30.0f); // bad hardcodedness 50 is disruptor charge unit and 30 is max charge units allowed.
  359. else
  360. max = ((cg.time - cg.chargeTime) + cg.timeFraction) / (50.0f * 30.0f);
  361. if ( max > 1.0f )
  362. {
  363. max = 1.0f;
  364. }
  365. trap_R_DrawStretchPic(257, 435, 134*max, 34, 0, 0, max, 1, cgs.media.disruptorChargeShader);
  366. }
  367. // trap_R_SetColor( colorTable[CT_WHITE] );
  368. // CG_DrawPic( 0, 0, 640, 480, cgs.media.disruptorMask );
  369. }
  370. }
  371. /*
  372. ================
  373. CG_Draw3DModel
  374. ================
  375. */
  376. void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, void *ghoul2, int g2radius, qhandle_t skin, vec3_t origin, vec3_t angles ) {
  377. refdef_t refdef;
  378. refEntity_t ent;
  379. if ( !cg_draw3DIcons.integer || !cg_drawIcons.integer ) {
  380. return;
  381. }
  382. memset( &refdef, 0, sizeof( refdef ) );
  383. memset( &ent, 0, sizeof( ent ) );
  384. AnglesToAxis( angles, ent.axis );
  385. VectorCopy( origin, ent.origin );
  386. ent.hModel = model;
  387. ent.ghoul2 = ghoul2;
  388. ent.radius = g2radius;
  389. ent.customSkin = skin;
  390. ent.renderfx = RF_NOSHADOW; // no stencil shadows
  391. refdef.rdflags = RDF_NOWORLDMODEL;
  392. AxisClear( refdef.viewaxis );
  393. refdef.fov_x = 30;
  394. refdef.fov_y = 30;
  395. refdef.x = x;
  396. refdef.y = y;
  397. refdef.width = w;
  398. refdef.height = h;
  399. refdef.time = cg.time;
  400. trap_R_ClearScene();
  401. trap_R_AddRefEntityToScene( &ent );
  402. trap_R_RenderScene( &refdef );
  403. }
  404. /*
  405. ================
  406. CG_DrawHead
  407. Used for both the status bar and the scoreboard
  408. ================
  409. */
  410. void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles )
  411. {
  412. clientInfo_t *ci;
  413. if (clientNum >= MAX_CLIENTS)
  414. { //npc?
  415. return;
  416. }
  417. ci = &cgs.clientinfo[ clientNum ];
  418. CG_DrawPic( x, y, w, h, ci->modelIcon );
  419. // if they are deferred, draw a cross out
  420. if ( ci->deferred )
  421. {
  422. CG_DrawPic( x, y, w, h, cgs.media.deferShader );
  423. }
  424. }
  425. /*
  426. ================
  427. CG_DrawFlagModel
  428. Used for both the status bar and the scoreboard
  429. ================
  430. */
  431. void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) {
  432. qhandle_t cm;
  433. float len;
  434. vec3_t origin, angles;
  435. vec3_t mins, maxs;
  436. qhandle_t handle;
  437. if ( !force2D && cg_draw3DIcons.integer ) {
  438. //Raz: need to adjust the coords only for 3d models
  439. // 2d icons use virtual screen coords
  440. x *= cgs.screenXScale;
  441. y *= cgs.screenYScale;
  442. w *= cgs.screenXScale;
  443. h *= cgs.screenYScale;
  444. VectorClear( angles );
  445. cm = cgs.media.redFlagModel;
  446. // offset the origin y and z to center the flag
  447. trap_R_ModelBounds( cm, mins, maxs );
  448. origin[2] = -0.5 * ( mins[2] + maxs[2] );
  449. origin[1] = 0.5 * ( mins[1] + maxs[1] );
  450. // calculate distance so the flag nearly fills the box
  451. // assume heads are taller than wide
  452. len = 0.5 * ( maxs[2] - mins[2] );
  453. origin[0] = len / 0.268; // len / tan( fov/2 )
  454. angles[YAW] = 60 * sin((double)((double)cg.time + cg.timeFraction) / 2000.0);;
  455. if( team == TEAM_RED ) {
  456. handle = cgs.media.redFlagModel;
  457. } else if( team == TEAM_BLUE ) {
  458. handle = cgs.media.blueFlagModel;
  459. } else if( team == TEAM_FREE ) {
  460. handle = 0;//cgs.media.neutralFlagModel;
  461. } else {
  462. return;
  463. }
  464. CG_Draw3DModel( x, y, w, h, handle, NULL, 0, 0, origin, angles );
  465. } else if ( cg_drawIcons.integer ) {
  466. gitem_t *item;
  467. if( team == TEAM_RED ) {
  468. item = BG_FindItemForPowerup( PW_REDFLAG );
  469. } else if( team == TEAM_BLUE ) {
  470. item = BG_FindItemForPowerup( PW_BLUEFLAG );
  471. } else if( team == TEAM_FREE ) {
  472. item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
  473. } else {
  474. return;
  475. }
  476. if (item) {
  477. CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon );
  478. }
  479. }
  480. }
  481. /*
  482. ================
  483. CG_DrawHealth
  484. ================
  485. */
  486. void CG_DrawHealth( menuDef_t *menuHUD )
  487. {
  488. vec4_t calcColor;
  489. playerState_t *ps;
  490. int healthAmt;
  491. int i,currValue,inc;
  492. itemDef_t *focusItem;
  493. float percent;
  494. // Can we find the menu?
  495. if (!menuHUD)
  496. {
  497. return;
  498. }
  499. ps = &cg.snap->ps;
  500. // What's the health?
  501. healthAmt = ps->stats[STAT_HEALTH];
  502. if (healthAmt > ps->stats[STAT_MAX_HEALTH])
  503. {
  504. healthAmt = ps->stats[STAT_MAX_HEALTH];
  505. }
  506. inc = (float) ps->stats[STAT_MAX_HEALTH] / MAX_HUD_TICS;
  507. currValue = healthAmt;
  508. // Print the health tics, fading out the one which is partial health
  509. for (i=(MAX_HUD_TICS-1);i>=0;i--)
  510. {
  511. focusItem = Menu_FindItemByName(menuHUD, healthTicName[i]);
  512. if (!focusItem) // This is bad
  513. {
  514. continue;
  515. }
  516. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  517. if (currValue <= 0) // don't show tic
  518. {
  519. break;
  520. }
  521. else if (currValue < inc) // partial tic (alpha it out)
  522. {
  523. percent = (float) currValue / inc;
  524. calcColor[3] *= percent; // Fade it out
  525. }
  526. trap_R_SetColor( calcColor);
  527. CG_DrawPic(
  528. focusItem->window.rect.x*cgs.widthRatioCoef,
  529. focusItem->window.rect.y,
  530. focusItem->window.rect.w*cgs.widthRatioCoef,
  531. focusItem->window.rect.h,
  532. focusItem->window.background
  533. );
  534. currValue -= inc;
  535. }
  536. // Print the mueric amount
  537. focusItem = Menu_FindItemByName(menuHUD, "healthamount");
  538. if (focusItem)
  539. {
  540. // Print health amount
  541. trap_R_SetColor( focusItem->window.foreColor );
  542. CG_DrawNumField (
  543. focusItem->window.rect.x*cgs.widthRatioCoef,
  544. focusItem->window.rect.y,
  545. 3,
  546. ps->stats[STAT_HEALTH],
  547. focusItem->window.rect.w*cgs.widthRatioCoef,
  548. focusItem->window.rect.h,
  549. NUM_FONT_SMALL,
  550. qfalse);
  551. }
  552. }
  553. /*
  554. ================
  555. CG_DrawArmor
  556. ================
  557. */
  558. void CG_DrawArmor( menuDef_t *menuHUD )
  559. {
  560. vec4_t calcColor;
  561. playerState_t *ps;
  562. int maxArmor;
  563. itemDef_t *focusItem;
  564. float percent,quarterArmor;
  565. int i,currValue,inc;
  566. //ps = &cg.snap->ps;
  567. ps = &cg.predictedPlayerState;
  568. // Can we find the menu?
  569. if (!menuHUD)
  570. {
  571. return;
  572. }
  573. maxArmor = ps->stats[STAT_MAX_HEALTH];
  574. currValue = ps->stats[STAT_ARMOR];
  575. inc = (float) maxArmor / MAX_HUD_TICS;
  576. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  577. for (i=(MAX_HUD_TICS-1);i>=0;i--)
  578. {
  579. focusItem = Menu_FindItemByName(menuHUD, armorTicName[i]);
  580. if (!focusItem) // This is bad
  581. {
  582. continue;
  583. }
  584. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  585. if (currValue <= 0) // don't show tic
  586. {
  587. break;
  588. }
  589. else if (currValue < inc) // partial tic (alpha it out)
  590. {
  591. percent = (float) currValue / inc;
  592. calcColor[3] *= percent;
  593. }
  594. trap_R_SetColor( calcColor);
  595. if ((i==(MAX_HUD_TICS-1)) && (currValue < inc))
  596. {
  597. if (cg.HUDArmorFlag)
  598. {
  599. CG_DrawPic(
  600. focusItem->window.rect.x*cgs.widthRatioCoef,
  601. focusItem->window.rect.y,
  602. focusItem->window.rect.w*cgs.widthRatioCoef,
  603. focusItem->window.rect.h,
  604. focusItem->window.background
  605. );
  606. }
  607. }
  608. else
  609. {
  610. CG_DrawPic(
  611. focusItem->window.rect.x*cgs.widthRatioCoef,
  612. focusItem->window.rect.y,
  613. focusItem->window.rect.w*cgs.widthRatioCoef,
  614. focusItem->window.rect.h,
  615. focusItem->window.background
  616. );
  617. }
  618. currValue -= inc;
  619. }
  620. focusItem = Menu_FindItemByName(menuHUD, "armoramount");
  621. if (focusItem)
  622. {
  623. // Print armor amount
  624. trap_R_SetColor( focusItem->window.foreColor );
  625. CG_DrawNumField (
  626. focusItem->window.rect.x*cgs.widthRatioCoef,
  627. focusItem->window.rect.y,
  628. 3,
  629. ps->stats[STAT_ARMOR],
  630. focusItem->window.rect.w*cgs.widthRatioCoef,
  631. focusItem->window.rect.h,
  632. NUM_FONT_SMALL,
  633. qfalse);
  634. }
  635. // If armor is low, flash a graphic to warn the player
  636. if (ps->stats[STAT_ARMOR]) // Is there armor? Draw the HUD Armor TIC
  637. {
  638. quarterArmor = (float) (ps->stats[STAT_MAX_HEALTH] / 4.0f);
  639. // Make tic flash if armor is at 25% of full armor
  640. if (ps->stats[STAT_ARMOR] < quarterArmor) // Do whatever the flash timer says
  641. {
  642. if (cg.HUDTickFlashTime < cg.time) // Flip at the same time
  643. {
  644. cg.HUDTickFlashTime = cg.time + 400;
  645. if (cg.HUDArmorFlag)
  646. {
  647. cg.HUDArmorFlag = qfalse;
  648. }
  649. else
  650. {
  651. cg.HUDArmorFlag = qtrue;
  652. }
  653. }
  654. }
  655. else
  656. {
  657. cg.HUDArmorFlag=qtrue;
  658. }
  659. }
  660. else // No armor? Don't show it.
  661. {
  662. cg.HUDArmorFlag=qfalse;
  663. }
  664. }
  665. /*
  666. ================
  667. CG_DrawSaberStyle
  668. If the weapon is a light saber (which needs no ammo) then draw a graphic showing
  669. the saber style (fast, medium, strong)
  670. ================
  671. */
  672. static void CG_DrawSaberStyle( centity_t *cent, menuDef_t *menuHUD)
  673. {
  674. itemDef_t *focusItem;
  675. if (!cent->currentState.weapon ) // We don't have a weapon right now
  676. {
  677. return;
  678. }
  679. if ( cent->currentState.weapon != WP_SABER )
  680. {
  681. return;
  682. }
  683. // Can we find the menu?
  684. if (!menuHUD)
  685. {
  686. return;
  687. }
  688. // draw the current saber style in this window
  689. switch ( cg.predictedPlayerState.fd.saberDrawAnimLevel )
  690. {
  691. case 1://FORCE_LEVEL_1:
  692. case 5://FORCE_LEVEL_5://Tavion
  693. focusItem = Menu_FindItemByName(menuHUD, "saberstyle_fast");
  694. if (focusItem)
  695. {
  696. trap_R_SetColor( colorTable[CT_WHITE] );
  697. CG_DrawPic(
  698. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  699. focusItem->window.rect.y,
  700. focusItem->window.rect.w*cgs.widthRatioCoef,
  701. focusItem->window.rect.h,
  702. focusItem->window.background
  703. );
  704. }
  705. break;
  706. case 2://FORCE_LEVEL_2:
  707. case 6://SS_DUAL
  708. case 7://SS_STAFF
  709. focusItem = Menu_FindItemByName(menuHUD, "saberstyle_medium");
  710. if (focusItem)
  711. {
  712. trap_R_SetColor( colorTable[CT_WHITE] );
  713. CG_DrawPic(
  714. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  715. focusItem->window.rect.y,
  716. focusItem->window.rect.w*cgs.widthRatioCoef,
  717. focusItem->window.rect.h,
  718. focusItem->window.background
  719. );
  720. }
  721. break;
  722. case 3://FORCE_LEVEL_3:
  723. case 4://FORCE_LEVEL_4://Desann
  724. focusItem = Menu_FindItemByName(menuHUD, "saberstyle_strong");
  725. if (focusItem)
  726. {
  727. trap_R_SetColor( colorTable[CT_WHITE] );
  728. CG_DrawPic(
  729. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  730. focusItem->window.rect.y,
  731. focusItem->window.rect.w*cgs.widthRatioCoef,
  732. focusItem->window.rect.h,
  733. focusItem->window.background
  734. );
  735. }
  736. break;
  737. }
  738. }
  739. /*
  740. ================
  741. CG_DrawAmmo
  742. ================
  743. */
  744. static void CG_DrawAmmo( centity_t *cent,menuDef_t *menuHUD)
  745. {
  746. playerState_t *ps;
  747. int i;
  748. vec4_t calcColor;
  749. float value=0.0f,inc = 0.0f,percent;
  750. itemDef_t *focusItem;
  751. ps = &cg.snap->ps;
  752. // Can we find the menu?
  753. if (!menuHUD)
  754. {
  755. return;
  756. }
  757. if (!cent->currentState.weapon ) // We don't have a weapon right now
  758. {
  759. return;
  760. }
  761. value = ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
  762. if (value < 0) // No ammo
  763. {
  764. return;
  765. }
  766. //
  767. // ammo
  768. //
  769. if (cg.oldammo < value)
  770. {
  771. cg.oldAmmoTime = cg.time + 200;
  772. }
  773. cg.oldammo = value;
  774. focusItem = Menu_FindItemByName(menuHUD, "ammoamount");
  775. if (weaponData[cent->currentState.weapon].energyPerShot == 0 &&
  776. weaponData[cent->currentState.weapon].altEnergyPerShot == 0)
  777. { //just draw "infinite"
  778. inc = 8 / MAX_HUD_TICS;
  779. value = 8;
  780. focusItem = Menu_FindItemByName(menuHUD, "ammoinfinite");
  781. trap_R_SetColor( colorTable[CT_YELLOW] );
  782. if (focusItem)
  783. {
  784. UI_DrawProportionalString(SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef, focusItem->window.rect.y, "--", NUM_FONT_SMALL, focusItem->window.foreColor);
  785. }
  786. }
  787. else
  788. {
  789. focusItem = Menu_FindItemByName(menuHUD, "ammoamount");
  790. // Firing or reloading?
  791. if (( cg.predictedPlayerState.weaponstate == WEAPON_FIRING
  792. && cg.predictedPlayerState.weaponTime > 100 ))
  793. {
  794. memcpy(calcColor, colorTable[CT_LTGREY], sizeof(vec4_t));
  795. }
  796. else
  797. {
  798. if ( value > 0 )
  799. {
  800. if (cg.oldAmmoTime > cg.time)
  801. {
  802. memcpy(calcColor, colorTable[CT_YELLOW], sizeof(vec4_t));
  803. }
  804. else
  805. {
  806. memcpy(calcColor, focusItem->window.foreColor, sizeof(vec4_t));
  807. }
  808. }
  809. else
  810. {
  811. memcpy(calcColor, colorTable[CT_RED], sizeof(vec4_t));
  812. }
  813. }
  814. trap_R_SetColor( calcColor );
  815. if (focusItem)
  816. {
  817. if ( (cent->currentState.eFlags & EF_DOUBLE_AMMO) )
  818. {
  819. inc = (float) (ammoData[weaponData[cent->currentState.weapon].ammoIndex].max*2.0f) / MAX_HUD_TICS;
  820. }
  821. else
  822. {
  823. inc = (float) ammoData[weaponData[cent->currentState.weapon].ammoIndex].max / MAX_HUD_TICS;
  824. }
  825. value =ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
  826. CG_DrawNumField (
  827. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  828. focusItem->window.rect.y,
  829. 3,
  830. value,
  831. focusItem->window.rect.w*cgs.widthRatioCoef,
  832. focusItem->window.rect.h,
  833. NUM_FONT_SMALL,
  834. qfalse);
  835. }
  836. }
  837. trap_R_SetColor( colorTable[CT_WHITE] );
  838. // Draw tics
  839. for (i=MAX_HUD_TICS-1;i>=0;i--)
  840. {
  841. focusItem = Menu_FindItemByName(menuHUD, ammoTicName[i]);
  842. if (!focusItem)
  843. {
  844. continue;
  845. }
  846. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  847. if ( value <= 0 ) // done
  848. {
  849. break;
  850. }
  851. else if (value < inc) // partial tic
  852. {
  853. percent = value / inc;
  854. calcColor[3] = percent;
  855. }
  856. trap_R_SetColor( calcColor);
  857. CG_DrawPic(
  858. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  859. focusItem->window.rect.y,
  860. focusItem->window.rect.w*cgs.widthRatioCoef,
  861. focusItem->window.rect.h,
  862. focusItem->window.background
  863. );
  864. value -= inc;
  865. }
  866. }
  867. /*
  868. ================
  869. CG_DrawForcePower
  870. ================
  871. */
  872. void CG_DrawForcePower( menuDef_t *menuHUD )
  873. {
  874. int i;
  875. vec4_t calcColor;
  876. float value,inc,percent;
  877. itemDef_t *focusItem;
  878. const int maxForcePower = 100;
  879. qboolean flash=qfalse;
  880. // Can we find the menu?
  881. if (!menuHUD)
  882. {
  883. return;
  884. }
  885. if (cg.forceHUDTotalFlashTime < cg.time) {
  886. cg.forceHUDTotalFlashTime = 0;
  887. cg.forceHUDNextFlashTime = 0;
  888. }// else {
  889. // cg.forceHUDTotalFlashTime = cg.time + 1000;
  890. // }
  891. // Make the hud flash by setting forceHUDTotalFlashTime above cg.time
  892. if (cg.forceHUDTotalFlashTime > cg.time )
  893. {
  894. flash = qtrue;
  895. if (cg.forceHUDNextFlashTime < cg.time)
  896. {
  897. cg.forceHUDNextFlashTime = cg.time + 400;
  898. trap_S_StartSound (NULL, 0, CHAN_LOCAL, cgs.media.noforceSound );
  899. if (cg.forceHUDActive)
  900. {
  901. cg.forceHUDActive = qfalse;
  902. }
  903. else
  904. {
  905. cg.forceHUDActive = qtrue;
  906. }
  907. }
  908. }
  909. else // turn HUD back on if it had just finished flashing time.
  910. {
  911. cg.forceHUDNextFlashTime = 0;
  912. cg.forceHUDActive = qtrue;
  913. }
  914. // if (!cg.forceHUDActive)
  915. // {
  916. // return;
  917. // }
  918. inc = (float) maxForcePower / MAX_HUD_TICS;
  919. value = cg.snap->ps.fd.forcePower;
  920. for (i=MAX_HUD_TICS-1;i>=0;i--)
  921. {
  922. focusItem = Menu_FindItemByName(menuHUD, forceTicName[i]);
  923. if (!focusItem)
  924. {
  925. continue;
  926. }
  927. // memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  928. if ( value <= 0 ) // done
  929. {
  930. break;
  931. }
  932. else if (value < inc) // partial tic
  933. {
  934. if (flash)
  935. {
  936. memcpy(calcColor, colorTable[CT_RED], sizeof(vec4_t));
  937. }
  938. else
  939. {
  940. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  941. }
  942. percent = value / inc;
  943. calcColor[3] = percent;
  944. }
  945. else
  946. {
  947. if (flash)
  948. {
  949. memcpy(calcColor, colorTable[CT_RED], sizeof(vec4_t));
  950. }
  951. else
  952. {
  953. memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
  954. }
  955. }
  956. trap_R_SetColor( calcColor);
  957. CG_DrawPic(
  958. SCREEN_WIDTH - (SCREEN_WIDTH - (float)focusItem->window.rect.x)*cgs.widthRatioCoef,
  959. focusItem->window.rect.y,
  960. focusItem->window.rect.w*cgs.widthRatioCoef,
  961. focusItem->window.rect.h,
  962. focusItem->window.background
  963. );
  964. value -= inc;
  965. }
  966. focusItem = Menu_FindItemByName(menuHUD, "forceamount");
  967. if (focusItem)
  968. {
  969. // Print force amount
  970. trap_R_SetColor( focusItem->window.foreColor );
  971. CG_DrawNumField (
  972. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef,
  973. focusItem->window.rect.y,
  974. 3,
  975. cg.snap->ps.fd.forcePower,
  976. focusItem->window.rect.w*cgs.widthRatioCoef,
  977. focusItem->window.rect.h,
  978. NUM_FONT_SMALL,
  979. qfalse);
  980. }
  981. }
  982. /*
  983. ================
  984. CG_DrawHUD
  985. ================
  986. */
  987. void CG_DrawHUD(centity_t *cent)
  988. {
  989. menuDef_t *menuHUD = NULL;
  990. itemDef_t *focusItem = NULL;
  991. const char *scoreStr = NULL;
  992. int scoreBias;
  993. char scoreBiasStr[16];
  994. if (cg_hudFiles.integer)
  995. {
  996. int x = 0;
  997. int y = SCREEN_HEIGHT-80;
  998. char ammoString[64];
  999. int weapX = x;
  1000. if (cg.predictedPlayerState.pm_type != PM_SPECTATOR)
  1001. {
  1002. UI_DrawProportionalString( (x+16)*cgs.widthRatioCoef, y+40, va( "%i", cg.snap->ps.stats[STAT_HEALTH] ),
  1003. UI_SMALLFONT|UI_DROPSHADOW, colorTable[CT_HUD_RED] );
  1004. UI_DrawProportionalString( (x+18+14)*cgs.widthRatioCoef, y+40+14, va( "%i", cg.snap->ps.stats[STAT_ARMOR] ),
  1005. UI_SMALLFONT|UI_DROPSHADOW, colorTable[CT_HUD_GREEN] );
  1006. if (cg.snap->ps.weapon == WP_SABER)
  1007. {
  1008. if (cg.snap->ps.fd.saberDrawAnimLevel == SS_DUAL)
  1009. {
  1010. Com_sprintf(ammoString, sizeof(ammoString), "AKIMBO");
  1011. weapX += 16;
  1012. }
  1013. else if (cg.snap->ps.fd.saberDrawAnimLevel == SS_STAFF)
  1014. {
  1015. Com_sprintf(ammoString, sizeof(ammoString), "STAFF");
  1016. weapX += 16;
  1017. }
  1018. else if (cg.snap->ps.fd.saberDrawAnimLevel == FORCE_LEVEL_3)
  1019. {
  1020. Com_sprintf(ammoString, sizeof(ammoString), "STRONG");
  1021. weapX += 16;
  1022. }
  1023. else if (cg.snap->ps.fd.saberDrawAnimLevel == FORCE_LEVEL_2)
  1024. {
  1025. Com_sprintf(ammoString, sizeof(ammoString), "MEDIUM");
  1026. weapX += 16;
  1027. }
  1028. else
  1029. {
  1030. Com_sprintf(ammoString, sizeof(ammoString), "FAST");
  1031. }
  1032. }
  1033. else if (weaponData[cent->currentState.weapon].energyPerShot == 0 && weaponData[cent->currentState.weapon].altEnergyPerShot == 0)
  1034. {
  1035. Q_strncpyz(ammoString, "--", sizeof(ammoString));
  1036. }
  1037. else
  1038. {
  1039. Com_sprintf(ammoString, sizeof(ammoString), "%i", cg.snap->ps.ammo[weaponData[cent->currentState.weapon].ammoIndex]);
  1040. }
  1041. UI_DrawProportionalString( SCREEN_WIDTH-(weapX+16+32), y+40, va( "%s", ammoString ),
  1042. UI_SMALLFONT|UI_DROPSHADOW, colorTable[CT_HUD_ORANGE] );
  1043. UI_DrawProportionalString( SCREEN_WIDTH-(x+18+14+32), y+40+14, va( "%i", cg.snap->ps.fd.forcePower),
  1044. UI_SMALLFONT|UI_DROPSHADOW, colorTable[CT_ICON_BLUE] );
  1045. }
  1046. return;
  1047. }
  1048. if (cg.predictedPlayerState.pm_type != PM_SPECTATOR)
  1049. {
  1050. // Draw the left HUD
  1051. menuHUD = Menus_FindByName("lefthud");
  1052. Menu_Paint( menuHUD, qtrue );
  1053. if (menuHUD)
  1054. {
  1055. itemDef_t *focusItem;
  1056. // Print scanline
  1057. focusItem = Menu_FindItemByName(menuHUD, "scanline");
  1058. if (focusItem)
  1059. {
  1060. trap_R_SetColor( colorTable[CT_WHITE] );
  1061. CG_DrawPic(
  1062. focusItem->window.rect.x,
  1063. focusItem->window.rect.y,
  1064. focusItem->window.rect.w*cgs.widthRatioCoef,
  1065. focusItem->window.rect.h,
  1066. focusItem->window.background
  1067. );
  1068. }
  1069. // Print frame
  1070. focusItem = Menu_FindItemByName(menuHUD, "frame");
  1071. if (focusItem)
  1072. {
  1073. trap_R_SetColor( colorTable[CT_WHITE] );
  1074. CG_DrawPic(
  1075. focusItem->window.rect.x,
  1076. focusItem->window.rect.y,
  1077. focusItem->window.rect.w*cgs.widthRatioCoef,
  1078. focusItem->window.rect.h,
  1079. focusItem->window.background
  1080. );
  1081. }
  1082. CG_DrawArmor(menuHUD);
  1083. CG_DrawHealth(menuHUD);
  1084. }
  1085. else
  1086. {
  1087. //CG_Error("CG_ChatBox_ArrayInsert: unable to locate HUD menu file ");
  1088. }
  1089. //scoreStr = va("Score: %i", cgs.clientinfo[cg.snap->ps.clientNum].score);
  1090. if ( cgs.gametype == GT_DUEL )
  1091. {//A duel that requires more than one kill to knock the current enemy back to the queue
  1092. //show current kills out of how many needed
  1093. scoreStr = va("%s: %i/%i", CG_GetStringEdString("MP_INGAME", "SCORE"), cg.snap->ps.persistant[PERS_SCORE], cgs.fraglimit);
  1094. }
  1095. else if (0 && cgs.gametype < GT_TEAM )
  1096. { // This is a teamless mode, draw the score bias.
  1097. scoreBias = cg.snap->ps.persistant[PERS_SCORE] - cgs.scores1;
  1098. if (scoreBias == 0)
  1099. { // We are the leader!
  1100. if (cgs.scores2 <= 0)
  1101. { // Nobody to be ahead of yet.
  1102. Com_sprintf(scoreBiasStr, sizeof(scoreBiasStr), "");
  1103. }
  1104. else
  1105. {
  1106. scoreBias = cg.snap->ps.persistant[PERS_SCORE] - cgs.scores2;
  1107. if (scoreBias == 0)
  1108. {
  1109. Com_sprintf(scoreBiasStr, sizeof(scoreBiasStr), " (Tie)");
  1110. }
  1111. else
  1112. {
  1113. Com_sprintf(scoreBiasStr, sizeof(scoreBiasStr), " (+%d)", scoreBias);
  1114. }
  1115. }
  1116. }
  1117. else // if (scoreBias < 0)
  1118. { // We are behind!
  1119. Com_sprintf(scoreBiasStr, sizeof(scoreBiasStr), " (%d)", scoreBias);
  1120. }
  1121. scoreStr = va("%s: %i%s", CG_GetStringEdString("MP_INGAME", "SCORE"), cg.snap->ps.persistant[PERS_SCORE], scoreBiasStr);
  1122. }
  1123. else
  1124. { // Don't draw a bias.
  1125. scoreStr = va("%s: %i", CG_GetStringEdString("MP_INGAME", "SCORE"), cg.snap->ps.persistant[PERS_SCORE]);
  1126. }
  1127. menuHUD = Menus_FindByName("righthud");
  1128. Menu_Paint( menuHUD, qtrue );
  1129. if (menuHUD)
  1130. {
  1131. if (cgs.gametype != GT_POWERDUEL)
  1132. {
  1133. focusItem = Menu_FindItemByName(menuHUD, "score_line");
  1134. if (focusItem)
  1135. {
  1136. UI_DrawScaledProportionalString(
  1137. SCREEN_WIDTH - (SCREEN_WIDTH - focusItem->window.rect.x)*cgs.widthRatioCoef - 17,
  1138. focusItem->window.rect.y,
  1139. scoreStr,
  1140. UI_RIGHT|UI_DROPSHADOW,
  1141. focusItem->window.foreColor,
  1142. 0.7f);
  1143. }
  1144. }
  1145. // Print scanline
  1146. focusItem = Menu_FindItemByName(menuHUD, "scanline");
  1147. if (focusItem)
  1148. {
  1149. trap_R_SetColor( colorTable[CT_WHITE] );
  1150. CG_DrawPic(
  1151. focusItem->window.rect.x,
  1152. focusItem->window.rect.y,
  1153. focusItem->window.rect.w*cgs.widthRatioCoef,
  1154. focusItem->window.rect.h,
  1155. focusItem->window.background
  1156. );
  1157. }
  1158. focusItem = Menu_FindItemByName(menuHUD, "frame");
  1159. if (focusItem)
  1160. {
  1161. trap_R_SetColor( colorTable[CT_WHITE] );
  1162. CG_DrawPic(
  1163. focusItem->window.rect.x,
  1164. focusItem->window.rect.y,
  1165. focusItem->window.rect.w*cgs.widthRatioCoef,
  1166. focusItem->window.rect.h,
  1167. focusItem->window.background
  1168. );
  1169. }
  1170. CG_DrawForcePower(menuHUD);
  1171. // Draw ammo tics or saber style
  1172. if ( cent->currentState.weapon == WP_SABER )
  1173. {
  1174. CG_DrawSaberStyle(cent,menuHUD);
  1175. }
  1176. else
  1177. {
  1178. CG_DrawAmmo(cent,menuHUD);
  1179. }
  1180. }
  1181. else
  1182. {
  1183. //CG_Error("CG_ChatBox_ArrayInsert: unable to locate HUD menu file ");
  1184. }
  1185. }
  1186. }
  1187. #define MAX_SHOWPOWERS NUM_FORCE_POWERS
  1188. qboolean ForcePower_Valid(int i)
  1189. {
  1190. if (i == FP_LEVITATION ||
  1191. i == FP_SABER_OFFENSE ||
  1192. i == FP_SABER_DEFENSE ||
  1193. i == FP_SABERTHROW)
  1194. {
  1195. return qfalse;
  1196. }
  1197. if (cg.snap->ps.fd.forcePowersKnown & (1 << i))
  1198. {
  1199. return qtrue;
  1200. }
  1201. return qfalse;
  1202. }
  1203. /*
  1204. ===================
  1205. CG_DrawForceSelect
  1206. ===================
  1207. */
  1208. void CG_DrawForceSelect( void )
  1209. {
  1210. int i;
  1211. int count;
  1212. int smallIconSize,bigIconSize;
  1213. int holdX,x,y,x2,y2,pad,length;
  1214. int sideLeftIconCnt,sideRightIconCnt;
  1215. int sideMax,holdCount,iconCnt;
  1216. int yOffset = 0;
  1217. x2 = 0;
  1218. y2 = 0;
  1219. // don't display if dead
  1220. if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
  1221. {
  1222. return;
  1223. }
  1224. if ((cg.forceSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
  1225. {
  1226. cg.forceSelect = cg.snap->ps.fd.forcePowerSelected;
  1227. return;
  1228. }
  1229. if (!cg.snap->ps.fd.forcePowersKnown)
  1230. {
  1231. return;
  1232. }
  1233. // count the number of powers owned
  1234. count = 0;
  1235. for (i=0;i < NUM_FORCE_POWERS;++i)
  1236. {
  1237. if (ForcePower_Valid(i))
  1238. {
  1239. count++;
  1240. }
  1241. }
  1242. if (count == 0) // If no force powers, don't display
  1243. {
  1244. return;
  1245. }
  1246. sideMax = 3; // Max number of icons on the side
  1247. // Calculate how many icons will appear to either side of the center one
  1248. holdCount = count - 1; // -1 for the center icon
  1249. if (holdCount == 0) // No icons to either side
  1250. {
  1251. sideLeftIconCnt = 0;
  1252. sideRightIconCnt = 0;
  1253. }
  1254. else if (count > (2*sideMax)) // Go to the max on each side
  1255. {
  1256. sideLeftIconCnt = sideMax;
  1257. sideRightIconCnt = sideMax;
  1258. }
  1259. else // Less than max, so do the calc
  1260. {
  1261. sideLeftIconCnt = holdCount/2;
  1262. sideRightIconCnt = holdCount - sideLeftIconCnt;
  1263. }
  1264. smallIconSize = 30;
  1265. bigIconSize = 60;
  1266. pad = 12;
  1267. x = 320;
  1268. y = 425;
  1269. // Background
  1270. length = (sideLeftIconCnt * smallIconSize) + (sideLeftIconCnt*pad) +
  1271. bigIconSize + (sideRightIconCnt * smallIconSize) + (sideRightIconCnt*pad) + 12;
  1272. i = BG_ProperForceIndex(cg.forceSelect) - 1;
  1273. if (i < 0)
  1274. {
  1275. i = MAX_SHOWPOWERS;
  1276. }
  1277. trap_R_SetColor(NULL);
  1278. // Work backwards from current icon
  1279. holdX = x - ((bigIconSize/2) + pad + smallIconSize)*cgs.widthRatioCoef;
  1280. for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);i--)
  1281. {
  1282. if (i < 0)
  1283. {
  1284. i = MAX_SHOWPOWERS;
  1285. }
  1286. if (!ForcePower_Valid(forcePowerSorted[i])) // Does he have this power?
  1287. {
  1288. continue;
  1289. }
  1290. ++iconCnt; // Good icon
  1291. if (cgs.media.forcePowerIcons[forcePowerSorted[i]])
  1292. {
  1293. CG_DrawPic( holdX, y + yOffset, smallIconSize*cgs.widthRatioCoef, smallIconSize, cgs.media.forcePowerIcons[forcePowerSorted[i]] );
  1294. holdX -= (smallIconSize+pad)*cgs.widthRatioCoef;
  1295. }
  1296. }
  1297. if (ForcePower_Valid(cg.forceSelect))
  1298. {
  1299. // Current Center Icon
  1300. if (cgs.media.forcePowerIcons[cg.forceSelect])
  1301. {
  1302. CG_DrawPic( x-(bigIconSize/2)*cgs.widthRatioCoef, (y-((bigIconSize-smallIconSize)/2)) + yOffset, bigIconSize*cgs.widthRatioCoef, bigIconSize, cgs.media.forcePowerIcons[cg.forceSelect] ); //only cache the icon for display
  1303. }
  1304. }
  1305. i = BG_ProperForceIndex(cg.forceSelect) + 1;
  1306. if (i>MAX_SHOWPOWERS)
  1307. {
  1308. i = 0;
  1309. }
  1310. // Work forwards from current icon
  1311. holdX = x + ((bigIconSize/2) + pad)*cgs.widthRatioCoef;
  1312. for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++)
  1313. {
  1314. if (i>MAX_SHOWPOWERS)
  1315. {
  1316. i = 0;
  1317. }
  1318. if (!ForcePower_Valid(forcePowerSorted[i])) // Does he have this power?
  1319. {
  1320. continue;
  1321. }
  1322. ++iconCnt; // Good icon
  1323. if (cgs.media.forcePowerIcons[forcePowerSorted[i]])
  1324. {
  1325. CG_DrawPic( holdX, y + yOffset, smallIconSize*cgs.widthRatioCoef, smallIconSize, cgs.media.forcePowerIcons[forcePowerSorted[i]] ); //only cache the icon for display
  1326. holdX += (smallIconSize+pad)*cgs.widthRatioCoef;
  1327. }
  1328. }
  1329. if ( showPowersName[cg.forceSelect] )
  1330. {
  1331. UI_DrawProportionalString(320, y + 30 + yOffset, CG_GetStringEdString("SP_INGAME", showPowersName[cg.forceSelect]), UI_CENTER | UI_SMALLFONT, colorTable[CT_ICON_BLUE]);
  1332. }
  1333. }
  1334. /*
  1335. ===================
  1336. CG_DrawInventorySelect
  1337. ===================
  1338. */
  1339. void CG_DrawInvenSelect( void )
  1340. {
  1341. int i;
  1342. int sideMax,holdCount,iconCnt;
  1343. int smallIconSize,bigIconSize;
  1344. int sideLeftIconCnt,sideRightIconCnt;
  1345. int count;
  1346. int holdX,x,y,y2,pad;
  1347. int height;
  1348. float addX;
  1349. // don't display if dead
  1350. if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
  1351. {
  1352. return;
  1353. }
  1354. if ((cg.invenSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
  1355. {
  1356. return;
  1357. }
  1358. if (!cg.snap->ps.stats[STAT_HOLDABLE_ITEM] || !cg.snap->ps.stats[STAT_HOLDABLE_ITEMS])
  1359. {
  1360. return;
  1361. }
  1362. if (cg.itemSelect == -1)
  1363. {
  1364. cg.itemSelect = bg_itemlist[cg.snap->ps.stats[STAT_HOLDABLE_ITEM]].giTag;
  1365. }
  1366. //const int bits = cg.snap->ps.stats[ STAT_ITEMS ];
  1367. // count the number of items owned
  1368. count = 0;
  1369. for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ )
  1370. {
  1371. if (/*CG_InventorySelectable(i) && inv_icons[i]*/
  1372. (cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) )
  1373. {
  1374. count++;
  1375. }
  1376. }
  1377. if (!count)
  1378. {
  1379. y2 = 0; //err?
  1380. UI_DrawProportionalString(320, y2 + 22, "EMPTY INVENTORY", UI_CENTER | UI_SMALLFONT, colorTable[CT_ICON_BLUE]);
  1381. return;
  1382. }
  1383. sideMax = 3; // Max number of icons on the side
  1384. // Calculate how many icons will appear to either side of the center one
  1385. holdCount = count - 1; // -1 for the center icon
  1386. if (holdCount == 0) // No icons to either side
  1387. {
  1388. sideLeftIconCnt = 0;
  1389. sideRightIconCnt = 0;
  1390. }
  1391. else if (count > (2*sideMax)) // Go to the max on each side
  1392. {
  1393. sideLeftIconCnt = sideMax;
  1394. sideRightIconCnt = sideMax;
  1395. }
  1396. else // Less than max, so do the calc
  1397. {
  1398. sideLeftIconCnt = holdCount/2;
  1399. sideRightIconCnt = holdCount - sideLeftIconCnt;
  1400. }
  1401. i = cg.itemSelect - 1;
  1402. if (i<0)
  1403. {
  1404. i = HI_NUM_HOLDABLE-1;
  1405. }
  1406. smallIconSize = 40;
  1407. bigIconSize = 80;
  1408. pad = 16;
  1409. x = 320;
  1410. y = 410;
  1411. // Left side ICONS
  1412. // Work backwards from current icon
  1413. holdX = x - ((bigIconSize/2) + pad + smallIconSize)*cgs.widthRatioCoef;
  1414. height = smallIconSize * cg.iconHUDPercent;
  1415. addX = (float) smallIconSize * .75;
  1416. for (iconCnt=0;iconCnt<sideLeftIconCnt;i--)
  1417. {
  1418. if (i<0)
  1419. {
  1420. i = HI_NUM_HOLDABLE-1;
  1421. }
  1422. if ( !(cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) || i == cg.itemSelect )
  1423. {
  1424. continue;
  1425. }
  1426. ++iconCnt; // Good icon
  1427. if (!BG_IsItemSelectable(&cg.predictedPlayerState, i))
  1428. {
  1429. continue;
  1430. }
  1431. if (cgs.media.invenIcons[i])
  1432. {
  1433. trap_R_SetColor(NULL);
  1434. CG_DrawPic( holdX, y+10, smallIconSize*cgs.widthRatioCoef, smallIconSize, cgs.media.invenIcons[i] );
  1435. trap_R_SetColor(colorTable[CT_ICON_BLUE]);
  1436. /*CG_DrawNumField (holdX + addX, y + smallIconSize, 2, cg.snap->ps.inventory[i], 6, 12,
  1437. NUM_FONT_SMALL,qfalse);
  1438. */
  1439. holdX -= (smallIconSize+pad)*cgs.widthRatioCoef;
  1440. }
  1441. }
  1442. // Current Center Icon
  1443. height = bigIconSize * cg.iconHUDPercent;
  1444. if (cgs.media.invenIcons[cg.itemSelect] && BG_IsItemSelectable(&cg.predictedPlayerState, cg.itemSelect))
  1445. {
  1446. int itemNdex;
  1447. trap_R_SetColor(NULL);
  1448. CG_DrawPic( x-(bigIconSize/2)*cgs.widthRatioCoef, (y-((bigIconSize-smallIconSize)/2))+10, bigIconSize*cgs.widthRatioCoef, bigIconSize, cgs.media.invenIcons[cg.itemSelect] );
  1449. addX = (float) bigIconSize * .75;
  1450. trap_R_SetColor(colorTable[CT_ICON_BLUE]);
  1451. /*CG_DrawNumField ((x-(bigIconSize/2)) + addX, y, 2, cg.snap->ps.inventory[cg.inventorySelect], 6, 12,
  1452. NUM_FONT_SMALL,qfalse);*/
  1453. itemNdex = BG_GetItemIndexByTag(cg.itemSelect, IT_HOLDABLE);
  1454. if (bg_itemlist[itemNdex].classname)
  1455. {
  1456. vec4_t textColor = { .312f, .75f, .621f, 1.0f };
  1457. char text[1024];
  1458. char upperKey[1024];
  1459. strcpy(upperKey, bg_itemlist[itemNdex].classname);
  1460. if ( trap_SP_GetStringTextString( va("SP_INGAME_%s",Q_strupr(upperKey)), text, sizeof( text )))
  1461. {
  1462. UI_DrawProportionalString(320, y+45, text, UI_CENTER | UI_SMALLFONT, textColor);
  1463. }
  1464. else
  1465. {
  1466. UI_DrawProportionalString(320, y+45, bg_itemlist[itemNdex].classname, UI_CENTER | UI_SMALLFONT, textColor);
  1467. }
  1468. }
  1469. }
  1470. i = cg.itemSelect + 1;
  1471. if (i> HI_NUM_HOLDABLE-1)
  1472. {
  1473. i = 0;
  1474. }
  1475. // Right side ICONS
  1476. // Work forwards from current icon
  1477. holdX = x + ((bigIconSize/2) + pad)*cgs.widthRatioCoef;
  1478. height = smallIconSize * cg.iconHUDPercent;
  1479. addX = (float) smallIconSize * .75;
  1480. for (iconCnt=0;iconCnt<sideRightIconCnt;i++)
  1481. {
  1482. if (i> HI_NUM_HOLDABLE-1)
  1483. {
  1484. i = 0;
  1485. }
  1486. if ( !(cg.snap->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << i)) || i == cg.itemSelect )
  1487. {
  1488. continue;
  1489. }
  1490. ++iconCnt; // Good icon
  1491. if (!BG_IsItemSelectable(&cg.predictedPlayerState, i))
  1492. {
  1493. continue;
  1494. }
  1495. if (cgs.media.invenIcons[i])
  1496. {
  1497. trap_R_SetColor(NULL);
  1498. CG_DrawPic( holdX, y+10, smallIconSize*cgs.widthRatioCoef, smallIconSize, cgs.media.invenIcons[i] );
  1499. trap_R_SetColor(colorTable[CT_ICON_BLUE]);
  1500. /*CG_DrawNumField (holdX + addX, y + smallIconSize, 2, cg.snap->ps.inventory[i], 6, 12,
  1501. NUM_FONT_SMALL,qfalse);*/
  1502. holdX += (smallIconSize+pad)*cgs.widthRatioCoef;
  1503. }
  1504. }
  1505. }
  1506. int cg_targVeh = ENTITYNUM_NONE;
  1507. int cg_targVehLastTime = 0;
  1508. qboolean CG_CheckTargetVehicle( centity_t **pTargetVeh, float *alpha )
  1509. {
  1510. int targetNum = ENTITYNUM_NONE;
  1511. centity_t *targetVeh = NULL;
  1512. if ( !pTargetVeh || !alpha )
  1513. {//hey, where are my pointers?
  1514. return qfalse;
  1515. }
  1516. *alpha = 1.0f;
  1517. //FIXME: need to clear all of these when you die?
  1518. if ( cg.predictedPlayerState.rocketLockIndex < ENTITYNUM_WORLD )
  1519. {
  1520. targetNum = cg.predictedPlayerState.rocketLockIndex;
  1521. }
  1522. else if ( cg.crosshairVehNum < ENTITYNUM_WORLD
  1523. && cg.time - cg.crosshairVehTime < 3000 )
  1524. {//crosshair was on a vehicle in the last 3 seconds
  1525. targetNum = cg.crosshairVehNum;
  1526. }
  1527. else if ( cg.crosshairClientNum < ENTITYNUM_WORLD )
  1528. {
  1529. targetNum = cg.crosshairClientNum;
  1530. }
  1531. if ( targetNum < MAX_CLIENTS )
  1532. {//real client
  1533. if ( cg_entities[targetNum].currentState.m_iVehicleNum >= MAX_CLIENTS )
  1534. {//in a vehicle
  1535. targetNum = cg_entities[targetNum].currentState.m_iVehicleNum;
  1536. }
  1537. }
  1538. if ( targetNum < ENTITYNUM_WORLD
  1539. && targetNum >= MAX_CLIENTS )
  1540. {
  1541. //centity_t *targetVeh = &cg_entities[targetNum];
  1542. targetVeh = &cg_entities[targetNum];
  1543. if ( targetVeh->currentState.NPC_class == CLASS_VEHICLE
  1544. && targetVeh->m_pVehicle
  1545. && targetVeh->m_pVehicle->m_pVehicleInfo
  1546. && targetVeh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
  1547. {//it's a vehicle
  1548. cg_targVeh = targetNum;
  1549. cg_targVehLastTime = cg.time;
  1550. *alpha = 1.0f;
  1551. }
  1552. else
  1553. {
  1554. targetVeh = NULL;
  1555. }
  1556. }
  1557. if ( targetVeh )
  1558. {
  1559. *pTargetVeh = targetVeh;
  1560. return qtrue;
  1561. }
  1562. if ( cg_targVehLastTime && cg.time - cg_targVehLastTime < 3000 )
  1563. {
  1564. targetVeh = &cg_entities[cg_targVeh];
  1565. //stay at full alpha for 1 sec after lose them from crosshair
  1566. if ( cg.time-cg_targVehLastTime < 1000 )
  1567. *alpha = 1.0f;
  1568. else //fade out over 2 secs
  1569. *alpha = 1.0f-(((cg.time-cg_targVehLastTime)+cg.timeFraction-1000)/2000.0f);
  1570. }
  1571. return qfalse;
  1572. }
  1573. #define MAX_VHUD_SHIELD_TICS 12
  1574. #define MAX_VHUD_SPEED_TICS 5
  1575. #define MAX_VHUD_ARMOR_TICS 5
  1576. #define MAX_VHUD_AMMO_TICS 5
  1577. float CG_DrawVehicleShields( const menuDef_t *menuHUD, const centity_t *veh )
  1578. {
  1579. int i;
  1580. char itemName[64];
  1581. float inc, currValue,maxShields;
  1582. vec4_t calcColor;
  1583. itemDef_t *item;
  1584. float percShields;
  1585. item = Menu_FindItemByName((menuDef_t *) menuHUD, "armorbackground");
  1586. if (item)
  1587. {
  1588. trap_R_SetColor( item->window.foreColor );
  1589. CG_DrawPic(
  1590. item->window.rect.x,
  1591. item->window.rect.y,
  1592. item->window.rect.w,
  1593. item->window.rect.h,
  1594. item->window.background );
  1595. }
  1596. maxShields = veh->m_pVehicle->m_pVehicleInfo->shields;
  1597. currValue = cg.predictedVehicleState.stats[STAT_ARMOR];
  1598. percShields = (float)currValue/(float)maxShields;
  1599. // Print all the tics of the shield graphic
  1600. // Look at the amount of health left and show only as much of the graphic as there is health.
  1601. // Use alpha to fade out partial section of health
  1602. inc = (float) maxShields / MAX_VHUD_ARMOR_TICS;
  1603. for (i=1;i<=MAX_VHUD_ARMOR_TICS;i++)
  1604. {
  1605. sprintf( itemName, "armor_tic%d", i );
  1606. item = Menu_FindItemByName((menuDef_t *) menuHUD, itemName);
  1607. if (!item)
  1608. {
  1609. continue;
  1610. }
  1611. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1612. if (currValue <= 0) // don't show tic
  1613. {
  1614. break;
  1615. }
  1616. else if (currValue < inc) // partial tic (alpha it out)
  1617. {
  1618. float percent = currValue / inc;
  1619. calcColor[3] *= percent; // Fade it out
  1620. }
  1621. trap_R_SetColor( calcColor);
  1622. CG_DrawPic(
  1623. item->window.rect.x,
  1624. item->window.rect.y,
  1625. item->window.rect.w,
  1626. item->window.rect.h,
  1627. item->window.background );
  1628. currValue -= inc;
  1629. }
  1630. return percShields;
  1631. }
  1632. int cg_vehicleAmmoWarning = 0;
  1633. int cg_vehicleAmmoWarningTime = 0;
  1634. void CG_DrawVehicleAmmo( const menuDef_t *menuHUD, const centity_t *veh )
  1635. {
  1636. int i;
  1637. char itemName[64];
  1638. float inc, currValue,maxAmmo;
  1639. vec4_t calcColor;
  1640. itemDef_t *item;
  1641. item = Menu_FindItemByName((menuDef_t *) menuHUD, "ammobackground");
  1642. if (item)
  1643. {
  1644. trap_R_SetColor( item->window.foreColor );
  1645. CG_DrawPic(
  1646. item->window.rect.x,
  1647. item->window.rect.y,
  1648. item->window.rect.w,
  1649. item->window.rect.h,
  1650. item->window.background );
  1651. }
  1652. maxAmmo = veh->m_pVehicle->m_pVehicleInfo->weapon[0].ammoMax;
  1653. currValue = cg.predictedVehicleState.ammo[0];
  1654. inc = (float) maxAmmo / MAX_VHUD_AMMO_TICS;
  1655. for (i=1;i<=MAX_VHUD_AMMO_TICS;i++)
  1656. {
  1657. sprintf( itemName, "ammo_tic%d", i );
  1658. item = Menu_FindItemByName((menuDef_t *)menuHUD, itemName);
  1659. if (!item)
  1660. {
  1661. continue;
  1662. }
  1663. if ( cg_vehicleAmmoWarningTime > cg.time
  1664. && cg_vehicleAmmoWarning == 0 )
  1665. {
  1666. memcpy(calcColor, g_color_table[ColorIndex(COLOR_RED)], sizeof(vec4_t));
  1667. calcColor[3] = sin(cg.time*0.005+cg.timeFraction*0.005)*0.5f+0.5f;
  1668. }
  1669. else
  1670. {
  1671. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1672. if (currValue <= 0) // don't show tic
  1673. {
  1674. break;
  1675. }
  1676. else if (currValue < inc) // partial tic (alpha it out)
  1677. {
  1678. float percent = currValue / inc;
  1679. calcColor[3] *= percent; // Fade it out
  1680. }
  1681. }
  1682. trap_R_SetColor( calcColor);
  1683. CG_DrawPic(
  1684. item->window.rect.x,
  1685. item->window.rect.y,
  1686. item->window.rect.w,
  1687. item->window.rect.h,
  1688. item->window.background );
  1689. currValue -= inc;
  1690. }
  1691. }
  1692. void CG_DrawVehicleAmmoUpper( const menuDef_t *menuHUD, const centity_t *veh )
  1693. {
  1694. int i;
  1695. char itemName[64];
  1696. float inc, currValue,maxAmmo;
  1697. vec4_t calcColor;
  1698. itemDef_t *item;
  1699. item = Menu_FindItemByName((menuDef_t *)menuHUD, "ammoupperbackground");
  1700. if (item)
  1701. {
  1702. trap_R_SetColor( item->window.foreColor );
  1703. CG_DrawPic(
  1704. item->window.rect.x,
  1705. item->window.rect.y,
  1706. item->window.rect.w,
  1707. item->window.rect.h,
  1708. item->window.background );
  1709. }
  1710. maxAmmo = veh->m_pVehicle->m_pVehicleInfo->weapon[0].ammoMax;
  1711. currValue = cg.predictedVehicleState.ammo[0];
  1712. inc = (float) maxAmmo / MAX_VHUD_AMMO_TICS;
  1713. for (i=1;i<MAX_VHUD_AMMO_TICS;i++)
  1714. {
  1715. sprintf( itemName, "ammoupper_tic%d", i );
  1716. // Com_sprintf(itemName, sizeof(itemName), "ammoupper_tic%d", i );
  1717. item = Menu_FindItemByName((menuDef_t *)menuHUD, itemName);
  1718. if (!item)
  1719. {
  1720. continue;
  1721. }
  1722. if ( cg_vehicleAmmoWarningTime > cg.time
  1723. && cg_vehicleAmmoWarning == 0 )
  1724. {
  1725. memcpy(calcColor, g_color_table[ColorIndex(COLOR_RED)], sizeof(vec4_t));
  1726. calcColor[3] = sin(cg.time*0.005+cg.timeFraction*0.005)*0.5f+0.5f;
  1727. }
  1728. else
  1729. {
  1730. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1731. if (currValue <= 0) // don't show tic
  1732. {
  1733. break;
  1734. }
  1735. else if (currValue < inc) // partial tic (alpha it out)
  1736. {
  1737. float percent = currValue / inc;
  1738. calcColor[3] *= percent; // Fade it out
  1739. }
  1740. }
  1741. trap_R_SetColor( calcColor);
  1742. CG_DrawPic(
  1743. item->window.rect.x,
  1744. item->window.rect.y,
  1745. item->window.rect.w,
  1746. item->window.rect.h,
  1747. item->window.background );
  1748. currValue -= inc;
  1749. }
  1750. }
  1751. void CG_DrawVehicleAmmoLower( const menuDef_t *menuHUD, const centity_t *veh )
  1752. {
  1753. int i;
  1754. char itemName[64];
  1755. float inc, currValue,maxAmmo;
  1756. vec4_t calcColor;
  1757. itemDef_t *item;
  1758. item = Menu_FindItemByName((menuDef_t *)menuHUD, "ammolowerbackground");
  1759. if (item)
  1760. {
  1761. trap_R_SetColor( item->window.foreColor );
  1762. CG_DrawPic(
  1763. item->window.rect.x,
  1764. item->window.rect.y,
  1765. item->window.rect.w,
  1766. item->window.rect.h,
  1767. item->window.background );
  1768. }
  1769. maxAmmo = veh->m_pVehicle->m_pVehicleInfo->weapon[1].ammoMax;
  1770. currValue = cg.predictedVehicleState.ammo[1];
  1771. inc = (float) maxAmmo / MAX_VHUD_AMMO_TICS;
  1772. for (i=1;i<MAX_VHUD_AMMO_TICS;i++)
  1773. {
  1774. sprintf( itemName, "ammolower_tic%d", i );
  1775. item = Menu_FindItemByName((menuDef_t *)menuHUD, itemName);
  1776. if (!item)
  1777. {
  1778. continue;
  1779. }
  1780. if ( cg_vehicleAmmoWarningTime > cg.time
  1781. && cg_vehicleAmmoWarning == 1 )
  1782. {
  1783. memcpy(calcColor, g_color_table[ColorIndex(COLOR_RED)], sizeof(vec4_t));
  1784. calcColor[3] = sin(cg.time*0.005+cg.timeFraction*0.005)*0.5f+0.5f;
  1785. }
  1786. else
  1787. {
  1788. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1789. if (currValue <= 0) // don't show tic
  1790. {
  1791. break;
  1792. }
  1793. else if (currValue < inc) // partial tic (alpha it out)
  1794. {
  1795. float percent = currValue / inc;
  1796. calcColor[3] *= percent; // Fade it out
  1797. }
  1798. }
  1799. trap_R_SetColor( calcColor);
  1800. CG_DrawPic(
  1801. item->window.rect.x,
  1802. item->window.rect.y,
  1803. item->window.rect.w,
  1804. item->window.rect.h,
  1805. item->window.background );
  1806. currValue -= inc;
  1807. }
  1808. }
  1809. // The HUD.menu file has the graphic print with a negative height, so it will print from the bottom up.
  1810. void CG_DrawVehicleTurboRecharge(const menuDef_t *menuHUD, const centity_t *veh)
  1811. {
  1812. itemDef_t *item;
  1813. item = Menu_FindItemByName((menuDef_t *)menuHUD, "turborecharge");
  1814. if (item) {
  1815. float height = item->window.rect.h;
  1816. float percent = 0.0f;
  1817. float diff = (cg.time - veh->m_pVehicle->m_iTurboTime) + cg.timeFraction;
  1818. if (diff > veh->m_pVehicle->m_pVehicleInfo->turboRecharge) {
  1819. percent = 1.0f;
  1820. trap_R_SetColor(colorTable[CT_GREEN]);
  1821. } else {
  1822. percent = diff / veh->m_pVehicle->m_pVehicleInfo->turboRecharge;
  1823. if (percent < 0.0f)
  1824. percent = 0.0f;
  1825. trap_R_SetColor(colorTable[CT_RED]);
  1826. }
  1827. height *= percent;
  1828. CG_DrawPic(
  1829. item->window.rect.x,
  1830. item->window.rect.y,
  1831. item->window.rect.w,
  1832. height,
  1833. cgs.media.whiteShader);
  1834. }
  1835. }
  1836. qboolean cg_drawLink = qfalse;
  1837. void CG_DrawVehicleWeaponsLinked( const menuDef_t *menuHUD, const centity_t *veh )
  1838. {
  1839. qboolean drawLink = qfalse;
  1840. if ( veh->m_pVehicle
  1841. && veh->m_pVehicle->m_pVehicleInfo
  1842. && (veh->m_pVehicle->m_pVehicleInfo->weapon[0].linkable == 2|| veh->m_pVehicle->m_pVehicleInfo->weapon[1].linkable == 2) )
  1843. {//weapon is always linked
  1844. drawLink = qtrue;
  1845. }
  1846. else
  1847. {
  1848. //MP way:
  1849. //must get sent over network
  1850. if ( cg.predictedVehicleState.vehWeaponsLinked )
  1851. {
  1852. drawLink = qtrue;
  1853. }
  1854. //NOTE: below is SP way
  1855. /*
  1856. //just cheat it
  1857. if ( veh->gent->m_pVehicle->weaponStatus[0].linked
  1858. || veh->gent->m_pVehicle->weaponStatus[1].linked )
  1859. {
  1860. drawLink = qtrue;
  1861. }
  1862. */
  1863. }
  1864. if ( cg_drawLink != drawLink ) { //state changed, play sound
  1865. cg_drawLink = drawLink;
  1866. trap_S_StartSound (NULL, cg.predictedPlayerState.clientNum, CHAN_LOCAL, trap_S_RegisterSound( "sound/vehicles/common/linkweaps.wav" ) );
  1867. }
  1868. if ( drawLink )
  1869. {
  1870. itemDef_t *item;
  1871. item = Menu_FindItemByName( (menuDef_t *) menuHUD, "weaponslinked");
  1872. if (item)
  1873. {
  1874. trap_R_SetColor( colorTable[CT_CYAN] );
  1875. CG_DrawPic(
  1876. item->window.rect.x,
  1877. item->window.rect.y,
  1878. item->window.rect.w,
  1879. item->window.rect.h,
  1880. cgs.media.whiteShader);
  1881. }
  1882. }
  1883. }
  1884. void CG_DrawVehicleSpeed( const menuDef_t *menuHUD, const centity_t *veh )
  1885. {
  1886. int i;
  1887. char itemName[64];
  1888. float inc, currValue,maxSpeed;
  1889. vec4_t calcColor;
  1890. itemDef_t *item;
  1891. item = Menu_FindItemByName((menuDef_t *) menuHUD, "speedbackground");
  1892. if (item)
  1893. {
  1894. trap_R_SetColor( item->window.foreColor );
  1895. CG_DrawPic(
  1896. item->window.rect.x,
  1897. item->window.rect.y,
  1898. item->window.rect.w,
  1899. item->window.rect.h,
  1900. item->window.background );
  1901. }
  1902. maxSpeed = veh->m_pVehicle->m_pVehicleInfo->speedMax;
  1903. currValue = cg.predictedVehicleState.speed;
  1904. // Print all the tics of the shield graphic
  1905. // Look at the amount of health left and show only as much of the graphic as there is health.
  1906. // Use alpha to fade out partial section of health
  1907. inc = (float) maxSpeed / MAX_VHUD_SPEED_TICS;
  1908. for (i=1;i<=MAX_VHUD_SPEED_TICS;i++)
  1909. {
  1910. sprintf( itemName, "speed_tic%d", i );
  1911. item = Menu_FindItemByName((menuDef_t *)menuHUD, itemName);
  1912. if (!item)
  1913. {
  1914. continue;
  1915. }
  1916. if ( cg.time > veh->m_pVehicle->m_iTurboTime )
  1917. {
  1918. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1919. }
  1920. else // In turbo mode
  1921. {
  1922. if (cg.VHUDFlashTime < cg.time)
  1923. {
  1924. cg.VHUDFlashTime = cg.time + 200;
  1925. if (cg.VHUDTurboFlag)
  1926. {
  1927. cg.VHUDTurboFlag = qfalse;
  1928. }
  1929. else
  1930. {
  1931. cg.VHUDTurboFlag = qtrue;
  1932. }
  1933. }
  1934. if (cg.VHUDTurboFlag)
  1935. {
  1936. memcpy(calcColor, colorTable[CT_LTRED1], sizeof(vec4_t));
  1937. }
  1938. else
  1939. {
  1940. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1941. }
  1942. }
  1943. if (currValue <= 0) // don't show tic
  1944. {
  1945. break;
  1946. }
  1947. else if (currValue < inc) // partial tic (alpha it out)
  1948. {
  1949. float percent = currValue / inc;
  1950. calcColor[3] *= percent; // Fade it out
  1951. }
  1952. trap_R_SetColor( calcColor);
  1953. CG_DrawPic(
  1954. item->window.rect.x,
  1955. item->window.rect.y,
  1956. item->window.rect.w,
  1957. item->window.rect.h,
  1958. item->window.background );
  1959. currValue -= inc;
  1960. }
  1961. }
  1962. void CG_DrawVehicleArmor( const menuDef_t *menuHUD, const centity_t *veh )
  1963. {
  1964. int i;
  1965. vec4_t calcColor;
  1966. char itemName[64];
  1967. float inc, currValue,maxArmor;
  1968. itemDef_t *item;
  1969. maxArmor = veh->m_pVehicle->m_pVehicleInfo->armor;
  1970. currValue = cg.predictedVehicleState.stats[STAT_HEALTH];
  1971. item = Menu_FindItemByName( (menuDef_t *) menuHUD, "shieldbackground");
  1972. if (item)
  1973. {
  1974. trap_R_SetColor( item->window.foreColor );
  1975. CG_DrawPic(
  1976. item->window.rect.x,
  1977. item->window.rect.y,
  1978. item->window.rect.w,
  1979. item->window.rect.h,
  1980. item->window.background );
  1981. }
  1982. // Print all the tics of the shield graphic
  1983. // Look at the amount of health left and show only as much of the graphic as there is health.
  1984. // Use alpha to fade out partial section of health
  1985. inc = (float) maxArmor / MAX_VHUD_SHIELD_TICS;
  1986. for (i=1;i <= MAX_VHUD_SHIELD_TICS;i++)
  1987. {
  1988. sprintf( itemName, "shield_tic%d", i );
  1989. item = Menu_FindItemByName((menuDef_t *) menuHUD, itemName);
  1990. if (!item)
  1991. {
  1992. continue;
  1993. }
  1994. memcpy(calcColor, item->window.foreColor, sizeof(vec4_t));
  1995. if (currValue <= 0) // don't show tic
  1996. {
  1997. break;
  1998. }
  1999. else if (currValue < inc) // partial tic (alpha it out)
  2000. {
  2001. float percent = currValue / inc;
  2002. calcColor[3] *= percent; // Fade it out
  2003. }
  2004. trap_R_SetColor( calcColor);
  2005. CG_DrawPic(
  2006. item->window.rect.x,
  2007. item->window.rect.y,
  2008. item->window.rect.w,
  2009. item->window.rect.h,
  2010. item->window.background );
  2011. currValue -= inc;
  2012. }
  2013. }
  2014. enum
  2015. {
  2016. VEH_DAMAGE_FRONT=0,
  2017. VEH_DAMAGE_BACK,
  2018. VEH_DAMAGE_LEFT,
  2019. VEH_DAMAGE_RIGHT,
  2020. };
  2021. typedef struct
  2022. {
  2023. char *itemName;
  2024. short heavyDamage;
  2025. short lightDamage;
  2026. } veh_damage_t;
  2027. veh_damage_t vehDamageData[4] =
  2028. {
  2029. "vehicle_front",SHIPSURF_DAMAGE_FRONT_HEAVY,SHIPSURF_DAMAGE_FRONT_LIGHT,
  2030. "vehicle_back",SHIPSURF_DAMAGE_BACK_HEAVY,SHIPSURF_DAMAGE_BACK_LIGHT,
  2031. "vehicle_left",SHIPSURF_DAMAGE_LEFT_HEAVY,SHIPSURF_DAMAGE_LEFT_LIGHT,
  2032. "vehicle_right",SHIPSURF_DAMAGE_RIGHT_HEAVY,SHIPSURF_DAMAGE_RIGHT_LIGHT,
  2033. };
  2034. // Draw health graphic for given part of vehicle
  2035. void CG_DrawVehicleDamage(const centity_t *veh,int brokenLimbs,const menuDef_t *menuHUD,float alpha,int index)
  2036. {
  2037. itemDef_t *item;
  2038. int colorI;
  2039. vec4_t color;
  2040. int graphicHandle=0;
  2041. item = Menu_FindItemByName((menuDef_t *)menuHUD, vehDamageData[index].itemName);
  2042. if (item)
  2043. {
  2044. if (brokenLimbs & (1<<vehDamageData[index].heavyDamage))
  2045. {
  2046. colorI = CT_RED;
  2047. if (brokenLimbs & (1<<vehDamageData[index].lightDamage))
  2048. {
  2049. colorI = CT_DKGREY;
  2050. }
  2051. }
  2052. else if (brokenLimbs & (1<<vehDamageData[index].lightDamage))
  2053. {
  2054. colorI = CT_YELLOW;
  2055. }
  2056. else
  2057. {
  2058. colorI = CT_GREEN;
  2059. }
  2060. VectorCopy4 ( colorTable[colorI], color );
  2061. color[3] = alpha;
  2062. trap_R_SetColor( color );
  2063. switch ( index )
  2064. {
  2065. case VEH_DAMAGE_FRONT :
  2066. graphicHandle = veh->m_pVehicle->m_pVehicleInfo->iconFrontHandle;
  2067. break;
  2068. case VEH_DAMAGE_BACK :
  2069. graphicHandle = veh->m_pVehicle->m_pVehicleInfo->iconBackHandle;
  2070. break;
  2071. case VEH_DAMAGE_LEFT :
  2072. graphicHandle = veh->m_pVehicle->m_pVehicleInfo->iconLeftHandle;
  2073. break;
  2074. case VEH_DAMAGE_RIGHT :
  2075. graphicHandle = veh->m_pVehicle->m_pVehicleInfo->iconRightHandle;
  2076. break;
  2077. }
  2078. if (graphicHandle)
  2079. {
  2080. if (item->window.rect.x <= SCREEN_WIDTH / 2) {
  2081. CG_DrawPic(
  2082. item->window.rect.x,
  2083. item->window.rect.y,
  2084. item->window.rect.w*cgs.widthRatioCoef,
  2085. item->window.rect.h,
  2086. graphicHandle );
  2087. } else {
  2088. CG_DrawPic(
  2089. SCREEN_WIDTH - (SCREEN_WIDTH - item->window.rect.x)*cgs.widthRatioCoef,
  2090. item->window.rect.y,
  2091. item->window.rect.w*cgs.widthRatioCoef,
  2092. item->window.rect.h,
  2093. graphicHandle );
  2094. }
  2095. }
  2096. }
  2097. }
  2098. // Used on both damage indicators : player vehicle and the vehicle the player is locked on
  2099. void CG_DrawVehicleDamageHUD(const centity_t *veh,int brokenLimbs,float percShields,char *menuName, float alpha)
  2100. {
  2101. menuDef_t *menuHUD;
  2102. itemDef_t *item;
  2103. vec4_t color;
  2104. menuHUD = Menus_FindByName(menuName);
  2105. if ( !menuHUD )
  2106. {
  2107. return;
  2108. }
  2109. item = Menu_FindItemByName(menuHUD, "background");
  2110. if (item)
  2111. {
  2112. if (veh->m_pVehicle->m_pVehicleInfo->dmgIndicBackgroundHandle)
  2113. {
  2114. if (veh->damageTime > cg.time)
  2115. {//ship shields currently taking damage
  2116. //NOTE: cent->damageAngle can be accessed to get the direction from the ship origin to the impact point (in 3-D space)
  2117. float perc = 1.0f - (((veh->damageTime - cg.time) - cg.timeFraction) / 2000.0f/*MIN_SHIELD_TIME*/);
  2118. if (perc < 0.0f)
  2119. perc = 0.0f;
  2120. else if (perc > 1.0f)
  2121. perc = 1.0f;
  2122. color[0] = item->window.foreColor[0];//flash red
  2123. color[1] = item->window.foreColor[1]*perc;//fade other colors back in over time
  2124. color[2] = item->window.foreColor[2]*perc;//fade other colors back in over time
  2125. color[3] = item->window.foreColor[3];//always normal alpha
  2126. trap_R_SetColor(color);
  2127. }
  2128. else
  2129. {
  2130. trap_R_SetColor( item->window.foreColor );
  2131. }
  2132. if (item->window.rect.x <= SCREEN_WIDTH / 2) {
  2133. CG_DrawPic(
  2134. item->window.rect.x,
  2135. item->window.rect.y,
  2136. item->window.rect.w*cgs.widthRatioCoef,
  2137. item->window.rect.h,
  2138. veh->m_pVehicle->m_pVehicleInfo->dmgIndicBackgroundHandle );
  2139. } else {
  2140. CG_DrawPic(
  2141. SCREEN_WIDTH - (SCREEN_WIDTH - item->window.rect.x)*cgs.widthRatioCoef,
  2142. item->window.rect.y,
  2143. item->window.rect.w*cgs.widthRatioCoef,
  2144. item->window.rect.h,
  2145. veh->m_pVehicle->m_pVehicleInfo->dmgIndicBackgroundHandle );
  2146. }
  2147. }
  2148. }
  2149. item = Menu_FindItemByName(menuHUD, "outer_frame");
  2150. if (item)
  2151. {
  2152. if (veh->m_pVehicle->m_pVehicleInfo->dmgIndicFrameHandle)
  2153. {
  2154. trap_R_SetColor( item->window.foreColor );
  2155. if (item->window.rect.x <= SCREEN_WIDTH / 2) {
  2156. CG_DrawPic(
  2157. item->window.rect.x,
  2158. item->window.rect.y,
  2159. item->window.rect.w*cgs.widthRatioCoef,
  2160. item->window.rect.h,
  2161. veh->m_pVehicle->m_pVehicleInfo->dmgIndicFrameHandle );
  2162. } else {
  2163. CG_DrawPic(
  2164. SCREEN_WIDTH - (SCREEN_WIDTH - item->window.rect.x)*cgs.widthRatioCoef,
  2165. item->window.rect.y,
  2166. item->window.rect.w*cgs.widthRatioCoef,
  2167. item->window.rect.h,
  2168. veh->m_pVehicle->m_pVehicleInfo->dmgIndicFrameHandle );
  2169. }
  2170. }
  2171. }
  2172. item = Menu_FindItemByName(menuHUD, "shields");
  2173. if (item)
  2174. {
  2175. if (veh->m_pVehicle->m_pVehicleInfo->dmgIndicShieldHandle)
  2176. {
  2177. VectorCopy4 ( colorTable[CT_HUD_GREEN], color );
  2178. color[3] = percShields;
  2179. trap_R_SetColor( color );
  2180. if (item->window.rect.x <= SCREEN_WIDTH / 2) {
  2181. CG_DrawPic(
  2182. item->window.rect.x,
  2183. item->window.rect.y,
  2184. item->window.rect.w*cgs.widthRatioCoef,
  2185. item->window.rect.h,
  2186. veh->m_pVehicle->m_pVehicleInfo->dmgIndicShieldHandle );
  2187. } else {
  2188. CG_DrawPic(
  2189. SCREEN_WIDTH - (SCREEN_WIDTH - item->window.rect.x)*cgs.widthRatioCoef,
  2190. item->window.rect.y,
  2191. item->window.rect.w*cgs.widthRatioCoef,
  2192. item->window.rect.h,
  2193. veh->m_pVehicle->m_pVehicleInfo->dmgIndicShieldHandle );
  2194. }
  2195. }
  2196. }
  2197. //TODO: if we check nextState.brokenLimbs & prevState.brokenLimbs, we can tell when a damage flag has been added and flash that part of the ship
  2198. //FIXME: when ship explodes, either stop drawing ship or draw all parts black
  2199. CG_DrawVehicleDamage(veh,brokenLimbs,menuHUD,alpha,VEH_DAMAGE_FRONT);
  2200. CG_DrawVehicleDamage(veh,brokenLimbs,menuHUD,alpha,VEH_DAMAGE_BACK);
  2201. CG_DrawVehicleDamage(veh,brokenLimbs,menuHUD,alpha,VEH_DAMAGE_LEFT);
  2202. CG_DrawVehicleDamage(veh,brokenLimbs,menuHUD,alpha,VEH_DAMAGE_RIGHT);
  2203. }
  2204. qboolean CG_DrawVehicleHud( const centity_t *cent )
  2205. {
  2206. itemDef_t *item;
  2207. menuDef_t *menuHUD;
  2208. playerState_t *ps;
  2209. centity_t *veh;
  2210. float shieldPerc,alpha;
  2211. menuHUD = Menus_FindByName("swoopvehiclehud");
  2212. if (!menuHUD)
  2213. {
  2214. return qtrue; // Draw player HUD
  2215. }
  2216. ps = &cg.predictedPlayerState;
  2217. if (!ps || !(ps->m_iVehicleNum))
  2218. {
  2219. return qtrue; // Draw player HUD
  2220. }
  2221. veh = &cg_entities[ps->m_iVehicleNum];
  2222. if ( !veh || !veh->m_pVehicle )
  2223. {
  2224. return qtrue; // Draw player HUD
  2225. }
  2226. CG_DrawVehicleTurboRecharge( menuHUD, veh );
  2227. CG_DrawVehicleWeaponsLinked( menuHUD, veh );
  2228. item = Menu_FindItemByName(menuHUD, "leftframe");
  2229. // Draw frame
  2230. if (item)
  2231. {
  2232. trap_R_SetColor( item->window.foreColor );
  2233. CG_DrawPic(
  2234. item->window.rect.x,
  2235. item->window.rect.y,
  2236. item->window.rect.w,
  2237. item->window.rect.h,
  2238. item->window.background );
  2239. }
  2240. item = Menu_FindItemByName(menuHUD, "rightframe");
  2241. if (item)
  2242. {
  2243. trap_R_SetColor( item->window.foreColor );
  2244. CG_DrawPic(
  2245. item->window.rect.x,
  2246. item->window.rect.y,
  2247. item->window.rect.w,
  2248. item->window.rect.h,
  2249. item->window.background );
  2250. }
  2251. CG_DrawVehicleArmor( menuHUD, veh );
  2252. // Get animal hud for speed
  2253. // if (veh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
  2254. // {
  2255. // menuHUD = Menus_FindByName("tauntaunhud");
  2256. // }
  2257. CG_DrawVehicleSpeed( menuHUD, veh );
  2258. // Revert to swoophud
  2259. // if (veh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
  2260. // {
  2261. // menuHUD = Menus_FindByName("swoopvehiclehud");
  2262. // }
  2263. // if (veh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL)
  2264. // {
  2265. shieldPerc = CG_DrawVehicleShields( menuHUD, veh );
  2266. // }
  2267. if (veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID && !veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID)
  2268. {
  2269. CG_DrawVehicleAmmo( menuHUD, veh );
  2270. }
  2271. else if (veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID && veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID)
  2272. {
  2273. CG_DrawVehicleAmmoUpper( menuHUD, veh );
  2274. CG_DrawVehicleAmmoLower( menuHUD, veh );
  2275. }
  2276. // If he's hidden, he must be in a vehicle
  2277. if (veh->m_pVehicle->m_pVehicleInfo->hideRider)
  2278. {
  2279. CG_DrawVehicleDamageHUD(veh,cg.predictedVehicleState.brokenLimbs,shieldPerc,"vehicledamagehud",1.0f);
  2280. // Has he targeted an enemy?
  2281. if (CG_CheckTargetVehicle( &veh, &alpha ))
  2282. {
  2283. CG_DrawVehicleDamageHUD(veh,veh->currentState.brokenLimbs,((float)veh->currentState.activeForcePass/10.0f),"enemyvehicledamagehud",alpha);
  2284. }
  2285. return qfalse; // Don't draw player HUD
  2286. }
  2287. return qtrue; // Draw player HUD
  2288. }
  2289. /*
  2290. ================
  2291. CG_DrawStats
  2292. ================
  2293. */
  2294. static void CG_DrawStats( void ) {
  2295. centity_t *cent;
  2296. playerState_t *ps;
  2297. qboolean drawHUD = qtrue;
  2298. /* playerState_t *ps;
  2299. vec3_t angles;
  2300. // vec3_t origin;
  2301. if ( cg_drawStatus.integer == 0 ) {
  2302. return;
  2303. }
  2304. */
  2305. cent = &cg_entities[cg.snap->ps.clientNum];
  2306. /* ps = &cg.snap->ps;
  2307. VectorClear( angles );
  2308. // Do start
  2309. if (!cg.interfaceStartupDone)
  2310. {
  2311. CG_InterfaceStartup();
  2312. }
  2313. cgi_UI_MenuPaintAll();*/
  2314. if (cent) {
  2315. ps = &cg.predictedPlayerState;
  2316. if (ps->m_iVehicleNum) { // In a vehicle???
  2317. drawHUD = CG_DrawVehicleHud( cent );
  2318. }
  2319. }
  2320. if (drawHUD) {
  2321. CG_DrawHUD(cent);
  2322. }
  2323. /*CG_DrawArmor(cent);
  2324. CG_DrawHealth(cent);
  2325. CG_DrawAmmo(cent);
  2326. CG_DrawTalk(cent);*/
  2327. }
  2328. /*
  2329. ===================
  2330. CG_DrawPickupItem
  2331. ===================
  2332. */
  2333. static void CG_DrawPickupItem( void ) {
  2334. int value;
  2335. float *fadeColor;
  2336. int yOffset;
  2337. if ( cg_lagometer.integer ) {
  2338. yOffset = 170;
  2339. } else {
  2340. yOffset = 170 - ICON_SIZE;
  2341. }
  2342. value = cg.itemPickup;
  2343. if ( value && cg_items[ value ].icon != -1 )
  2344. {
  2345. fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
  2346. if ( fadeColor )
  2347. {
  2348. CG_RegisterItemVisuals( value );
  2349. trap_R_SetColor( fadeColor );
  2350. CG_DrawPic( SCREEN_WIDTH - (SCREEN_WIDTH - 585)*cgs.widthRatioCoef, SCREEN_HEIGHT - yOffset - ICON_SIZE, ICON_SIZE*cgs.widthRatioCoef, ICON_SIZE, cg_items[ value ].icon );
  2351. trap_R_SetColor( NULL );
  2352. }
  2353. }
  2354. }
  2355. static void CG_DrawMovementKeys( void ) {
  2356. usercmd_t cmd = { 0 };
  2357. char str1[32] = { 0 }, str2[32] = { 0 };
  2358. float w1 = 0.0f, w2 = 0.0f, height = 0.0f;
  2359. int fontIndex = FONT_MEDIUM;
  2360. if ( !cg_drawMovementKeys.integer || !cg.snap ) //RAZTODO: works with demo playback??
  2361. return;
  2362. if ( cg.clientNum == cg.predictedPlayerState.clientNum && !cg.demoPlayback )
  2363. trap_GetUserCmd( trap_GetCurrentCmdNumber(), &cmd );
  2364. else {
  2365. int moveDir = cg.snap->ps.movementDir;
  2366. float xyspeed = sqrtf( cg.snap->ps.velocity[0]*cg.snap->ps.velocity[0] + cg.snap->ps.velocity[1]*cg.snap->ps.velocity[1] );
  2367. if ( (cg.snap->ps.pm_flags & PMF_JUMP_HELD) )//zspeed > lastZSpeed || zspeed > 10 )
  2368. cmd.upmove = 1;
  2369. else if ( (cg.snap->ps.pm_flags & PMF_DUCKED) )
  2370. cmd.upmove = -1;
  2371. if ( xyspeed < 10 )
  2372. moveDir = -1;
  2373. switch ( moveDir ) {
  2374. case 0: // W
  2375. cmd.forwardmove = 1;
  2376. break;
  2377. case 1: // WA
  2378. cmd.forwardmove = 1;
  2379. cmd.rightmove = -1;
  2380. break;
  2381. case 2: // A
  2382. cmd.rightmove = -1;
  2383. break;
  2384. case 3: // AS
  2385. cmd.rightmove = -1;
  2386. cmd.forwardmove = -1;
  2387. break;
  2388. case 4: // S
  2389. cmd.forwardmove = -1;
  2390. break;
  2391. case 5: // SD
  2392. cmd.forwardmove = -1;
  2393. cmd.rightmove = 1;
  2394. break;
  2395. case 6: // D
  2396. cmd.rightmove = 1;
  2397. break;
  2398. case 7: // DW
  2399. cmd.rightmove = 1;
  2400. cmd.forwardmove = 1;
  2401. break;
  2402. default:
  2403. break;
  2404. }
  2405. }
  2406. w1 = CG_Text_Width( "v W ^", cg_drawMovementKeysScale.value, fontIndex );
  2407. w2 = CG_Text_Width( "A S D", cg_drawMovementKeysScale.value, fontIndex );
  2408. height = CG_Text_Height( "A S D v W ^", cg_drawMovementKeysScale.value, fontIndex );
  2409. Com_sprintf( str1, sizeof(str1), va( "^%cv ^%cW ^%c^", (cmd.upmove < 0) ? COLOR_RED : COLOR_WHITE,
  2410. (cmd.forwardmove > 0) ? COLOR_RED : COLOR_WHITE, (cmd.upmove > 0) ? COLOR_RED : COLOR_WHITE ) );
  2411. Com_sprintf( str2, sizeof(str2), va( "^%cA ^%cS ^%cD", (cmd.rightmove < 0) ? COLOR_RED : COLOR_WHITE,
  2412. (cmd.forwardmove < 0) ? COLOR_RED : COLOR_WHITE, (cmd.rightmove > 0) ? COLOR_RED : COLOR_WHITE ) );
  2413. CG_Text_Paint( cg.moveKeysPos[0] - max( w1, w2 ) / 2.0f, cg.moveKeysPos[1], cg_drawMovementKeysScale.value, colorWhite,
  2414. str1, 0.0f, 0, ITEM_TEXTSTYLE_OUTLINED, fontIndex );
  2415. CG_Text_Paint( cg.moveKeysPos[0] - max( w1, w2 ) / 2.0f, cg.moveKeysPos[1] + height, cg_drawMovementKeysScale.value,
  2416. colorWhite, str2, 0.0f, 0, ITEM_TEXTSTYLE_OUTLINED, fontIndex );
  2417. }
  2418. /*
  2419. ================
  2420. CG_DrawTeamBackground
  2421. ================
  2422. */
  2423. void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
  2424. {
  2425. vec4_t hcolor;
  2426. hcolor[3] = alpha;
  2427. if ( team == TEAM_RED ) {
  2428. hcolor[0] = 1;
  2429. hcolor[1] = .2f;
  2430. hcolor[2] = .2f;
  2431. } else if ( team == TEAM_BLUE ) {
  2432. hcolor[0] = .2f;
  2433. hcolor[1] = .2f;
  2434. hcolor[2] = 1;
  2435. } else {
  2436. return;
  2437. }
  2438. // trap_R_SetColor( hcolor );
  2439. CG_FillRect ( x, y, w, h, hcolor );
  2440. // CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
  2441. trap_R_SetColor( NULL );
  2442. }
  2443. /*
  2444. ===========================================================================================
  2445. UPPER RIGHT CORNER
  2446. ===========================================================================================
  2447. */
  2448. /*
  2449. ================
  2450. CG_DrawMiniScoreboard
  2451. ================
  2452. */
  2453. static float CG_DrawMiniScoreboard ( float y )
  2454. {
  2455. char temp[MAX_QPATH];
  2456. int xOffset = 0;
  2457. if ( !cg_drawScores.integer )
  2458. {
  2459. return y;
  2460. }
  2461. if (cgs.gametype == GT_SIEGE)
  2462. { //don't bother with this in siege
  2463. return y;
  2464. }
  2465. if ( cgs.gametype >= GT_TEAM )
  2466. {
  2467. strcpy ( temp, va("%s: ", CG_GetStringEdString("MP_INGAME", "RED")));
  2468. Q_strcat ( temp, MAX_QPATH, cgs.scores1==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores1)) );
  2469. Q_strcat ( temp, MAX_QPATH, va(" %s: ", CG_GetStringEdString("MP_INGAME", "BLUE")) );
  2470. Q_strcat ( temp, MAX_QPATH, cgs.scores2==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores2)) );
  2471. CG_Text_Paint( 630 - CG_Text_Width ( temp, 0.7f, FONT_MEDIUM ) + xOffset, y, 0.7f, colorWhite, temp, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM );
  2472. y += 15;
  2473. }
  2474. else
  2475. {
  2476. /*
  2477. strcpy ( temp, "1st: " );
  2478. Q_strcat ( temp, MAX_QPATH, cgs.scores1==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores1)) );
  2479. Q_strcat ( temp, MAX_QPATH, " 2nd: " );
  2480. Q_strcat ( temp, MAX_QPATH, cgs.scores2==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores2)) );
  2481. CG_Text_Paint( 630 - CG_Text_Width ( temp, 0.7f, FONT_SMALL ), y, 0.7f, colorWhite, temp, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM );
  2482. y += 15;
  2483. */
  2484. //rww - no longer doing this. Since the attacker now shows who is first, we print the score there.
  2485. }
  2486. return y;
  2487. }
  2488. /*
  2489. ================
  2490. CG_DrawEnemyInfo
  2491. ================
  2492. */
  2493. static float CG_DrawEnemyInfo ( float y ) {
  2494. float size;
  2495. int clientNum;
  2496. const char *title;
  2497. clientInfo_t *ci;
  2498. int xOffset = 0;
  2499. if (!cg.snap || !cg_drawEnemyInfo.integer || cgs.gametype == GT_POWERDUEL)
  2500. return y;
  2501. if ( cg.playerPredicted && cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 )
  2502. return y;
  2503. if ( cgs.gametype == GT_JEDIMASTER ) {
  2504. //title = "Jedi Master";
  2505. title = CG_GetStringEdString("MP_INGAME", "MASTERY7");
  2506. clientNum = cgs.jediMaster;
  2507. if ( clientNum < 0 ) {
  2508. //return y;
  2509. // title = "Get Saber!";
  2510. title = CG_GetStringEdString("MP_INGAME", "GET_SABER");
  2511. size = ICON_SIZE * 1.25;
  2512. y += 5;
  2513. CG_DrawPic( SCREEN_WIDTH - size*cgs.widthRatioCoef - 12 + xOffset, y, size*cgs.widthRatioCoef, size, cgs.media.weaponIcons[WP_SABER] );
  2514. y += size;
  2515. CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( title, 0.7f, FONT_MEDIUM ) + xOffset, y, 0.7f, colorWhite, title, 0, 0, 0, FONT_MEDIUM );
  2516. return y + BIGCHAR_HEIGHT + 2;
  2517. }
  2518. } else if ( cg.snap->ps.duelInProgress ) {
  2519. // title = "Dueling";
  2520. title = CG_GetStringEdString("MP_INGAME", "DUELING");
  2521. // good fix yeah?
  2522. if (cg.playerPredicted)
  2523. clientNum = cg.snap->ps.duelIndex;
  2524. else
  2525. clientNum = (cg.playerCent->currentState.number == cg.snap->ps.clientNum) ? cg.snap->ps.clientNum : cg.snap->ps.duelIndex;
  2526. }
  2527. else if ( cgs.gametype == GT_DUEL && cgs.clientinfo[cg.playerCent->currentState.number].team != TEAM_SPECTATOR) {
  2528. title = CG_GetStringEdString("MP_INGAME", "DUELING");
  2529. if (cg.playerCent->currentState.number == cgs.duelist1)
  2530. clientNum = cgs.duelist2; //if power duel, should actually draw both duelists 2 and 3 I guess
  2531. else if (cg.playerCent->currentState.number == cgs.duelist2)
  2532. clientNum = cgs.duelist1;
  2533. else if (cg.playerCent->currentState.number == cgs.duelist3)
  2534. clientNum = cgs.duelist1;
  2535. else
  2536. return y;
  2537. } else {
  2538. //As of current, we don't want to draw the attacker. Instead, draw whoever is in first place.
  2539. if (cgs.duelWinner < 0 || cgs.duelWinner >= MAX_CLIENTS)
  2540. return y;
  2541. title = va("%s: %i",CG_GetStringEdString("MP_INGAME", "LEADER"), cgs.scores1);
  2542. clientNum = cgs.duelWinner;
  2543. }
  2544. if ( clientNum >= MAX_CLIENTS || !(&cgs.clientinfo[ clientNum ]) )
  2545. return y;
  2546. ci = &cgs.clientinfo[ clientNum ];
  2547. size = ICON_SIZE * 1.25;
  2548. y += 5;
  2549. if ( ci->modelIcon )
  2550. CG_DrawPic( SCREEN_WIDTH - size*cgs.widthRatioCoef - 5 + xOffset, y, size*cgs.widthRatioCoef, size, ci->modelIcon );
  2551. y += size;
  2552. // CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( ci->name, 0.7f, FONT_MEDIUM ) + xOffset, y, 0.7f, colorWhite, ci->name, 0, 0, 0, FONT_MEDIUM );
  2553. CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( ci->name, 1.0f, FONT_SMALL2 ) + xOffset, y, 1.0f, colorWhite, ci->name, 0, 0, 0, FONT_SMALL2 );
  2554. y += 15;
  2555. // CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( title, 0.7f, FONT_MEDIUM ) + xOffset, y, 0.7f, colorWhite, title, 0, 0, 0, FONT_MEDIUM );
  2556. CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( title, 1.0f, FONT_SMALL2 ) + xOffset, y, 1.0f, colorWhite, title, 0, 0, 0, FONT_SMALL2 );
  2557. if ( (cgs.gametype == GT_DUEL || cgs.gametype == GT_POWERDUEL) && cgs.clientinfo[cg.playerCent->currentState.number].team != TEAM_SPECTATOR) {
  2558. //also print their score
  2559. char text[1024];
  2560. y += 23;//15;
  2561. Com_sprintf(text, sizeof(text), "%i/%i", cgs.clientinfo[clientNum].score, cgs.fraglimit );
  2562. CG_Text_Paint( SCREEN_WIDTH - 10 - CG_Text_Width ( text, 0.7f, FONT_MEDIUM ) + xOffset, y, 0.7f, colorWhite, text, 0, 0, 0, FONT_MEDIUM );
  2563. }
  2564. // nmckenzie: DUEL_HEALTH - fixme - need checks and such here. And this is coded to duelist 1 right now, which is wrongly.
  2565. if ( cgs.showDuelHealths >= 2) {
  2566. y += 15;
  2567. if ( cgs.duelist1 == clientNum )
  2568. CG_DrawDuelistHealth ( SCREEN_WIDTH - size - 5 + xOffset, y, 64, 8, 1 );
  2569. else if ( cgs.duelist2 == clientNum )
  2570. CG_DrawDuelistHealth ( SCREEN_WIDTH - size - 5 + xOffset, y, 64, 8, 2 );
  2571. }
  2572. return y + BIGCHAR_HEIGHT + 2;
  2573. }
  2574. /*
  2575. ==================
  2576. CG_DrawSnapshot
  2577. ==================
  2578. */
  2579. static float CG_DrawSnapshot( float y ) {
  2580. char *s;
  2581. int w;
  2582. int xOffset = 0;
  2583. s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime,
  2584. cg.latestSnapshotNum, cgs.serverCommandSequence );
  2585. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  2586. CG_DrawBigString( 635 - w + xOffset, y + 2, s, 1.0F);
  2587. return y + BIGCHAR_HEIGHT + 4;
  2588. }
  2589. /*
  2590. ==================
  2591. CG_DrawFPS
  2592. ==================
  2593. */
  2594. #define FPS_FRAMES 16
  2595. float CG_DrawFPS( float y ) {
  2596. char *s;
  2597. int w;
  2598. static unsigned short previousTimes[FPS_FRAMES];
  2599. static unsigned short index;
  2600. static int previous, lastupdate;
  2601. int t, i, fps, total;
  2602. unsigned short frameTime;
  2603. const int xOffset = 0;
  2604. // don't use serverTime, because that will be drifting to
  2605. // correct for internet lag changes, timescales, timedemos, etc
  2606. t = trap_Milliseconds();
  2607. frameTime = t - previous;
  2608. previous = t;
  2609. if (t - lastupdate > 50) //don't sample faster than this
  2610. {
  2611. lastupdate = t;
  2612. previousTimes[index % FPS_FRAMES] = frameTime;
  2613. index++;
  2614. }
  2615. // average multiple frames together to smooth changes out a bit
  2616. total = 0;
  2617. for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
  2618. total += previousTimes[i];
  2619. }
  2620. if ( !total ) {
  2621. total = 1;
  2622. }
  2623. fps = 1000 * FPS_FRAMES / total;
  2624. s = va( "%ifps", fps );
  2625. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  2626. CG_DrawBigString( 635 - w + xOffset, y + 2, s, 1.0F);
  2627. return y + BIGCHAR_HEIGHT + 4;
  2628. }
  2629. // nmckenzie: DUEL_HEALTH
  2630. #define MAX_HEALTH_FOR_IFACE 100
  2631. void CG_DrawHealthBarRough (float x, float y, int width, int height, float ratio, const float *color1, const float *color2)
  2632. {
  2633. float midpoint, remainder;
  2634. float color3[4] = {1, 0, 0, .7f};
  2635. midpoint = width * ratio - 1;
  2636. remainder = width - midpoint;
  2637. color3[0] = color1[0] * 0.5f;
  2638. assert(!(height%4));//this won't line up otherwise.
  2639. CG_DrawRect(x + 1, y + height/2-1, midpoint, 1, height/4+1, color1); // creme-y filling.
  2640. CG_DrawRect(x + midpoint, y + height/2-1, remainder, 1, height/4+1, color3); // used-up-ness.
  2641. CG_DrawRect(x, y, width, height, 1, color2); // hard crispy shell
  2642. }
  2643. void CG_DrawDuelistHealth ( float x, float y, float w, float h, int duelist )
  2644. {
  2645. float duelHealthColor[4] = {1, 0, 0, 0.7f};
  2646. float healthSrc = 0.0f;
  2647. float ratio;
  2648. if ( duelist == 1 )
  2649. {
  2650. healthSrc = cgs.duelist1health;
  2651. }
  2652. else if (duelist == 2 )
  2653. {
  2654. healthSrc = cgs.duelist2health;
  2655. }
  2656. ratio = healthSrc / MAX_HEALTH_FOR_IFACE;
  2657. if ( ratio > 1.0f )
  2658. {
  2659. ratio = 1.0f;
  2660. }
  2661. if ( ratio < 0.0f )
  2662. {
  2663. ratio = 0.0f;
  2664. }
  2665. duelHealthColor[0] = (ratio * 0.2f) + 0.5f;
  2666. CG_DrawHealthBarRough (x, y, w, h, ratio, duelHealthColor, colorTable[CT_WHITE]); // new art for this? I'm not crazy about how this looks.
  2667. }
  2668. /*
  2669. =====================
  2670. CG_DrawRadar
  2671. =====================
  2672. */
  2673. //#define RADAR_RANGE 2500
  2674. float cg_radarRange = 2500.0f;
  2675. #define RADAR_RADIUS 60
  2676. #define RADAR_X (SCREEN_WIDTH - (SCREEN_WIDTH - 580)*cgs.widthRatioCoef - RADAR_RADIUS*cgs.widthRatioCoef)
  2677. //#define RADAR_Y 10 //dynamic based on passed Y val
  2678. #define RADAR_CHAT_DURATION 6000
  2679. static int radarLockSoundDebounceTime = 0;
  2680. static int impactSoundDebounceTime = 0;
  2681. #define RADAR_MISSILE_RANGE 3000.0f
  2682. #define RADAR_ASTEROID_RANGE 10000.0f
  2683. #define RADAR_MIN_ASTEROID_SURF_WARN_DIST 1200.0f
  2684. float CG_DrawRadar ( float y )
  2685. {
  2686. vec4_t color;
  2687. vec4_t teamColor;
  2688. float arrow_w;
  2689. float arrow_h;
  2690. clientInfo_t *cl;
  2691. clientInfo_t *local;
  2692. int i;
  2693. float arrowBaseScale;
  2694. float zScale;
  2695. int xOffset = 0;
  2696. int radar_x;
  2697. // Make sure the radar should be showing
  2698. if (!cg.snap || cg.snap->ps.stats[STAT_HEALTH] <= 0 || !cg.playerPredicted)
  2699. return y;
  2700. if ( (cg.predictedPlayerState.pm_flags & PMF_FOLLOW) || cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR )
  2701. return y;
  2702. local = &cgs.clientinfo[ cg.snap->ps.clientNum ];
  2703. if ( !local->infoValid )
  2704. return y;
  2705. radar_x = SCREEN_WIDTH - (SCREEN_WIDTH - (580 - RADAR_RADIUS)) * cgs.widthRatioCoef;
  2706. // Draw the radar background image
  2707. color[0] = color[1] = color[2] = 1.0f;
  2708. color[3] = 0.6f;
  2709. trap_R_SetColor ( color );
  2710. CG_DrawPic( /*RADAR_X*/radar_x + xOffset, y, RADAR_RADIUS*2*cgs.widthRatioCoef, RADAR_RADIUS*2, cgs.media.radarShader );
  2711. //Always green for your own team.
  2712. VectorCopy ( g_color_table[ColorIndex(COLOR_GREEN)], teamColor );
  2713. teamColor[3] = 1.0f;
  2714. // Draw all of the radar entities. Draw them backwards so players are drawn last
  2715. for ( i = cg.radarEntityCount -1 ; i >= 0 ; i-- )
  2716. {
  2717. vec3_t dirLook;
  2718. vec3_t dirPlayer;
  2719. float angleLook;
  2720. float anglePlayer;
  2721. float angle;
  2722. float distance, actualDist;
  2723. centity_t* cent;
  2724. cent = &cg_entities[cg.radarEntities[i]];
  2725. // Get the distances first
  2726. VectorSubtract ( cg.predictedPlayerState.origin, cent->lerpOrigin, dirPlayer );
  2727. dirPlayer[2] = 0;
  2728. actualDist = distance = VectorNormalize ( dirPlayer );
  2729. if ( distance > cg_radarRange * 0.8f)
  2730. {
  2731. if ( (cent->currentState.eFlags & EF_RADAROBJECT)//still want to draw the direction
  2732. || ( cent->currentState.eType==ET_NPC//FIXME: draw last, with players...
  2733. && cent->currentState.NPC_class == CLASS_VEHICLE
  2734. && cent->currentState.speed > 0 ) )//always draw vehicles
  2735. {
  2736. distance = cg_radarRange*0.8f;
  2737. }
  2738. else
  2739. {
  2740. continue;
  2741. }
  2742. }
  2743. distance = distance / cg_radarRange;
  2744. distance *= RADAR_RADIUS;
  2745. AngleVectors ( cg.predictedPlayerState.viewangles, dirLook, NULL, NULL );
  2746. dirLook[2] = 0;
  2747. anglePlayer = atan2(dirPlayer[0],dirPlayer[1]);
  2748. VectorNormalize ( dirLook );
  2749. angleLook = atan2(dirLook[0],dirLook[1]);
  2750. angle = angleLook - anglePlayer;
  2751. switch ( cent->currentState.eType )
  2752. {
  2753. default:
  2754. {
  2755. float x;
  2756. float ly;
  2757. qhandle_t shader;
  2758. vec4_t color;
  2759. x = (float)/*RADAR_X*/radar_x + ((float)RADAR_RADIUS + (float)sin (angle) * distance)*cgs.widthRatioCoef;
  2760. ly = y + (float)RADAR_RADIUS + (float)cos (angle) * distance;
  2761. arrowBaseScale = 9.0f;
  2762. shader = 0;
  2763. zScale = 1.0f;
  2764. //we want to scale the thing up/down based on the relative Z (up/down) positioning
  2765. if (cent->lerpOrigin[2] > cg.predictedPlayerState.origin[2])
  2766. { //higher, scale up (between 16 and 24)
  2767. float dif = (cent->lerpOrigin[2] - cg.predictedPlayerState.origin[2]);
  2768. //max out to 1.5x scale at 512 units above local player's height
  2769. dif /= 1024.0f;
  2770. if (dif > 0.5f)
  2771. {
  2772. dif = 0.5f;
  2773. }
  2774. zScale += dif;
  2775. }
  2776. else if (cent->lerpOrigin[2] < cg.predictedPlayerState.origin[2])
  2777. { //lower, scale down (between 16 and 8)
  2778. float dif = (cg.predictedPlayerState.origin[2] - cent->lerpOrigin[2]);
  2779. //half scale at 512 units below local player's height
  2780. dif /= 1024.0f;
  2781. if (dif > 0.5f)
  2782. {
  2783. dif = 0.5f;
  2784. }
  2785. zScale -= dif;
  2786. }
  2787. arrowBaseScale *= zScale;
  2788. if (cent->currentState.brokenLimbs)
  2789. { //slightly misleading to use this value, but don't want to add more to entstate.
  2790. //any ent with brokenLimbs non-0 and on radar is an objective ent.
  2791. //brokenLimbs is literal team value.
  2792. char objState[1024];
  2793. int complete;
  2794. //we only want to draw it if the objective for it is not complete.
  2795. //frame represents objective num.
  2796. trap_Cvar_VariableStringBuffer(va("team%i_objective%i", cent->currentState.brokenLimbs, cent->currentState.frame), objState, 1024);
  2797. complete = atoi(objState);
  2798. if (!complete)
  2799. {
  2800. // generic enemy index specifies a shader to use for the radar entity.
  2801. if ( cent->currentState.genericenemyindex && cent->currentState.genericenemyindex < MAX_ICONS )
  2802. {
  2803. color[0] = color[1] = color[2] = color[3] = 1.0f;
  2804. shader = cgs.gameIcons[cent->currentState.genericenemyindex];
  2805. }
  2806. else
  2807. {
  2808. if (cg.snap &&
  2809. cent->currentState.brokenLimbs == cg.snap->ps.persistant[PERS_TEAM])
  2810. {
  2811. VectorCopy ( g_color_table[ColorIndex(COLOR_RED)], color );
  2812. }
  2813. else
  2814. {
  2815. VectorCopy ( g_color_table[ColorIndex(COLOR_GREEN)], color );
  2816. }
  2817. shader = cgs.media.siegeItemShader;
  2818. }
  2819. }
  2820. }
  2821. else
  2822. {
  2823. color[0] = color[1] = color[2] = color[3] = 1.0f;
  2824. // generic enemy index specifies a shader to use for the radar entity.
  2825. if ( cent->currentState.genericenemyindex )
  2826. {
  2827. shader = cgs.gameIcons[cent->currentState.genericenemyindex];
  2828. }
  2829. else
  2830. {
  2831. shader = cgs.media.siegeItemShader;
  2832. }
  2833. }
  2834. if ( shader )
  2835. {
  2836. // Pulse the alpha if time2 is set. time2 gets set when the entity takes pain
  2837. if ( (cent->currentState.time2 && cg.time - cent->currentState.time2 < 5000) ||
  2838. (cent->currentState.time2 == 0xFFFFFFFF) )
  2839. {
  2840. if ( (cg.time / 200) & 1 )
  2841. {
  2842. color[3] = 0.1f + 0.9f * (float) (cg.time % 200) / 200.0f;
  2843. }
  2844. else
  2845. {
  2846. color[3] = 1.0f - 0.9f * (float) (cg.time % 200) / 200.0f;
  2847. }
  2848. }
  2849. trap_R_SetColor ( color );
  2850. CG_DrawPic ( x - 4 + xOffset, ly - 4, arrowBaseScale*cgs.widthRatioCoef, arrowBaseScale, shader );
  2851. }
  2852. }
  2853. break;
  2854. case ET_NPC://FIXME: draw last, with players...
  2855. if ( cent->currentState.NPC_class == CLASS_VEHICLE
  2856. && cent->currentState.speed > 0 )
  2857. {
  2858. if ( cent->m_pVehicle && cent->m_pVehicle->m_pVehicleInfo->radarIconHandle )
  2859. {
  2860. float x;
  2861. float ly;
  2862. x = (float)/*RADAR_X*/radar_x + ((float)RADAR_RADIUS + (float)sin (angle) * distance)*cgs.widthRatioCoef;
  2863. ly = y + (float)RADAR_RADIUS + (float)cos (angle) * distance;
  2864. arrowBaseScale = 9.0f;
  2865. zScale = 1.0f;
  2866. //we want to scale the thing up/down based on the relative Z (up/down) positioning
  2867. if (cent->lerpOrigin[2] > cg.predictedPlayerState.origin[2])
  2868. { //higher, scale up (between 16 and 24)
  2869. float dif = (cent->lerpOrigin[2] - cg.predictedPlayerState.origin[2]);
  2870. //max out to 1.5x scale at 512 units above local player's height
  2871. dif /= 4096.0f;
  2872. if (dif > 0.5f)
  2873. {
  2874. dif = 0.5f;
  2875. }
  2876. zScale += dif;
  2877. }
  2878. else if (cent->lerpOrigin[2] < cg.predictedPlayerState.origin[2])
  2879. { //lower, scale down (between 16 and 8)
  2880. float dif = (cg.predictedPlayerState.origin[2] - cent->lerpOrigin[2]);
  2881. //half scale at 512 units below local player's height
  2882. dif /= 4096.0f;
  2883. if (dif > 0.5f)
  2884. {
  2885. dif = 0.5f;
  2886. }
  2887. zScale -= dif;
  2888. }
  2889. arrowBaseScale *= zScale;
  2890. if ( cent->currentState.m_iVehicleNum //vehicle has a driver
  2891. && cgs.clientinfo[ cent->currentState.m_iVehicleNum-1 ].infoValid )
  2892. {
  2893. if ( cgs.clientinfo[ cent->currentState.m_iVehicleNum-1 ].team == local->team )
  2894. {
  2895. trap_R_SetColor ( teamColor );
  2896. }
  2897. else
  2898. {
  2899. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  2900. }
  2901. }
  2902. else
  2903. {
  2904. trap_R_SetColor ( NULL );
  2905. }
  2906. CG_DrawPic ( x - 4 + xOffset, ly - 4, arrowBaseScale*cgs.widthRatioCoef, arrowBaseScale, cent->m_pVehicle->m_pVehicleInfo->radarIconHandle );
  2907. }
  2908. }
  2909. break; //maybe do something?
  2910. case ET_MOVER:
  2911. if ( cent->currentState.speed//the mover's size, actually
  2912. && actualDist < (cent->currentState.speed+RADAR_ASTEROID_RANGE)
  2913. && cg.predictedPlayerState.m_iVehicleNum )
  2914. {//a mover that's close to me and I'm in a vehicle
  2915. qboolean mayImpact = qfalse;
  2916. float surfaceDist = (actualDist-cent->currentState.speed);
  2917. if ( surfaceDist < 0.0f )
  2918. {
  2919. surfaceDist = 0.0f;
  2920. }
  2921. if ( surfaceDist < RADAR_MIN_ASTEROID_SURF_WARN_DIST )
  2922. {//always warn!
  2923. mayImpact = qtrue;
  2924. }
  2925. else
  2926. {//not close enough to always warn, yet, so check its direction
  2927. vec3_t asteroidPos, myPos, moveDir;
  2928. int predictTime, timeStep = 500;
  2929. float newDist;
  2930. for ( predictTime = timeStep; predictTime < 5000; predictTime+=timeStep )
  2931. {
  2932. //asteroid dir, speed, size, + my dir & speed...
  2933. BG_EvaluateTrajectory( &cent->currentState.pos, cg.time+predictTime, asteroidPos );
  2934. //FIXME: I don't think it's calcing "myPos" correctly
  2935. AngleVectors( cg.predictedVehicleState.viewangles, moveDir, NULL, NULL );
  2936. VectorMA( cg.predictedVehicleState.origin, cg.predictedVehicleState.speed*predictTime/1000.0f, moveDir, myPos );
  2937. newDist = Distance( myPos, asteroidPos );
  2938. if ( (newDist-cent->currentState.speed) <= RADAR_MIN_ASTEROID_SURF_WARN_DIST )//200.0f )
  2939. {//heading for an impact within the next 5 seconds
  2940. mayImpact = qtrue;
  2941. break;
  2942. }
  2943. }
  2944. }
  2945. if ( mayImpact )
  2946. {//possible collision
  2947. vec4_t asteroidColor = {0.5f,0.5f,0.5f,1.0f};
  2948. float x;
  2949. float ly;
  2950. float asteroidScale = (cent->currentState.speed/2000.0f);//average asteroid radius?
  2951. if ( actualDist > RADAR_ASTEROID_RANGE )
  2952. {
  2953. actualDist = RADAR_ASTEROID_RANGE;
  2954. }
  2955. distance = (actualDist/RADAR_ASTEROID_RANGE)*RADAR_RADIUS;
  2956. x = (float)/*RADAR_X*/radar_x + ((float)RADAR_RADIUS + (float)sin (angle) * distance)*cgs.widthRatioCoef;
  2957. ly = y + (float)RADAR_RADIUS + (float)cos (angle) * distance;
  2958. if ( asteroidScale > 3.0f )
  2959. {
  2960. asteroidScale = 3.0f;
  2961. }
  2962. else if ( asteroidScale < 0.2f )
  2963. {
  2964. asteroidScale = 0.2f;
  2965. }
  2966. arrowBaseScale = (9.0f*asteroidScale);
  2967. if ( impactSoundDebounceTime < cg.time )
  2968. {
  2969. vec3_t soundOrg;
  2970. if ( surfaceDist > RADAR_ASTEROID_RANGE*0.66f )
  2971. {
  2972. impactSoundDebounceTime = cg.time + 1000;
  2973. }
  2974. else if ( surfaceDist > RADAR_ASTEROID_RANGE/3.0f )
  2975. {
  2976. impactSoundDebounceTime = cg.time + 400;
  2977. }
  2978. else
  2979. {
  2980. impactSoundDebounceTime = cg.time + 100;
  2981. }
  2982. VectorMA( cg.refdef.vieworg, -500.0f*(surfaceDist/RADAR_ASTEROID_RANGE), dirPlayer, soundOrg );
  2983. trap_S_StartSound( soundOrg, ENTITYNUM_WORLD, CHAN_AUTO, trap_S_RegisterSound( "sound/vehicles/common/impactalarm.wav" ) );
  2984. }
  2985. //brighten it the closer it is
  2986. if ( surfaceDist > RADAR_ASTEROID_RANGE*0.66f )
  2987. {
  2988. asteroidColor[0] = asteroidColor[1] = asteroidColor[2] = 0.7f;
  2989. }
  2990. else if ( surfaceDist > RADAR_ASTEROID_RANGE/3.0f )
  2991. {
  2992. asteroidColor[0] = asteroidColor[1] = asteroidColor[2] = 0.85f;
  2993. }
  2994. else
  2995. {
  2996. asteroidColor[0] = asteroidColor[1] = asteroidColor[2] = 1.0f;
  2997. }
  2998. //alpha out the longer it's been since it was considered dangerous
  2999. if ( (cg.time-impactSoundDebounceTime) > 100 )
  3000. {
  3001. asteroidColor[3] = (float)((cg.time-impactSoundDebounceTime)+cg.timeFraction-100.0f)/900.0f;
  3002. }
  3003. trap_R_SetColor ( asteroidColor );
  3004. CG_DrawPic ( x - 4 + xOffset, ly - 4, arrowBaseScale*cgs.widthRatioCoef, arrowBaseScale, trap_R_RegisterShaderNoMip( "gfx/menus/radar/asteroid" ) );
  3005. }
  3006. }
  3007. break;
  3008. case ET_MISSILE:
  3009. if ( //cent->currentState.weapon == WP_ROCKET_LAUNCHER &&//a rocket
  3010. cent->currentState.owner > MAX_CLIENTS //belongs to an NPC
  3011. && cg_entities[cent->currentState.owner].currentState.NPC_class == CLASS_VEHICLE )
  3012. {//a rocket belonging to an NPC, FIXME: only tracking rockets!
  3013. float x;
  3014. float ly;
  3015. x = (float)/*RADAR_X*/radar_x + ((float)RADAR_RADIUS + (float)sin (angle) * distance)*cgs.widthRatioCoef;
  3016. ly = y + (float)RADAR_RADIUS + (float)cos (angle) * distance;
  3017. arrowBaseScale = 3.0f;
  3018. if ( cg.predictedPlayerState.m_iVehicleNum )
  3019. {//I'm in a vehicle
  3020. //if it's targetting me, then play an alarm sound if I'm in a vehicle
  3021. if ( cent->currentState.otherEntityNum == cg.predictedPlayerState.clientNum || cent->currentState.otherEntityNum == cg.predictedPlayerState.m_iVehicleNum )
  3022. {
  3023. if ( radarLockSoundDebounceTime < cg.time )
  3024. {
  3025. vec3_t soundOrg;
  3026. int alarmSound;
  3027. if ( actualDist > RADAR_MISSILE_RANGE * 0.66f )
  3028. {
  3029. radarLockSoundDebounceTime = cg.time + 1000;
  3030. arrowBaseScale = 3.0f;
  3031. alarmSound = trap_S_RegisterSound( "sound/vehicles/common/lockalarm1.wav" );
  3032. }
  3033. else if ( actualDist > RADAR_MISSILE_RANGE/3.0f )
  3034. {
  3035. radarLockSoundDebounceTime = cg.time + 500;
  3036. arrowBaseScale = 6.0f;
  3037. alarmSound = trap_S_RegisterSound( "sound/vehicles/common/lockalarm2.wav" );
  3038. }
  3039. else
  3040. {
  3041. radarLockSoundDebounceTime = cg.time + 250;
  3042. arrowBaseScale = 9.0f;
  3043. alarmSound = trap_S_RegisterSound( "sound/vehicles/common/lockalarm3.wav" );
  3044. }
  3045. if ( actualDist > RADAR_MISSILE_RANGE )
  3046. {
  3047. actualDist = RADAR_MISSILE_RANGE;
  3048. }
  3049. VectorMA( cg.refdef.vieworg, -500.0f*(actualDist/RADAR_MISSILE_RANGE), dirPlayer, soundOrg );
  3050. trap_S_StartSound( soundOrg, ENTITYNUM_WORLD, CHAN_AUTO, alarmSound );
  3051. }
  3052. }
  3053. }
  3054. zScale = 1.0f;
  3055. //we want to scale the thing up/down based on the relative Z (up/down) positioning
  3056. if (cent->lerpOrigin[2] > cg.predictedPlayerState.origin[2])
  3057. { //higher, scale up (between 16 and 24)
  3058. float dif = (cent->lerpOrigin[2] - cg.predictedPlayerState.origin[2]);
  3059. //max out to 1.5x scale at 512 units above local player's height
  3060. dif /= 1024.0f;
  3061. if (dif > 0.5f)
  3062. {
  3063. dif = 0.5f;
  3064. }
  3065. zScale += dif;
  3066. }
  3067. else if (cent->lerpOrigin[2] < cg.predictedPlayerState.origin[2])
  3068. { //lower, scale down (between 16 and 8)
  3069. float dif = (cg.predictedPlayerState.origin[2] - cent->lerpOrigin[2]);
  3070. //half scale at 512 units below local player's height
  3071. dif /= 1024.0f;
  3072. if (dif > 0.5f)
  3073. {
  3074. dif = 0.5f;
  3075. }
  3076. zScale -= dif;
  3077. }
  3078. arrowBaseScale *= zScale;
  3079. if ( cent->currentState.owner >= MAX_CLIENTS//missile owned by an NPC
  3080. && cg_entities[cent->currentState.owner].currentState.NPC_class == CLASS_VEHICLE//NPC is a vehicle
  3081. && cg_entities[cent->currentState.owner].currentState.m_iVehicleNum <= MAX_CLIENTS//Vehicle has a player driver
  3082. && cgs.clientinfo[cg_entities[cent->currentState.owner].currentState.m_iVehicleNum-1].infoValid ) //player driver is valid
  3083. {
  3084. cl = &cgs.clientinfo[cg_entities[cent->currentState.owner].currentState.m_iVehicleNum-1];
  3085. if ( cl->team == local->team )
  3086. {
  3087. trap_R_SetColor ( teamColor );
  3088. }
  3089. else
  3090. {
  3091. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  3092. }
  3093. }
  3094. else
  3095. {
  3096. trap_R_SetColor ( NULL );
  3097. }
  3098. CG_DrawPic ( x - 4 + xOffset, ly - 4, arrowBaseScale*cgs.widthRatioCoef, arrowBaseScale, cgs.media.mAutomapRocketIcon );
  3099. }
  3100. break;
  3101. case ET_PLAYER:
  3102. {
  3103. vec4_t color;
  3104. cl = &cgs.clientinfo[ cent->currentState.number ];
  3105. // not valid then dont draw it
  3106. if ( !cl->infoValid )
  3107. {
  3108. continue;
  3109. }
  3110. VectorCopy4 ( teamColor, color );
  3111. arrowBaseScale = 16.0f;
  3112. zScale = 1.0f;
  3113. // Pulse the radar icon after a voice message
  3114. if ( cent->vChatTime + 2000 > cg.time )
  3115. {
  3116. float f = ((cent->vChatTime + 2000 - cg.time) - cg.timeFraction) / 3000.0f;
  3117. arrowBaseScale = 16.0f + 4.0f * f;
  3118. color[0] = teamColor[0] + (1.0f - teamColor[0]) * f;
  3119. color[1] = teamColor[1] + (1.0f - teamColor[1]) * f;
  3120. color[2] = teamColor[2] + (1.0f - teamColor[2]) * f;
  3121. }
  3122. trap_R_SetColor ( color );
  3123. //we want to scale the thing up/down based on the relative Z (up/down) positioning
  3124. if (cent->lerpOrigin[2] > cg.predictedPlayerState.origin[2])
  3125. { //higher, scale up (between 16 and 32)
  3126. float dif = (cent->lerpOrigin[2] - cg.predictedPlayerState.origin[2]);
  3127. //max out to 2x scale at 1024 units above local player's height
  3128. dif /= 1024.0f;
  3129. if (dif > 1.0f)
  3130. {
  3131. dif = 1.0f;
  3132. }
  3133. zScale += dif;
  3134. }
  3135. else if (cent->lerpOrigin[2] < cg.predictedPlayerState.origin[2])
  3136. { //lower, scale down (between 16 and 8)
  3137. float dif = (cg.predictedPlayerState.origin[2] - cent->lerpOrigin[2]);
  3138. //half scale at 512 units below local player's height
  3139. dif /= 1024.0f;
  3140. if (dif > 0.5f)
  3141. {
  3142. dif = 0.5f;
  3143. }
  3144. zScale -= dif;
  3145. }
  3146. arrowBaseScale *= zScale;
  3147. arrow_w = arrowBaseScale * RADAR_RADIUS / 128;
  3148. arrow_h = arrowBaseScale * RADAR_RADIUS / 128;
  3149. CG_DrawRotatePic2( /*RADAR_X*/radar_x + (RADAR_RADIUS + sin (angle) * distance + xOffset)*cgs.widthRatioCoef,
  3150. y + RADAR_RADIUS + cos (angle) * distance,
  3151. arrow_w*cgs.widthRatioCoef, arrow_h,
  3152. (360 - cent->lerpAngles[YAW]) + cg.predictedPlayerState.viewangles[YAW], cgs.media.mAutomapPlayerIcon );
  3153. break;
  3154. }
  3155. }
  3156. }
  3157. arrowBaseScale = 16.0f;
  3158. arrow_w = arrowBaseScale * RADAR_RADIUS / 128;
  3159. arrow_h = arrowBaseScale * RADAR_RADIUS / 128;
  3160. trap_R_SetColor ( colorWhite );
  3161. CG_DrawRotatePic2( /*RADAR_X*/radar_x + (RADAR_RADIUS + xOffset)*cgs.widthRatioCoef, y + RADAR_RADIUS, arrow_w*cgs.widthRatioCoef, arrow_h,
  3162. 0, cgs.media.mAutomapPlayerIcon );
  3163. return y+(RADAR_RADIUS*2);
  3164. }
  3165. /*
  3166. =================
  3167. CG_DrawTimer
  3168. =================
  3169. */
  3170. static float CG_DrawTimer( float y ) {
  3171. char *s;
  3172. int w;
  3173. int mins, seconds, tens;
  3174. int msec;
  3175. int xOffset = 0;
  3176. msec = cg.time - cgs.levelStartTime;
  3177. seconds = msec / 1000;
  3178. mins = seconds / 60;
  3179. seconds -= mins * 60;
  3180. tens = seconds / 10;
  3181. seconds -= tens * 10;
  3182. s = va( "%i:%i%i", mins, tens, seconds );
  3183. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  3184. CG_DrawBigString( 635 - w + xOffset, y + 2, s, 1.0F);
  3185. return y + BIGCHAR_HEIGHT + 4;
  3186. }
  3187. /*
  3188. =================
  3189. CG_DrawTeamOverlay
  3190. =================
  3191. */
  3192. extern const char *CG_GetLocationString(const char *loc); //cg_main.c
  3193. static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) {
  3194. int x, w, h, xx;
  3195. int i, j, len;
  3196. const char *p;
  3197. vec4_t hcolor;
  3198. int pwidth, lwidth;
  3199. int plyrs;
  3200. char st[16];
  3201. clientInfo_t *ci;
  3202. gitem_t *item;
  3203. int ret_y, count;
  3204. int xOffset = 0;
  3205. if ( !cg_drawTeamOverlay.integer ) {
  3206. return y;
  3207. }
  3208. if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) {
  3209. return y; // Not on any team
  3210. }
  3211. plyrs = 0;
  3212. //TODO: On basejka servers, we won't have valid teaminfo if we're spectating someone.
  3213. // Find a way to detect invalid info and return early?
  3214. // max player name width
  3215. pwidth = 0;
  3216. count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers;
  3217. for (i = 0; i < count; i++) {
  3218. ci = cgs.clientinfo + sortedTeamPlayers[i];
  3219. if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
  3220. plyrs++;
  3221. len = CG_DrawStrlen(ci->name);
  3222. if (len > pwidth)
  3223. pwidth = len;
  3224. }
  3225. }
  3226. if (!plyrs)
  3227. return y;
  3228. if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH)
  3229. pwidth = TEAM_OVERLAY_MAXNAME_WIDTH;
  3230. // max location name width
  3231. lwidth = 0;
  3232. for (i = 1; i < MAX_LOCATIONS; i++) {
  3233. p = CG_GetLocationString(CG_ConfigString(CS_LOCATIONS+i));
  3234. if (p && *p) {
  3235. len = CG_DrawStrlen(p);
  3236. if (len > lwidth)
  3237. lwidth = len;
  3238. }
  3239. }
  3240. if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH)
  3241. lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH;
  3242. w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH*cgs.widthRatioCoef;
  3243. if ( right )
  3244. x = SCREEN_WIDTH - w;
  3245. else
  3246. x = 0;
  3247. h = plyrs * TINYCHAR_HEIGHT;
  3248. if ( upper ) {
  3249. ret_y = y + h;
  3250. } else {
  3251. y -= h;
  3252. ret_y = y;
  3253. }
  3254. if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
  3255. hcolor[0] = 1.0f;
  3256. hcolor[1] = 0.0f;
  3257. hcolor[2] = 0.0f;
  3258. hcolor[3] = 0.33f;
  3259. } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
  3260. hcolor[0] = 0.0f;
  3261. hcolor[1] = 0.0f;
  3262. hcolor[2] = 1.0f;
  3263. hcolor[3] = 0.33f;
  3264. }
  3265. trap_R_SetColor( hcolor );
  3266. CG_DrawPic( x + xOffset, y, w, h, cgs.media.teamStatusBar );
  3267. trap_R_SetColor( NULL );
  3268. for (i = 0; i < count; i++) {
  3269. ci = cgs.clientinfo + sortedTeamPlayers[i];
  3270. if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
  3271. hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0;
  3272. xx = x + TINYCHAR_WIDTH*cgs.widthRatioCoef;
  3273. CG_DrawStringExt( xx + xOffset, y,
  3274. ci->name, hcolor, qfalse, qfalse,
  3275. TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH);
  3276. if (lwidth) {
  3277. p = CG_GetLocationString(CG_ConfigString(CS_LOCATIONS+ci->location));
  3278. if (!p || !*p)
  3279. p = "unknown";
  3280. len = CG_DrawStrlen(p);
  3281. if (len > lwidth)
  3282. len = lwidth;
  3283. // xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth +
  3284. // ((lwidth/2 - len/2) * TINYCHAR_WIDTH);
  3285. xx = x + TINYCHAR_WIDTH * 2*cgs.widthRatioCoef + TINYCHAR_WIDTH * pwidth*cgs.widthRatioCoef;
  3286. CG_DrawStringExt( xx + xOffset, y,
  3287. p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT,
  3288. TEAM_OVERLAY_MAXLOCATION_WIDTH);
  3289. }
  3290. CG_GetColorForHealth( ci->health, ci->armor, hcolor );
  3291. Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor);
  3292. xx = x + TINYCHAR_WIDTH * 3*cgs.widthRatioCoef +
  3293. TINYCHAR_WIDTH * pwidth*cgs.widthRatioCoef + TINYCHAR_WIDTH * lwidth*cgs.widthRatioCoef;
  3294. CG_DrawStringExt( xx + xOffset, y,
  3295. st, hcolor, qfalse, qfalse,
  3296. TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT, 0 );
  3297. // draw weapon icon
  3298. xx += TINYCHAR_WIDTH * 3*cgs.widthRatioCoef;
  3299. if ( cg_weapons[ci->curWeapon].weaponIcon ) {
  3300. CG_DrawPic( xx + xOffset, y, TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT,
  3301. cg_weapons[ci->curWeapon].weaponIcon );
  3302. } else {
  3303. CG_DrawPic( xx + xOffset, y, TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT,
  3304. cgs.media.deferShader );
  3305. }
  3306. // Draw powerup icons
  3307. if (right) {
  3308. xx = x;
  3309. } else {
  3310. xx = x + w - TINYCHAR_WIDTH*cgs.widthRatioCoef;
  3311. }
  3312. for (j = 0; j <= PW_NUM_POWERUPS; j++) {
  3313. if (ci->powerups & (1 << j)) {
  3314. item = BG_FindItemForPowerup( j );
  3315. if (item) {
  3316. CG_DrawPic( xx + xOffset, y, TINYCHAR_WIDTH*cgs.widthRatioCoef, TINYCHAR_HEIGHT,
  3317. trap_R_RegisterShader( item->icon ) );
  3318. if (right) {
  3319. xx -= TINYCHAR_WIDTH*cgs.widthRatioCoef;
  3320. } else {
  3321. xx += TINYCHAR_WIDTH*cgs.widthRatioCoef;
  3322. }
  3323. }
  3324. }
  3325. }
  3326. y += TINYCHAR_HEIGHT;
  3327. }
  3328. }
  3329. return ret_y;
  3330. //#endif
  3331. }
  3332. static void CG_DrawPowerupIcons(int y)
  3333. {
  3334. int j;
  3335. int ico_size = 64;
  3336. //int y = ico_size/2;
  3337. int xOffset = 0;
  3338. gitem_t *item;
  3339. //Raz: was missing this
  3340. trap_R_SetColor( NULL );
  3341. if (cg.playerPredicted && !cg.snap)
  3342. return;
  3343. if ( !cg.playerCent )
  3344. return;
  3345. y += 16;
  3346. //Raz: fixed potential buffer overrun of cg.snap->ps.powerups
  3347. for (j = 0; j < PW_NUM_POWERUPS; j++)
  3348. {
  3349. if ((!cg.playerPredicted?cg.playerCent->currentState.powerups & (1 << j):cg.snap->ps.powerups[j] > cg.time))
  3350. {
  3351. int secondsleft = (cg.snap->ps.powerups[j] - cg.time)/1000;
  3352. if (!cg.playerPredicted)
  3353. secondsleft = 1000; //we don't draw timer
  3354. item = BG_FindItemForPowerup( j );
  3355. if (item)
  3356. {
  3357. int icoShader = 0;
  3358. if (cgs.gametype == GT_CTY && (j == PW_REDFLAG || j == PW_BLUEFLAG))
  3359. {
  3360. if (j == PW_REDFLAG)
  3361. {
  3362. icoShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_rflag_ys" );
  3363. }
  3364. else
  3365. {
  3366. icoShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_bflag_ys" );
  3367. }
  3368. }
  3369. else
  3370. {
  3371. icoShader = trap_R_RegisterShader( item->icon );
  3372. }
  3373. CG_DrawPic( (SCREEN_WIDTH-(ico_size*1.1)*cgs.widthRatioCoef) + xOffset, y, ico_size*cgs.widthRatioCoef, ico_size, icoShader );
  3374. y += ico_size;
  3375. if (j != PW_REDFLAG && j != PW_BLUEFLAG && secondsleft < 999)
  3376. {
  3377. UI_DrawProportionalString((SCREEN_WIDTH-(ico_size*1.1)*cgs.widthRatioCoef)+(ico_size/2*cgs.widthRatioCoef) + xOffset, y-8, va("%i", secondsleft), UI_CENTER | UI_BIGFONT | UI_DROPSHADOW, colorTable[CT_WHITE]);
  3378. }
  3379. y += (ico_size/3);
  3380. }
  3381. }
  3382. }
  3383. }
  3384. /*
  3385. =====================
  3386. CG_DrawUpperRight
  3387. =====================
  3388. */
  3389. void CG_DrawUpperRight( void ) {
  3390. float y = 0;
  3391. trap_R_SetColor( colorTable[CT_WHITE] );
  3392. if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) {
  3393. y = CG_DrawTeamOverlay( y, qtrue, qtrue );
  3394. }
  3395. if ( cg_drawSnapshot.integer ) {
  3396. y = CG_DrawSnapshot( y );
  3397. }
  3398. if ( cg_drawFPS.integer ) {
  3399. y = CG_DrawFPS( y );
  3400. }
  3401. if ( cg_drawTimer.integer ) {
  3402. y = CG_DrawTimer( y );
  3403. }
  3404. if ( ( cgs.gametype >= GT_TEAM || cg.predictedPlayerState.m_iVehicleNum )
  3405. && cg_drawRadar.integer )
  3406. {//draw Radar in Siege mode or when in a vehicle of any kind
  3407. y = CG_DrawRadar ( y );
  3408. }
  3409. y = CG_DrawEnemyInfo ( y );
  3410. y = CG_DrawMiniScoreboard ( y );
  3411. CG_DrawPowerupIcons(y);
  3412. }
  3413. /*
  3414. ===================
  3415. CG_DrawReward
  3416. ===================
  3417. */
  3418. #ifdef JK2AWARDS
  3419. static void CG_DrawReward( void ) {
  3420. float *color;
  3421. int i, count;
  3422. float x, y;
  3423. char buf[32];
  3424. if ( !cg_drawRewards.integer ) {
  3425. return;
  3426. }
  3427. color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
  3428. if ( !color ) {
  3429. if (cg.rewardStack > 0) {
  3430. for(i = 0; i < cg.rewardStack; i++) {
  3431. cg.rewardSound[i] = cg.rewardSound[i+1];
  3432. cg.rewardShader[i] = cg.rewardShader[i+1];
  3433. cg.rewardCount[i] = cg.rewardCount[i+1];
  3434. }
  3435. cg.rewardTime = cg.time;
  3436. cg.rewardStack--;
  3437. color = CG_FadeColor( cg.rewardTime, REWARD_TIME );
  3438. if (!(mov_soundDisable.integer & SDISABLE_REWARD)) {
  3439. trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER);
  3440. }
  3441. } else {
  3442. return;
  3443. }
  3444. }
  3445. trap_R_SetColor( color );
  3446. /*
  3447. count = cg.rewardCount[0]/10; // number of big rewards to draw
  3448. if (count) {
  3449. y = 4;
  3450. x = 320 - count * ICON_SIZE;
  3451. for ( i = 0 ; i < count ; i++ ) {
  3452. CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] );
  3453. x += (ICON_SIZE*2);
  3454. }
  3455. }
  3456. count = cg.rewardCount[0] - count*10; // number of small rewards to draw
  3457. */
  3458. if ( cg.rewardCount[0] >= 10 ) {
  3459. y = 56;
  3460. x = 320 - (ICON_SIZE-4)*cgs.widthRatioCoef/2;
  3461. CG_DrawPic( x, y, (ICON_SIZE-4)*cgs.widthRatioCoef, ICON_SIZE-4, cg.rewardShader[0] );
  3462. Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]);
  3463. x = ( SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen( buf ) ) / 2;
  3464. CG_DrawStringExt( x, y+ICON_SIZE, buf, color, qfalse, qtrue,
  3465. SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
  3466. }
  3467. else {
  3468. count = cg.rewardCount[0];
  3469. y = 56;
  3470. x = 320 + 2 - count * (ICON_SIZE/2)*cgs.widthRatioCoef;
  3471. for ( i = 0 ; i < count ; i++ ) {
  3472. CG_DrawPic( x, y, (ICON_SIZE-4)*cgs.widthRatioCoef, ICON_SIZE-4, cg.rewardShader[0] );
  3473. x += ICON_SIZE*cgs.widthRatioCoef;
  3474. }
  3475. }
  3476. trap_R_SetColor( NULL );
  3477. }
  3478. #endif
  3479. /*
  3480. ===============================================================================
  3481. LAGOMETER
  3482. ===============================================================================
  3483. */
  3484. #define LAG_SAMPLES 128
  3485. typedef struct {
  3486. int frameSamples[LAG_SAMPLES];
  3487. int frameCount;
  3488. int snapshotFlags[LAG_SAMPLES];
  3489. int snapshotSamples[LAG_SAMPLES];
  3490. int snapshotCount;
  3491. } lagometer_t;
  3492. lagometer_t lagometer;
  3493. /*
  3494. ==============
  3495. CG_AddLagometerFrameInfo
  3496. Adds the current interpolate / extrapolate bar for this frame
  3497. ==============
  3498. */
  3499. void CG_AddLagometerFrameInfo( void ) {
  3500. int offset;
  3501. offset = cg.time - cg.latestSnapshotTime;
  3502. lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset;
  3503. lagometer.frameCount++;
  3504. }
  3505. /*
  3506. ==============
  3507. CG_AddLagometerSnapshotInfo
  3508. Each time a snapshot is received, log its ping time and
  3509. the number of snapshots that were dropped before it.
  3510. Pass NULL for a dropped packet.
  3511. ==============
  3512. */
  3513. void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
  3514. // dropped packet
  3515. if ( !snap ) {
  3516. lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1;
  3517. lagometer.snapshotCount++;
  3518. return;
  3519. }
  3520. // add this snapshot's info
  3521. if (cg.demoPlayback) {
  3522. //from pugmod, maybe useful
  3523. // static int lasttime = 0;
  3524. // rain - #67 - display snapshot time delta instead of ping when
  3525. // playing a demo, since I can't think of any way to get the
  3526. // real ping
  3527. // lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->serverTime - lasttime;
  3528. // lasttime = snap->serverTime;
  3529. //enemy terriroty version
  3530. snap->ping = (snap->serverTime - snap->ps.commandTime) - cg.frametime;
  3531. }// else
  3532. lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping;
  3533. lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags;
  3534. lagometer.snapshotCount++;
  3535. }
  3536. /*
  3537. ==============
  3538. CG_DrawDisconnect
  3539. Should we draw something differnet for long lag vs no packets?
  3540. ==============
  3541. */
  3542. static void CG_DrawDisconnect( void ) {
  3543. float x, y;
  3544. int cmdNum;
  3545. usercmd_t cmd;
  3546. const char *s;
  3547. int w; // bk010215 - FIXME char message[1024];
  3548. if (cg.demoPlayback == 2)
  3549. return; //we don't need that msg in demos
  3550. if (cg.mMapChange) {
  3551. s = CG_GetStringEdString("MP_INGAME", "SERVER_CHANGING_MAPS"); // s = "Server Changing Maps";
  3552. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  3553. CG_DrawBigString( SCREEN_WIDTH / 2 - w/2, 100, s, 1.0F);
  3554. s = CG_GetStringEdString("MP_INGAME", "PLEASE_WAIT"); // s = "Please wait...";
  3555. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  3556. CG_DrawBigString( SCREEN_WIDTH / 2 - w/2, 200, s, 1.0F);
  3557. return;
  3558. }
  3559. // draw the phone jack if we are completely past our buffers
  3560. cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1;
  3561. trap_GetUserCmd( cmdNum, &cmd );
  3562. if ( cmd.serverTime <= cg.snap->ps.commandTime
  3563. || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME
  3564. return;
  3565. }
  3566. // also add text in center of screen
  3567. s = CG_GetStringEdString("MP_INGAME", "CONNECTION_INTERRUPTED"); // s = "Connection Interrupted"; // bk 010215 - FIXME
  3568. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH*cgs.widthRatioCoef;
  3569. CG_DrawBigString( SCREEN_WIDTH / 2 - w/2, 100, s, 1.0F);
  3570. // blink the icon
  3571. if ( ( cg.time >> 9 ) & 1 ) {
  3572. return;
  3573. }
  3574. x = SCREEN_WIDTH - 48*cgs.widthRatioCoef;
  3575. y = SCREEN_HEIGHT - 48;
  3576. CG_DrawPic( x, y, 48*cgs.widthRatioCoef, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) );
  3577. }
  3578. #define MAX_LAGOMETER_PING 900
  3579. #define MAX_LAGOMETER_RANGE 300
  3580. /*
  3581. ==============
  3582. CG_DrawLagometer
  3583. ==============
  3584. */
  3585. static void CG_DrawLagometer( void ) {
  3586. int a, x, y, i;
  3587. float v;
  3588. float ax, ay, aw, ah, mid, range;
  3589. int color;
  3590. float vscale;
  3591. if ( !cg_lagometer.integer || cgs.localServer || !cg.playerPredicted) {
  3592. CG_DrawDisconnect();
  3593. return;
  3594. }
  3595. //
  3596. // draw the graph
  3597. //
  3598. x = SCREEN_WIDTH - 48*cgs.widthRatioCoef;
  3599. y = SCREEN_HEIGHT - 165;
  3600. trap_R_SetColor( NULL );
  3601. CG_DrawPic( x, y, 48*cgs.widthRatioCoef, 48, cgs.media.lagometerShader );
  3602. ax = x;
  3603. ay = y;
  3604. aw = 96;
  3605. ah = 48;
  3606. color = -1;
  3607. range = ah / 3;
  3608. mid = ay + range;
  3609. vscale = range / MAX_LAGOMETER_RANGE;
  3610. // draw the frame interpoalte / extrapolate graph
  3611. for ( a = 0 ; a < aw ; a++ ) {
  3612. i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1);
  3613. v = lagometer.frameSamples[i];
  3614. v *= vscale;
  3615. if ( v > 0 ) {
  3616. if ( color != 1 ) {
  3617. color = 1;
  3618. trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
  3619. }
  3620. if ( v > range ) {
  3621. v = range;
  3622. }
  3623. trap_R_DrawStretchPic ( ax + (0.5*aw - a*0.5)*cgs.widthRatioCoef, mid - v, 0.5*cgs.widthRatioCoef, v, 0, 0, 0, 0, cgs.media.whiteShader );
  3624. } else if ( v < 0 ) {
  3625. if ( color != 2 ) {
  3626. color = 2;
  3627. trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] );
  3628. }
  3629. v = -v;
  3630. if ( v > range ) {
  3631. v = range;
  3632. }
  3633. trap_R_DrawStretchPic( ax + (0.5*aw - a*0.5)*cgs.widthRatioCoef, mid, 0.5*cgs.widthRatioCoef, v, 0, 0, 0, 0, cgs.media.whiteShader );
  3634. }
  3635. }
  3636. // draw the snapshot latency / drop graph
  3637. range = ah / 2;
  3638. vscale = range / MAX_LAGOMETER_PING;
  3639. for ( a = 0 ; a < aw ; a++ ) {
  3640. i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1);
  3641. v = lagometer.snapshotSamples[i];
  3642. if ( v > 0 ) {
  3643. if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) {
  3644. if ( color != 5 ) {
  3645. color = 5; // YELLOW for rate delay
  3646. trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] );
  3647. }
  3648. } else {
  3649. if ( color != 3 ) {
  3650. color = 3;
  3651. trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] );
  3652. }
  3653. }
  3654. v = v * vscale;
  3655. if ( v > range ) {
  3656. v = range;
  3657. }
  3658. trap_R_DrawStretchPic( ax + (0.5*aw - 0.5*a)*cgs.widthRatioCoef, ay + ah - v, 0.5*cgs.widthRatioCoef, v, 0, 0, 0, 0, cgs.media.whiteShader );
  3659. } else if ( v < 0 ) {
  3660. if ( color != 4 ) {
  3661. color = 4; // RED for dropped snapshots
  3662. trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] );
  3663. }
  3664. trap_R_DrawStretchPic( ax + (0.5*aw - 0.5*a)*cgs.widthRatioCoef, ay + ah - range, 0.5*cgs.widthRatioCoef, range, 0, 0, 0, 0, cgs.media.whiteShader );
  3665. }
  3666. }
  3667. //draw ping, WORKS WRONG
  3668. // CG_Text_Paint(ax + 2, ay + 2, 0.5f, colorWhite, va("%i",lagometer.snapshotSamples[ lagometer.snapshotCount - 1 ]), 0, 0, 3, FONT_SMALL );
  3669. trap_R_SetColor( NULL );
  3670. if ( cg_noPredict.integer || g_synchronousClients.integer ) {
  3671. CG_DrawBigString( ax, ay, "snc", 1.0 );
  3672. }
  3673. CG_DrawDisconnect();
  3674. }
  3675. void CG_DrawSiegeMessage( const char *str, int objectiveScreen )
  3676. {
  3677. // if (!( trap_Key_GetCatcher() & KEYCATCH_UI ))
  3678. {
  3679. trap_OpenUIMenu(UIMENU_CLOSEALL);
  3680. trap_Cvar_Set("cg_siegeMessage", str);
  3681. if (objectiveScreen)
  3682. {
  3683. trap_OpenUIMenu(UIMENU_SIEGEOBJECTIVES);
  3684. }
  3685. else
  3686. {
  3687. trap_OpenUIMenu(UIMENU_SIEGEMESSAGE);
  3688. }
  3689. }
  3690. }
  3691. void CG_DrawSiegeMessageNonMenu( const char *str )
  3692. {
  3693. char text[1024];
  3694. if (str[0]=='@')
  3695. {
  3696. trap_SP_GetStringTextString(str+1, text, sizeof(text));
  3697. str = text;
  3698. }
  3699. CG_CenterPrint(str, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH);
  3700. }
  3701. /*
  3702. ===============================================================================
  3703. CENTER PRINTING
  3704. ===============================================================================
  3705. */
  3706. /*
  3707. ==============
  3708. CG_CenterPrint
  3709. Called for important messages that should stay in the center of the screen
  3710. for a few moments
  3711. ==============
  3712. */
  3713. void CG_CenterPrint( const char *str, int y, int charWidth ) {
  3714. char *s;
  3715. //[BugFix19]
  3716. int i = 0;
  3717. //[/BugFix19]
  3718. if( mov_fragsOnly.integer ) {
  3719. char sKilledStr[256];
  3720. trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", sKilledStr, sizeof(sKilledStr));
  3721. if(strncmp(str,sKilledStr,strlen(sKilledStr))!=0)return;
  3722. }
  3723. Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
  3724. cg.centerPrintTime = cg.time;
  3725. cg.centerPrintY = y;
  3726. cg.centerPrintCharWidth = charWidth;
  3727. // count the number of lines for centering
  3728. cg.centerPrintLines = 1;
  3729. s = cg.centerPrint;
  3730. Q_StripColorNew(s);
  3731. while( *s )
  3732. {
  3733. //[BugFix19]
  3734. i++;
  3735. if(i >= 50)
  3736. {//maxed out a line of text, this will make the line spill over onto another line.
  3737. i = 0;
  3738. cg.centerPrintLines++;
  3739. }
  3740. else if (*s == '\n')
  3741. //if (*s == '\n')
  3742. //[/BugFix19]
  3743. cg.centerPrintLines++;
  3744. s++;
  3745. }
  3746. Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
  3747. }
  3748. /*
  3749. ===================
  3750. CG_DrawCenterString
  3751. ===================
  3752. */
  3753. qboolean BG_IsWhiteSpace( char c )
  3754. {//this function simply checks to see if the given character is whitespace.
  3755. if ( c == ' ' || c == '\n' || c == '\0' )
  3756. return qtrue;
  3757. return qfalse;
  3758. }
  3759. static void CG_DrawCenterString( void ) {
  3760. char *start;
  3761. int l, len;
  3762. int x, y, w;
  3763. int h;
  3764. float *color;
  3765. const float scale = 1.0; //0.5
  3766. if ( !cg.centerPrintTime ) {
  3767. return;
  3768. }
  3769. color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centerTime.value );
  3770. if ( !color ) {
  3771. return;
  3772. }
  3773. trap_R_SetColor( color );
  3774. len = strlen(cg.centerPrint);
  3775. len -= Q_PrintStrlen(cg.centerPrint);
  3776. if (len < 0) //must never happen
  3777. len = 0;
  3778. start = cg.centerPrint;
  3779. if( mov_fragsOnly.integer != 0 ) {
  3780. char sKilledStr[256];
  3781. trap_SP_GetStringTextString("MP_INGAME_KILLED_MESSAGE", sKilledStr, sizeof(sKilledStr));
  3782. if(strncmp(cg.centerPrint,sKilledStr,strlen(sKilledStr))!=0)return;
  3783. }
  3784. y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2;
  3785. while ( 1 ) {
  3786. if (cg.centerPrintLines > 1) {
  3787. char linebuffer[1024];
  3788. for ( l = 0; l < 50+len; l++ ) {
  3789. if ( !start[l] || start[l] == '\n' ) {
  3790. break;
  3791. }
  3792. linebuffer[l] = start[l];
  3793. }
  3794. linebuffer[l] = 0;
  3795. //[BugFix19]
  3796. if(!BG_IsWhiteSpace(start[l]) && !BG_IsWhiteSpace(linebuffer[l-1]) )
  3797. {//we might have cut a word off, attempt to find a spot where we won't cut words off at.
  3798. int savedL = l;
  3799. int counter = l-2;
  3800. for(; counter >= 0; counter--)
  3801. {
  3802. if(BG_IsWhiteSpace(start[counter]))
  3803. {//this location is whitespace, line break from this position
  3804. linebuffer[counter] = 0;
  3805. l = counter + 1;
  3806. break;
  3807. }
  3808. }
  3809. if(counter < 0)
  3810. {//couldn't find a break in the text, just go ahead and cut off the word mid-word.
  3811. l = savedL;
  3812. }
  3813. }
  3814. //[/BugFix19]
  3815. w = CG_Text_Width(linebuffer, scale, FONT_MEDIUM);
  3816. h = CG_Text_Height(linebuffer, scale, FONT_MEDIUM);
  3817. x = (SCREEN_WIDTH - w) / 2;
  3818. CG_Text_Paint(x, y + h, scale, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM);
  3819. y += h + 6;
  3820. //[BugFix19]
  3821. //this method of advancing to new line from the start of the array was causing long lines without
  3822. //new lines to be totally truncated.
  3823. if(start[l] && start[l] == '\n')
  3824. {//next char is a newline, advance past
  3825. l++;
  3826. }
  3827. len = 0; //only for the first string
  3828. if ( !start[l] )
  3829. {//end of string, we're done.
  3830. break;
  3831. }
  3832. //advance pointer to the last character that we didn't read in.
  3833. start = &start[l];
  3834. //[/BugFix19]
  3835. } else {
  3836. char linebuffer[1024];
  3837. for ( l = 0; l < 50+len; l++ ) {
  3838. if ( !start[l] || start[l] == '\n' ) {
  3839. break;
  3840. }
  3841. linebuffer[l] = start[l];
  3842. }
  3843. linebuffer[l] = 0;
  3844. w = CG_Text_Width(linebuffer, scale, FONT_MEDIUM);
  3845. h = CG_Text_Height(linebuffer, scale, FONT_MEDIUM);
  3846. x = (SCREEN_WIDTH - w) / 2;
  3847. CG_Text_Paint(x, y + h, scale, color, linebuffer, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM);
  3848. y += h + 6;
  3849. while ( *start && ( *start != '\n' ) ) {
  3850. start++;
  3851. }
  3852. if ( !*start ) {
  3853. break;
  3854. }
  3855. start++;
  3856. }
  3857. }
  3858. trap_R_SetColor( NULL );
  3859. }
  3860. /*
  3861. ================================================================================
  3862. CROSSHAIR
  3863. ================================================================================
  3864. */
  3865. #define HEALTH_WIDTH 50.0f
  3866. #define HEALTH_HEIGHT 5.0f
  3867. //see if we can draw some extra info on this guy based on our class
  3868. void CG_DrawSiegeInfo(centity_t *cent, float chX, float chY, float chW, float chH)
  3869. {
  3870. siegeExtended_t *se = &cg_siegeExtendedData[cent->currentState.number];
  3871. clientInfo_t *ci;
  3872. const char *configstring, *v;
  3873. siegeClass_t *siegeClass;
  3874. vec4_t aColor;
  3875. vec4_t bColor;
  3876. vec4_t cColor;
  3877. float x;
  3878. float y;
  3879. float percent;
  3880. int ammoMax;
  3881. assert(cent->currentState.number < MAX_CLIENTS);
  3882. if (se->lastUpdated > cg.time)
  3883. { //strange, shouldn't happen
  3884. return;
  3885. }
  3886. if ((cg.time - se->lastUpdated) > 10000)
  3887. { //if you haven't received a status update on this guy in 10 seconds, forget about it
  3888. return;
  3889. }
  3890. if (cent->currentState.eFlags & EF_DEAD)
  3891. { //he's dead, don't display info on him
  3892. return;
  3893. }
  3894. if (cent->currentState.weapon != se->weapon)
  3895. { //data is invalidated until it syncs back again
  3896. return;
  3897. }
  3898. ci = &cgs.clientinfo[cent->currentState.number];
  3899. if (ci->team != cg.predictedPlayerState.persistant[PERS_TEAM])
  3900. { //not on the same team
  3901. return;
  3902. }
  3903. configstring = CG_ConfigString( cg.predictedPlayerState.clientNum + CS_PLAYERS );
  3904. v = Info_ValueForKey( configstring, "siegeclass" );
  3905. if (!v || !v[0])
  3906. { //don't have siege class in info?
  3907. return;
  3908. }
  3909. siegeClass = BG_SiegeFindClassByName(v);
  3910. if (!siegeClass)
  3911. { //invalid
  3912. return;
  3913. }
  3914. if (!(siegeClass->classflags & (1<<CFL_STATVIEWER)))
  3915. { //doesn't really have the ability to see others' stats
  3916. return;
  3917. }
  3918. x = chX+((chW/2)-(HEALTH_WIDTH/2));
  3919. y = (chY+chH) + 8.0f;
  3920. percent = ((float)se->health/(float)se->maxhealth)*HEALTH_WIDTH;
  3921. //color of the bar
  3922. aColor[0] = 0.0f;
  3923. aColor[1] = 1.0f;
  3924. aColor[2] = 0.0f;
  3925. aColor[3] = 0.4f;
  3926. //color of the border
  3927. bColor[0] = 0.0f;
  3928. bColor[1] = 0.0f;
  3929. bColor[2] = 0.0f;
  3930. bColor[3] = 0.3f;
  3931. //color of greyed out "missing health"
  3932. cColor[0] = 0.5f;
  3933. cColor[1] = 0.5f;
  3934. cColor[2] = 0.5f;
  3935. cColor[3] = 0.4f;
  3936. //draw the background (black)
  3937. CG_DrawRect(x, y, HEALTH_WIDTH, HEALTH_HEIGHT, 1.0f, colorTable[CT_BLACK]);
  3938. //now draw the part to show how much health there is in the color specified
  3939. CG_FillRect(x+1.0f, y+1.0f, percent-1.0f, HEALTH_HEIGHT-1.0f, aColor);
  3940. //then draw the other part greyed out
  3941. CG_FillRect(x+percent, y+1.0f, HEALTH_WIDTH-percent-1.0f, HEALTH_HEIGHT-1.0f, cColor);
  3942. //now draw his ammo
  3943. ammoMax = ammoData[weaponData[cent->currentState.weapon].ammoIndex].max;
  3944. if ( (cent->currentState.eFlags & EF_DOUBLE_AMMO) )
  3945. {
  3946. ammoMax *= 2;
  3947. }
  3948. x = chX+((chW/2)-(HEALTH_WIDTH/2));
  3949. y = (chY+chH) + HEALTH_HEIGHT + 10.0f;
  3950. if (!weaponData[cent->currentState.weapon].energyPerShot &&
  3951. !weaponData[cent->currentState.weapon].altEnergyPerShot)
  3952. { //a weapon that takes no ammo, so show full
  3953. percent = HEALTH_WIDTH;
  3954. }
  3955. else
  3956. {
  3957. percent = ((float)se->ammo/(float)ammoMax)*HEALTH_WIDTH;
  3958. }
  3959. //color of the bar
  3960. aColor[0] = 1.0f;
  3961. aColor[1] = 1.0f;
  3962. aColor[2] = 0.0f;
  3963. aColor[3] = 0.4f;
  3964. //color of the border
  3965. bColor[0] = 0.0f;
  3966. bColor[1] = 0.0f;
  3967. bColor[2] = 0.0f;
  3968. bColor[3] = 0.3f;
  3969. //color of greyed out "missing health"
  3970. cColor[0] = 0.5f;
  3971. cColor[1] = 0.5f;
  3972. cColor[2] = 0.5f;
  3973. cColor[3] = 0.4f;
  3974. //draw the background (black)
  3975. CG_DrawRect(x, y, HEALTH_WIDTH, HEALTH_HEIGHT, 1.0f, colorTable[CT_BLACK]);
  3976. //now draw the part to show how much health there is in the color specified
  3977. CG_FillRect(x+1.0f, y+1.0f, percent-1.0f, HEALTH_HEIGHT-1.0f, aColor);
  3978. //then draw the other part greyed out
  3979. CG_FillRect(x+percent, y+1.0f, HEALTH_WIDTH-percent-1.0f, HEALTH_HEIGHT-1.0f, cColor);
  3980. }
  3981. //draw the health bar based on current "health" and maxhealth
  3982. void CG_DrawHealthBar(centity_t *cent, float chX, float chY, float chW, float chH)
  3983. {
  3984. vec4_t aColor;
  3985. vec4_t bColor;
  3986. vec4_t cColor;
  3987. float x = chX+((chW/2)-(HEALTH_WIDTH/2));
  3988. float y = (chY+chH) + 8.0f;
  3989. float percent = ((float)cent->currentState.health/(float)cent->currentState.maxhealth)*HEALTH_WIDTH;
  3990. if (percent <= 0)
  3991. {
  3992. return;
  3993. }
  3994. //color of the bar
  3995. if (!cent->currentState.teamowner || cgs.gametype < GT_TEAM)
  3996. { //not owned by a team or teamplay
  3997. aColor[0] = 1.0f;
  3998. aColor[1] = 1.0f;
  3999. aColor[2] = 0.0f;
  4000. aColor[3] = 0.4f;
  4001. }
  4002. else if (cent->currentState.teamowner == cg.predictedPlayerState.persistant[PERS_TEAM])
  4003. { //owned by my team
  4004. aColor[0] = 0.0f;
  4005. aColor[1] = 1.0f;
  4006. aColor[2] = 0.0f;
  4007. aColor[3] = 0.4f;
  4008. }
  4009. else
  4010. { //hostile
  4011. aColor[0] = 1.0f;
  4012. aColor[1] = 0.0f;
  4013. aColor[2] = 0.0f;
  4014. aColor[3] = 0.4f;
  4015. }
  4016. //color of the border
  4017. bColor[0] = 0.0f;
  4018. bColor[1] = 0.0f;
  4019. bColor[2] = 0.0f;
  4020. bColor[3] = 0.3f;
  4021. //color of greyed out "missing health"
  4022. cColor[0] = 0.5f;
  4023. cColor[1] = 0.5f;
  4024. cColor[2] = 0.5f;
  4025. cColor[3] = 0.4f;
  4026. //draw the background (black)
  4027. CG_DrawRect(x, y, HEALTH_WIDTH, HEALTH_HEIGHT, 1.0f, colorTable[CT_BLACK]);
  4028. //now draw the part to show how much health there is in the color specified
  4029. CG_FillRect(x+1.0f, y+1.0f, percent-1.0f, HEALTH_HEIGHT-1.0f, aColor);
  4030. //then draw the other part greyed out
  4031. CG_FillRect(x+percent, y+1.0f, HEALTH_WIDTH-percent-1.0f, HEALTH_HEIGHT-1.0f, cColor);
  4032. }
  4033. //same routine (at least for now), draw progress of a "hack" or whatever
  4034. void CG_DrawHaqrBar(float chX, float chY, float chW, float chH)
  4035. {
  4036. vec4_t aColor;
  4037. vec4_t bColor;
  4038. vec4_t cColor;
  4039. float x = chX+((chW/2)-(HEALTH_WIDTH/2)*cgs.widthRatioCoef);
  4040. float y = (chY+chH) + 8.0f;
  4041. float percent = ((((float)cg.predictedPlayerState.hackingTime-(float)cg.time)-cg.timeFraction)/(float)cg.predictedPlayerState.hackingBaseTime)*HEALTH_WIDTH*cgs.widthRatioCoef;
  4042. if (percent > HEALTH_WIDTH*cgs.widthRatioCoef ||
  4043. percent < 1.0f)
  4044. {
  4045. return;
  4046. }
  4047. //color of the bar
  4048. aColor[0] = 1.0f;
  4049. aColor[1] = 1.0f;
  4050. aColor[2] = 0.0f;
  4051. aColor[3] = 0.4f;
  4052. //color of the border
  4053. bColor[0] = 0.0f;
  4054. bColor[1] = 0.0f;
  4055. bColor[2] = 0.0f;
  4056. bColor[3] = 0.3f;
  4057. //color of greyed out done area
  4058. cColor[0] = 0.5f;
  4059. cColor[1] = 0.5f;
  4060. cColor[2] = 0.5f;
  4061. cColor[3] = 0.1f;
  4062. //draw the background (black)
  4063. CG_DrawRect(x, y, HEALTH_WIDTH*cgs.widthRatioCoef, HEALTH_HEIGHT, 1.0f, colorTable[CT_BLACK]);
  4064. //now draw the part to show how much health there is in the color specified
  4065. CG_FillRect(x+1.0f, y+1.0f, (percent-1.0f), HEALTH_HEIGHT-1.0f, aColor);
  4066. //then draw the other part greyed out
  4067. CG_FillRect(x+percent, y+1.0f, (HEALTH_WIDTH*cgs.widthRatioCoef-percent-1.0f), HEALTH_HEIGHT-1.0f, cColor);
  4068. //draw the hacker icon
  4069. CG_DrawPic(x, y-HEALTH_WIDTH, HEALTH_WIDTH*cgs.widthRatioCoef, HEALTH_WIDTH, cgs.media.hackerIconShader);
  4070. }
  4071. //generic timing bar
  4072. int cg_genericTimerBar = 0;
  4073. int cg_genericTimerDur = 0;
  4074. vec4_t cg_genericTimerColor;
  4075. #define CGTIMERBAR_H 50.0f
  4076. #define CGTIMERBAR_W 10.0f
  4077. #define CGTIMERBAR_X (SCREEN_WIDTH-CGTIMERBAR_W-120.0f)
  4078. #define CGTIMERBAR_Y (SCREEN_HEIGHT-CGTIMERBAR_H-20.0f)
  4079. void CG_DrawGenericTimerBar(void)
  4080. {
  4081. vec4_t aColor;
  4082. vec4_t bColor;
  4083. vec4_t cColor;
  4084. float x = CGTIMERBAR_X;
  4085. float y = CGTIMERBAR_Y;
  4086. float percent = (((cg_genericTimerBar-cg.time)-cg.timeFraction)/(float)cg_genericTimerDur)*CGTIMERBAR_H;
  4087. if (percent > CGTIMERBAR_H)
  4088. {
  4089. return;
  4090. }
  4091. if (percent < 0.1f)
  4092. {
  4093. percent = 0.1f;
  4094. }
  4095. //color of the bar
  4096. aColor[0] = cg_genericTimerColor[0];
  4097. aColor[1] = cg_genericTimerColor[1];
  4098. aColor[2] = cg_genericTimerColor[2];
  4099. aColor[3] = cg_genericTimerColor[3];
  4100. //color of the border
  4101. bColor[0] = 0.0f;
  4102. bColor[1] = 0.0f;
  4103. bColor[2] = 0.0f;
  4104. bColor[3] = 0.3f;
  4105. //color of greyed out "missing fuel"
  4106. cColor[0] = 0.5f;
  4107. cColor[1] = 0.5f;
  4108. cColor[2] = 0.5f;
  4109. cColor[3] = 0.1f;
  4110. //draw the background (black)
  4111. CG_DrawRect(x, y, CGTIMERBAR_W, CGTIMERBAR_H, 1.0f, colorTable[CT_BLACK]);
  4112. //now draw the part to show how much health there is in the color specified
  4113. CG_FillRect(x+1.0f, y+1.0f+(CGTIMERBAR_H-percent), CGTIMERBAR_W-2.0f, CGTIMERBAR_H-1.0f-(CGTIMERBAR_H-percent), aColor);
  4114. //then draw the other part greyed out
  4115. CG_FillRect(x+1.0f, y+1.0f, CGTIMERBAR_W-2.0f, CGTIMERBAR_H-percent, cColor);
  4116. }
  4117. static qboolean CG_CrosshairColour(vec4_t ecolor, centity_t *crossEnt, int chEntValid) {
  4118. qboolean corona = qfalse;
  4119. entityState_t *crossES = &cg_entities[cg.crosshairClientNum].currentState;
  4120. //set color based on what kind of ent is under crosshair
  4121. if ( cg.crosshairClientNum >= ENTITYNUM_WORLD ) {
  4122. trap_R_SetColor( NULL );
  4123. }
  4124. //rwwFIXMEFIXME: Write this a different way, it's getting a bit too sloppy looking
  4125. else if (chEntValid &&
  4126. (crossES->number < MAX_CLIENTS || crossES->eType == ET_NPC || crossES->shouldtarget ||
  4127. crossES->health || //always show ents with health data under crosshair
  4128. (crossES->eType == ET_MOVER && crossES->bolt1 && cg.playerCent->currentState.weapon == WP_SABER) ||
  4129. (crossES->eType == ET_MOVER && crossES->teamowner))) {
  4130. clientInfo_t *ciCross = &cgs.clientinfo[crossES->number];
  4131. clientInfo_t *ciFollow = &cgs.clientinfo[cg.playerCent->currentState.number];
  4132. crossEnt = &cg_entities[cg.crosshairClientNum];
  4133. if ( crossES->powerups & (1 <<PW_CLOAKED) ) {
  4134. //don't show up for cloaked guys
  4135. ecolor[0] = 1.0;//R
  4136. ecolor[1] = 1.0;//G
  4137. ecolor[2] = 1.0;//B
  4138. } else if ( crossES->number < MAX_CLIENTS ) {
  4139. if (cgs.gametype >= GT_TEAM &&
  4140. ciCross->team == ciFollow->team ) {
  4141. //Allies are green
  4142. ecolor[0] = 0.0;//R
  4143. ecolor[1] = 1.0;//G
  4144. ecolor[2] = 0.0;//B
  4145. } else {
  4146. if (cgs.gametype == GT_POWERDUEL &&
  4147. ciCross->team == ciFollow->duelTeam) {
  4148. //on the same duel team in powerduel, so he's a friend
  4149. ecolor[0] = 0.0;//R
  4150. ecolor[1] = 1.0;//G
  4151. ecolor[2] = 0.0;//B
  4152. } else { //Enemies are red
  4153. ecolor[0] = 1.0;//R
  4154. ecolor[1] = 0.0;//G
  4155. ecolor[2] = 0.0;//B
  4156. }
  4157. }
  4158. if (cg.snap->ps.duelInProgress) {
  4159. if (crossES->number != cg.snap->ps.duelIndex &&
  4160. crossES->number != cg.snap->ps.clientNum) {
  4161. //grey out crosshair for everyone but your foe if you're in a duel
  4162. ecolor[0] = 0.4f;
  4163. ecolor[1] = 0.4f;
  4164. ecolor[2] = 0.4f;
  4165. }
  4166. } else if (crossES->bolt1) {
  4167. //this fellow is in a duel. We just checked if we were in a duel above, so
  4168. //this means we aren't and he is. Which of course means our crosshair greys out over him.
  4169. ecolor[0] = 0.4f;
  4170. ecolor[1] = 0.4f;
  4171. ecolor[2] = 0.4f;
  4172. }
  4173. } else if (crossES->shouldtarget || crossES->eType == ET_NPC) {
  4174. //VectorCopy( crossEnt->startRGBA, ecolor );
  4175. if ( !ecolor[0] && !ecolor[1] && !ecolor[2] ) {
  4176. // We really don't want black, so set it to yellow
  4177. ecolor[0] = 1.0f;//R
  4178. ecolor[1] = 0.8f;//G
  4179. ecolor[2] = 0.3f;//B
  4180. }
  4181. if (crossES->eType == ET_NPC) {
  4182. int plTeam;
  4183. if (cgs.gametype == GT_SIEGE) {
  4184. plTeam = ciFollow->team;
  4185. } else {
  4186. plTeam = NPCTEAM_PLAYER;
  4187. }
  4188. if ( crossES->powerups & (1 <<PW_CLOAKED) ) {
  4189. ecolor[0] = 1.0f;//R
  4190. ecolor[1] = 1.0f;//G
  4191. ecolor[2] = 1.0f;//B
  4192. } else if ( !crossES->teamowner ) {
  4193. //not on a team
  4194. if (!crossES->teamowner ||
  4195. crossES->NPC_class == CLASS_VEHICLE) {
  4196. //neutral
  4197. if (crossES->owner < MAX_CLIENTS) {
  4198. //base color on who is pilotting this thing
  4199. if (cgs.gametype >= GT_TEAM && ciCross->team == ciFollow->team) {
  4200. //friendly
  4201. ecolor[0] = 0.0f;//R
  4202. ecolor[1] = 1.0f;//G
  4203. ecolor[2] = 0.0f;//B
  4204. } else {
  4205. //hostile
  4206. ecolor[0] = 1.0f;//R
  4207. ecolor[1] = 0.0f;//G
  4208. ecolor[2] = 0.0f;//B
  4209. }
  4210. } else { //unmanned
  4211. ecolor[0] = 1.0f;//R
  4212. ecolor[1] = 1.0f;//G
  4213. ecolor[2] = 0.0f;//B
  4214. }
  4215. } else {
  4216. ecolor[0] = 1.0f;//R
  4217. ecolor[1] = 0.0f;//G
  4218. ecolor[2] = 0.0f;//B
  4219. }
  4220. } else if ( crossES->teamowner != plTeam ) {
  4221. // on enemy team
  4222. ecolor[0] = 1.0f;//R
  4223. ecolor[1] = 0.0f;//G
  4224. ecolor[2] = 0.0f;//B
  4225. } else {
  4226. //a friend
  4227. ecolor[0] = 0.0f;//R
  4228. ecolor[1] = 1.0f;//G
  4229. ecolor[2] = 0.0f;//B
  4230. }
  4231. } else if ( crossES->teamowner == TEAM_RED
  4232. || crossES->teamowner == TEAM_BLUE ) {
  4233. if (cgs.gametype < GT_TEAM) {
  4234. //not teamplay, just neutral then
  4235. ecolor[0] = 1.0f;//R
  4236. ecolor[1] = 1.0f;//G
  4237. ecolor[2] = 0.0f;//B
  4238. } else if ( crossES->teamowner != ciFollow->team ) {
  4239. //on the enemy team
  4240. ecolor[0] = 1.0f;//R
  4241. ecolor[1] = 0.0f;//G
  4242. ecolor[2] = 0.0f;//B
  4243. } else {
  4244. //on my team
  4245. ecolor[0] = 0.0f;//R
  4246. ecolor[1] = 1.0f;//G
  4247. ecolor[2] = 0.0f;//B
  4248. }
  4249. } else if (crossES->owner == cg.playerCent->currentState.number ||
  4250. (cgs.gametype >= GT_TEAM && crossES->teamowner == ciFollow->team)) {
  4251. ecolor[0] = 0.0f;//R
  4252. ecolor[1] = 1.0f;//G
  4253. ecolor[2] = 0.0f;//B
  4254. } else if (crossES->teamowner == 16
  4255. || (cgs.gametype >= GT_TEAM && crossES->teamowner
  4256. && crossES->teamowner != ciFollow->team)) {
  4257. ecolor[0] = 1.0f;//R
  4258. ecolor[1] = 0.0f;//G
  4259. ecolor[2] = 0.0f;//B
  4260. }
  4261. } else if (crossES->eType == ET_MOVER && crossES->bolt1
  4262. && cg.playerCent->currentState.weapon == WP_SABER) {
  4263. //can push/pull this mover. Only show it if we're using the saber.
  4264. ecolor[0] = 0.2f;
  4265. ecolor[1] = 0.5f;
  4266. ecolor[2] = 1.0f;
  4267. corona = qtrue;
  4268. } else if (crossES->eType == ET_MOVER && crossES->teamowner) {
  4269. //a team owns this - if it's my team green, if not red, if not teamplay then yellow
  4270. if (cgs.gametype < GT_TEAM) {
  4271. ecolor[0] = 1.0f;//R
  4272. ecolor[1] = 1.0f;//G
  4273. ecolor[2] = 0.0f;//B
  4274. } else if (ciFollow->team != crossES->teamowner) {
  4275. //not my team
  4276. ecolor[0] = 1.0f;//R
  4277. ecolor[1] = 0.0f;//G
  4278. ecolor[2] = 0.0f;//B
  4279. } else {
  4280. //my team
  4281. ecolor[0] = 0.0f;//R
  4282. ecolor[1] = 1.0f;//G
  4283. ecolor[2] = 0.0f;//B
  4284. }
  4285. } else if (crossES->health) {
  4286. if (!crossES->teamowner || cgs.gametype < GT_TEAM) {
  4287. //not owned by a team or teamplay
  4288. ecolor[0] = 1.0f;
  4289. ecolor[1] = 1.0f;
  4290. ecolor[2] = 0.0f;
  4291. } else if (crossES->teamowner == ciFollow->team) {
  4292. //owned by my team
  4293. ecolor[0] = 0.0f;
  4294. ecolor[1] = 1.0f;
  4295. ecolor[2] = 0.0f;
  4296. } else {
  4297. //hostile
  4298. ecolor[0] = 1.0f;
  4299. ecolor[1] = 0.0f;
  4300. ecolor[2] = 0.0f;
  4301. }
  4302. }
  4303. ecolor[3] = 1.0f;
  4304. trap_R_SetColor( ecolor );
  4305. }
  4306. return corona;
  4307. }
  4308. /*
  4309. =================
  4310. CG_DrawCrosshair
  4311. =================
  4312. */
  4313. float cg_crosshairPrevPosX = 0;
  4314. float cg_crosshairPrevPosY = 0;
  4315. #define CRAZY_CROSSHAIR_MAX_ERROR_X (100.0f*SCREEN_WIDTH/SCREEN_HEIGHT)
  4316. #define CRAZY_CROSSHAIR_MAX_ERROR_Y (100.0f)
  4317. void CG_LerpCrosshairPos( float *x, float *y )
  4318. {
  4319. if ( cg_crosshairPrevPosX )
  4320. {//blend from old pos
  4321. float maxMove = 30.0f * ((float)cg.frametime/500.0f) * (float)SCREEN_WIDTH/(float)SCREEN_HEIGHT;
  4322. float xDiff = (*x - cg_crosshairPrevPosX);
  4323. if ( fabs(xDiff) > CRAZY_CROSSHAIR_MAX_ERROR_X )
  4324. {
  4325. maxMove = CRAZY_CROSSHAIR_MAX_ERROR_X;
  4326. }
  4327. if ( xDiff > maxMove )
  4328. {
  4329. *x = cg_crosshairPrevPosX + maxMove;
  4330. }
  4331. else if ( xDiff < -maxMove )
  4332. {
  4333. *x = cg_crosshairPrevPosX - maxMove;
  4334. }
  4335. }
  4336. cg_crosshairPrevPosX = *x;
  4337. if ( cg_crosshairPrevPosY )
  4338. {//blend from old pos
  4339. float maxMove = 30.0f * ((float)cg.frametime/500.0f);
  4340. float yDiff = (*y - cg_crosshairPrevPosY);
  4341. if ( fabs(yDiff) > CRAZY_CROSSHAIR_MAX_ERROR_Y )
  4342. {
  4343. maxMove = CRAZY_CROSSHAIR_MAX_ERROR_X;
  4344. }
  4345. if ( yDiff > maxMove )
  4346. {
  4347. *y = cg_crosshairPrevPosY + maxMove;
  4348. }
  4349. else if ( yDiff < -maxMove )
  4350. {
  4351. *y = cg_crosshairPrevPosY - maxMove;
  4352. }
  4353. }
  4354. cg_crosshairPrevPosY = *y;
  4355. }
  4356. vec3_t cg_crosshairPos={0,0,0};
  4357. static void CG_DrawCrosshair( vec3_t worldPoint, int chEntValid ) {
  4358. float w, h;
  4359. qhandle_t hShader = 0;
  4360. float f;
  4361. float x, y;
  4362. qboolean corona = qfalse;
  4363. vec4_t ecolor = {0,0,0,0};
  4364. centity_t *crossEnt = NULL;
  4365. float chX, chY;
  4366. if (worldPoint)
  4367. VectorCopy( worldPoint, cg_crosshairPos );
  4368. if (!cg_drawCrosshair.integer)
  4369. return;
  4370. if (cg.fallingToDeath)
  4371. return;
  4372. //not while scoped
  4373. if (cg.zoomMode) {
  4374. return;
  4375. }
  4376. if (cg.playerPredicted && cg_crosshairHealth.integer) {
  4377. vec4_t hcolor;
  4378. CG_ColorForHealth( hcolor );
  4379. trap_R_SetColor( hcolor );
  4380. } else {
  4381. corona = CG_CrosshairColour(ecolor, crossEnt, chEntValid);
  4382. }
  4383. //I'm in a vehicle
  4384. if (cg.predictedPlayerState.m_iVehicleNum) {
  4385. centity_t *vehCent = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  4386. if ( vehCent
  4387. && vehCent->m_pVehicle
  4388. && vehCent->m_pVehicle->m_pVehicleInfo
  4389. && vehCent->m_pVehicle->m_pVehicleInfo->crosshairShaderHandle )
  4390. {
  4391. hShader = vehCent->m_pVehicle->m_pVehicleInfo->crosshairShaderHandle;
  4392. }
  4393. //bigger by default
  4394. w = cg_crosshairSize.value*2.0f;
  4395. h = w;
  4396. } else {
  4397. w = h = cg_crosshairSize.value;
  4398. }
  4399. // pulse the size of the crosshair when picking up items
  4400. f = (cg.time - cg.itemPickupBlendTime) + cg.timeFraction;
  4401. if ( f > 0 && f < ITEM_BLOB_TIME ) {
  4402. f /= ITEM_BLOB_TIME;
  4403. w *= ( 1 + f );
  4404. h *= ( 1 + f );
  4405. }
  4406. if (worldPoint && VectorLength(worldPoint)){
  4407. if (!CG_WorldCoordToScreenCoordFloat(worldPoint, &x, &y)) {
  4408. //off screen, don't draw it
  4409. return;
  4410. }
  4411. //CG_LerpCrosshairPos( &x, &y );
  4412. x -= SCREEN_WIDTH / 2;
  4413. y -= SCREEN_HEIGHT / 2;
  4414. } else {
  4415. x = cg_crosshairX.integer;
  4416. y = cg_crosshairY.integer;
  4417. }
  4418. if (!hShader)
  4419. hShader = cgs.media.crosshairShader[cg_drawCrosshair.integer % NUM_CROSSHAIRS];
  4420. chX = x + (float)cg.refdef.x + 0.5f * ((float)SCREEN_WIDTH - w*cgs.widthRatioCoef);
  4421. chY = y + (float)cg.refdef.y + 0.5f * ((float)SCREEN_HEIGHT - h);
  4422. trap_R_DrawStretchPic(chX, chY, w*cgs.widthRatioCoef, h, 0, 0, 1, 1, hShader);
  4423. //draw a health bar directly under the crosshair if we're looking at something
  4424. //that takes damage
  4425. if (crossEnt &&
  4426. crossEnt->currentState.maxhealth) {
  4427. CG_DrawHealthBar(crossEnt, chX, chY, w, h);
  4428. chY += HEALTH_HEIGHT*2;
  4429. } else if (crossEnt && crossEnt->currentState.number < MAX_CLIENTS) {
  4430. if (cgs.gametype == GT_SIEGE) {
  4431. CG_DrawSiegeInfo(crossEnt, chX, chY, w, h);
  4432. chY += HEALTH_HEIGHT*4;
  4433. } if (cg.crosshairVehNum && cg.time == cg.crosshairVehTime) {
  4434. //it was in the crosshair this frame
  4435. centity_t *hisVeh = &cg_entities[cg.crosshairVehNum];
  4436. if (hisVeh->currentState.eType == ET_NPC &&
  4437. hisVeh->currentState.NPC_class == CLASS_VEHICLE &&
  4438. hisVeh->currentState.maxhealth &&
  4439. hisVeh->m_pVehicle)
  4440. { //draw the health for this vehicle
  4441. CG_DrawHealthBar(hisVeh, chX, chY, w, h);
  4442. chY += HEALTH_HEIGHT*2;
  4443. }
  4444. }
  4445. }
  4446. //hacking something
  4447. if (cg.playerPredicted && cg.predictedPlayerState.hackingTime)
  4448. CG_DrawHaqrBar(chX, chY, w, h);
  4449. //draw generic timing bar, can be used for whatever
  4450. if (cg_genericTimerBar > cg.time)
  4451. CG_DrawGenericTimerBar();
  4452. // drawing extra bits
  4453. if (corona) {
  4454. ecolor[3] = 0.5f;
  4455. ecolor[0] = ecolor[1] = ecolor[2] = (1 - ecolor[3]) * ((float)sin(cg.time * 0.001 + cg.timeFraction * 0.001) * 0.08f + 0.35f); // don't draw full color
  4456. ecolor[3] = 1.0f;
  4457. trap_R_SetColor( ecolor );
  4458. w *= 2.0f;
  4459. h *= 2.0f;
  4460. trap_R_DrawStretchPic( x + (float)cg.refdef.x + 0.5f * ((float)SCREEN_WIDTH - w*cgs.widthRatioCoef),
  4461. y + (float)cg.refdef.y + 0.5f * ((float)SCREEN_HEIGHT - h),
  4462. w*cgs.widthRatioCoef, h, 0, 0, 1, 1, cgs.media.forceCoronaShader );
  4463. }
  4464. //Raz: Was missing this
  4465. trap_R_SetColor( NULL );
  4466. }
  4467. qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y) {
  4468. vec3_t trans;
  4469. vec_t xc, yc;
  4470. vec_t px, py;
  4471. vec_t z;
  4472. px = tan(cg.refdef.fov_x * (M_PI / 360) );
  4473. py = tan(cg.refdef.fov_y * (M_PI / 360) );
  4474. VectorSubtract(worldCoord, cg.refdef.vieworg, trans);
  4475. xc = SCREEN_WIDTH / 2.0f;
  4476. yc = SCREEN_HEIGHT / 2.0f;
  4477. // z = how far is the object in our forward direction
  4478. z = DotProduct(trans, cg.refdef.viewaxis[0]);
  4479. if (z <= 0.001)
  4480. return qfalse;
  4481. *x = xc - DotProduct(trans, cg.refdef.viewaxis[1])*xc/(z*px);
  4482. *y = yc - DotProduct(trans, cg.refdef.viewaxis[2])*yc/(z*py);
  4483. return qtrue;
  4484. }
  4485. qboolean CG_WorldCoordToScreenCoord( vec3_t worldCoord, int *x, int *y ) {
  4486. float xF, yF;
  4487. qboolean retVal = CG_WorldCoordToScreenCoordFloat( worldCoord, &xF, &yF );
  4488. *x = (int)xF;
  4489. *y = (int)yF;
  4490. return retVal;
  4491. }
  4492. /*
  4493. ====================
  4494. CG_SaberClashFlare
  4495. ====================
  4496. */
  4497. int cg_saberFlashTime = 0;
  4498. vec3_t cg_saberFlashPos = {0, 0, 0};
  4499. void CG_SaberClashFlare( void ) {
  4500. float maxTime = 150.0f;
  4501. float t;
  4502. vec3_t dif;
  4503. vec3_t color;
  4504. float x,y;
  4505. float v, len;
  4506. trace_t tr;
  4507. t = (cg.time - cg_saberFlashTime) + cg.timeFraction;
  4508. if ( t <= 0 || t >= maxTime ) {
  4509. return;
  4510. }
  4511. // Don't do clashes for things that are behind us
  4512. VectorSubtract( cg_saberFlashPos, cg.refdef.vieworg, dif );
  4513. if ( DotProduct( dif, cg.refdef.viewaxis[0] ) < 0.2 ) {
  4514. return;
  4515. }
  4516. CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, cg_saberFlashPos, -1, CONTENTS_SOLID );
  4517. if ( tr.fraction < 1.0f ) {
  4518. return;
  4519. }
  4520. len = VectorNormalize( dif );
  4521. // clamp to a known range
  4522. if ( len > 1200 ) {
  4523. return;
  4524. }
  4525. v = (1.0f - (t / maxTime )) * ((1.0f - (len / 1200.0f)) * 2.0f + 0.35f);
  4526. if (v < 0.001f) {
  4527. v = 0.001f;
  4528. }
  4529. CG_WorldCoordToScreenCoordFloat( cg_saberFlashPos, &x, &y );
  4530. VectorSet( color, 0.8f, 0.8f, 0.8f );
  4531. trap_R_SetColor( color );
  4532. CG_DrawPic( x - ( v * 300 )*cgs.widthRatioCoef, y - ( v * 300 ),
  4533. v * 600*cgs.widthRatioCoef, v * 600,
  4534. trap_R_RegisterShader( "gfx/effects/saberFlare" ));
  4535. }
  4536. void CG_DottedLine( float x1, float y1, float x2, float y2, float dotSize, int numDots, vec4_t color, float alpha )
  4537. {
  4538. float x, y, xDiff, yDiff, xStep, yStep;
  4539. vec4_t colorRGBA;
  4540. int dotNum = 0;
  4541. VectorCopy4( color, colorRGBA );
  4542. colorRGBA[3] = alpha;
  4543. trap_R_SetColor( colorRGBA );
  4544. xDiff = x2-x1;
  4545. yDiff = y2-y1;
  4546. xStep = xDiff/(float)numDots;
  4547. yStep = yDiff/(float)numDots;
  4548. for ( dotNum = 0; dotNum < numDots; dotNum++ )
  4549. {
  4550. x = x1 + (xStep*dotNum) - (dotSize*0.5f);
  4551. y = y1 + (yStep*dotNum) - (dotSize*0.5f);
  4552. CG_DrawPic( x, y, dotSize, dotSize, cgs.media.whiteShader );
  4553. }
  4554. }
  4555. void CG_BracketEntity( centity_t *cent, float radius )
  4556. {
  4557. trace_t tr;
  4558. vec3_t dif;
  4559. float len, size, lineLength, lineWidth;
  4560. float x, y;
  4561. clientInfo_t *local;
  4562. qboolean isEnemy = qfalse;
  4563. VectorSubtract( cent->lerpOrigin, cg.refdef.vieworg, dif );
  4564. len = VectorNormalize( dif );
  4565. if ( cg.crosshairClientNum != cent->currentState.clientNum
  4566. && (!cg.snap||cg.snap->ps.rocketLockIndex!= cent->currentState.clientNum) )
  4567. {//if they're the entity you're locking onto or under your crosshair, always draw bracket
  4568. //Hmm... for now, if they're closer than 2000, don't bracket?
  4569. if ( len < 2000.0f )
  4570. {
  4571. return;
  4572. }
  4573. CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, cent->lerpOrigin, -1, CONTENTS_OPAQUE );
  4574. //don't bracket if can't see them
  4575. if ( tr.fraction < 1.0f )
  4576. {
  4577. return;
  4578. }
  4579. }
  4580. if ( !CG_WorldCoordToScreenCoordFloat(cent->lerpOrigin, &x, &y) )
  4581. {//off-screen, don't draw it
  4582. return;
  4583. }
  4584. //just to see if it's centered
  4585. //CG_DrawPic( x-2, y-2, 4, 4, cgs.media.whiteShader );
  4586. local = &cgs.clientinfo[cg.snap->ps.clientNum];
  4587. if ( cent->currentState.m_iVehicleNum //vehicle has a driver
  4588. && (cent->currentState.m_iVehicleNum-1) < MAX_CLIENTS
  4589. && cgs.clientinfo[ cent->currentState.m_iVehicleNum-1 ].infoValid )
  4590. {
  4591. if ( cgs.gametype < GT_TEAM )
  4592. {//ffa?
  4593. isEnemy = qtrue;
  4594. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  4595. }
  4596. else if ( cgs.clientinfo[ cent->currentState.m_iVehicleNum-1 ].team == local->team )
  4597. {
  4598. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_GREEN)] );
  4599. }
  4600. else
  4601. {
  4602. isEnemy = qtrue;
  4603. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  4604. }
  4605. }
  4606. else if ( cent->currentState.teamowner )
  4607. {
  4608. if ( cgs.gametype < GT_TEAM )
  4609. {//ffa?
  4610. isEnemy = qtrue;
  4611. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  4612. }
  4613. else if ( cent->currentState.teamowner != cg.predictedPlayerState.persistant[PERS_TEAM] )
  4614. {// on enemy team
  4615. isEnemy = qtrue;
  4616. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  4617. }
  4618. else
  4619. { //a friend
  4620. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_GREEN)] );
  4621. }
  4622. }
  4623. else
  4624. {//FIXME: if we want to ever bracket anything besides vehicles (like siege objectives we want to blow up), we should handle the coloring here
  4625. trap_R_SetColor ( NULL );
  4626. }
  4627. if ( len <= 1.0f )
  4628. {//super-close, max out at 400 times radius (which is HUGE)
  4629. size = radius*400.0f;
  4630. }
  4631. else
  4632. {//scale by dist
  4633. size = radius*(400.0f/len);
  4634. }
  4635. if ( size < 1.0f )
  4636. {
  4637. size = 1.0f;
  4638. }
  4639. //length scales with dist
  4640. lineLength = (size*0.1f);
  4641. if ( lineLength < 0.5f )
  4642. {//always visible
  4643. lineLength = 0.5f;
  4644. }
  4645. //always visible width
  4646. lineWidth = 1.0f;
  4647. x -= (size*0.5f);
  4648. y -= (size*0.5f);
  4649. /*
  4650. if ( x >= 0 && x <= 640
  4651. && y >= 0 && y <= 480 )
  4652. */
  4653. {//brackets would be drawn on the screen, so draw them
  4654. //upper left corner
  4655. //horz
  4656. CG_DrawPic( x, y, lineLength, lineWidth, cgs.media.whiteShader );
  4657. //vert
  4658. CG_DrawPic( x, y, lineWidth, lineLength, cgs.media.whiteShader );
  4659. //upper right corner
  4660. //horz
  4661. CG_DrawPic( x+size-lineLength, y, lineLength, lineWidth, cgs.media.whiteShader );
  4662. //vert
  4663. CG_DrawPic( x+size-lineWidth, y, lineWidth, lineLength, cgs.media.whiteShader );
  4664. //lower left corner
  4665. //horz
  4666. CG_DrawPic( x, y+size-lineWidth, lineLength, lineWidth, cgs.media.whiteShader );
  4667. //vert
  4668. CG_DrawPic( x, y+size-lineLength, lineWidth, lineLength, cgs.media.whiteShader );
  4669. //lower right corner
  4670. //horz
  4671. CG_DrawPic( x+size-lineLength, y+size-lineWidth, lineLength, lineWidth, cgs.media.whiteShader );
  4672. //vert
  4673. CG_DrawPic( x+size-lineWidth, y+size-lineLength, lineWidth, lineLength, cgs.media.whiteShader );
  4674. }
  4675. //Lead Indicator...
  4676. if ( cg_drawVehLeadIndicator.integer )
  4677. {//draw the lead indicator
  4678. if ( isEnemy )
  4679. {//an enemy object
  4680. if ( cent->currentState.NPC_class == CLASS_VEHICLE )
  4681. {//enemy vehicle
  4682. if ( !VectorCompare( cent->currentState.pos.trDelta, vec3_origin ) )
  4683. {//enemy vehicle is moving
  4684. if ( cg.predictedPlayerState.m_iVehicleNum )
  4685. {//I'm in a vehicle
  4686. centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  4687. if ( veh //vehicle cent
  4688. && veh->m_pVehicle//vehicle
  4689. && veh->m_pVehicle->m_pVehicleInfo//vehicle stats
  4690. && veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID > VEH_WEAPON_BASE )//valid vehicle weapon
  4691. {
  4692. vehWeaponInfo_t *vehWeapon = &g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID];
  4693. if ( vehWeapon
  4694. && vehWeapon->bIsProjectile//primary weapon's shot is a projectile
  4695. && !vehWeapon->bHasGravity//primary weapon's shot is not affected by gravity
  4696. && !vehWeapon->fHoming//primary weapon's shot is not homing
  4697. && vehWeapon->fSpeed )//primary weapon's shot has speed
  4698. {//our primary weapon's projectile has a speed
  4699. vec3_t vehDiff, vehLeadPos;
  4700. float vehDist, eta;
  4701. float leadX, leadY;
  4702. VectorSubtract( cent->lerpOrigin, cg.predictedVehicleState.origin, vehDiff );
  4703. vehDist = VectorNormalize( vehDiff );
  4704. eta = (vehDist/vehWeapon->fSpeed);//how many seconds it would take for my primary weapon's projectile to get from my ship to theirs
  4705. //now extrapolate their position that number of seconds into the future based on their velocity
  4706. VectorMA( cent->lerpOrigin, eta, cent->currentState.pos.trDelta, vehLeadPos );
  4707. //now we have where we should be aiming at, project that onto the screen at a 2D co-ord
  4708. if ( !CG_WorldCoordToScreenCoordFloat(cent->lerpOrigin, &x, &y) )
  4709. {//off-screen, don't draw it
  4710. return;
  4711. }
  4712. if ( !CG_WorldCoordToScreenCoordFloat(vehLeadPos, &leadX, &leadY) )
  4713. {//off-screen, don't draw it
  4714. //just draw the line
  4715. CG_DottedLine( x, y, leadX, leadY, 1, 10, g_color_table[ColorIndex(COLOR_RED)], 0.5f );
  4716. return;
  4717. }
  4718. //draw a line from the ship's cur pos to the lead pos
  4719. CG_DottedLine( x, y, leadX, leadY, 1, 10, g_color_table[ColorIndex(COLOR_RED)], 0.5f );
  4720. //now draw the lead indicator
  4721. trap_R_SetColor ( g_color_table[ColorIndex(COLOR_RED)] );
  4722. CG_DrawPic( leadX-8, leadY-8, 16, 16, trap_R_RegisterShader( "gfx/menus/radar/lead" ) );
  4723. }
  4724. }
  4725. }
  4726. }
  4727. }
  4728. }
  4729. }
  4730. }
  4731. qboolean CG_InFighter( void )
  4732. {
  4733. if ( cg.predictedPlayerState.m_iVehicleNum )
  4734. {//I'm in a vehicle
  4735. centity_t *vehCent = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  4736. if ( vehCent
  4737. && vehCent->m_pVehicle
  4738. && vehCent->m_pVehicle->m_pVehicleInfo
  4739. && vehCent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
  4740. {//I'm in a fighter
  4741. return qtrue;
  4742. }
  4743. }
  4744. return qfalse;
  4745. }
  4746. qboolean CG_InATST( void )
  4747. {
  4748. if ( cg.predictedPlayerState.m_iVehicleNum )
  4749. {//I'm in a vehicle
  4750. centity_t *vehCent = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  4751. if ( vehCent
  4752. && vehCent->m_pVehicle
  4753. && vehCent->m_pVehicle->m_pVehicleInfo
  4754. && vehCent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
  4755. {//I'm in an atst
  4756. return qtrue;
  4757. }
  4758. }
  4759. return qfalse;
  4760. }
  4761. void CG_DrawBracketedEntities( void )
  4762. {
  4763. int i;
  4764. for ( i = 0; i < cg.bracketedEntityCount; i++ )
  4765. {
  4766. centity_t *cent = &cg_entities[cg.bracketedEntities[i]];
  4767. CG_BracketEntity( cent, CG_RadiusForCent( cent ) );
  4768. }
  4769. }
  4770. //--------------------------------------------------------------
  4771. static void CG_DrawHolocronIcons(void) {
  4772. //--------------------------------------------------------------
  4773. int icon_size = 40;
  4774. int i = 0;
  4775. int startx = 10;
  4776. int starty = 10;//SCREEN_HEIGHT - icon_size*3;
  4777. int endx = icon_size;
  4778. int endy = icon_size;
  4779. int holocronBits = cg.playerPredicted ? cg.snap->ps.holocronBits : cg.playerCent->currentState.time2;
  4780. if (cg.zoomMode) {
  4781. //don't display over zoom mask
  4782. return;
  4783. }
  4784. if (cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_SPECTATOR) {
  4785. return;
  4786. }
  4787. while (i < NUM_FORCE_POWERS) {
  4788. if (holocronBits & (1 << forcePowerSorted[i])) {
  4789. CG_DrawPic( startx, starty, endx*cgs.widthRatioCoef, endy, cgs.media.forcePowerIcons[forcePowerSorted[i]]);
  4790. starty += icon_size+2; //+2 for spacing
  4791. if ((starty+icon_size) >= SCREEN_HEIGHT-100) {
  4792. starty = 10;//SCREEN_HEIGHT - icon_size*3;
  4793. startx += (icon_size+2)*cgs.widthRatioCoef;
  4794. }
  4795. }
  4796. i++;
  4797. }
  4798. }
  4799. static qboolean CG_IsDurationPower(int power) {
  4800. if (power == FP_HEAL ||
  4801. power == FP_SPEED ||
  4802. power == FP_TELEPATHY ||
  4803. power == FP_RAGE ||
  4804. power == FP_PROTECT ||
  4805. power == FP_ABSORB ||
  4806. power == FP_SEE)
  4807. {
  4808. return qtrue;
  4809. }
  4810. return qfalse;
  4811. }
  4812. //--------------------------------------------------------------
  4813. static void CG_DrawActivePowers(void) {
  4814. //--------------------------------------------------------------
  4815. int icon_size = 40;
  4816. int i = 0;
  4817. int startx = icon_size*2+16;
  4818. int starty = SCREEN_HEIGHT - icon_size*2;
  4819. int endx = icon_size;
  4820. int endy = icon_size;
  4821. //don't display over zoom mask
  4822. if (cg.zoomMode) {
  4823. return;
  4824. }
  4825. if (cgs.clientinfo[cg.playerCent->currentState.number].team == TEAM_SPECTATOR)
  4826. return;
  4827. trap_R_SetColor( NULL );
  4828. while (i < NUM_FORCE_POWERS) {
  4829. if ((cg.playerCent->currentState.forcePowersActive & (1 << forcePowerSorted[i])) &&
  4830. CG_IsDurationPower(forcePowerSorted[i])) {
  4831. CG_DrawPic( startx, starty, endx*cgs.widthRatioCoef, endy, cgs.media.forcePowerIcons[forcePowerSorted[i]]);
  4832. startx += (icon_size+2)*cgs.widthRatioCoef; //+2 for spacing
  4833. if ((startx+icon_size*cgs.widthRatioCoef) >= SCREEN_WIDTH-80) {
  4834. startx = icon_size*2+16;
  4835. starty += (icon_size+2);
  4836. }
  4837. }
  4838. i++;
  4839. }
  4840. //additionally, draw an icon force force rage recovery
  4841. if (cg.playerPredicted && cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
  4842. CG_DrawPic( startx, starty, endx*cgs.widthRatioCoef, endy, cgs.media.rageRecShader);
  4843. }
  4844. //--------------------------------------------------------------
  4845. static void CG_DrawRocketLocking( int lockEntNum, int lockTime )
  4846. //--------------------------------------------------------------
  4847. {
  4848. int cx, cy;
  4849. vec3_t org;
  4850. static int oldDif = 0;
  4851. centity_t *cent = &cg_entities[lockEntNum];
  4852. vec4_t color={0.0f,0.0f,0.0f,0.0f};
  4853. float lockTimeInterval = ((cgs.gametype==GT_SIEGE)?2400.0f:1200.0f)/16.0f;
  4854. //FIXME: if in a vehicle, use the vehicle's lockOnTime...
  4855. int dif = ((cg.time - cg.snap->ps.rocketLockTime)/* + cg.timeFraction*/) / lockTimeInterval;
  4856. int i;
  4857. if (!cg.snap->ps.rocketLockTime || cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_SPECTATOR)
  4858. return;
  4859. if ( cg.snap->ps.m_iVehicleNum )
  4860. {//driving a vehicle
  4861. centity_t *veh = &cg_entities[cg.snap->ps.m_iVehicleNum];
  4862. if ( veh->m_pVehicle )
  4863. {
  4864. vehWeaponInfo_t *vehWeapon = NULL;
  4865. if ( cg.predictedVehicleState.weaponstate == WEAPON_CHARGING_ALT )
  4866. {
  4867. if ( veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID > VEH_WEAPON_BASE
  4868. && veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID < MAX_VEH_WEAPONS )
  4869. {
  4870. vehWeapon = &g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID];
  4871. }
  4872. }
  4873. else
  4874. {
  4875. if ( veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID > VEH_WEAPON_BASE
  4876. && veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID < MAX_VEH_WEAPONS )
  4877. {
  4878. vehWeapon = &g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID];
  4879. }
  4880. }
  4881. if ( vehWeapon != NULL )
  4882. {//we are trying to lock on with a valid vehicle weapon, so use *its* locktime, not the hard-coded one
  4883. if ( !vehWeapon->iLockOnTime )
  4884. {//instant lock-on
  4885. dif = 10.0f;
  4886. }
  4887. else
  4888. {//use the custom vehicle lockOnTime
  4889. lockTimeInterval = (vehWeapon->iLockOnTime/16.0f);
  4890. dif = ((cg.time - cg.snap->ps.rocketLockTime)/* + cg.timeFraction*/) / lockTimeInterval;
  4891. }
  4892. }
  4893. }
  4894. }
  4895. //We can't check to see in pmove if players are on the same team, so we resort
  4896. //to just not drawing the lock if a teammate is the locked on ent
  4897. if (cg.snap->ps.rocketLockIndex >= 0 &&
  4898. cg.snap->ps.rocketLockIndex < ENTITYNUM_NONE)
  4899. {
  4900. clientInfo_t *ci = NULL;
  4901. if (cg.snap->ps.rocketLockIndex < MAX_CLIENTS)
  4902. {
  4903. ci = &cgs.clientinfo[cg.snap->ps.rocketLockIndex];
  4904. }
  4905. else
  4906. {
  4907. ci = cg_entities[cg.snap->ps.rocketLockIndex].npcClient;
  4908. }
  4909. if (ci)
  4910. {
  4911. if (ci->team == cgs.clientinfo[cg.snap->ps.clientNum].team)
  4912. {
  4913. if (cgs.gametype >= GT_TEAM)
  4914. {
  4915. return;
  4916. }
  4917. }
  4918. else if (cgs.gametype >= GT_TEAM)
  4919. {
  4920. centity_t *hitEnt = &cg_entities[cg.snap->ps.rocketLockIndex];
  4921. if (hitEnt->currentState.eType == ET_NPC &&
  4922. hitEnt->currentState.NPC_class == CLASS_VEHICLE &&
  4923. hitEnt->currentState.owner < ENTITYNUM_WORLD)
  4924. { //this is a vehicle, if it has a pilot and that pilot is on my team, then...
  4925. if (hitEnt->currentState.owner < MAX_CLIENTS)
  4926. {
  4927. ci = &cgs.clientinfo[hitEnt->currentState.owner];
  4928. }
  4929. else
  4930. {
  4931. ci = cg_entities[hitEnt->currentState.owner].npcClient;
  4932. }
  4933. if (ci && ci->team == cgs.clientinfo[cg.snap->ps.clientNum].team)
  4934. {
  4935. return;
  4936. }
  4937. }
  4938. }
  4939. }
  4940. }
  4941. if (cg.snap->ps.rocketLockTime != -1) {
  4942. lastvalidlockdif = dif;
  4943. } else {
  4944. dif = lastvalidlockdif;
  4945. }
  4946. if (!cent) {
  4947. return;
  4948. }
  4949. VectorCopy( cent->lerpOrigin, org );
  4950. if (CG_WorldCoordToScreenCoord(org, &cx, &cy)) {
  4951. // we care about distance from enemy to eye, so this is good enough
  4952. float sz = Distance( cent->lerpOrigin, cg.refdef.vieworg ) / 1024.0f;
  4953. if ( sz > 1.0f ) {
  4954. sz = 1.0f;
  4955. } else if ( sz < 0.0f ) {
  4956. sz = 0.0f;
  4957. }
  4958. sz = (1.0f - sz) * (1.0f - sz) * 32 + 6;
  4959. cy += sz * 0.5f;
  4960. if ( dif < 0 ) {
  4961. oldDif = 0;
  4962. return;
  4963. } else if ( dif > 8 ) {
  4964. dif = 8;
  4965. }
  4966. // do sounds
  4967. if (oldDif != dif) {
  4968. if (dif == 8) {
  4969. if (cg.snap->ps.m_iVehicleNum) {
  4970. trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/vehicles/weapons/common/lock.wav" ));
  4971. } else {
  4972. trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/rocket/lock.wav" ));
  4973. }
  4974. } else {
  4975. if (cg.snap->ps.m_iVehicleNum) {
  4976. trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/vehicles/weapons/common/tick.wav" ));
  4977. } else {
  4978. trap_S_StartSound( org, 0, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/rocket/tick.wav" ));
  4979. }
  4980. }
  4981. }
  4982. oldDif = dif;
  4983. for (i = 0; i < dif; i++) {
  4984. color[0] = 1.0f;
  4985. color[1] = 0.0f;
  4986. color[2] = 0.0f;
  4987. color[3] = 0.1f * i + 0.2f;
  4988. trap_R_SetColor( color );
  4989. // our slices are offset by about 45 degrees.
  4990. CG_DrawRotatePic( cx - sz, cy - sz, sz, sz, i * 45.0f, trap_R_RegisterShaderNoMip( "gfx/2d/wedge" ));
  4991. }
  4992. // we are locked and loaded baby
  4993. if (dif == 8) {
  4994. //since we have hardcoded rotated pic with ratio fix above,
  4995. //we want undepended (on mov_ratioFix) hardcoded coef here too
  4996. float ratioFix = ((float)SCREEN_WIDTH*cgs.glconfig.vidHeight) / ((float)SCREEN_HEIGHT*cgs.glconfig.vidWidth);
  4997. color[0] = color[1] = color[2] = sin(cg.time * 0.05 + cg.timeFraction * 0.05) * 0.5f + 0.5f;
  4998. color[3] = 1.0f; // this art is additive, so the alpha value does nothing
  4999. trap_R_SetColor( color );
  5000. CG_DrawPic( cx - sz * ratioFix, cy - sz * 2, sz * 2 * ratioFix, sz * 2, trap_R_RegisterShaderNoMip( "gfx/2d/lock" ));
  5001. }
  5002. }
  5003. trap_R_SetColor( NULL );
  5004. }
  5005. extern void CG_CalcVehMuzzle(Vehicle_t *pVeh, centity_t *ent, int muzzleNum);
  5006. qboolean CG_CalcVehicleMuzzlePoint( int entityNum, vec3_t start, vec3_t d_f, vec3_t d_rt, vec3_t d_up) {
  5007. centity_t *vehCent = &cg_entities[entityNum];
  5008. if ( vehCent->m_pVehicle && vehCent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER ) {
  5009. //draw from barrels
  5010. VectorCopy( vehCent->lerpOrigin, start );
  5011. start[2] += vehCent->m_pVehicle->m_pVehicleInfo->height-DEFAULT_MINS_2-48;
  5012. AngleVectors( vehCent->lerpAngles, d_f, d_rt, d_up );
  5013. /*
  5014. mdxaBone_t boltMatrix;
  5015. int bolt;
  5016. vec3_t yawOnlyAngles;
  5017. VectorSet( yawOnlyAngles, 0, vehCent->lerpAngles[YAW], 0 );
  5018. bolt = trap_G2API_AddBolt( vehCent->ghoul2, 0, "*flash1");
  5019. trap_G2API_GetBoltMatrix( vehCent->ghoul2, 0, bolt, &boltMatrix,
  5020. yawOnlyAngles, vehCent->lerpOrigin, cg.time,
  5021. NULL, vehCent->modelScale );
  5022. // work the matrix axis stuff into the original axis and origins used.
  5023. BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, start );
  5024. BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_X, d_f );
  5025. VectorClear( d_rt );//don't really need this, do we?
  5026. VectorClear( d_up );//don't really need this, do we?
  5027. */
  5028. } else {
  5029. //check to see if we're a turret gunner on this vehicle
  5030. if ( cg.predictedPlayerState.generic1 )//as a passenger
  5031. {//passenger in a vehicle
  5032. if ( vehCent->m_pVehicle
  5033. && vehCent->m_pVehicle->m_pVehicleInfo
  5034. && vehCent->m_pVehicle->m_pVehicleInfo->maxPassengers )
  5035. {//a vehicle capable of carrying passengers
  5036. int turretNum;
  5037. for ( turretNum = 0; turretNum < MAX_VEHICLE_TURRETS; turretNum++ )
  5038. {
  5039. if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].iAmmoMax )
  5040. {// valid turret
  5041. if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].passengerNum == cg.predictedPlayerState.generic1 )
  5042. {//I control this turret
  5043. //Go through all muzzles, average their positions and directions and use the result for crosshair trace
  5044. int vehMuzzle, numMuzzles = 0;
  5045. vec3_t muzzlesAvgPos={0},muzzlesAvgDir={0};
  5046. int i;
  5047. for ( i = 0; i < MAX_VEHICLE_TURRET_MUZZLES; i++ )
  5048. {
  5049. vehMuzzle = vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].iMuzzle[i];
  5050. if ( vehMuzzle )
  5051. {
  5052. vehMuzzle -= 1;
  5053. CG_CalcVehMuzzle( vehCent->m_pVehicle, vehCent, vehMuzzle );
  5054. VectorAdd( muzzlesAvgPos, vehCent->m_pVehicle->m_vMuzzlePos[vehMuzzle], muzzlesAvgPos );
  5055. VectorAdd( muzzlesAvgDir, vehCent->m_pVehicle->m_vMuzzleDir[vehMuzzle], muzzlesAvgDir );
  5056. numMuzzles++;
  5057. }
  5058. if ( numMuzzles )
  5059. {
  5060. VectorScale( muzzlesAvgPos, 1.0f/(float)numMuzzles, start );
  5061. VectorScale( muzzlesAvgDir, 1.0f/(float)numMuzzles, d_f );
  5062. VectorClear( d_rt );
  5063. VectorClear( d_up );
  5064. return qtrue;
  5065. }
  5066. }
  5067. }
  5068. }
  5069. }
  5070. }
  5071. }
  5072. VectorCopy( vehCent->lerpOrigin, start );
  5073. AngleVectors( vehCent->lerpAngles, d_f, d_rt, d_up );
  5074. }
  5075. return qfalse;
  5076. }
  5077. //calc the muzzle point from the e-web itself
  5078. void CG_CalcEWebMuzzlePoint(centity_t *cent, vec3_t start, vec3_t d_f, vec3_t d_rt, vec3_t d_up)
  5079. {
  5080. int bolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*cannonflash");
  5081. assert(bolt != -1);
  5082. if (bolt != -1)
  5083. {
  5084. mdxaBone_t boltMatrix;
  5085. trap_G2API_GetBoltMatrix_NoRecNoRot(cent->ghoul2, 0, bolt, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time, NULL, cent->modelScale);
  5086. BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, start);
  5087. BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_X, d_f);
  5088. //these things start the shot a little inside the bbox to assure not starting in something solid
  5089. VectorMA(start, -16.0f, d_f, start);
  5090. //I guess
  5091. VectorClear( d_rt );//don't really need this, do we?
  5092. VectorClear( d_up );//don't really need this, do we?
  5093. }
  5094. }
  5095. /*
  5096. =================
  5097. CG_`Entity
  5098. =================
  5099. */
  5100. #define MAX_XHAIR_DIST_ACCURACY 20000.0f
  5101. static void CG_ScanForCrosshairEntity( void ) {
  5102. trace_t trace;
  5103. vec3_t start, end;
  5104. int content;
  5105. int ignore;
  5106. qboolean bVehCheckTraceFromCamPos = qfalse;
  5107. ignore = cg.playerCent->currentState.number;
  5108. if ( cg_dynamicCrosshair.integer && cg.playerPredicted)
  5109. {
  5110. vec3_t d_f, d_rt, d_up;
  5111. /*
  5112. if ( cg.snap->ps.weapon == WP_NONE ||
  5113. cg.snap->ps.weapon == WP_SABER ||
  5114. cg.snap->ps.weapon == WP_STUN_BATON)
  5115. {
  5116. VectorCopy( cg.refdef.vieworg, start );
  5117. AngleVectors( cg.refdef.viewangles, d_f, d_rt, d_up );
  5118. }
  5119. else
  5120. */
  5121. //For now we still want to draw the crosshair in relation to the player's world coordinates
  5122. //even if we have a melee weapon/no weapon.
  5123. if ( cg.predictedPlayerState.m_iVehicleNum && (cg.predictedPlayerState.eFlags&EF_NODRAW) )
  5124. {//we're *inside* a vehicle
  5125. //do the vehicle's crosshair instead
  5126. centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  5127. qboolean gunner = qfalse;
  5128. //if (veh->currentState.owner == cg.predictedPlayerState.clientNum)
  5129. { //the pilot
  5130. ignore = cg.predictedPlayerState.m_iVehicleNum;
  5131. gunner = CG_CalcVehicleMuzzlePoint(cg.predictedPlayerState.m_iVehicleNum, start, d_f, d_rt, d_up);
  5132. }
  5133. /*
  5134. else
  5135. { //a passenger
  5136. ignore = cg.predictedPlayerState.m_iVehicleNum;
  5137. VectorCopy( veh->lerpOrigin, start );
  5138. AngleVectors( veh->lerpAngles, d_f, d_rt, d_up );
  5139. VectorMA(start, 32.0f, d_f, start); //super hack
  5140. }
  5141. */
  5142. if ( veh->m_pVehicle
  5143. && veh->m_pVehicle->m_pVehicleInfo
  5144. && veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
  5145. && cg.distanceCull > MAX_XHAIR_DIST_ACCURACY
  5146. && !gunner )
  5147. {
  5148. //NOTE: on huge maps, the crosshair gets inaccurate at close range,
  5149. // so we'll do an extra G2 trace from the cg.refdef.vieworg
  5150. // to see if we hit anything closer and auto-aim at it if so
  5151. bVehCheckTraceFromCamPos = qtrue;
  5152. }
  5153. }
  5154. else if (cg.snap && cg.snap->ps.weapon == WP_EMPLACED_GUN && cg.snap->ps.emplacedIndex &&
  5155. cg_entities[cg.snap->ps.emplacedIndex].ghoul2 && cg_entities[cg.snap->ps.emplacedIndex].currentState.weapon == WP_NONE)
  5156. { //locked into our e-web, calc the muzzle from it
  5157. CG_CalcEWebMuzzlePoint(&cg_entities[cg.snap->ps.emplacedIndex], start, d_f, d_rt, d_up);
  5158. }
  5159. else
  5160. {
  5161. if (cg.snap && cg.snap->ps.weapon == WP_EMPLACED_GUN && cg.snap->ps.emplacedIndex)
  5162. {
  5163. vec3_t pitchConstraint;
  5164. ignore = cg.snap->ps.emplacedIndex;
  5165. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5166. if (cg.renderingThirdPerson)
  5167. {
  5168. VectorCopy(cg.predictedPlayerState.viewangles, pitchConstraint);
  5169. }
  5170. else
  5171. {
  5172. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5173. }
  5174. if (pitchConstraint[PITCH] > 40)
  5175. {
  5176. pitchConstraint[PITCH] = 40;
  5177. }
  5178. AngleVectors( pitchConstraint, d_f, d_rt, d_up );
  5179. }
  5180. else
  5181. {
  5182. vec3_t pitchConstraint;
  5183. if (cg.renderingThirdPerson)
  5184. {
  5185. VectorCopy(cg.predictedPlayerState.viewangles, pitchConstraint);
  5186. }
  5187. else
  5188. {
  5189. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5190. }
  5191. AngleVectors( pitchConstraint, d_f, d_rt, d_up );
  5192. }
  5193. CG_CalcMuzzlePoint(cg.snap->ps.clientNum, start);
  5194. }
  5195. VectorMA( start, cg.distanceCull, d_f, end );
  5196. }
  5197. else if (cg_dynamicCrosshair.integer)
  5198. {
  5199. vec3_t d_f, d_rt, d_up;
  5200. if (cg.playerCent->currentState.weapon == WP_EMPLACED_GUN && cg.playerCent->playerState->emplacedIndex &&
  5201. cg_entities[cg.playerCent->playerState->emplacedIndex].ghoul2 && cg_entities[cg.playerCent->playerState->emplacedIndex].currentState.weapon == WP_NONE)
  5202. { //locked into our e-web, calc the muzzle from it
  5203. CG_CalcEWebMuzzlePoint(&cg_entities[cg.playerCent->playerState->emplacedIndex], start, d_f, d_rt, d_up);
  5204. }
  5205. else
  5206. {
  5207. if (cg.playerCent->currentState.weapon == WP_EMPLACED_GUN)
  5208. {
  5209. vec3_t pitchConstraint;
  5210. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5211. if (cg.renderingThirdPerson)
  5212. {
  5213. VectorCopy(cg.playerCent->lerpAngles, pitchConstraint);
  5214. }
  5215. else
  5216. {
  5217. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5218. }
  5219. if (pitchConstraint[PITCH] > 40)
  5220. {
  5221. pitchConstraint[PITCH] = 40;
  5222. }
  5223. AngleVectors( pitchConstraint, d_f, d_rt, d_up );
  5224. }
  5225. else
  5226. {
  5227. vec3_t pitchConstraint;
  5228. if (cg.renderingThirdPerson)
  5229. {
  5230. VectorCopy(cg.playerCent->lerpAngles, pitchConstraint);
  5231. }
  5232. else
  5233. {
  5234. VectorCopy(cg.refdef.viewangles, pitchConstraint);
  5235. }
  5236. AngleVectors( pitchConstraint, d_f, d_rt, d_up );
  5237. }
  5238. CG_CalcMuzzlePoint(cg.playerCent->currentState.number, start);
  5239. }
  5240. VectorMA( start, cg.distanceCull, d_f, end );
  5241. }
  5242. else
  5243. {
  5244. VectorCopy( cg.refdef.vieworg, start );
  5245. VectorMA( start, 131072, cg.refdef.viewaxis[0], end );
  5246. }
  5247. if ( cg_dynamicCrosshair.integer && cg_dynamicCrosshairPrecision.integer )
  5248. { //then do a trace with ghoul2 models in mind
  5249. CG_G2Trace( &trace, start, vec3_origin, vec3_origin, end,
  5250. ignore, CONTENTS_SOLID|CONTENTS_BODY );
  5251. if ( bVehCheckTraceFromCamPos )
  5252. {
  5253. //NOTE: this MUST stay up to date with the method used in WP_VehCheckTraceFromCamPos
  5254. centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
  5255. trace_t extraTrace;
  5256. vec3_t viewDir2End, extraEnd;
  5257. float minAutoAimDist = Distance( veh->lerpOrigin, cg.refdef.vieworg ) + (veh->m_pVehicle->m_pVehicleInfo->length/2.0f) + 200.0f;
  5258. VectorSubtract( end, cg.refdef.vieworg, viewDir2End );
  5259. VectorNormalize( viewDir2End );
  5260. VectorMA( cg.refdef.vieworg, MAX_XHAIR_DIST_ACCURACY, viewDir2End, extraEnd );
  5261. CG_G2Trace( &extraTrace, cg.refdef.vieworg, vec3_origin, vec3_origin, extraEnd,
  5262. ignore, CONTENTS_SOLID|CONTENTS_BODY );
  5263. if ( !extraTrace.allsolid
  5264. && !extraTrace.startsolid )
  5265. {
  5266. if ( extraTrace.fraction < 1.0f )
  5267. {
  5268. if ( (extraTrace.fraction*MAX_XHAIR_DIST_ACCURACY) > minAutoAimDist )
  5269. {
  5270. if ( ((extraTrace.fraction*MAX_XHAIR_DIST_ACCURACY)-Distance( veh->lerpOrigin, cg.refdef.vieworg )) < (trace.fraction*cg.distanceCull) )
  5271. {//this trace hit *something* that's closer than the thing the main trace hit, so use this result instead
  5272. memcpy( &trace, &extraTrace, sizeof( trace_t ) );
  5273. }
  5274. }
  5275. }
  5276. }
  5277. }
  5278. }
  5279. else
  5280. {
  5281. CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
  5282. ignore, CONTENTS_SOLID|CONTENTS_BODY );
  5283. }
  5284. if (trace.entityNum < MAX_CLIENTS)
  5285. {
  5286. if (CG_IsMindTricked(cg_entities[trace.entityNum].currentState.trickedentindex,
  5287. cg_entities[trace.entityNum].currentState.trickedentindex2,
  5288. cg_entities[trace.entityNum].currentState.trickedentindex3,
  5289. cg_entities[trace.entityNum].currentState.trickedentindex4,
  5290. cg.playerCent->currentState.number))
  5291. {
  5292. if (cg.crosshairClientNum == trace.entityNum)
  5293. {
  5294. cg.crosshairClientNum = ENTITYNUM_NONE;
  5295. cg.crosshairClientTime = 0;
  5296. }
  5297. CG_DrawCrosshair(trace.endpos, 0);
  5298. return; //this entity is mind-tricking the current client, so don't render it
  5299. }
  5300. }
  5301. if (cgs.clientinfo[cg.playerCent->currentState.number].team != TEAM_SPECTATOR) {
  5302. if (trace.entityNum < /*MAX_CLIENTS*/ENTITYNUM_WORLD) {
  5303. cg.crosshairClientNum = trace.entityNum;
  5304. cg.crosshairClientTime = cg.time;
  5305. if (cg.snap->ps.duelInProgress && mov_duelIsolation.integer
  5306. && trace.entityNum != cg.snap->ps.duelIndex
  5307. && trace.entityNum != cg.snap->ps.clientNum) {
  5308. cg.crosshairClientNum = ENTITYNUM_NONE;
  5309. cg.crosshairClientTime = 0;
  5310. CG_DrawCrosshair(trace.endpos, 0);
  5311. return;
  5312. }
  5313. if (cg.crosshairClientNum < ENTITYNUM_WORLD) {
  5314. centity_t *veh = &cg_entities[cg.crosshairClientNum];
  5315. if (veh->currentState.eType == ET_NPC &&
  5316. veh->currentState.NPC_class == CLASS_VEHICLE &&
  5317. veh->currentState.owner < MAX_CLIENTS) { //draw the name of the pilot then
  5318. cg.crosshairClientNum = veh->currentState.owner;
  5319. cg.crosshairVehNum = veh->currentState.number;
  5320. cg.crosshairVehTime = cg.time;
  5321. }
  5322. }
  5323. CG_DrawCrosshair(trace.endpos, 1);
  5324. } else {
  5325. CG_DrawCrosshair(trace.endpos, 0);
  5326. }
  5327. }
  5328. //Raz: Put this back in so fading works again
  5329. if ( trace.entityNum >= MAX_CLIENTS ) {
  5330. return;
  5331. }
  5332. // if the player is in fog, don't show it
  5333. content = trap_CM_PointContents( trace.endpos, 0 );
  5334. if ( content & CONTENTS_FOG ) {
  5335. return;
  5336. }
  5337. // update the fade timer
  5338. cg.crosshairClientNum = trace.entityNum;
  5339. cg.crosshairClientTime = cg.time;
  5340. }
  5341. //mme, taken from pugmod
  5342. static void CG_SanitizeString( char *in, char *out )
  5343. {
  5344. int i = 0;
  5345. int r = 0;
  5346. while (in[i])
  5347. {
  5348. if (i >= 128-1)
  5349. { //the ui truncates the name here..
  5350. break;
  5351. }
  5352. if (in[i] == '^')
  5353. {
  5354. if (in[i+1] >= 48 && //'0'
  5355. in[i+1] <= 57) //'9'
  5356. { //only skip it if there's a number after it for the color
  5357. i += 2;
  5358. continue;
  5359. }
  5360. else
  5361. { //just skip the ^
  5362. i++;
  5363. continue;
  5364. }
  5365. }
  5366. if (in[i] < 32)
  5367. {
  5368. i++;
  5369. continue;
  5370. }
  5371. out[r] = in[i];
  5372. r++;
  5373. i++;
  5374. }
  5375. out[r] = 0;
  5376. }
  5377. /*
  5378. =====================
  5379. CG_DrawCrosshairNames
  5380. =====================
  5381. */
  5382. static void CG_DrawCrosshairNames(void) {
  5383. float *color;
  5384. vec4_t tcolor;
  5385. char *name;
  5386. char sanitized[1024];
  5387. int baseColor;
  5388. qboolean isVeh = qfalse;
  5389. if ( !cg_drawCrosshair.integer ) {
  5390. return;
  5391. }
  5392. // scan the known entities to see if the crosshair is sighted on one
  5393. CG_ScanForCrosshairEntity();
  5394. if ( !cg_drawCrosshairNames.integer ) {
  5395. return;
  5396. }
  5397. //rww - still do the trace, our dynamic crosshair depends on it
  5398. if (cg.crosshairClientNum < ENTITYNUM_WORLD)
  5399. {
  5400. centity_t *veh = &cg_entities[cg.crosshairClientNum];
  5401. if (veh->currentState.eType == ET_NPC &&
  5402. veh->currentState.NPC_class == CLASS_VEHICLE &&
  5403. veh->currentState.owner < MAX_CLIENTS)
  5404. { //draw the name of the pilot then
  5405. cg.crosshairClientNum = veh->currentState.owner;
  5406. cg.crosshairVehNum = veh->currentState.number;
  5407. cg.crosshairVehTime = cg.time;
  5408. isVeh = qtrue; //so we know we're drawing the pilot's name
  5409. }
  5410. }
  5411. if (cg.crosshairClientNum >= MAX_CLIENTS)
  5412. {
  5413. return;
  5414. }
  5415. if (cg_entities[cg.crosshairClientNum].currentState.powerups & (1 << PW_CLOAKED))
  5416. {
  5417. return;
  5418. }
  5419. // draw the name of the player being looked at
  5420. color = CG_FadeColor( cg.crosshairClientTime, 1000 );
  5421. if ( !color ) {
  5422. trap_R_SetColor( NULL );
  5423. return;
  5424. }
  5425. name = cgs.clientinfo[ cg.crosshairClientNum ].cleanname;
  5426. if (cgs.gametype >= GT_TEAM)
  5427. {
  5428. //if (cgs.gametype == GT_SIEGE)
  5429. if (1)
  5430. { //instead of team-based we'll make it oriented based on which team we're on
  5431. if (cgs.clientinfo[cg.crosshairClientNum].team == cgs.clientinfo[cg.playerCent->currentState.number].team)
  5432. {
  5433. baseColor = CT_GREEN;
  5434. }
  5435. else
  5436. {
  5437. baseColor = CT_RED;
  5438. }
  5439. }
  5440. else
  5441. {
  5442. if (cgs.clientinfo[cg.crosshairClientNum].team == TEAM_RED)
  5443. {
  5444. baseColor = CT_RED;
  5445. }
  5446. else
  5447. {
  5448. baseColor = CT_BLUE;
  5449. }
  5450. }
  5451. }
  5452. else
  5453. {
  5454. //baseColor = CT_WHITE;
  5455. if (cgs.gametype == GT_POWERDUEL &&
  5456. cgs.clientinfo[cg.playerCent->currentState.number].team != TEAM_SPECTATOR &&
  5457. cgs.clientinfo[cg.crosshairClientNum].duelTeam == cgs.clientinfo[cg.playerCent->currentState.number].duelTeam)
  5458. { //on the same duel team in powerduel, so he's a friend
  5459. baseColor = CT_GREEN;
  5460. }
  5461. else
  5462. {
  5463. baseColor = CT_RED; //just make it red in nonteam modes since everyone is hostile and crosshair will be red on them too
  5464. }
  5465. }
  5466. if (cg.snap->ps.duelInProgress) {
  5467. if (cg.crosshairClientNum != cg.snap->ps.duelIndex &&
  5468. cg.crosshairClientNum != cg.snap->ps.clientNum) {
  5469. //grey out crosshair for everyone but your foe if you're in a duel
  5470. baseColor = CT_BLACK;
  5471. }
  5472. } else if (cg_entities[cg.crosshairClientNum].currentState.bolt1) {
  5473. //this fellow is in a duel. We just checked if we were in a duel above, so
  5474. //this means we aren't and he is. Which of course means our crosshair greys out over him.
  5475. baseColor = CT_BLACK;
  5476. }
  5477. tcolor[0] = colorTable[baseColor][0];
  5478. tcolor[1] = colorTable[baseColor][1];
  5479. tcolor[2] = colorTable[baseColor][2];
  5480. tcolor[3] = color[3]*0.5f;
  5481. CG_SanitizeString(name, sanitized);
  5482. if (isVeh) {
  5483. char str[MAX_STRING_CHARS];
  5484. //mme, taken from pugmod
  5485. Com_sprintf(str, MAX_STRING_CHARS, "%s (pilot)", sanitized);
  5486. UI_DrawProportionalString(320, 170, str, UI_CENTER, tcolor);
  5487. } else {
  5488. //mme, taken from pugmod
  5489. UI_DrawProportionalString(320, 170, sanitized, UI_CENTER, tcolor);
  5490. }
  5491. trap_R_SetColor( NULL );
  5492. }
  5493. //==============================================================================
  5494. /*
  5495. =================
  5496. CG_DrawSpectator
  5497. =================
  5498. */
  5499. static void CG_DrawSpectator(void)
  5500. {
  5501. const char* s;
  5502. s = CG_GetStringEdString("MP_INGAME", "SPECTATOR");
  5503. if ((cgs.gametype == GT_DUEL || cgs.gametype == GT_POWERDUEL) &&
  5504. cgs.duelist1 != -1 &&
  5505. cgs.duelist2 != -1)
  5506. {
  5507. char text[1024];
  5508. int size = 64;
  5509. if (cgs.gametype == GT_POWERDUEL && cgs.duelist3 != -1)
  5510. {
  5511. Com_sprintf(text, sizeof(text), "%s^7 %s %s^7 %s %s", cgs.clientinfo[cgs.duelist1].name, CG_GetStringEdString("MP_INGAME", "SPECHUD_VERSUS"), cgs.clientinfo[cgs.duelist2].name, CG_GetStringEdString("MP_INGAME", "AND"), cgs.clientinfo[cgs.duelist3].name);
  5512. }
  5513. else
  5514. {
  5515. Com_sprintf(text, sizeof(text), "%s^7 %s %s", cgs.clientinfo[cgs.duelist1].name, CG_GetStringEdString("MP_INGAME", "SPECHUD_VERSUS"), cgs.clientinfo[cgs.duelist2].name);
  5516. }
  5517. CG_Text_Paint ( 320 - CG_Text_Width ( text, 1.0f, 3 ) / 2, 420, 1.0f, colorWhite, text, 0, 0, 0, 3 );
  5518. trap_R_SetColor( colorTable[CT_WHITE] );
  5519. if ( cgs.clientinfo[cgs.duelist1].modelIcon )
  5520. {
  5521. CG_DrawPic( 10, SCREEN_HEIGHT-(size*1.5), size, size, cgs.clientinfo[cgs.duelist1].modelIcon );
  5522. }
  5523. if ( cgs.clientinfo[cgs.duelist2].modelIcon )
  5524. {
  5525. CG_DrawPic( SCREEN_WIDTH-size-10, SCREEN_HEIGHT-(size*1.5), size, size, cgs.clientinfo[cgs.duelist2].modelIcon );
  5526. }
  5527. // nmckenzie: DUEL_HEALTH
  5528. if (cgs.gametype == GT_DUEL)
  5529. {
  5530. if ( cgs.showDuelHealths >= 1)
  5531. { // draw the healths on the two guys - how does this interact with power duel, though?
  5532. CG_DrawDuelistHealth ( 10, SCREEN_HEIGHT-(size*1.5) - 12, 64, 8, 1 );
  5533. CG_DrawDuelistHealth ( SCREEN_WIDTH-size-10, SCREEN_HEIGHT-(size*1.5) - 12, 64, 8, 2 );
  5534. }
  5535. }
  5536. if (cgs.gametype != GT_POWERDUEL)
  5537. {
  5538. Com_sprintf(text, sizeof(text), "%i/%i", cgs.clientinfo[cgs.duelist1].score, cgs.fraglimit );
  5539. CG_Text_Paint( 42 - CG_Text_Width( text, 1.0f, 2 ) / 2, SCREEN_HEIGHT-(size*1.5) + 64, 1.0f, colorWhite, text, 0, 0, 0, 2 );
  5540. Com_sprintf(text, sizeof(text), "%i/%i", cgs.clientinfo[cgs.duelist2].score, cgs.fraglimit );
  5541. CG_Text_Paint( SCREEN_WIDTH-size+22 - CG_Text_Width( text, 1.0f, 2 ) / 2, SCREEN_HEIGHT-(size*1.5) + 64, 1.0f, colorWhite, text, 0, 0, 0, 2 );
  5542. }
  5543. if (cgs.gametype == GT_POWERDUEL && cgs.duelist3 != -1)
  5544. {
  5545. if ( cgs.clientinfo[cgs.duelist3].modelIcon )
  5546. {
  5547. CG_DrawPic( SCREEN_WIDTH-size-10, SCREEN_HEIGHT-(size*2.8), size, size, cgs.clientinfo[cgs.duelist3].modelIcon );
  5548. }
  5549. }
  5550. }
  5551. else
  5552. {
  5553. CG_Text_Paint ( 320 - CG_Text_Width ( s, 1.0f, 3 ) / 2, 420, 1.0f, colorWhite, s, 0, 0, 0, 3 );
  5554. }
  5555. if ( cgs.gametype == GT_DUEL || cgs.gametype == GT_POWERDUEL )
  5556. {
  5557. s = CG_GetStringEdString("MP_INGAME", "WAITING_TO_PLAY"); // "waiting to play";
  5558. CG_Text_Paint ( 320 - CG_Text_Width ( s, 1.0f, 3 ) / 2, 440, 1.0f, colorWhite, s, 0, 0, 0, 3 );
  5559. }
  5560. else //if ( cgs.gametype >= GT_TEAM )
  5561. {
  5562. //s = "press ESC and use the JOIN menu to play";
  5563. s = CG_GetStringEdString("MP_INGAME", "SPEC_CHOOSEJOIN");
  5564. CG_Text_Paint ( 320 - CG_Text_Width ( s, 1.0f, 3 ) / 2, 440, 1.0f, colorWhite, s, 0, 0, 0, 3 );
  5565. }
  5566. }
  5567. /*
  5568. =================
  5569. CG_DrawVote
  5570. =================
  5571. */
  5572. static void CG_DrawVote(void) {
  5573. const char *s = NULL, *sParm = NULL;
  5574. int sec;
  5575. char sYes[20] = {0}, sNo[20] = {0}, sVote[20] = {0}, sCmd[100] = {0};
  5576. if ( !cgs.voteTime )
  5577. return;
  5578. // play a talk beep whenever it is modified
  5579. if ( cgs.voteModified ) {
  5580. cgs.voteModified = qfalse;
  5581. trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  5582. }
  5583. sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000;
  5584. if ( sec < 0 ) {
  5585. sec = 0;
  5586. }
  5587. if (strncmp(cgs.voteString, "map_restart", 11)==0)
  5588. {
  5589. trap_SP_GetStringTextString("MENUS_RESTART_MAP", sCmd, sizeof(sCmd) );
  5590. }
  5591. else if (strncmp(cgs.voteString, "vstr nextmap", 12)==0)
  5592. {
  5593. trap_SP_GetStringTextString("MENUS_NEXT_MAP", sCmd, sizeof(sCmd) );
  5594. }
  5595. else if (strncmp(cgs.voteString, "g_doWarmup", 10)==0)
  5596. {
  5597. trap_SP_GetStringTextString("MENUS_WARMUP", sCmd, sizeof(sCmd) );
  5598. }
  5599. else if (strncmp(cgs.voteString, "g_gametype", 10)==0)
  5600. {
  5601. trap_SP_GetStringTextString("MENUS_GAME_TYPE", sCmd, sizeof(sCmd) );
  5602. if ( stricmp("Free For All", cgs.voteString+11)==0 )
  5603. {
  5604. sParm = CG_GetStringEdString("MENUS", "FREE_FOR_ALL");
  5605. }
  5606. else if ( stricmp("Duel", cgs.voteString+11)==0 )
  5607. {
  5608. sParm = CG_GetStringEdString("MENUS", "DUEL");
  5609. }
  5610. else if ( stricmp("Holocron FFA", cgs.voteString+11)==0 )
  5611. {
  5612. sParm = CG_GetStringEdString("MENUS", "HOLOCRON_FFA");
  5613. }
  5614. else if ( stricmp("Power Duel", cgs.voteString+11)==0 )
  5615. {
  5616. sParm = CG_GetStringEdString("MENUS", "POWERDUEL");
  5617. }
  5618. else if ( stricmp("Team FFA", cgs.voteString+11)==0 )
  5619. {
  5620. sParm = CG_GetStringEdString("MENUS", "TEAM_FFA");
  5621. }
  5622. else if ( stricmp("Siege", cgs.voteString+11)==0 )
  5623. {
  5624. sParm = CG_GetStringEdString("MENUS", "SIEGE");
  5625. }
  5626. else if ( stricmp("Capture the Flag", cgs.voteString+11)==0 )
  5627. {
  5628. sParm = CG_GetStringEdString("MENUS", "CAPTURE_THE_FLAG");
  5629. }
  5630. else if ( stricmp("Capture the Ysalamiri", cgs.voteString+11)==0 )
  5631. {
  5632. sParm = CG_GetStringEdString("MENUS", "CAPTURE_THE_YSALIMARI");
  5633. }
  5634. }
  5635. else if (strncmp(cgs.voteString, "map", 3)==0)
  5636. {
  5637. trap_SP_GetStringTextString("MENUS_NEW_MAP", sCmd, sizeof(sCmd) );
  5638. sParm = cgs.voteString+4;
  5639. }
  5640. else if (strncmp(cgs.voteString, "kick", 4)==0)
  5641. {
  5642. trap_SP_GetStringTextString("MENUS_KICK_PLAYER", sCmd, sizeof(sCmd) );
  5643. sParm = cgs.voteString+5;
  5644. }
  5645. else
  5646. {//Raz: Added an else case for custom votes like ampoll, cointoss, etc
  5647. sParm = cgs.voteString;
  5648. }
  5649. trap_SP_GetStringTextString("MENUS_VOTE", sVote, sizeof(sVote) );
  5650. trap_SP_GetStringTextString("MENUS_YES", sYes, sizeof(sYes) );
  5651. trap_SP_GetStringTextString("MENUS_NO", sNo, sizeof(sNo) );
  5652. if (sParm && sParm[0])
  5653. {
  5654. s = va("%s(%i):<%s %s> %s:%i %s:%i", sVote, sec, sCmd, sParm, sYes, cgs.voteYes, sNo, cgs.voteNo);
  5655. }
  5656. else
  5657. {
  5658. s = va("%s(%i):<%s> %s:%i %s:%i", sVote, sec, sCmd, sYes, cgs.voteYes, sNo, cgs.voteNo);
  5659. }
  5660. CG_DrawSmallString( 4, 58, s, 1.0F );
  5661. s = CG_GetStringEdString("MP_INGAME", "OR_PRESS_ESC_THEN_CLICK_VOTE"); // s = "or press ESC then click Vote";
  5662. CG_DrawSmallString( 4, 58 + SMALLCHAR_HEIGHT + 2, s, 1.0F );
  5663. }
  5664. /*
  5665. =================
  5666. CG_DrawTeamVote
  5667. =================
  5668. */
  5669. static void CG_DrawTeamVote(void) {
  5670. char *s;
  5671. int sec, cs_offset;
  5672. if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
  5673. cs_offset = 0;
  5674. else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
  5675. cs_offset = 1;
  5676. else
  5677. return;
  5678. if ( !cgs.teamVoteTime[cs_offset] ) {
  5679. return;
  5680. }
  5681. // play a talk beep whenever it is modified
  5682. if ( cgs.teamVoteModified[cs_offset] ) {
  5683. cgs.teamVoteModified[cs_offset] = qfalse;
  5684. // trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  5685. }
  5686. sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[cs_offset] ) ) / 1000;
  5687. if ( sec < 0 ) {
  5688. sec = 0;
  5689. }
  5690. if (strstr(cgs.teamVoteString[cs_offset], "leader"))
  5691. {
  5692. int i = 0;
  5693. while (cgs.teamVoteString[cs_offset][i] && cgs.teamVoteString[cs_offset][i] != ' ')
  5694. {
  5695. i++;
  5696. }
  5697. if (cgs.teamVoteString[cs_offset][i] == ' ')
  5698. {
  5699. int voteIndex = 0;
  5700. char voteIndexStr[256];
  5701. i++;
  5702. while (cgs.teamVoteString[cs_offset][i])
  5703. {
  5704. voteIndexStr[voteIndex] = cgs.teamVoteString[cs_offset][i];
  5705. voteIndex++;
  5706. i++;
  5707. }
  5708. voteIndexStr[voteIndex] = 0;
  5709. voteIndex = atoi(voteIndexStr);
  5710. s = va("TEAMVOTE(%i):(Make %s the new team leader) yes:%i no:%i", sec, cgs.clientinfo[voteIndex].name,
  5711. cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] );
  5712. }
  5713. else
  5714. {
  5715. s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset],
  5716. cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] );
  5717. }
  5718. }
  5719. else
  5720. {
  5721. s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset],
  5722. cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] );
  5723. }
  5724. CG_DrawSmallString( 4, 90, s, 1.0F );
  5725. }
  5726. static qboolean CG_DrawScoreboard() {
  5727. return CG_DrawOldScoreboard();
  5728. #if 0
  5729. static qboolean firstTime = qtrue;
  5730. float fade, *fadeColor;
  5731. if (menuScoreboard) {
  5732. menuScoreboard->window.flags &= ~WINDOW_FORCED;
  5733. }
  5734. if (cg_paused.integer) {
  5735. cg.deferredPlayerLoading = 0;
  5736. firstTime = qtrue;
  5737. return qfalse;
  5738. }
  5739. // should never happen in Team Arena
  5740. if (cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
  5741. cg.deferredPlayerLoading = 0;
  5742. firstTime = qtrue;
  5743. return qfalse;
  5744. }
  5745. // don't draw scoreboard during death while warmup up
  5746. if ( cg.warmup && !cg.showScores ) {
  5747. return qfalse;
  5748. }
  5749. if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
  5750. fade = 1.0;
  5751. fadeColor = colorWhite;
  5752. } else {
  5753. fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME );
  5754. if ( !fadeColor ) {
  5755. // next time scoreboard comes up, don't print killer
  5756. cg.deferredPlayerLoading = 0;
  5757. cg.killerName[0] = 0;
  5758. firstTime = qtrue;
  5759. return qfalse;
  5760. }
  5761. fade = *fadeColor;
  5762. }
  5763. if (menuScoreboard == NULL) {
  5764. if ( cgs.gametype >= GT_TEAM ) {
  5765. menuScoreboard = Menus_FindByName("teamscore_menu");
  5766. } else {
  5767. menuScoreboard = Menus_FindByName("score_menu");
  5768. }
  5769. }
  5770. if (menuScoreboard) {
  5771. if (firstTime) {
  5772. CG_SetScoreSelection(menuScoreboard);
  5773. firstTime = qfalse;
  5774. }
  5775. Menu_Paint(menuScoreboard, qtrue);
  5776. }
  5777. // load any models that have been deferred
  5778. if ( ++cg.deferredPlayerLoading > 10 ) {
  5779. CG_LoadDeferredPlayers();
  5780. }
  5781. return qtrue;
  5782. #endif
  5783. }
  5784. /*
  5785. =================
  5786. CG_DrawIntermission
  5787. =================
  5788. */
  5789. static void CG_DrawIntermission( void ) {
  5790. // int key;
  5791. //if (cg_singlePlayer.integer) {
  5792. // CG_DrawCenterString();
  5793. // return;
  5794. //}
  5795. cg.scoreFadeTime = cg.time;
  5796. cg.scoreBoardShowing = CG_DrawScoreboard();
  5797. }
  5798. /*
  5799. =================
  5800. CG_DrawFollow
  5801. =================
  5802. */
  5803. static qboolean CG_DrawFollow( void ) {
  5804. char *s;
  5805. if (!(cg.snap->ps.pm_flags & PMF_FOLLOW)) {
  5806. return qfalse;
  5807. }
  5808. // s = "following";
  5809. if (cgs.gametype == GT_POWERDUEL) {
  5810. clientInfo_t *ci = &cgs.clientinfo[ cg.snap->ps.clientNum ];
  5811. if (ci->duelTeam == DUELTEAM_LONE) {
  5812. s = CG_GetStringEdString("MP_INGAME", "FOLLOWINGLONE");
  5813. } else if (ci->duelTeam == DUELTEAM_DOUBLE) {
  5814. s = CG_GetStringEdString("MP_INGAME", "FOLLOWINGDOUBLE");
  5815. } else {
  5816. s = CG_GetStringEdString("MP_INGAME", "FOLLOWING");
  5817. }
  5818. } else {
  5819. s = CG_GetStringEdString("MP_INGAME", "FOLLOWING");
  5820. }
  5821. /*
  5822. CG_Text_Paint ( 320 - CG_Text_Width ( s, 1.0f, FONT_MEDIUM ) / 2, 60, 1.0f, colorWhite, s, 0, 0, 0, FONT_MEDIUM );
  5823. s = cgs.clientinfo[ cg.snap->ps.clientNum ].name;
  5824. CG_Text_Paint ( 320 - CG_Text_Width ( s, 2.0f, FONT_MEDIUM ) / 2, 80, 2.0f, colorWhite, s, 0, 0, 0, FONT_MEDIUM );
  5825. */
  5826. strcat( s, va(": %s", cgs.clientinfo[ cg.snap->ps.clientNum ].name));
  5827. CG_Text_Paint ( 320 - CG_Text_Width ( s, 0.7f, FONT_LARGE ) / 2, 1, 0.7f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWED, FONT_LARGE );
  5828. return qtrue;
  5829. }
  5830. #if 0
  5831. static void CG_DrawTemporaryStats()
  5832. { //placeholder for testing (draws ammo and force power)
  5833. char s[512];
  5834. if (!cg.snap)
  5835. {
  5836. return;
  5837. }
  5838. sprintf(s, "Force: %i", cg.snap->ps.fd.forcePower);
  5839. CG_DrawBigString(SCREEN_WIDTH-164, SCREEN_HEIGHT-dmgIndicSize, s, 1.0f);
  5840. sprintf(s, "Ammo: %i", cg.snap->ps.ammo[weaponData[cg.snap->ps.weapon].ammoIndex]);
  5841. CG_DrawBigString(SCREEN_WIDTH-164, SCREEN_HEIGHT-112, s, 1.0f);
  5842. sprintf(s, "Health: %i", cg.snap->ps.stats[STAT_HEALTH]);
  5843. CG_DrawBigString(8, SCREEN_HEIGHT-dmgIndicSize, s, 1.0f);
  5844. sprintf(s, "Armor: %i", cg.snap->ps.stats[STAT_ARMOR]);
  5845. CG_DrawBigString(8, SCREEN_HEIGHT-112, s, 1.0f);
  5846. }
  5847. #endif
  5848. /*
  5849. =================
  5850. CG_DrawAmmoWarning
  5851. =================
  5852. */
  5853. static void CG_DrawAmmoWarning( void ) {
  5854. #if 0
  5855. const char *s;
  5856. int w;
  5857. if (!cg_drawStatus.integer)
  5858. {
  5859. return;
  5860. }
  5861. if ( cg_drawAmmoWarning.integer == 0 ) {
  5862. return;
  5863. }
  5864. if ( !cg.lowAmmoWarning ) {
  5865. return;
  5866. }
  5867. if ( cg.lowAmmoWarning == 2 ) {
  5868. s = "OUT OF AMMO";
  5869. } else {
  5870. s = "LOW AMMO WARNING";
  5871. }
  5872. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  5873. CG_DrawBigString(320 - w / 2, 64, s, 1.0F);
  5874. #endif
  5875. }
  5876. /*
  5877. =================
  5878. CG_DrawWarmup
  5879. =================
  5880. */
  5881. static void CG_DrawWarmup( void ) {
  5882. int w;
  5883. int sec;
  5884. int i;
  5885. float scale;
  5886. int cw;
  5887. const char *s;
  5888. sec = cg.warmup;
  5889. if ( !sec ) {
  5890. return;
  5891. }
  5892. if ( sec < 0 ) {
  5893. // s = "Waiting for players";
  5894. s = CG_GetStringEdString("MP_INGAME", "WAITING_FOR_PLAYERS");
  5895. w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
  5896. CG_DrawBigString(320 - w / 2, 24, s, 1.0F);
  5897. cg.warmupCount = 0;
  5898. return;
  5899. }
  5900. if (cgs.gametype == GT_DUEL || cgs.gametype == GT_POWERDUEL)
  5901. {
  5902. // find the two active players
  5903. clientInfo_t *ci1, *ci2, *ci3;
  5904. ci1 = NULL;
  5905. ci2 = NULL;
  5906. ci3 = NULL;
  5907. if (cgs.gametype == GT_POWERDUEL)
  5908. {
  5909. if (cgs.duelist1 != -1)
  5910. {
  5911. ci1 = &cgs.clientinfo[cgs.duelist1];
  5912. }
  5913. if (cgs.duelist2 != -1)
  5914. {
  5915. ci2 = &cgs.clientinfo[cgs.duelist2];
  5916. }
  5917. if (cgs.duelist3 != -1)
  5918. {
  5919. ci3 = &cgs.clientinfo[cgs.duelist3];
  5920. }
  5921. }
  5922. else
  5923. {
  5924. for ( i = 0 ; i < cgs.maxclients ; i++ ) {
  5925. if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) {
  5926. if ( !ci1 ) {
  5927. ci1 = &cgs.clientinfo[i];
  5928. } else {
  5929. ci2 = &cgs.clientinfo[i];
  5930. }
  5931. }
  5932. }
  5933. }
  5934. if ( ci1 && ci2 )
  5935. {
  5936. if (ci3)
  5937. {
  5938. s = va( "%s vs %s and %s", ci1->name, ci2->name, ci3->name );
  5939. }
  5940. else
  5941. {
  5942. s = va( "%s vs %s", ci1->name, ci2->name );
  5943. }
  5944. w = CG_Text_Width(s, 0.6f, FONT_MEDIUM);
  5945. CG_Text_Paint(320 - w / 2, 60, 0.6f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE,FONT_MEDIUM);
  5946. }
  5947. } else {
  5948. if ( cgs.gametype == GT_FFA ) {
  5949. s = CG_GetStringEdString("MENUS", "FREE_FOR_ALL");//"Free For All";
  5950. } else if ( cgs.gametype == GT_HOLOCRON ) {
  5951. s = CG_GetStringEdString("MENUS", "HOLOCRON_FFA");//"Holocron FFA";
  5952. } else if ( cgs.gametype == GT_JEDIMASTER ) {
  5953. s = CG_GetStringEdString("MENUS", "POWERDUEL");//"Jedi Master";??
  5954. } else if ( cgs.gametype == GT_TEAM ) {
  5955. s = CG_GetStringEdString("MENUS", "TEAM_FFA");//"Team FFA";
  5956. } else if ( cgs.gametype == GT_SIEGE ) {
  5957. s = CG_GetStringEdString("MENUS", "SIEGE");//"Siege";
  5958. } else if ( cgs.gametype == GT_CTF ) {
  5959. s = CG_GetStringEdString("MENUS", "CAPTURE_THE_FLAG");//"Capture the Flag";
  5960. } else if ( cgs.gametype == GT_CTY ) {
  5961. s = CG_GetStringEdString("MENUS", "CAPTURE_THE_YSALIMARI");//"Capture the Ysalamiri";
  5962. } else {
  5963. s = "";
  5964. }
  5965. w = CG_Text_Width(s, 1.5f, FONT_MEDIUM);
  5966. CG_Text_Paint(320 - w / 2, 90, 1.5f, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE,FONT_MEDIUM);
  5967. }
  5968. sec = ( sec - cg.time ) / 1000;
  5969. if ( sec < 0 ) {
  5970. cg.warmup = 0;
  5971. sec = 0;
  5972. }
  5973. // s = va( "Starts in: %i", sec + 1 );
  5974. s = va( "%s: %i",CG_GetStringEdString("MP_INGAME", "STARTS_IN"), sec + 1 );
  5975. if ( sec != cg.warmupCount ) {
  5976. cg.warmupCount = sec;
  5977. if (!(mov_soundDisable.integer & SDISABLE_ANNOUNCER) && cgs.gametype != GT_SIEGE)
  5978. {
  5979. switch ( sec ) {
  5980. case 0:
  5981. trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER );
  5982. break;
  5983. case 1:
  5984. trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER );
  5985. break;
  5986. case 2:
  5987. trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER );
  5988. break;
  5989. default:
  5990. break;
  5991. }
  5992. }
  5993. }
  5994. scale = 0.45f;
  5995. switch ( cg.warmupCount ) {
  5996. case 0:
  5997. cw = 28;
  5998. scale = 1.25f;
  5999. break;
  6000. case 1:
  6001. cw = 24;
  6002. scale = 1.15f;
  6003. break;
  6004. case 2:
  6005. cw = 20;
  6006. scale = 1.05f;
  6007. break;
  6008. default:
  6009. cw = 16;
  6010. scale = 0.9f;
  6011. break;
  6012. }
  6013. w = CG_Text_Width(s, scale, FONT_MEDIUM);
  6014. CG_Text_Paint(320 - w / 2, 125, scale, colorWhite, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE, FONT_MEDIUM);
  6015. }
  6016. //==================================================================================
  6017. /*
  6018. =================
  6019. CG_DrawTimedMenus
  6020. =================
  6021. */
  6022. void CG_DrawTimedMenus() {
  6023. if (cg.voiceTime) {
  6024. int t = cg.time - cg.voiceTime;
  6025. if ( t > 2500 ) {
  6026. Menus_CloseByName("voiceMenu");
  6027. trap_Cvar_Set("cl_conXOffset", "0");
  6028. cg.voiceTime = 0;
  6029. }
  6030. }
  6031. }
  6032. static qboolean CG_YourFlagDropped(void) {
  6033. if (cgs.gametype == GT_CTF || cgs.gametype == GT_CTY) {
  6034. int team = cgs.clientinfo[cg.playerCent->currentState.number].team;
  6035. if (team == TEAM_RED && cgs.redflag == FLAG_DROPPED) {
  6036. return qtrue;
  6037. } else if (team == TEAM_BLUE && cgs.blueflag == FLAG_DROPPED) {
  6038. return qtrue;
  6039. } else {
  6040. return qfalse;
  6041. }
  6042. }
  6043. return qfalse;
  6044. }
  6045. static qboolean CG_OtherFlagDropped(void) {
  6046. if (cgs.gametype == GT_CTF || cgs.gametype == GT_CTY) {
  6047. int team = cgs.clientinfo[cg.playerCent->currentState.number].team;
  6048. if (team == TEAM_RED && cgs.blueflag == FLAG_DROPPED) {
  6049. return qtrue;
  6050. } else if (team == TEAM_BLUE && cgs.redflag == FLAG_DROPPED) {
  6051. return qtrue;
  6052. } else {
  6053. return qfalse;
  6054. }
  6055. }
  6056. return qfalse;
  6057. }
  6058. void CG_DrawFlagStatus() {
  6059. int myFlagTakenShader = 0;
  6060. int theirFlagShader = 0;
  6061. int team = 0;
  6062. int startDrawPos = 2;
  6063. int ico_size = 32;
  6064. //Raz: was missing this
  6065. trap_R_SetColor( NULL );
  6066. if (!cg.snap)
  6067. return;
  6068. if (cgs.gametype != GT_CTF && cgs.gametype != GT_CTY)
  6069. return;
  6070. team = cgs.clientinfo[cg.playerCent->currentState.number].team;
  6071. if (cgs.gametype == GT_CTY) {
  6072. if (team == TEAM_RED) {
  6073. myFlagTakenShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_rflag_x" );
  6074. theirFlagShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_bflag_ys" );
  6075. } else {
  6076. myFlagTakenShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_bflag_x" );
  6077. theirFlagShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_rflag_ys" );
  6078. }
  6079. } else {
  6080. if (team == TEAM_RED) {
  6081. myFlagTakenShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_rflag_x" );
  6082. theirFlagShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_bflag" );
  6083. } else {
  6084. myFlagTakenShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_bflag_x" );
  6085. theirFlagShader = trap_R_RegisterShaderNoMip( "gfx/hud/mpi_rflag" );
  6086. }
  6087. }
  6088. if (CG_YourTeamHasFlag()) {
  6089. CG_DrawPic( 7, 330-startDrawPos, ico_size*cgs.widthRatioCoef, ico_size, theirFlagShader );
  6090. startDrawPos += ico_size+2;
  6091. } else if (CG_OtherFlagDropped()) {
  6092. vec4_t c = {1.0f, 1.0f, 1.0f, 0.5f};
  6093. trap_R_SetColor(c);
  6094. CG_DrawPic( 7, 330-startDrawPos, ico_size*cgs.widthRatioCoef, ico_size, theirFlagShader );
  6095. startDrawPos += ico_size+2;
  6096. trap_R_SetColor( NULL );
  6097. }
  6098. if (CG_OtherTeamHasFlag()) {
  6099. CG_DrawPic( 7, 330-startDrawPos, ico_size*cgs.widthRatioCoef, ico_size, myFlagTakenShader );
  6100. } else if (CG_YourFlagDropped()) {
  6101. vec4_t c = {1.0f, 1.0f, 1.0f, 0.5f};
  6102. trap_R_SetColor(c);
  6103. CG_DrawPic( 7, 330-startDrawPos, ico_size*cgs.widthRatioCoef, ico_size, myFlagTakenShader );
  6104. trap_R_SetColor( NULL );
  6105. }
  6106. }
  6107. //draw meter showing jetpack fuel when it's not full
  6108. #define JPFUELBAR_H 100.0f
  6109. #define JPFUELBAR_W 20.0f
  6110. #define JPFUELBAR_X (SCREEN_WIDTH-JPFUELBAR_W-8.0f)
  6111. #define JPFUELBAR_Y 260.0f
  6112. void CG_DrawJetpackFuel(void)
  6113. {
  6114. vec4_t aColor;
  6115. vec4_t bColor;
  6116. vec4_t cColor;
  6117. float x = SCREEN_WIDTH - (SCREEN_WIDTH - JPFUELBAR_X)*cgs.widthRatioCoef;
  6118. float y = JPFUELBAR_Y;
  6119. float percent = ((float)cg.snap->ps.jetpackFuel/100.0f)*JPFUELBAR_H;
  6120. if (percent > JPFUELBAR_H)
  6121. {
  6122. return;
  6123. }
  6124. if (percent < 0.1f)
  6125. {
  6126. percent = 0.1f;
  6127. }
  6128. //color of the bar
  6129. aColor[0] = 0.5f;
  6130. aColor[1] = 0.0f;
  6131. aColor[2] = 0.0f;
  6132. aColor[3] = 0.8f;
  6133. //color of the border
  6134. bColor[0] = 0.0f;
  6135. bColor[1] = 0.0f;
  6136. bColor[2] = 0.0f;
  6137. bColor[3] = 0.3f;
  6138. //color of greyed out "missing fuel"
  6139. cColor[0] = 0.5f;
  6140. cColor[1] = 0.5f;
  6141. cColor[2] = 0.5f;
  6142. cColor[3] = 0.1f;
  6143. //draw the background (black)
  6144. CG_DrawRect(x, y, JPFUELBAR_W*cgs.widthRatioCoef, JPFUELBAR_H, 1.0f, colorTable[CT_BLACK]);
  6145. //now draw the part to show how much health there is in the color specified
  6146. CG_FillRect(x+1.0f, y+1.0f+(JPFUELBAR_H-percent), (JPFUELBAR_W-1.0f)*cgs.widthRatioCoef, JPFUELBAR_H-1.0f-(JPFUELBAR_H-percent), aColor);
  6147. //then draw the other part greyed out
  6148. CG_FillRect(x+1.0f, y+1.0f, (JPFUELBAR_W-1.0f)*cgs.widthRatioCoef, JPFUELBAR_H-percent, cColor);
  6149. }
  6150. //draw meter showing e-web health when it is in use
  6151. #define EWEBHEALTH_H 100.0f
  6152. #define EWEBHEALTH_W 20.0f
  6153. #define EWEBHEALTH_X (SCREEN_WIDTH-EWEBHEALTH_W-8.0f)
  6154. #define EWEBHEALTH_Y 290.0f
  6155. void CG_DrawEWebHealth(void)
  6156. {
  6157. vec4_t aColor;
  6158. vec4_t bColor;
  6159. vec4_t cColor;
  6160. float x = EWEBHEALTH_X;
  6161. float y = EWEBHEALTH_Y;
  6162. centity_t *eweb = &cg_entities[cg.predictedPlayerState.emplacedIndex];
  6163. float percent = ((float)eweb->currentState.health/eweb->currentState.maxhealth)*EWEBHEALTH_H;
  6164. if (percent > EWEBHEALTH_H)
  6165. {
  6166. return;
  6167. }
  6168. if (percent < 0.1f)
  6169. {
  6170. percent = 0.1f;
  6171. }
  6172. //kind of hacky, need to pass a coordinate in here
  6173. if (cg.snap->ps.jetpackFuel < 100)
  6174. {
  6175. x -= (JPFUELBAR_W+8.0f);
  6176. }
  6177. if (cg.snap->ps.cloakFuel < 100)
  6178. {
  6179. x -= (JPFUELBAR_W+8.0f);
  6180. }
  6181. //color of the bar
  6182. aColor[0] = 0.5f;
  6183. aColor[1] = 0.0f;
  6184. aColor[2] = 0.0f;
  6185. aColor[3] = 0.8f;
  6186. //color of the border
  6187. bColor[0] = 0.0f;
  6188. bColor[1] = 0.0f;
  6189. bColor[2] = 0.0f;
  6190. bColor[3] = 0.3f;
  6191. //color of greyed out "missing fuel"
  6192. cColor[0] = 0.5f;
  6193. cColor[1] = 0.5f;
  6194. cColor[2] = 0.5f;
  6195. cColor[3] = 0.1f;
  6196. //draw the background (black)
  6197. CG_DrawRect(x, y, EWEBHEALTH_W, EWEBHEALTH_H, 1.0f, colorTable[CT_BLACK]);
  6198. //now draw the part to show how much health there is in the color specified
  6199. CG_FillRect(x+1.0f, y+1.0f+(EWEBHEALTH_H-percent), EWEBHEALTH_W-1.0f, EWEBHEALTH_H-1.0f-(EWEBHEALTH_H-percent), aColor);
  6200. //then draw the other part greyed out
  6201. CG_FillRect(x+1.0f, y+1.0f, EWEBHEALTH_W-1.0f, EWEBHEALTH_H-percent, cColor);
  6202. }
  6203. //draw meter showing cloak fuel when it's not full
  6204. #define CLFUELBAR_H 100.0f
  6205. #define CLFUELBAR_W 20.0f
  6206. #define CLFUELBAR_X (SCREEN_WIDTH-CLFUELBAR_W-8.0f)
  6207. #define CLFUELBAR_Y 260.0f
  6208. void CG_DrawCloakFuel(void)
  6209. {
  6210. vec4_t aColor;
  6211. vec4_t bColor;
  6212. vec4_t cColor;
  6213. float x = CLFUELBAR_X;
  6214. float y = CLFUELBAR_Y;
  6215. float percent = ((float)cg.snap->ps.cloakFuel/100.0f)*CLFUELBAR_H;
  6216. if (percent > CLFUELBAR_H)
  6217. {
  6218. return;
  6219. }
  6220. if ( cg.snap->ps.jetpackFuel < 100 )
  6221. {//if drawing jetpack fuel bar too, then move this over...?
  6222. x -= (JPFUELBAR_W+8.0f);
  6223. }
  6224. if (percent < 0.1f)
  6225. {
  6226. percent = 0.1f;
  6227. }
  6228. //color of the bar
  6229. aColor[0] = 0.0f;
  6230. aColor[1] = 0.0f;
  6231. aColor[2] = 0.6f;
  6232. aColor[3] = 0.8f;
  6233. //color of the border
  6234. bColor[0] = 0.0f;
  6235. bColor[1] = 0.0f;
  6236. bColor[2] = 0.0f;
  6237. bColor[3] = 0.3f;
  6238. //color of greyed out "missing fuel"
  6239. cColor[0] = 0.1f;
  6240. cColor[1] = 0.1f;
  6241. cColor[2] = 0.3f;
  6242. cColor[3] = 0.1f;
  6243. //draw the background (black)
  6244. CG_DrawRect(x, y, CLFUELBAR_W, CLFUELBAR_H, 1.0f, colorTable[CT_BLACK]);
  6245. //now draw the part to show how much fuel there is in the color specified
  6246. CG_FillRect(x+1.0f, y+1.0f+(CLFUELBAR_H-percent), CLFUELBAR_W-1.0f, CLFUELBAR_H-1.0f-(CLFUELBAR_H-percent), aColor);
  6247. //then draw the other part greyed out
  6248. CG_FillRect(x+1.0f, y+1.0f, CLFUELBAR_W-1.0f, CLFUELBAR_H-percent, cColor);
  6249. }
  6250. int cgRageTime = 0;
  6251. int cgRageFadeTime = 0;
  6252. float cgRageFadeVal = 0;
  6253. int cgRageRecTime = 0;
  6254. int cgRageRecFadeTime = 0;
  6255. float cgRageRecFadeVal = 0;
  6256. int cgAbsorbTime = 0;
  6257. int cgAbsorbFadeTime = 0;
  6258. float cgAbsorbFadeVal = 0;
  6259. int cgProtectTime = 0;
  6260. int cgProtectFadeTime = 0;
  6261. float cgProtectFadeVal = 0;
  6262. int cgYsalTime = 0;
  6263. int cgYsalFadeTime = 0;
  6264. float cgYsalFadeVal = 0;
  6265. qboolean gCGHasFallVector = qfalse;
  6266. vec3_t gCGFallVector;
  6267. /*
  6268. =================
  6269. CG_Draw2D
  6270. =================
  6271. */
  6272. extern int cgSiegeRoundState;
  6273. extern int cgSiegeRoundTime;
  6274. extern int team1Timed;
  6275. extern int team2Timed;
  6276. int cg_beatingSiegeTime = 0;
  6277. int cgSiegeRoundBeganTime = 0;
  6278. int cgSiegeRoundCountTime = 0;
  6279. static void CG_DrawSiegeTimer(int timeRemaining, qboolean isMyTeam)
  6280. { //rwwFIXMEFIXME: Make someone make assets and use them.
  6281. //this function is pretty much totally placeholder.
  6282. // int x = 0;
  6283. // int y = SCREEN_HEIGHT-160;
  6284. int fColor = 0;
  6285. int minutes = 0;
  6286. int seconds = 0;
  6287. char timeStr[1024];
  6288. menuDef_t *menuHUD = NULL;
  6289. itemDef_t *item = NULL;
  6290. menuHUD = Menus_FindByName("mp_timer");
  6291. if (!menuHUD)
  6292. {
  6293. return;
  6294. }
  6295. item = Menu_FindItemByName(menuHUD, "frame");
  6296. if (item)
  6297. {
  6298. trap_R_SetColor( item->window.foreColor );
  6299. CG_DrawPic(
  6300. item->window.rect.x,
  6301. item->window.rect.y,
  6302. item->window.rect.w,
  6303. item->window.rect.h,
  6304. item->window.background );
  6305. }
  6306. seconds = timeRemaining;
  6307. while (seconds >= 60)
  6308. {
  6309. minutes++;
  6310. seconds -= 60;
  6311. }
  6312. strcpy(timeStr, va( "%i:%02i", minutes, seconds ));
  6313. if (isMyTeam)
  6314. {
  6315. fColor = CT_HUD_RED;
  6316. }
  6317. else
  6318. {
  6319. fColor = CT_HUD_GREEN;
  6320. }
  6321. // trap_Cvar_Set("ui_siegeTimer", timeStr);
  6322. // UI_DrawProportionalString( x+16, y+40, timeStr,
  6323. // UI_SMALLFONT|UI_DROPSHADOW, colorTable[fColor] );
  6324. item = Menu_FindItemByName(menuHUD, "timer");
  6325. if (item)
  6326. {
  6327. UI_DrawProportionalString(
  6328. item->window.rect.x,
  6329. item->window.rect.y,
  6330. timeStr,
  6331. UI_SMALLFONT|UI_DROPSHADOW,
  6332. colorTable[fColor] );
  6333. }
  6334. }
  6335. static void CG_DrawSiegeDeathTimer( int timeRemaining )
  6336. {
  6337. int minutes = 0;
  6338. int seconds = 0;
  6339. char timeStr[1024];
  6340. menuDef_t *menuHUD = NULL;
  6341. itemDef_t *item = NULL;
  6342. menuHUD = Menus_FindByName("mp_timer");
  6343. if (!menuHUD)
  6344. {
  6345. return;
  6346. }
  6347. item = Menu_FindItemByName(menuHUD, "frame");
  6348. if (item)
  6349. {
  6350. trap_R_SetColor( item->window.foreColor );
  6351. CG_DrawPic(
  6352. item->window.rect.x,
  6353. item->window.rect.y,
  6354. item->window.rect.w,
  6355. item->window.rect.h,
  6356. item->window.background );
  6357. }
  6358. seconds = timeRemaining;
  6359. while (seconds >= 60)
  6360. {
  6361. minutes++;
  6362. seconds -= 60;
  6363. }
  6364. if (seconds < 10)
  6365. {
  6366. strcpy(timeStr, va( "%i:0%i", minutes, seconds ));
  6367. }
  6368. else
  6369. {
  6370. strcpy(timeStr, va( "%i:%i", minutes, seconds ));
  6371. }
  6372. item = Menu_FindItemByName(menuHUD, "deathtimer");
  6373. if (item)
  6374. {
  6375. UI_DrawProportionalString(
  6376. item->window.rect.x,
  6377. item->window.rect.y,
  6378. timeStr,
  6379. UI_SMALLFONT|UI_DROPSHADOW,
  6380. item->window.foreColor );
  6381. }
  6382. }
  6383. int cgSiegeEntityRender = 0;
  6384. static void CG_DrawSiegeHUDItem(void)
  6385. {
  6386. void *g2;
  6387. qhandle_t handle;
  6388. vec3_t origin, angles;
  6389. vec3_t mins, maxs;
  6390. float len;
  6391. centity_t *cent = &cg_entities[cgSiegeEntityRender];
  6392. if (cent->ghoul2)
  6393. {
  6394. g2 = cent->ghoul2;
  6395. handle = 0;
  6396. }
  6397. else
  6398. {
  6399. handle = cgs.gameModels[cent->currentState.modelindex];
  6400. g2 = NULL;
  6401. }
  6402. if (handle)
  6403. {
  6404. trap_R_ModelBounds( handle, mins, maxs );
  6405. }
  6406. else
  6407. {
  6408. VectorSet(mins, -16, -16, -20);
  6409. VectorSet(maxs, 16, 16, 32);
  6410. }
  6411. origin[2] = -0.5 * ( mins[2] + maxs[2] );
  6412. origin[1] = 0.5 * ( mins[1] + maxs[1] );
  6413. len = 0.5 * ( maxs[2] - mins[2] );
  6414. origin[0] = len / 0.268;
  6415. VectorClear(angles);
  6416. angles[YAW] = cg.autoAngles[YAW];
  6417. CG_Draw3DModel( 8, 8, 64, 64, handle, g2, cent->currentState.g2radius, 0, origin, angles );
  6418. cgSiegeEntityRender = 0; //reset for next frame
  6419. }
  6420. /*====================================
  6421. chatbox functionality -rww
  6422. ====================================*/
  6423. #define CHATBOX_CUTOFF_LEN 550
  6424. #define CHATBOX_FONT_HEIGHT 20
  6425. //utility func, insert a string into a string at the specified
  6426. //place (assuming this will not overflow the buffer)
  6427. static void CG_ChatBox_StrInsert(char *buffer, int place, char *str) {
  6428. int insLen = strlen(str);
  6429. int i = strlen(buffer);
  6430. int k = 0;
  6431. buffer[i+insLen+1] = 0; //terminate the string at its new length
  6432. while (i >= place) {
  6433. buffer[i+insLen] = buffer[i];
  6434. i--;
  6435. }
  6436. i++;
  6437. while (k < insLen) {
  6438. buffer[i] = str[k];
  6439. i++;
  6440. k++;
  6441. }
  6442. }
  6443. //add chatbox string
  6444. void CG_ChatBox_AddString(char *chatStr) {
  6445. chatBoxItem_t *chat = &cg.chatItems[cg.chatItemActive];
  6446. float chatLen;
  6447. if (cg_chatBox.integer<=0) {
  6448. //don't bother then.
  6449. return;
  6450. }
  6451. memset(chat, 0, sizeof(chatBoxItem_t));
  6452. if (strlen(chatStr) > sizeof(chat->string)) {
  6453. //too long, terminate at proper len.
  6454. chatStr[sizeof(chat->string)-1] = 0;
  6455. }
  6456. strcpy(chat->string, chatStr);
  6457. chat->time = cg.time + cg_chatBox.integer;
  6458. chat->lines = 1;
  6459. chatLen = CG_Text_Width(chat->string, 1.0f, FONT_SMALL);
  6460. if (chatLen > CHATBOX_CUTOFF_LEN) {
  6461. //we have to break it into segments...
  6462. int i = 0;
  6463. int lastLinePt = 0;
  6464. char s[2];
  6465. chatLen = 0;
  6466. while (chat->string[i]) {
  6467. s[0] = chat->string[i];
  6468. s[1] = 0;
  6469. chatLen += CG_Text_Width(s, 0.65f, FONT_SMALL);
  6470. if (chatLen >= CHATBOX_CUTOFF_LEN) {
  6471. int j = i;
  6472. while (j > 0 && j > lastLinePt) {
  6473. if (chat->string[j] == ' ') {
  6474. break;
  6475. }
  6476. j--;
  6477. }
  6478. if (chat->string[j] == ' ') {
  6479. i = j;
  6480. }
  6481. chat->lines++;
  6482. CG_ChatBox_StrInsert(chat->string, i, "\n");
  6483. i++;
  6484. chatLen = 0;
  6485. lastLinePt = i+1;
  6486. }
  6487. i++;
  6488. }
  6489. }
  6490. cg.chatItemActive++;
  6491. if (cg.chatItemActive >= MAX_CHATBOX_ITEMS) {
  6492. cg.chatItemActive = 0;
  6493. }
  6494. }
  6495. //insert item into array (rearranging the array if necessary)
  6496. void CG_ChatBox_ArrayInsert(chatBoxItem_t **array, int insPoint, int maxNum, chatBoxItem_t *item) {
  6497. if (array[insPoint]) { //recursively call, to move everything up to the top
  6498. if (insPoint+1 >= maxNum) {
  6499. CG_Error("CG_ChatBox_ArrayInsert: Exceeded array size");
  6500. }
  6501. CG_ChatBox_ArrayInsert(array, insPoint+1, maxNum, array[insPoint]);
  6502. }
  6503. //now that we have moved anything that would be in this slot up, insert what we want into the slot
  6504. array[insPoint] = item;
  6505. }
  6506. //go through all the chat strings and draw them if they are not yet expired
  6507. static CGAME_INLINE void CG_ChatBox_DrawStrings(void) {
  6508. chatBoxItem_t *drawThese[MAX_CHATBOX_ITEMS];
  6509. int numToDraw = 0;
  6510. int linesToDraw = 0;
  6511. int i = 0;
  6512. int x = 30;
  6513. float y = cg.scoreBoardShowing ? 475 : cg_chatBoxHeight.integer;
  6514. float fontScale = 0.65f;
  6515. if (!cg_chatBox.integer) {
  6516. return;
  6517. }
  6518. // if (cg.chatItems->time > cg_chatBox.integer + cg.time) {
  6519. // cg.chatItems->time = cg.time;
  6520. // return;
  6521. // }
  6522. memset(drawThese, 0, sizeof(drawThese));
  6523. while (i < MAX_CHATBOX_ITEMS) {
  6524. if (cg.chatItems[i].time >= cg.time) {
  6525. int check = numToDraw;
  6526. int insertionPoint = numToDraw;
  6527. if (cg.chatItems[i].time > cg_chatBox.integer + cg.time) {
  6528. cg.chatItems[i].time = cg.time;
  6529. return;
  6530. }
  6531. while (check >= 0) {
  6532. if (drawThese[check] && cg.chatItems[i].time < drawThese[check]->time) {
  6533. //insert here
  6534. insertionPoint = check;
  6535. }
  6536. check--;
  6537. }
  6538. CG_ChatBox_ArrayInsert(drawThese, insertionPoint, MAX_CHATBOX_ITEMS, &cg.chatItems[i]);
  6539. numToDraw++;
  6540. linesToDraw += cg.chatItems[i].lines;
  6541. }
  6542. i++;
  6543. }
  6544. if (!numToDraw) { //nothing, then, just get out of here now.
  6545. return;
  6546. }
  6547. //move initial point up so we draw bottom-up (visually)
  6548. y -= (CHATBOX_FONT_HEIGHT*fontScale)*linesToDraw;
  6549. //we have the items we want to draw, just quickly loop through them now
  6550. i = 0;
  6551. while (i < numToDraw) {
  6552. CG_Text_Paint(x, y, fontScale, colorWhite, drawThese[i]->string, 0, 0, ITEM_TEXTSTYLE_OUTLINED, FONT_SMALL );
  6553. y += ((CHATBOX_FONT_HEIGHT*fontScale)*drawThese[i]->lines);
  6554. i++;
  6555. }
  6556. trap_R_SetColor( NULL );
  6557. }
  6558. static void CG_DrawInWaterTints (void) {
  6559. if ((cg.refdef.viewContents&CONTENTS_LAVA)) {
  6560. //tint screen red
  6561. vec4_t hcolor;
  6562. float phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2;
  6563. hcolor[3] = 0.5 + (0.15f*sin(phase + (cg.timeFraction / 1000.0 * WAVE_FREQUENCY * M_PI * 2)));
  6564. hcolor[0] = 0.7f;
  6565. hcolor[1] = 0;
  6566. hcolor[2] = 0;
  6567. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6568. } else if ((cg.refdef.viewContents&CONTENTS_SLIME)) {
  6569. //tint screen green
  6570. vec4_t hcolor;
  6571. float phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2;
  6572. hcolor[3] = 0.4 + (0.1f*sin(phase + (cg.timeFraction / 1000.0 * WAVE_FREQUENCY * M_PI * 2)));
  6573. hcolor[0] = 0;
  6574. hcolor[1] = 0.7f;
  6575. hcolor[2] = 0;
  6576. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6577. } else if ((cg.refdef.viewContents&CONTENTS_WATER)) {
  6578. //tint screen light blue -- FIXME: don't do this if CONTENTS_FOG? (in case someone *does* make a water shader with fog in it?)
  6579. vec4_t hcolor;
  6580. float phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2;
  6581. hcolor[3] = 0.3f + (0.05f*(float)sin(phase + (cg.timeFraction / 1000.0 * WAVE_FREQUENCY * M_PI * 2)));
  6582. hcolor[0] = 0;
  6583. hcolor[1] = 0.2f;
  6584. hcolor[2] = 0.8f;
  6585. //better underwater colour
  6586. // hcolor[3] = 0.2f + (0.05f*(float)sin(phase + (cg.timeFraction / 1000.0 * WAVE_FREQUENCY * M_PI * 2)));
  6587. // hcolor[0] = 0.1;
  6588. // hcolor[1] = 0.4f;
  6589. // hcolor[2] = 0.7f;
  6590. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6591. }
  6592. }
  6593. static qboolean CG_HasYsalamiri(int gametype, entityState_t *es) {
  6594. if (gametype == GT_CTY && ((es->powerups & (1 << PW_REDFLAG)) || (es->powerups & (1 << PW_BLUEFLAG)))) {
  6595. return qtrue;
  6596. }
  6597. if (es->powerups & (1 << PW_YSALAMIRI)) {
  6598. return qtrue;
  6599. }
  6600. return qfalse;
  6601. }
  6602. static void CG_Draw2DScreenTints( void ) {
  6603. float rageTime, rageRecTime, absorbTime, protectTime, ysalTime;
  6604. vec4_t hcolor;
  6605. int forcePowersActive = cg.playerPredicted ?
  6606. cg.snap->ps.fd.forcePowersActive :
  6607. cg.playerCent->currentState.forcePowersActive;
  6608. if (cgs.clientinfo[cg.playerCent->currentState.clientNum].team != TEAM_SPECTATOR) {
  6609. if (forcePowersActive & (1 << FP_RAGE)) {
  6610. if (!cgRageTime)
  6611. cgRageTime = cg.time;
  6612. rageTime = (cg.time - cgRageTime) + cg.timeFraction;
  6613. rageTime /= 9000;
  6614. if (rageTime < 0)
  6615. rageTime = 0;
  6616. else if (rageTime > 0.15)
  6617. rageTime = 0.15f;
  6618. if (mov_rageColour.string[0] == '0') {
  6619. hcolor[0] = 0.7f;
  6620. hcolor[1] = 0;
  6621. hcolor[2] = 0;
  6622. } else {
  6623. Q_parseColor(mov_rageColour.string, defaultColors, hcolor);
  6624. }
  6625. hcolor[3] = rageTime;
  6626. if (!cg.renderingThirdPerson)
  6627. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6628. cgRageFadeTime = 0;
  6629. cgRageFadeVal = 0;
  6630. } else if (cgRageTime) {
  6631. if (!cgRageFadeTime) {
  6632. cgRageFadeTime = cg.time;
  6633. cgRageFadeVal = 0.15f;
  6634. }
  6635. rageTime = cgRageFadeVal;
  6636. cgRageFadeVal -= ((cg.time - cgRageFadeTime) + cg.timeFraction)*0.000005f;
  6637. if (rageTime < 0)
  6638. rageTime = 0;
  6639. else if (rageTime > 0.15f)
  6640. rageTime = 0.15f;
  6641. if (cg.playerPredicted && cg.snap->ps.fd.forceRageRecoveryTime > cg.time) {
  6642. float checkRageRecTime = rageTime;
  6643. if (checkRageRecTime < 0.15f) {
  6644. checkRageRecTime = 0.15f;
  6645. }
  6646. if (mov_rageColour.string[0] == '0') {
  6647. hcolor[0] = rageTime*4;
  6648. if (hcolor[0] < 0.2f) {
  6649. hcolor[0] = 0.2f;
  6650. }
  6651. hcolor[1] = 0.2f;
  6652. hcolor[2] = 0.2f;
  6653. } else {
  6654. Q_parseColor(mov_rageColour.string, defaultColors, hcolor);
  6655. hcolor[0] *= rageTime*6;
  6656. hcolor[1] *= rageTime*6;
  6657. hcolor[2] *= rageTime*6;
  6658. if (hcolor[0] < 0.1f) {
  6659. hcolor[0] = 0.1f;
  6660. }
  6661. if (hcolor[1] < 0.1f) {
  6662. hcolor[1] = 0.1f;
  6663. }
  6664. if (hcolor[2] < 0.1f) {
  6665. hcolor[2] = 0.1f;
  6666. }
  6667. }
  6668. hcolor[3] = checkRageRecTime;
  6669. } else {
  6670. if (mov_rageColour.string[0] == '0') {
  6671. hcolor[0] = 0.7f;
  6672. hcolor[1] = 0;
  6673. hcolor[2] = 0;
  6674. } else {
  6675. Q_parseColor(mov_rageColour.string, defaultColors, hcolor);
  6676. }
  6677. hcolor[3] = rageTime;
  6678. }
  6679. if (!cg.renderingThirdPerson && rageTime) {
  6680. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6681. } else {
  6682. if (cg.playerPredicted && cg.snap->ps.fd.forceRageRecoveryTime > cg.time) {
  6683. if (mov_rageColour.string[0] == '0') {
  6684. hcolor[0] = 0.2f;
  6685. hcolor[1] = 0.2f;
  6686. hcolor[2] = 0.2f;
  6687. } else {
  6688. hcolor[0] = 0.1f;
  6689. hcolor[1] = 0.1f;
  6690. hcolor[2] = 0.1f;
  6691. }
  6692. hcolor[3] = 0.15f;
  6693. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6694. }
  6695. cgRageTime = 0;
  6696. }
  6697. } else if (cg.playerPredicted && cg.snap->ps.fd.forceRageRecoveryTime > cg.time) {
  6698. if (!cgRageRecTime)
  6699. cgRageRecTime = cg.time;
  6700. rageRecTime = (cg.time - cgRageRecTime) + cg.timeFraction;
  6701. rageRecTime /= 9000;
  6702. if (rageRecTime < 0.15f)
  6703. rageRecTime = 0.15f;//0;
  6704. else if (rageRecTime > 0.15f)
  6705. rageRecTime = 0.15f;
  6706. hcolor[3] = rageRecTime;
  6707. hcolor[0] = 0.2f;
  6708. hcolor[1] = 0.2f;
  6709. hcolor[2] = 0.2f;
  6710. if (!cg.renderingThirdPerson)
  6711. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6712. cgRageRecFadeTime = 0;
  6713. cgRageRecFadeVal = 0;
  6714. } else if (cg.playerPredicted && cgRageRecTime) {
  6715. if (!cgRageRecFadeTime) {
  6716. cgRageRecFadeTime = cg.time;
  6717. cgRageRecFadeVal = 0.15f;
  6718. }
  6719. rageRecTime = cgRageRecFadeVal;
  6720. cgRageRecFadeVal -= ((cg.time - cgRageRecFadeTime) + cg.timeFraction)*0.000005f;
  6721. if (rageRecTime < 0)
  6722. rageRecTime = 0;
  6723. else if (rageRecTime > 0.15f)
  6724. rageRecTime = 0.15f;
  6725. hcolor[3] = rageRecTime;
  6726. hcolor[0] = 0.2f;
  6727. hcolor[1] = 0.2f;
  6728. hcolor[2] = 0.2f;
  6729. if (!cg.renderingThirdPerson && rageRecTime)
  6730. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6731. else
  6732. cgRageRecTime = 0;
  6733. }
  6734. if (forcePowersActive & (1 << FP_ABSORB)) {
  6735. if (!cgAbsorbTime)
  6736. cgAbsorbTime = cg.time;
  6737. absorbTime = (cg.time - cgAbsorbTime) + cg.timeFraction;
  6738. absorbTime /= 9000;
  6739. if (absorbTime < 0)
  6740. absorbTime = 0;
  6741. else if (absorbTime > 0.15f)
  6742. absorbTime = 0.15f;
  6743. if (mov_absorbColour.string[0] == '0') {
  6744. hcolor[0] = 0;
  6745. hcolor[1] = 0;
  6746. hcolor[2] = 0.7f;
  6747. } else {
  6748. Q_parseColor(mov_absorbColour.string, defaultColors, hcolor);
  6749. }
  6750. hcolor[3] = absorbTime/2;
  6751. if (!cg.renderingThirdPerson)
  6752. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6753. cgAbsorbFadeTime = 0;
  6754. cgAbsorbFadeVal = 0;
  6755. } else if (cgAbsorbTime) {
  6756. if (!cgAbsorbFadeTime) {
  6757. cgAbsorbFadeTime = cg.time;
  6758. cgAbsorbFadeVal = 0.15f;
  6759. }
  6760. absorbTime = cgAbsorbFadeVal;
  6761. cgAbsorbFadeVal -= ((cg.time - cgAbsorbFadeTime) + cg.timeFraction)*0.000005f;
  6762. if (absorbTime < 0)
  6763. absorbTime = 0;
  6764. else if (absorbTime > 0.15f)
  6765. absorbTime = 0.15f;
  6766. if (mov_absorbColour.string[0] == '0') {
  6767. hcolor[0] = 0;
  6768. hcolor[1] = 0;
  6769. hcolor[2] = 0.7f;
  6770. } else {
  6771. Q_parseColor(mov_absorbColour.string, defaultColors, hcolor);
  6772. }
  6773. hcolor[3] = absorbTime/2;
  6774. if (!cg.renderingThirdPerson && absorbTime)
  6775. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6776. else
  6777. cgAbsorbTime = 0;
  6778. }
  6779. if (forcePowersActive & (1 << FP_PROTECT)) {
  6780. if (!cgProtectTime) {
  6781. cgProtectTime = cg.time;
  6782. }
  6783. protectTime = (cg.time - cgProtectTime) + cg.timeFraction;
  6784. protectTime /= 9000;
  6785. if (protectTime < 0)
  6786. protectTime = 0;
  6787. else if (protectTime > 0.15f)
  6788. protectTime = 0.15f;
  6789. if (mov_protectColour.string[0] == '0') {
  6790. hcolor[0] = 0;
  6791. hcolor[1] = 0.7f;
  6792. hcolor[2] = 0;
  6793. } else {
  6794. Q_parseColor(mov_protectColour.string, defaultColors, hcolor);
  6795. }
  6796. hcolor[3] = protectTime/2;
  6797. if (!cg.renderingThirdPerson)
  6798. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6799. cgProtectFadeTime = 0;
  6800. cgProtectFadeVal = 0;
  6801. } else if (cgProtectTime) {
  6802. if (!cgProtectFadeTime) {
  6803. cgProtectFadeTime = cg.time;
  6804. cgProtectFadeVal = 0.15f;
  6805. }
  6806. protectTime = cgProtectFadeVal;
  6807. cgProtectFadeVal -= ((cg.time - cgProtectFadeTime) + cg.timeFraction)*0.000005f;
  6808. if (protectTime < 0)
  6809. protectTime = 0;
  6810. else if (protectTime > 0.15f)
  6811. protectTime = 0.15f;
  6812. if (mov_protectColour.string[0] == '0') {
  6813. hcolor[0] = 0;
  6814. hcolor[1] = 0.7f;
  6815. hcolor[2] = 0;
  6816. } else {
  6817. Q_parseColor(mov_protectColour.string, defaultColors, hcolor);
  6818. }
  6819. hcolor[3] = protectTime/2;
  6820. if (!cg.renderingThirdPerson && protectTime)
  6821. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6822. else
  6823. cgProtectTime = 0;
  6824. }
  6825. if ((cg.playerPredicted && BG_HasYsalamiri(cgs.gametype, &cg.snap->ps))
  6826. || (!cg.playerPredicted && CG_HasYsalamiri(cgs.gametype, &cg.playerCent->currentState))) {
  6827. if (!cgYsalTime)
  6828. cgYsalTime = cg.time;
  6829. ysalTime = (cg.time - cgYsalTime) + cg.timeFraction;
  6830. ysalTime /= 9000;
  6831. if (ysalTime < 0)
  6832. ysalTime = 0;
  6833. else if (ysalTime > 0.15f)
  6834. ysalTime = 0.15f;
  6835. hcolor[3] = ysalTime/2;
  6836. hcolor[0] = 0.7f;
  6837. hcolor[1] = 0.7f;
  6838. hcolor[2] = 0;
  6839. if (!cg.renderingThirdPerson)
  6840. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6841. cgYsalFadeTime = 0;
  6842. cgYsalFadeVal = 0;
  6843. } else if (cgYsalTime) {
  6844. if (!cgYsalFadeTime) {
  6845. cgYsalFadeTime = cg.time;
  6846. cgYsalFadeVal = 0.15f;
  6847. }
  6848. ysalTime = cgYsalFadeVal;
  6849. cgYsalFadeVal -= ((cg.time - cgYsalFadeTime) + cg.timeFraction)*0.000005f;
  6850. if (ysalTime < 0)
  6851. ysalTime = 0;
  6852. else if (ysalTime > 0.15f)
  6853. ysalTime = 0.15f;
  6854. hcolor[3] = ysalTime/2;
  6855. hcolor[0] = 0.7f;
  6856. hcolor[1] = 0.7f;
  6857. hcolor[2] = 0;
  6858. if (!cg.renderingThirdPerson && ysalTime)
  6859. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6860. else
  6861. cgYsalTime = 0;
  6862. }
  6863. }
  6864. CG_DrawInWaterTints();
  6865. }
  6866. static void CG_UpdateFallVector (void) {
  6867. if (!cg.playerCent)
  6868. goto clearFallVector;
  6869. if (cg.fallingToDeath) {
  6870. float fallTime;
  6871. vec4_t hcolor;
  6872. fallTime = (cg.time - cg.fallingToDeath) + cg.timeFraction;
  6873. fallTime /= (float)(FALL_FADE_TIME/2.0f);
  6874. if (fallTime < 0)
  6875. fallTime = 0;
  6876. else if (fallTime > 1)
  6877. fallTime = 1;
  6878. hcolor[3] = fallTime;
  6879. hcolor[0] = 0;
  6880. hcolor[1] = 0;
  6881. hcolor[2] = 0;
  6882. CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
  6883. if (!gCGHasFallVector) {
  6884. VectorCopy(cg.playerCent->lerpOrigin, gCGFallVector);
  6885. gCGHasFallVector = qtrue;
  6886. }
  6887. } else {
  6888. if (gCGHasFallVector) {
  6889. clearFallVector:
  6890. gCGHasFallVector = qfalse;
  6891. VectorClear(gCGFallVector);
  6892. }
  6893. }
  6894. }
  6895. void CG_CameraDraw2D( void ) {
  6896. CG_SaberClashFlare();
  6897. if (cg_drawFPS.integer) {
  6898. CG_DrawFPS(0);
  6899. }
  6900. if (cg_draw2D.integer > 1) {
  6901. if (cg_draw2D.integer == 3)
  6902. CG_DrawInWaterTints();
  6903. trap_S_UpdateEntityPosition(ENTITYNUM_NONE, cg.refdef.vieworg);
  6904. CG_ChatBox_DrawStrings();
  6905. }
  6906. }
  6907. void CG_Draw2D (void) {
  6908. float inTime = cg.invenSelectTime+WEAPON_SELECT_TIME;
  6909. float wpTime = cg.weaponSelectTime+WEAPON_SELECT_TIME;
  6910. float bestTime;
  6911. vec4_t hcolor = {0, 0, 0, 0};
  6912. // if we are taking a levelshot for the menu, don't draw anything
  6913. if (cg.levelShot) {
  6914. return;
  6915. }
  6916. if (cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_SPECTATOR) {
  6917. cgRageTime = 0;
  6918. cgRageFadeTime = 0;
  6919. cgRageFadeVal = 0;
  6920. cgRageRecTime = 0;
  6921. cgRageRecFadeTime = 0;
  6922. cgRageRecFadeVal = 0;
  6923. cgAbsorbTime = 0;
  6924. cgAbsorbFadeTime = 0;
  6925. cgAbsorbFadeVal = 0;
  6926. cgProtectTime = 0;
  6927. cgProtectFadeTime = 0;
  6928. cgProtectFadeVal = 0;
  6929. cgYsalTime = 0;
  6930. cgYsalFadeTime = 0;
  6931. cgYsalFadeVal = 0;
  6932. }
  6933. if (cg_draw2D.integer == 0) {
  6934. CG_UpdateFallVector();
  6935. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6936. return;
  6937. }
  6938. if (cg_draw2D.integer == 3 && mov_fragsOnly.integer == 0) {
  6939. CG_SaberClashFlare();
  6940. CG_DrawInWaterTints();
  6941. CG_UpdateFallVector();
  6942. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6943. return;
  6944. }
  6945. if (mov_fragsOnly.integer != 0) {
  6946. CG_SaberClashFlare();
  6947. if(!cg.renderingThirdPerson && mov_fragsOnly.integer == 2)
  6948. CG_DrawZoomMask();
  6949. if (cg.playerPredicted)
  6950. CG_DrawReward();
  6951. CG_DrawCenterString();
  6952. if (cg_draw2D.integer == 3) {
  6953. CG_DrawInWaterTints();
  6954. }
  6955. CG_UpdateFallVector();
  6956. CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, hcolor);
  6957. return;
  6958. }
  6959. if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
  6960. CG_DrawIntermission();
  6961. CG_ChatBox_DrawStrings();
  6962. return;
  6963. }
  6964. if (!cg.playerCent)
  6965. return;
  6966. else if (!cg.playerPredicted) {
  6967. cg.scoreBoardShowing = qfalse;
  6968. CG_Draw2DScreenTints();
  6969. if (cg.playerCent->currentState.time2)
  6970. CG_DrawHolocronIcons();
  6971. if (cg.playerCent->currentState.forcePowersActive)
  6972. CG_DrawActivePowers();
  6973. CG_DrawZoomMask();
  6974. if (!(cg.playerCent->currentState.eFlags & EF_DEAD))
  6975. CG_DrawCrosshairNames();
  6976. CG_SaberClashFlare();
  6977. if (cg_drawStatus.integer)
  6978. CG_DrawFlagStatus();
  6979. CG_DrawPickupItem();
  6980. CG_UpdateFallVector();
  6981. CG_DrawVote();
  6982. CG_DrawUpperRight();
  6983. CG_DrawTeamVote();
  6984. CG_DrawCenterString();
  6985. CG_ChatBox_DrawStrings();
  6986. return;
  6987. }
  6988. CG_Draw2DScreenTints();
  6989. if (cg.snap->ps.rocketLockIndex != ENTITYNUM_NONE && (cg.time - cg.snap->ps.rocketLockTime) > 0) {
  6990. CG_DrawRocketLocking( cg.snap->ps.rocketLockIndex, cg.snap->ps.rocketLockTime );
  6991. }
  6992. if (cg.snap->ps.holocronBits)
  6993. CG_DrawHolocronIcons();
  6994. if (cg.snap->ps.fd.forcePowersActive || cg.snap->ps.fd.forceRageRecoveryTime > cg.time)
  6995. CG_DrawActivePowers();
  6996. if (cg.snap->ps.jetpackFuel < 100) //draw it as long as it isn't full
  6997. CG_DrawJetpackFuel();
  6998. if (cg.snap->ps.cloakFuel < 100) //draw it as long as it isn't full
  6999. CG_DrawCloakFuel();
  7000. if (cg.predictedPlayerState.emplacedIndex > 0) {
  7001. centity_t *eweb = &cg_entities[cg.predictedPlayerState.emplacedIndex];
  7002. if (eweb->currentState.weapon == WP_NONE) //using an e-web, draw its health
  7003. CG_DrawEWebHealth();
  7004. }
  7005. // Draw this before the text so that any text won't get clipped off
  7006. CG_DrawZoomMask();
  7007. if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) {
  7008. if (!cg.demoPlayback)
  7009. CG_DrawSpectator();
  7010. CG_DrawCrosshair(NULL, 0);
  7011. CG_DrawCrosshairNames();
  7012. CG_SaberClashFlare();
  7013. } else {
  7014. // don't draw any status if dead or the scoreboard is being explicitly shown
  7015. if (!cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0) {
  7016. int drawSelect = 0;
  7017. //CG_DrawTemporaryStats();
  7018. CG_DrawAmmoWarning();
  7019. CG_DrawCrosshairNames();
  7020. if (cg_drawStatus.integer)
  7021. CG_DrawIconBackground();
  7022. if (inTime > wpTime) {
  7023. drawSelect = 1;
  7024. bestTime = cg.invenSelectTime;
  7025. } else { //only draw the most recent since they're drawn in the same place
  7026. drawSelect = 2;
  7027. bestTime = cg.weaponSelectTime;
  7028. }
  7029. if (cg.forceSelectTime > bestTime)
  7030. drawSelect = 3;
  7031. switch(drawSelect) {
  7032. case 1:
  7033. CG_DrawInvenSelect();
  7034. break;
  7035. case 2:
  7036. CG_DrawWeaponSelect();
  7037. break;
  7038. case 3:
  7039. CG_DrawForceSelect();
  7040. break;
  7041. default:
  7042. break;
  7043. }
  7044. if (cg_drawStatus.integer)
  7045. CG_DrawFlagStatus();
  7046. CG_SaberClashFlare();
  7047. if (cg_drawStatus.integer)
  7048. CG_DrawStats();
  7049. CG_DrawMovementKeys();
  7050. CG_DrawPickupItem();
  7051. //Do we want to use this system again at some point?
  7052. CG_DrawReward();
  7053. }
  7054. }
  7055. CG_UpdateFallVector();
  7056. CG_DrawVote();
  7057. CG_DrawTeamVote();
  7058. CG_DrawLagometer();
  7059. if (!cl_paused.integer) {
  7060. CG_DrawBracketedEntities();
  7061. CG_DrawUpperRight();
  7062. }
  7063. if ( !CG_DrawFollow() ) {
  7064. CG_DrawWarmup();
  7065. }
  7066. if (cgSiegeRoundState) {
  7067. char pStr[1024];
  7068. int rTime = 0;
  7069. //cgSiegeRoundBeganTime = 0;
  7070. switch (cgSiegeRoundState) {
  7071. case 1:
  7072. CG_CenterPrint(CG_GetStringEdString("MP_INGAME", "WAITING_FOR_PLAYERS"), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH);
  7073. break;
  7074. case 2:
  7075. rTime = (SIEGE_ROUND_BEGIN_TIME - (cg.time - cgSiegeRoundTime));
  7076. if (rTime < 0)
  7077. rTime = 0;
  7078. else if (rTime > SIEGE_ROUND_BEGIN_TIME)
  7079. rTime = SIEGE_ROUND_BEGIN_TIME;
  7080. rTime /= 1000;
  7081. rTime += 1;
  7082. if (rTime < 1)
  7083. rTime = 1;
  7084. if (rTime <= 3 && rTime != cgSiegeRoundCountTime) {
  7085. cgSiegeRoundCountTime = rTime;
  7086. if (mov_soundDisable.integer & SDISABLE_ANNOUNCER) goto skipCounter;
  7087. switch (rTime) {
  7088. case 1:
  7089. trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER );
  7090. break;
  7091. case 2:
  7092. trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER );
  7093. break;
  7094. case 3:
  7095. trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER );
  7096. break;
  7097. default:
  7098. break;
  7099. }
  7100. }
  7101. skipCounter:
  7102. strcpy(pStr, va("%s %i...", CG_GetStringEdString("MP_INGAME", "ROUNDBEGINSIN"), rTime));
  7103. CG_CenterPrint(pStr, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH);
  7104. //same
  7105. break;
  7106. default:
  7107. break;
  7108. }
  7109. cgSiegeEntityRender = 0;
  7110. } else if (cgSiegeRoundTime) {
  7111. CG_CenterPrint("", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH);
  7112. cgSiegeRoundTime = 0;
  7113. //cgSiegeRoundBeganTime = cg.time;
  7114. cgSiegeEntityRender = 0;
  7115. } else if (cgSiegeRoundBeganTime) { //Draw how much time is left in the round based on local info.
  7116. int timedTeam = TEAM_FREE;
  7117. int timedValue = 0;
  7118. //render the objective item model since this client has it
  7119. if (cgSiegeEntityRender)
  7120. CG_DrawSiegeHUDItem();
  7121. if (team1Timed) {
  7122. timedTeam = TEAM_RED; //team 1
  7123. if (cg_beatingSiegeTime)
  7124. timedValue = cg_beatingSiegeTime;
  7125. else
  7126. timedValue = team1Timed;
  7127. } else if (team2Timed) {
  7128. timedTeam = TEAM_BLUE; //team 2
  7129. if (cg_beatingSiegeTime)
  7130. timedValue = cg_beatingSiegeTime;
  7131. else
  7132. timedValue = team2Timed;
  7133. }
  7134. if (timedTeam != TEAM_FREE) { //one of the teams has a timer
  7135. int timeRemaining;
  7136. qboolean isMyTeam = qfalse;
  7137. //in switchy mode but not beating a time, so count up.
  7138. if (cgs.siegeTeamSwitch && !cg_beatingSiegeTime) {
  7139. timeRemaining = (cg.time-cgSiegeRoundBeganTime);
  7140. if (timeRemaining < 0)
  7141. timeRemaining = 0;
  7142. } else {
  7143. timeRemaining = (((cgSiegeRoundBeganTime)+timedValue) - cg.time);
  7144. }
  7145. if (timeRemaining > timedValue)
  7146. timeRemaining = timedValue;
  7147. else if (timeRemaining < 0)
  7148. timeRemaining = 0;
  7149. if (timeRemaining)
  7150. timeRemaining /= 1000;
  7151. //the team that's timed is the one this client is on
  7152. if (cg.predictedPlayerState.persistant[PERS_TEAM] == timedTeam)
  7153. isMyTeam = qtrue;
  7154. CG_DrawSiegeTimer(timeRemaining, isMyTeam);
  7155. }
  7156. } else {
  7157. cgSiegeEntityRender = 0;
  7158. }
  7159. if (cg_siegeDeathTime) {
  7160. int timeRemaining = (cg_siegeDeathTime - cg.time);
  7161. if (timeRemaining < 0 ) {
  7162. timeRemaining = 0;
  7163. cg_siegeDeathTime = 0;
  7164. }
  7165. if (timeRemaining)
  7166. timeRemaining /= 1000;
  7167. CG_DrawSiegeDeathTimer(timeRemaining);
  7168. }
  7169. // don't draw center string if scoreboard is up
  7170. cg.scoreBoardShowing = CG_DrawScoreboard();
  7171. if ( !cg.scoreBoardShowing) {
  7172. CG_DrawCenterString();
  7173. }
  7174. // always draw chat
  7175. CG_ChatBox_DrawStrings();
  7176. }
  7177. qboolean CG_CullPointAndRadius( const vec3_t pt, vec_t radius);
  7178. void CG_DrawMiscStaticModels( void ) {
  7179. int i, j;
  7180. refEntity_t ent;
  7181. vec3_t cullorg;
  7182. vec3_t diff;
  7183. memset( &ent, 0, sizeof( ent ) );
  7184. ent.reType = RT_MODEL;
  7185. ent.frame = 0;
  7186. ent.nonNormalizedAxes = qtrue;
  7187. // static models don't project shadows
  7188. ent.renderfx = RF_NOSHADOW;
  7189. for( i = 0; i < cgs.numMiscStaticModels; i++ ) {
  7190. VectorCopy(cgs.miscStaticModels[i].org, cullorg);
  7191. cullorg[2] += 1.0f;
  7192. if ( cgs.miscStaticModels[i].zoffset ) {
  7193. cullorg[2] += cgs.miscStaticModels[i].zoffset;
  7194. }
  7195. if( cgs.miscStaticModels[i].radius ) {
  7196. if( CG_CullPointAndRadius( cullorg, cgs.miscStaticModels[i].radius ) ) {
  7197. continue;
  7198. }
  7199. }
  7200. if( !trap_R_inPVS( cg.refdef.vieworg, cullorg, cg.refdef.areamask ) ) {
  7201. continue;
  7202. }
  7203. VectorCopy( cgs.miscStaticModels[i].org, ent.origin );
  7204. VectorCopy( cgs.miscStaticModels[i].org, ent.oldorigin );
  7205. VectorCopy( cgs.miscStaticModels[i].org, ent.lightingOrigin );
  7206. for( j = 0; j < 3; j++ ) {
  7207. VectorCopy( cgs.miscStaticModels[i].axes[j], ent.axis[j] );
  7208. }
  7209. ent.hModel = cgs.miscStaticModels[i].model;
  7210. VectorSubtract(ent.origin, cg.refdef.vieworg, diff);
  7211. if (VectorLength(diff)-(cgs.miscStaticModels[i].radius) <= cg.distanceCull) {
  7212. trap_R_AddRefEntityToScene( &ent );
  7213. }
  7214. }
  7215. }
  7216. static void CG_DrawTourneyScoreboard() {
  7217. }
  7218. /*
  7219. =====================
  7220. CG_DrawActive
  7221. Perform all drawing needed to completely fill the screen
  7222. =====================
  7223. */
  7224. void CG_DrawActive( stereoFrame_t stereoView ) {
  7225. float separation;
  7226. vec3_t baseOrg;
  7227. // optionally draw the info screen instead
  7228. if ( !cg.snap ) {
  7229. CG_DrawInformation();
  7230. return;
  7231. }
  7232. // optionally draw the tournement scoreboard instead
  7233. if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
  7234. ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
  7235. CG_DrawTourneyScoreboard();
  7236. return;
  7237. }
  7238. switch ( stereoView ) {
  7239. case STEREO_CENTER:
  7240. separation = 0;
  7241. break;
  7242. case STEREO_LEFT:
  7243. separation = -cg_stereoSeparation.value / 2;
  7244. break;
  7245. case STEREO_RIGHT:
  7246. separation = cg_stereoSeparation.value / 2;
  7247. break;
  7248. default:
  7249. separation = 0;
  7250. CG_Error( "CG_DrawActive: Undefined stereoView" );
  7251. }
  7252. // clear around the rendered view if sized down
  7253. CG_TileClear();
  7254. // offset vieworg appropriately if we're doing stereo separation
  7255. VectorCopy( cg.refdef.vieworg, baseOrg );
  7256. if ( separation != 0 ) {
  7257. VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg );
  7258. }
  7259. cg.refdef.rdflags |= RDF_DRAWSKYBOX;
  7260. CG_DrawMiscStaticModels();
  7261. // draw 3D view
  7262. trap_R_RenderScene( &cg.refdef );
  7263. // restore original viewpoint if running stereo
  7264. if ( separation != 0 ) {
  7265. VectorCopy( baseOrg, cg.refdef.vieworg );
  7266. }
  7267. // draw status bar and other floating elements
  7268. if (cg.demoPlayback != 2) {
  7269. vec4_t hcolor = {0, 0, 0, 0};
  7270. CG_DrawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH*SCREEN_HEIGHT, hcolor);
  7271. CG_Draw2D();
  7272. }
  7273. doFX = qfalse;
  7274. }