]> icculus.org git repositories - divverent/darkplaces.git/blob - sbar.c
implemented uint16 element array support, and use of it for all the
[divverent/darkplaces.git] / sbar.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sbar.c -- status bar code
21
22 #include "quakedef.h"
23
24 cachepic_t *sb_disc;
25
26 #define STAT_MINUS 10 // num frame for '-' stats digit
27 cachepic_t *sb_nums[2][11];
28 cachepic_t *sb_colon, *sb_slash;
29 cachepic_t *sb_ibar;
30 cachepic_t *sb_sbar;
31 cachepic_t *sb_scorebar;
32 // AK only used by NEX
33 cachepic_t *sb_sbar_minimal;
34 cachepic_t *sb_sbar_overlay;
35
36 // AK changed the bound to 9
37 cachepic_t *sb_weapons[7][9]; // 0 is active, 1 is owned, 2-5 are flashes
38 cachepic_t *sb_ammo[4];
39 cachepic_t *sb_sigil[4];
40 cachepic_t *sb_armor[3];
41 cachepic_t *sb_items[32];
42
43 // 0-4 are based on health (in 20 increments)
44 // 0 is static, 1 is temporary animation
45 cachepic_t *sb_faces[5][2];
46 cachepic_t *sb_health; // GAME_NEXUIZ
47
48 cachepic_t *sb_face_invis;
49 cachepic_t *sb_face_quad;
50 cachepic_t *sb_face_invuln;
51 cachepic_t *sb_face_invis_invuln;
52
53 qboolean sb_showscores;
54
55 int sb_lines;                   // scan lines to draw
56
57 cachepic_t *rsb_invbar[2];
58 cachepic_t *rsb_weapons[5];
59 cachepic_t *rsb_items[2];
60 cachepic_t *rsb_ammo[3];
61 cachepic_t *rsb_teambord;               // PGM 01/19/97 - team color border
62
63 //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
64 cachepic_t *hsb_weapons[7][5];   // 0 is active, 1 is owned, 2-5 are flashes
65 //MED 01/04/97 added array to simplify weapon parsing
66 int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
67 //MED 01/04/97 added hipnotic items array
68 cachepic_t *hsb_items[2];
69
70 //GAME_SOM stuff:
71 cachepic_t *somsb_health;
72 cachepic_t *somsb_ammo[4];
73 cachepic_t *somsb_armor[3];
74
75 cachepic_t *zymsb_crosshair_center;
76 cachepic_t *zymsb_crosshair_line;
77 cachepic_t *zymsb_crosshair_health;
78 cachepic_t *zymsb_crosshair_ammo;
79 cachepic_t *zymsb_crosshair_clip;
80 cachepic_t *zymsb_crosshair_background;
81 cachepic_t *zymsb_crosshair_left1;
82 cachepic_t *zymsb_crosshair_left2;
83 cachepic_t *zymsb_crosshair_right;
84
85 cachepic_t *sb_ranking;
86 cachepic_t *sb_complete;
87 cachepic_t *sb_inter;
88 cachepic_t *sb_finale;
89
90 cvar_t showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
91 cvar_t showsound = {CVAR_SAVE, "showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
92 cvar_t showspeed = {CVAR_SAVE, "showspeed", "0", "shows your current speed (qu per second); number selects unit: 1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots"};
93 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
94 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
95 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
96 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
97 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
98 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
99 cvar_t sbar_hudselector = {CVAR_SAVE, "sbar_hudselector", "0", "selects which of the builtin hud layouts to use (meaning is somewhat dependent on gamemode, so nexuiz has a very different set of hud layouts than quake for example)"};
100 cvar_t sbar_miniscoreboard_size = {CVAR_SAVE, "sbar_miniscoreboard_size", "-1", "sets the size of the mini deathmatch overlay in items, or disables it when set to 0, or sets it to a sane default when set to -1"};
101 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
102 cvar_t sbar_flagstatus_pos = {CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
103
104 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
105
106 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
107 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
108 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
109 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
110 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
111
112 void Sbar_MiniDeathmatchOverlay (int x, int y);
113 void Sbar_DeathmatchOverlay (void);
114 void Sbar_IntermissionOverlay (void);
115 void Sbar_FinaleOverlay (void);
116
117 void CL_VM_UpdateShowingScoresState (int showingscores);
118
119
120 /*
121 ===============
122 Sbar_ShowScores
123
124 Tab key down
125 ===============
126 */
127 void Sbar_ShowScores (void)
128 {
129         if (sb_showscores)
130                 return;
131         sb_showscores = true;
132         CL_VM_UpdateShowingScoresState(sb_showscores);
133 }
134
135 /*
136 ===============
137 Sbar_DontShowScores
138
139 Tab key up
140 ===============
141 */
142 void Sbar_DontShowScores (void)
143 {
144         sb_showscores = false;
145         CL_VM_UpdateShowingScoresState(sb_showscores);
146 }
147
148 void sbar_start(void)
149 {
150         int i;
151
152         if (gamemode == GAME_DELUXEQUAKE)
153         {
154         }
155         else if (gamemode == GAME_SOM)
156         {
157                 sb_disc = Draw_CachePic ("gfx/disc");
158
159                 for (i = 0;i < 10;i++)
160                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
161
162                 somsb_health = Draw_CachePic ("gfx/hud_health");
163                 somsb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
164                 somsb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
165                 somsb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
166                 somsb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
167                 somsb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
168                 somsb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
169                 somsb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
170         }
171         else if (gamemode == GAME_NEXUIZ)
172         {
173                 for (i = 0;i < 10;i++)
174                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
175                 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
176                 sb_colon = Draw_CachePic ("gfx/num_colon");
177
178                 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
179                 sb_ammo[1] = Draw_CachePic ("gfx/sb_bullets");
180                 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
181                 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
182
183                 sb_armor[0] = Draw_CachePic ("gfx/sb_armor");
184                 sb_armor[1] = NULL;
185                 sb_armor[2] = NULL;
186
187                 sb_health = Draw_CachePic ("gfx/sb_health");
188
189                 sb_items[2] = Draw_CachePic ("gfx/sb_slowmo");
190                 sb_items[3] = Draw_CachePic ("gfx/sb_invinc");
191                 sb_items[4] = Draw_CachePic ("gfx/sb_energy");
192                 sb_items[5] = Draw_CachePic ("gfx/sb_str");
193
194                 sb_items[11] = Draw_CachePic ("gfx/sb_flag_red_taken");
195                 sb_items[12] = Draw_CachePic ("gfx/sb_flag_red_lost");
196                 sb_items[13] = Draw_CachePic ("gfx/sb_flag_red_carrying");
197                 sb_items[14] = Draw_CachePic ("gfx/sb_key_carrying");
198                 sb_items[15] = Draw_CachePic ("gfx/sb_flag_blue_taken");
199                 sb_items[16] = Draw_CachePic ("gfx/sb_flag_blue_lost");
200                 sb_items[17] = Draw_CachePic ("gfx/sb_flag_blue_carrying");
201
202                 sb_sbar = Draw_CachePic ("gfx/sbar");
203                 sb_sbar_minimal = Draw_CachePic ("gfx/sbar_minimal");
204                 sb_sbar_overlay = Draw_CachePic ("gfx/sbar_overlay");
205
206                 for(i = 0; i < 9;i++)
207                         sb_weapons[0][i] = Draw_CachePic (va("gfx/inv_weapon%i",i));
208         }
209         else if (gamemode == GAME_ZYMOTIC)
210         {
211                 zymsb_crosshair_center = Draw_CachePic ("gfx/hud/crosshair_center");
212                 zymsb_crosshair_line = Draw_CachePic ("gfx/hud/crosshair_line");
213                 zymsb_crosshair_health = Draw_CachePic ("gfx/hud/crosshair_health");
214                 zymsb_crosshair_clip = Draw_CachePic ("gfx/hud/crosshair_clip");
215                 zymsb_crosshair_ammo = Draw_CachePic ("gfx/hud/crosshair_ammo");
216                 zymsb_crosshair_background = Draw_CachePic ("gfx/hud/crosshair_background");
217                 zymsb_crosshair_left1 = Draw_CachePic ("gfx/hud/crosshair_left1");
218                 zymsb_crosshair_left2 = Draw_CachePic ("gfx/hud/crosshair_left2");
219                 zymsb_crosshair_right = Draw_CachePic ("gfx/hud/crosshair_right");
220         }
221         else
222         {
223                 sb_disc = Draw_CachePic ("gfx/disc");
224
225                 for (i = 0;i < 10;i++)
226                 {
227                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
228                         sb_nums[1][i] = Draw_CachePic (va("gfx/anum_%i",i));
229                 }
230
231                 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
232                 sb_nums[1][10] = Draw_CachePic ("gfx/anum_minus");
233
234                 sb_colon = Draw_CachePic ("gfx/num_colon");
235                 sb_slash = Draw_CachePic ("gfx/num_slash");
236
237                 sb_weapons[0][0] = Draw_CachePic ("gfx/inv_shotgun");
238                 sb_weapons[0][1] = Draw_CachePic ("gfx/inv_sshotgun");
239                 sb_weapons[0][2] = Draw_CachePic ("gfx/inv_nailgun");
240                 sb_weapons[0][3] = Draw_CachePic ("gfx/inv_snailgun");
241                 sb_weapons[0][4] = Draw_CachePic ("gfx/inv_rlaunch");
242                 sb_weapons[0][5] = Draw_CachePic ("gfx/inv_srlaunch");
243                 sb_weapons[0][6] = Draw_CachePic ("gfx/inv_lightng");
244
245                 sb_weapons[1][0] = Draw_CachePic ("gfx/inv2_shotgun");
246                 sb_weapons[1][1] = Draw_CachePic ("gfx/inv2_sshotgun");
247                 sb_weapons[1][2] = Draw_CachePic ("gfx/inv2_nailgun");
248                 sb_weapons[1][3] = Draw_CachePic ("gfx/inv2_snailgun");
249                 sb_weapons[1][4] = Draw_CachePic ("gfx/inv2_rlaunch");
250                 sb_weapons[1][5] = Draw_CachePic ("gfx/inv2_srlaunch");
251                 sb_weapons[1][6] = Draw_CachePic ("gfx/inv2_lightng");
252
253                 for (i = 0;i < 5;i++)
254                 {
255                         sb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_shotgun",i+1));
256                         sb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_sshotgun",i+1));
257                         sb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_nailgun",i+1));
258                         sb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_snailgun",i+1));
259                         sb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_rlaunch",i+1));
260                         sb_weapons[2+i][5] = Draw_CachePic (va("gfx/inva%i_srlaunch",i+1));
261                         sb_weapons[2+i][6] = Draw_CachePic (va("gfx/inva%i_lightng",i+1));
262                 }
263
264                 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
265                 sb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
266                 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
267                 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
268
269                 sb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
270                 sb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
271                 sb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
272
273                 sb_items[0] = Draw_CachePic ("gfx/sb_key1");
274                 sb_items[1] = Draw_CachePic ("gfx/sb_key2");
275                 sb_items[2] = Draw_CachePic ("gfx/sb_invis");
276                 sb_items[3] = Draw_CachePic ("gfx/sb_invuln");
277                 sb_items[4] = Draw_CachePic ("gfx/sb_suit");
278                 sb_items[5] = Draw_CachePic ("gfx/sb_quad");
279
280                 sb_sigil[0] = Draw_CachePic ("gfx/sb_sigil1");
281                 sb_sigil[1] = Draw_CachePic ("gfx/sb_sigil2");
282                 sb_sigil[2] = Draw_CachePic ("gfx/sb_sigil3");
283                 sb_sigil[3] = Draw_CachePic ("gfx/sb_sigil4");
284
285                 sb_faces[4][0] = Draw_CachePic ("gfx/face1");
286                 sb_faces[4][1] = Draw_CachePic ("gfx/face_p1");
287                 sb_faces[3][0] = Draw_CachePic ("gfx/face2");
288                 sb_faces[3][1] = Draw_CachePic ("gfx/face_p2");
289                 sb_faces[2][0] = Draw_CachePic ("gfx/face3");
290                 sb_faces[2][1] = Draw_CachePic ("gfx/face_p3");
291                 sb_faces[1][0] = Draw_CachePic ("gfx/face4");
292                 sb_faces[1][1] = Draw_CachePic ("gfx/face_p4");
293                 sb_faces[0][0] = Draw_CachePic ("gfx/face5");
294                 sb_faces[0][1] = Draw_CachePic ("gfx/face_p5");
295
296                 sb_face_invis = Draw_CachePic ("gfx/face_invis");
297                 sb_face_invuln = Draw_CachePic ("gfx/face_invul2");
298                 sb_face_invis_invuln = Draw_CachePic ("gfx/face_inv2");
299                 sb_face_quad = Draw_CachePic ("gfx/face_quad");
300
301                 sb_sbar = Draw_CachePic ("gfx/sbar");
302                 sb_ibar = Draw_CachePic ("gfx/ibar");
303                 sb_scorebar = Draw_CachePic ("gfx/scorebar");
304
305         //MED 01/04/97 added new hipnotic weapons
306                 if (gamemode == GAME_HIPNOTIC)
307                 {
308                         hsb_weapons[0][0] = Draw_CachePic ("gfx/inv_laser");
309                         hsb_weapons[0][1] = Draw_CachePic ("gfx/inv_mjolnir");
310                         hsb_weapons[0][2] = Draw_CachePic ("gfx/inv_gren_prox");
311                         hsb_weapons[0][3] = Draw_CachePic ("gfx/inv_prox_gren");
312                         hsb_weapons[0][4] = Draw_CachePic ("gfx/inv_prox");
313
314                         hsb_weapons[1][0] = Draw_CachePic ("gfx/inv2_laser");
315                         hsb_weapons[1][1] = Draw_CachePic ("gfx/inv2_mjolnir");
316                         hsb_weapons[1][2] = Draw_CachePic ("gfx/inv2_gren_prox");
317                         hsb_weapons[1][3] = Draw_CachePic ("gfx/inv2_prox_gren");
318                         hsb_weapons[1][4] = Draw_CachePic ("gfx/inv2_prox");
319
320                         for (i = 0;i < 5;i++)
321                         {
322                                 hsb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_laser",i+1));
323                                 hsb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_mjolnir",i+1));
324                                 hsb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_gren_prox",i+1));
325                                 hsb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_prox_gren",i+1));
326                                 hsb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_prox",i+1));
327                         }
328
329                         hsb_items[0] = Draw_CachePic ("gfx/sb_wsuit");
330                         hsb_items[1] = Draw_CachePic ("gfx/sb_eshld");
331                 }
332                 else if (gamemode == GAME_ROGUE)
333                 {
334                         rsb_invbar[0] = Draw_CachePic ("gfx/r_invbar1");
335                         rsb_invbar[1] = Draw_CachePic ("gfx/r_invbar2");
336
337                         rsb_weapons[0] = Draw_CachePic ("gfx/r_lava");
338                         rsb_weapons[1] = Draw_CachePic ("gfx/r_superlava");
339                         rsb_weapons[2] = Draw_CachePic ("gfx/r_gren");
340                         rsb_weapons[3] = Draw_CachePic ("gfx/r_multirock");
341                         rsb_weapons[4] = Draw_CachePic ("gfx/r_plasma");
342
343                         rsb_items[0] = Draw_CachePic ("gfx/r_shield1");
344                         rsb_items[1] = Draw_CachePic ("gfx/r_agrav1");
345
346         // PGM 01/19/97 - team color border
347                         rsb_teambord = Draw_CachePic ("gfx/r_teambord");
348         // PGM 01/19/97 - team color border
349
350                         rsb_ammo[0] = Draw_CachePic ("gfx/r_ammolava");
351                         rsb_ammo[1] = Draw_CachePic ("gfx/r_ammomulti");
352                         rsb_ammo[2] = Draw_CachePic ("gfx/r_ammoplasma");
353                 }
354         }
355
356         sb_ranking = Draw_CachePic ("gfx/ranking");
357         sb_complete = Draw_CachePic ("gfx/complete");
358         sb_inter = Draw_CachePic ("gfx/inter");
359         sb_finale = Draw_CachePic ("gfx/finale");
360 }
361
362 void sbar_shutdown(void)
363 {
364 }
365
366 void sbar_newmap(void)
367 {
368 }
369
370 void Sbar_Init (void)
371 {
372         Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
373         Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
374         Cvar_RegisterVariable(&showfps);
375         Cvar_RegisterVariable(&showsound);
376         Cvar_RegisterVariable(&showspeed);
377         Cvar_RegisterVariable(&showtime);
378         Cvar_RegisterVariable(&showtime_format);
379         Cvar_RegisterVariable(&showdate);
380         Cvar_RegisterVariable(&showdate_format);
381         Cvar_RegisterVariable(&sbar_alpha_bg);
382         Cvar_RegisterVariable(&sbar_alpha_fg);
383         Cvar_RegisterVariable(&sbar_hudselector);
384         Cvar_RegisterVariable(&sbar_miniscoreboard_size);
385         Cvar_RegisterVariable(&cl_deathscoreboard);
386
387         Cvar_RegisterVariable(&crosshair_color_red);
388         Cvar_RegisterVariable(&crosshair_color_green);
389         Cvar_RegisterVariable(&crosshair_color_blue);
390         Cvar_RegisterVariable(&crosshair_color_alpha);
391         Cvar_RegisterVariable(&crosshair_size);
392
393         if(gamemode == GAME_NEXUIZ)
394         {
395                 Cvar_RegisterVariable(&sbar_flagstatus_right); // this cvar makes no sense in other games
396                 Cvar_RegisterVariable(&sbar_flagstatus_pos); // this cvar makes no sense in other games
397         }
398
399         R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap);
400 }
401
402
403 //=============================================================================
404
405 // drawing routines are relative to the status bar location
406
407 int sbar_x, sbar_y;
408
409 /*
410 =============
411 Sbar_DrawPic
412 =============
413 */
414 void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
415 {
416         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
417 }
418
419 void Sbar_DrawPic (int x, int y, cachepic_t *pic)
420 {
421         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
422 }
423
424 void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
425 {
426         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
427 }
428
429 /*
430 ================
431 Sbar_DrawCharacter
432
433 Draws one solid graphics character
434 ================
435 */
436 void Sbar_DrawCharacter (int x, int y, int num)
437 {
438         DrawQ_String_Font (sbar_x + x + 4 , sbar_y + y, va("%c", num), 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, true, FONT_SBAR);
439 }
440
441 /*
442 ================
443 Sbar_DrawString
444 ================
445 */
446 void Sbar_DrawString (int x, int y, char *str)
447 {
448         DrawQ_String_Font (sbar_x + x, sbar_y + y, str, 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR);
449 }
450
451 /*
452 =============
453 Sbar_DrawNum
454 =============
455 */
456 void Sbar_DrawNum (int x, int y, int num, int digits, int color)
457 {
458         char str[32], *ptr;
459         int l, frame;
460
461         l = sprintf(str, "%i", num);
462         ptr = str;
463         if (l > digits)
464                 ptr += (l-digits);
465         if (l < digits)
466                 x += (digits-l)*24;
467
468         while (*ptr)
469         {
470                 if (*ptr == '-')
471                         frame = STAT_MINUS;
472                 else
473                         frame = *ptr -'0';
474
475                 Sbar_DrawPic (x, y, sb_nums[color][frame]);
476                 x += 24;
477
478                 ptr++;
479         }
480 }
481
482 /*
483 =============
484 Sbar_DrawXNum
485 =============
486 */
487
488 void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
489 {
490         char str[32], *ptr;
491         int l, frame;
492
493         if (digits < 0)
494         {
495                 digits = -digits;
496                 l = sprintf(str, "%0*i", digits, num);
497         }
498         else
499                 l = sprintf(str, "%i", num);
500         ptr = str;
501         if (l > digits)
502                 ptr += (l-digits);
503         if (l < digits)
504                 x += (digits-l) * lettersize;
505
506         while (*ptr)
507         {
508                 if (*ptr == '-')
509                         frame = STAT_MINUS;
510                 else
511                         frame = *ptr -'0';
512
513                 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
514                 x += lettersize;
515
516                 ptr++;
517         }
518 }
519
520 //=============================================================================
521
522
523 int Sbar_IsTeammatch()
524 {
525         // currently only nexuiz uses the team score board
526         return ((gamemode == GAME_NEXUIZ)
527                 && (teamplay.integer > 0));
528 }
529
530 /*
531 ===============
532 Sbar_SortFrags
533 ===============
534 */
535 static int fragsort[MAX_SCOREBOARD];
536 static int scoreboardlines;
537
538 int Sbar_GetSortedPlayerIndex (int index)
539 {
540         return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
541 }
542
543 static scoreboard_t teams[MAX_SCOREBOARD];
544 static int teamsort[MAX_SCOREBOARD];
545 static int teamlines;
546 void Sbar_SortFrags (void)
547 {
548         int i, j, k, color;
549
550         // sort by frags
551         scoreboardlines = 0;
552         for (i=0 ; i<cl.maxclients ; i++)
553         {
554                 if (cl.scores[i].name[0])
555                 {
556                         fragsort[scoreboardlines] = i;
557                         scoreboardlines++;
558                 }
559         }
560
561         for (i=0 ; i<scoreboardlines ; i++)
562                 for (j=0 ; j<scoreboardlines-1-i ; j++)
563                         if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
564                         {
565                                 k = fragsort[j];
566                                 fragsort[j] = fragsort[j+1];
567                                 fragsort[j+1] = k;
568                         }
569
570         teamlines = 0;
571         if (Sbar_IsTeammatch ())
572         {
573                 // now sort players by teams.
574                 for (i=0 ; i<scoreboardlines ; i++)
575                 {
576                         for (j=0 ; j<scoreboardlines-1-i ; j++)
577                         {
578                                 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
579                                 {
580                                         k = fragsort[j];
581                                         fragsort[j] = fragsort[j+1];
582                                         fragsort[j+1] = k;
583                                 }
584                         }
585                 }
586
587                 // calculate team scores
588                 color = -1;
589                 for (i=0 ; i<scoreboardlines ; i++)
590                 {
591                         if (color != (cl.scores[fragsort[i]].colors & 15))
592                         {
593                                 const char* teamname;
594
595                                 color = cl.scores[fragsort[i]].colors & 15;
596                                 teamlines++;
597
598                                 switch (color)
599                                 {
600                                         case 4:
601                                                 teamname = "^1Red Team";
602                                                 break;
603                                         case 13:
604                                                 teamname = "^4Blue Team";
605                                                 break;
606                                         case 9:
607                                                 teamname = "^6Pink Team";
608                                                 break;
609                                         case 12:
610                                                 teamname = "^3Yellow Team";
611                                                 break;
612                                         default:
613                                                 teamname = "Total Team Score";
614                                                 break;
615                                 }
616                                 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
617
618                                 teams[teamlines-1].frags = 0;
619                                 teams[teamlines-1].colors = color + 16 * color;
620                         }
621
622                         if (cl.scores[fragsort[i]].frags != -666)
623                         {
624                                 // do not add spedcators
625                                 // (ugly hack for nexuiz)
626                                 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
627                         }
628                 }
629
630                 // now sort teams by scores.
631                 for (i=0 ; i<teamlines ; i++)
632                         teamsort[i] = i;
633                 for (i=0 ; i<teamlines ; i++)
634                 {
635                         for (j=0 ; j<teamlines-1-i ; j++)
636                         {
637                                 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
638                                 {
639                                         k = teamsort[j];
640                                         teamsort[j] = teamsort[j+1];
641                                         teamsort[j+1] = k;
642                                 }
643                         }
644                 }
645         }
646 }
647
648 /*
649 ===============
650 Sbar_SoloScoreboard
651 ===============
652 */
653 void Sbar_SoloScoreboard (void)
654 {
655 #if 1
656         char    str[80], timestr[40];
657         int             max, timelen;
658         int             minutes, seconds;
659         double  t;
660
661         t = (cl.intermission ? cl.completed_time : cl.time);
662         minutes = (int)(t / 60);
663         seconds = (int)(t - 60*floor(t/60));
664
665         // monsters and secrets are now both on the top row
666         if (cl.stats[STAT_TOTALMONSTERS])
667                 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
668         if (cl.stats[STAT_TOTALSECRETS])
669                 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
670
671         // figure out the map's filename without path or extension
672         strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
673         if (strrchr(str, '.'))
674                 *(strrchr(str, '.')) = 0;
675
676         // append a : separator and then the full title
677         strlcat(str, ":", sizeof(str));
678         strlcat(str, cl.levelname, sizeof(str));
679
680         // if there's a newline character, terminate the string there
681         if (strchr(str, '\n'))
682                 *(strchr(str, '\n')) = 0;
683
684         // make the time string
685         timelen = sprintf(timestr, " %i:%02i", minutes, seconds);
686
687         // truncate the level name if necessary to make room for time
688         max = 38 - timelen;
689         if ((int)strlen(str) > max)
690                 str[max] = 0;
691
692         // print the filename and message
693         Sbar_DrawString(8, 12, str);
694
695         // print the time
696         Sbar_DrawString(8 + max*8, 12, timestr);
697
698 #else
699         char    str[80];
700         int             minutes, seconds, tens, units;
701         int             l;
702
703         if (gamemode != GAME_NEXUIZ) {
704                 sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
705                 Sbar_DrawString (8, 4, str);
706
707                 sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
708                 Sbar_DrawString (8, 12, str);
709         }
710
711 // time
712         minutes = (int)(cl.time / 60);
713         seconds = (int)(cl.time - 60*minutes);
714         tens = seconds / 10;
715         units = seconds - 10*tens;
716         sprintf (str,"Time :%3i:%i%i", minutes, tens, units);
717         Sbar_DrawString (184, 4, str);
718
719 // draw level name
720         if (gamemode == GAME_NEXUIZ) {
721                 l = (int) strlen (cl.worldmodel->name);
722                 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
723         } else {
724                 l = (int) strlen (cl.levelname);
725                 Sbar_DrawString (232 - l*4, 12, cl.levelname);
726         }
727 #endif
728 }
729
730 /*
731 ===============
732 Sbar_DrawScoreboard
733 ===============
734 */
735 void Sbar_DrawScoreboard (void)
736 {
737         Sbar_SoloScoreboard ();
738         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
739         //if (cl.gametype == GAME_DEATHMATCH)
740         if (!cl.islocalgame)
741                 Sbar_DeathmatchOverlay ();
742 }
743
744 //=============================================================================
745
746 // AK to make DrawInventory smaller
747 static void Sbar_DrawWeapon(int nr, float fade, int active)
748 {
749         if (sbar_hudselector.integer == 1)
750         {
751                 // width = 300, height = 100
752                 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
753
754                 DrawQ_Pic((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr, vid_conheight.integer - w_height, sb_weapons[0][nr], w_width, w_height, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 0.6, (active ? 1 : 0.6) * fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
755                 // FIXME ??
756                 DrawQ_String((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr + w_space, vid_conheight.integer - w_height + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 1, 0, sbar_alpha_fg.value, 0, NULL, true);
757         }
758         else
759         {
760                 // width = 300, height = 100
761                 const int w_width = 300, w_height = 100, w_space = 10;
762                 const float w_scale = 0.4;
763
764                 DrawQ_Pic(vid_conwidth.integer - (w_width + w_space) * w_scale, (w_height + w_space) * w_scale * nr + w_space, sb_weapons[0][nr], w_width * w_scale, w_height * w_scale, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 1, fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
765                 //DrawQ_String(vid_conwidth.integer - (w_space + font_size ), (w_height + w_space) * w_scale * nr + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 0, 0, fade, 0, NULL, true);
766         }
767 }
768
769 /*
770 ===============
771 Sbar_DrawInventory
772 ===============
773 */
774 void Sbar_DrawInventory (void)
775 {
776         int i;
777         char num[6];
778         float time;
779         int flashon;
780
781         if (gamemode == GAME_ROGUE)
782         {
783                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
784                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
785                 else
786                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
787         }
788         else
789                 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
790
791         // weapons
792         for (i=0 ; i<7 ; i++)
793         {
794                 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
795                 {
796                         time = cl.item_gettime[i];
797                         flashon = (int)(max(0, cl.time - time)*10);
798                         if (flashon >= 10)
799                         {
800                                 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )
801                                         flashon = 1;
802                                 else
803                                         flashon = 0;
804                         }
805                         else
806                                 flashon = (flashon%5) + 2;
807
808                         Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
809                 }
810         }
811
812         // MED 01/04/97
813         // hipnotic weapons
814         if (gamemode == GAME_HIPNOTIC)
815         {
816                 int grenadeflashing=0;
817                 for (i=0 ; i<4 ; i++)
818                 {
819                         if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
820                         {
821                                 time = max(0, cl.item_gettime[hipweapons[i]]);
822                                 flashon = (int)((cl.time - time)*10);
823                                 if (flashon >= 10)
824                                 {
825                                         if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])  )
826                                                 flashon = 1;
827                                         else
828                                                 flashon = 0;
829                                 }
830                                 else
831                                         flashon = (flashon%5) + 2;
832
833                                 // check grenade launcher
834                                 if (i==2)
835                                 {
836                                         if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
837                                         {
838                                                 if (flashon)
839                                                 {
840                                                         grenadeflashing = 1;
841                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
842                                                 }
843                                         }
844                                 }
845                                 else if (i==3)
846                                 {
847                                         if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
848                                         {
849                                                 if (!grenadeflashing)
850                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
851                                         }
852                                         else
853                                                 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
854                                 }
855                                 else
856                                         Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
857                         }
858                 }
859         }
860
861         if (gamemode == GAME_ROGUE)
862         {
863                 // check for powered up weapon.
864                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
865                         for (i=0;i<5;i++)
866                                 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
867                                         Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
868         }
869
870         // ammo counts
871         for (i=0 ; i<4 ; i++)
872         {
873                 sprintf (num, "%4i",cl.stats[STAT_SHELLS+i] );
874                 if (num[0] != ' ')
875                         Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
876                 if (num[1] != ' ')
877                         Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
878                 if (num[2] != ' ')
879                         Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
880                 if (num[3] != ' ')
881                         Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
882         }
883
884         // items
885         for (i=0 ; i<6 ; i++)
886                 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
887                 {
888                         //MED 01/04/97 changed keys
889                         if (gamemode != GAME_HIPNOTIC || (i>1))
890                                 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
891                 }
892
893         //MED 01/04/97 added hipnotic items
894         // hipnotic items
895         if (gamemode == GAME_HIPNOTIC)
896         {
897                 for (i=0 ; i<2 ; i++)
898                         if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
899                                 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
900         }
901
902         if (gamemode == GAME_ROGUE)
903         {
904                 // new rogue items
905                 for (i=0 ; i<2 ; i++)
906                         if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
907                                 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
908         }
909         else
910         {
911                 // sigils
912                 for (i=0 ; i<4 ; i++)
913                         if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
914                                 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
915         }
916 }
917
918 //=============================================================================
919
920 /*
921 ===============
922 Sbar_DrawFrags
923 ===============
924 */
925 void Sbar_DrawFrags (void)
926 {
927         int i, k, l, x, f;
928         char num[12];
929         scoreboard_t *s;
930         unsigned char *c;
931
932         Sbar_SortFrags ();
933
934         // draw the text
935         l = min(scoreboardlines, 4);
936
937         x = 23 * 8;
938
939         for (i = 0;i < l;i++)
940         {
941                 k = fragsort[i];
942                 s = &cl.scores[k];
943
944                 // draw background
945                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
946                 DrawQ_Fill (sbar_x + x + 10, sbar_y     - 23, 28, 4, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
947                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
948                 DrawQ_Fill (sbar_x + x + 10, sbar_y + 4 - 23, 28, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
949
950                 // draw number
951                 f = s->frags;
952                 sprintf (num, "%3i",f);
953
954                 if (k == cl.viewentity - 1)
955                 {
956                         Sbar_DrawCharacter ( x      + 2, -24, 16);
957                         Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
958                 }
959                 Sbar_DrawCharacter (x +  8, -24, num[0]);
960                 Sbar_DrawCharacter (x + 16, -24, num[1]);
961                 Sbar_DrawCharacter (x + 24, -24, num[2]);
962                 x += 32;
963         }
964 }
965
966 //=============================================================================
967
968
969 /*
970 ===============
971 Sbar_DrawFace
972 ===============
973 */
974 void Sbar_DrawFace (void)
975 {
976         int f;
977
978 // PGM 01/19/97 - team color drawing
979 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
980         if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
981         {
982                 char num[12];
983                 scoreboard_t *s;
984                 unsigned char *c;
985
986                 s = &cl.scores[cl.viewentity - 1];
987                 // draw background
988                 Sbar_DrawPic (112, 0, rsb_teambord);
989                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
990                 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+3, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
991                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
992                 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+12, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
993
994                 // draw number
995                 f = s->frags;
996                 sprintf (num, "%3i",f);
997
998                 if ((s->colors & 0xf0)==0)
999                 {
1000                         if (num[0] != ' ')
1001                                 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1002                         if (num[1] != ' ')
1003                                 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1004                         if (num[2] != ' ')
1005                                 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1006                 }
1007                 else
1008                 {
1009                         Sbar_DrawCharacter ( 109, 3, num[0]);
1010                         Sbar_DrawCharacter ( 116, 3, num[1]);
1011                         Sbar_DrawCharacter ( 123, 3, num[2]);
1012                 }
1013
1014                 return;
1015         }
1016 // PGM 01/19/97 - team color drawing
1017
1018         if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1019                 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1020         else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1021                 Sbar_DrawPic (112, 0, sb_face_quad );
1022         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1023                 Sbar_DrawPic (112, 0, sb_face_invis );
1024         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1025                 Sbar_DrawPic (112, 0, sb_face_invuln);
1026         else
1027         {
1028                 f = cl.stats[STAT_HEALTH] / 20;
1029                 f = bound(0, f, 4);
1030                 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1031         }
1032 }
1033
1034 void Sbar_ShowFPS(void)
1035 {
1036         float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1037         char soundstring[32];
1038         char fpsstring[32];
1039         char timestring[32];
1040         char datestring[32];
1041         char speedstring[32];
1042         qboolean red = false;
1043         soundstring[0] = 0;
1044         fpsstring[0] = 0;
1045         timestring[0] = 0;
1046         datestring[0] = 0;
1047         speedstring[0] = 0;
1048         if (showfps.integer)
1049         {
1050                 float calc;
1051                 static double nexttime = 0, lasttime = 0;
1052                 static double framerate = 0;
1053                 static int framecount = 0;
1054                 double interval = 0.25;
1055                 double newtime;
1056                 newtime = realtime;
1057                 if (newtime >= nexttime)
1058                 {
1059                         framerate = framecount / (newtime - lasttime);
1060                         if (nexttime < newtime - interval * 1.5)
1061                                 nexttime = newtime;
1062                         lasttime = newtime;
1063                         nexttime += interval;
1064                         framecount = 0;
1065                 }
1066                 framecount++;
1067                 calc = framerate;
1068
1069                 if ((red = (calc < 1.0f)))
1070                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1071                 else
1072                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1073         }
1074         if (showtime.integer)
1075                 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1076         if (showdate.integer)
1077                 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1078         if (showsound.integer)
1079                 dpsnprintf(soundstring, sizeof(soundstring), "%4i/%4i at %3ims\n", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1080         if (showspeed.integer)
1081         {
1082                 double speed, speedxy, f;
1083                 const char *unit;
1084                 speed = VectorLength(cl.movement_velocity);
1085                 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1086                 switch(showspeed.integer)
1087                 {
1088                         default:
1089                         case 1:
1090                                 if(gamemode == GAME_NEXUIZ)
1091                                         unit = "in/s";
1092                                 else
1093                                         unit = "qu/s";
1094                                 f = 1.0;
1095                                 break;
1096                         case 2:
1097                                 unit = "m/s";
1098                                 f = 0.0254;
1099                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1100                                 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1101                                 break;
1102                         case 3:
1103                                 unit = "km/h";
1104                                 f = 0.0254 * 3.6;
1105                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1106                                 break;
1107                         case 4:
1108                                 unit = "mph";
1109                                 f = 0.0254 * 3.6 * 0.6213711922;
1110                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1111                                 break;
1112                         case 5:
1113                                 unit = "knots";
1114                                 f = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1115                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1116                                 break;
1117                 }
1118                 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1119         }
1120         if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0])
1121         {
1122                 fps_scalex = 12;
1123                 fps_scaley = 12;
1124                 fps_height = fps_scaley * ((soundstring[0] != 0) + (fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0));
1125                 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1126                 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1127                 fps_y = vid_conheight.integer - fps_height;
1128                 if (soundstring[0])
1129                 {
1130                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(soundstring, 0, true, FONT_INFOBAR) * fps_scalex;
1131                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1132                         DrawQ_String_Font(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1133                         fps_y += fps_scaley;
1134                 }
1135                 if (fpsstring[0])
1136                 {
1137                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1138                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1139                         if (red)
1140                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1141                         else
1142                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1143                         fps_y += fps_scaley;
1144                 }
1145                 if (timestring[0])
1146                 {
1147                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1148                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1149                         DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1150                         fps_y += fps_scaley;
1151                 }
1152                 if (datestring[0])
1153                 {
1154                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1155                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1156                         DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1157                         fps_y += fps_scaley;
1158                 }
1159                 if (speedstring[0])
1160                 {
1161                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1162                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1163                         DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1164                         fps_y += fps_scaley;
1165                 }
1166         }
1167 }
1168
1169 void Sbar_DrawGauge(float x, float y, cachepic_t *pic, float width, float height, float rangey, float rangeheight, float c1, float c2, float c1r, float c1g, float c1b, float c1a, float c2r, float c2g, float c2b, float c2a, float c3r, float c3g, float c3b, float c3a, int drawflags)
1170 {
1171         float r[5];
1172         c2 = bound(0, c2, 1);
1173         c1 = bound(0, c1, 1 - c2);
1174         r[0] = 0;
1175         r[1] = rangey + rangeheight * (c2 + c1);
1176         r[2] = rangey + rangeheight * (c2);
1177         r[3] = rangey;
1178         r[4] = height;
1179         if (r[1] > r[0])
1180                 DrawQ_SuperPic(x, y + r[0], pic, width, (r[1] - r[0]), 0,(r[0] / height), c3r,c3g,c3b,c3a, 1,(r[0] / height), c3r,c3g,c3b,c3a, 0,(r[1] / height), c3r,c3g,c3b,c3a, 1,(r[1] / height), c3r,c3g,c3b,c3a, drawflags);
1181         if (r[2] > r[1])
1182                 DrawQ_SuperPic(x, y + r[1], pic, width, (r[2] - r[1]), 0,(r[1] / height), c1r,c1g,c1b,c1a, 1,(r[1] / height), c1r,c1g,c1b,c1a, 0,(r[2] / height), c1r,c1g,c1b,c1a, 1,(r[2] / height), c1r,c1g,c1b,c1a, drawflags);
1183         if (r[3] > r[2])
1184                 DrawQ_SuperPic(x, y + r[2], pic, width, (r[3] - r[2]), 0,(r[2] / height), c2r,c2g,c2b,c2a, 1,(r[2] / height), c2r,c2g,c2b,c2a, 0,(r[3] / height), c2r,c2g,c2b,c2a, 1,(r[3] / height), c2r,c2g,c2b,c2a, drawflags);
1185         if (r[4] > r[3])
1186                 DrawQ_SuperPic(x, y + r[3], pic, width, (r[4] - r[3]), 0,(r[3] / height), c3r,c3g,c3b,c3a, 1,(r[3] / height), c3r,c3g,c3b,c3a, 0,(r[4] / height), c3r,c3g,c3b,c3a, 1,(r[4] / height), c3r,c3g,c3b,c3a, drawflags);
1187 }
1188
1189 /*
1190 ===============
1191 Sbar_Draw
1192 ===============
1193 */
1194 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1195 extern cvar_t v_kicktime;
1196 void Sbar_Score (int margin);
1197 void Sbar_Draw (void)
1198 {
1199         cachepic_t *pic;
1200
1201         if(cl.csqc_vidvars.drawenginesbar)      //[515]: csqc drawsbar
1202         {
1203                 if (sb_showscores)
1204                         Sbar_DrawScoreboard ();
1205                 else if (cl.intermission == 1)
1206                 {
1207                         if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1208                         {
1209                                 Sbar_DrawScoreboard();
1210                                 return;
1211                         }
1212                         Sbar_IntermissionOverlay();
1213                 }
1214                 else if (cl.intermission == 2)
1215                         Sbar_FinaleOverlay();
1216                 else if (gamemode == GAME_DELUXEQUAKE)
1217                 {
1218                 }
1219                 else if (gamemode == GAME_SOM)
1220                 {
1221                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1222                                 Sbar_DrawScoreboard ();
1223                         else if (sb_lines)
1224                         {
1225                                 // this is the top left of the sbar area
1226                                 sbar_x = 0;
1227                                 sbar_y = vid_conheight.integer - 24*3;
1228
1229                                 // armor
1230                                 if (cl.stats[STAT_ARMOR])
1231                                 {
1232                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1233                                                 Sbar_DrawPic(0, 0, somsb_armor[2]);
1234                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1235                                                 Sbar_DrawPic(0, 0, somsb_armor[1]);
1236                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1237                                                 Sbar_DrawPic(0, 0, somsb_armor[0]);
1238                                         Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1239                                 }
1240
1241                                 // health
1242                                 Sbar_DrawPic(0, 24, somsb_health);
1243                                 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1244
1245                                 // ammo icon
1246                                 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1247                                         Sbar_DrawPic(0, 48, somsb_ammo[0]);
1248                                 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1249                                         Sbar_DrawPic(0, 48, somsb_ammo[1]);
1250                                 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1251                                         Sbar_DrawPic(0, 48, somsb_ammo[2]);
1252                                 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1253                                         Sbar_DrawPic(0, 48, somsb_ammo[3]);
1254                                 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1255                                 if (cl.stats[STAT_SHELLS])
1256                                         Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1257                         }
1258                 }
1259                 else if (gamemode == GAME_NEXUIZ)
1260                 {
1261                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1262                         {
1263                                 sbar_x = (vid_conwidth.integer - 640)/2;
1264                                 sbar_y = vid_conheight.integer - 47;
1265                                 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1266                                 Sbar_DrawScoreboard ();
1267                         }
1268                         else if (sb_lines && sbar_hudselector.integer == 1)
1269                         {
1270                                 int i;
1271                                 float fade;
1272                                 int redflag, blueflag;
1273                                 float x;
1274
1275                                 sbar_x = (vid_conwidth.integer - 320)/2;
1276                                 sbar_y = vid_conheight.integer - 24 - 16;
1277
1278                                 // calculate intensity to draw weapons bar at
1279                                 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1280                                 fade = bound(0.7, fade, 1);
1281                                 for (i = 0; i < 8;i++)
1282                                         if (cl.stats[STAT_ITEMS] & (1 << i))
1283                                                 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1284                                 if((cl.stats[STAT_ITEMS] & (1<<12)))
1285                                         Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1286
1287                                 // flag icons
1288                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1289                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1290                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1291                                 if (redflag == 3 && blueflag == 3)
1292                                 {
1293                                         // The Impossible Combination[tm]
1294                                         // Can only happen in Key Hunt mode...
1295                                         Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128), sb_items[14]);
1296                                 }
1297                                 else
1298                                 {
1299                                         if (redflag)
1300                                                 Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64), sb_items[redflag+10]);
1301                                         if (blueflag)
1302                                                 Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128), sb_items[blueflag+14]);
1303                                 }
1304
1305                                 // armor
1306                                 if (cl.stats[STAT_ARMOR] > 0)
1307                                 {
1308                                         Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1309                                         if(cl.stats[STAT_ARMOR] > 200)
1310                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1311                                         else if(cl.stats[STAT_ARMOR] > 100)
1312                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1313                                         else if(cl.stats[STAT_ARMOR] > 50)
1314                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1315                                         else if(cl.stats[STAT_ARMOR] > 25)
1316                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1317                                         else
1318                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1319                                 }
1320
1321                                 // health
1322                                 if (cl.stats[STAT_HEALTH] != 0)
1323                                 {
1324                                         Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1325                                         if(cl.stats[STAT_HEALTH] > 200)
1326                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1327                                         else if(cl.stats[STAT_HEALTH] > 100)
1328                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1329                                         else if(cl.stats[STAT_HEALTH] > 50)
1330                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1331                                         else if(cl.stats[STAT_HEALTH] > 25)
1332                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1333                                         else
1334                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1335                                 }
1336
1337                                 // ammo
1338                                 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1339                                 {
1340                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1341                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1342                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1343                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1344                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1345                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1346                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1347                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1348                                         if(cl.stats[STAT_AMMO] > 10)
1349                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1350                                         else
1351                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1352                                 }
1353
1354                                 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1355                                         Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1356                                 if (sbar_x > 0)
1357                                         Sbar_Score(16);
1358                                         // The margin can be at most 8 to support 640x480 console size:
1359                                         //   320 + 2 * (144 + 16) = 640
1360                         }
1361                         else if (sb_lines)
1362                         {
1363                                 int i;
1364                                 float fade;
1365                                 int redflag, blueflag;
1366                                 float x;
1367
1368                                 sbar_x = (vid_conwidth.integer - 640)/2;
1369                                 sbar_y = vid_conheight.integer - 47;
1370
1371                                 // calculate intensity to draw weapons bar at
1372                                 fade = 3 - 2 * (cl.time - cl.weapontime);
1373                                 if (fade > 0)
1374                                 {
1375                                         fade = min(fade, 1);
1376                                         for (i = 0; i < 8;i++)
1377                                                 if (cl.stats[STAT_ITEMS] & (1 << i))
1378                                                         Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1379
1380                                         if((cl.stats[STAT_ITEMS] & (1<<12)))
1381                                                 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1382                                 }
1383
1384                                 //if (!cl.islocalgame)
1385                                 //      Sbar_DrawFrags ();
1386
1387                                 if (sb_lines > 24)
1388                                         Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1389                                 else
1390                                         Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1391
1392                                 // flag icons
1393                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1394                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1395                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1396                                 if (redflag == 3 && blueflag == 3)
1397                                 {
1398                                         // The Impossible Combination[tm]
1399                                         // Can only happen in Key Hunt mode...
1400                                         Sbar_DrawPic (x, -179, sb_items[14]);
1401                                 }
1402                                 else
1403                                 {
1404                                         if (redflag)
1405                                                 Sbar_DrawPic (x, -117, sb_items[redflag+10]);
1406                                         if (blueflag)
1407                                                 Sbar_DrawPic (x, -177, sb_items[blueflag+14]);
1408                                 }
1409
1410                                 // armor
1411                                 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1412
1413                                 // health
1414                                 if(cl.stats[STAT_HEALTH] > 100)
1415                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1416                                 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1417                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1418                                 else
1419                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1420
1421                                 // AK dont draw ammo for the laser
1422                                 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1423                                 {
1424                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1425                                                 Sbar_DrawPic (519, 0, sb_ammo[0]);
1426                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1427                                                 Sbar_DrawPic (519, 0, sb_ammo[1]);
1428                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1429                                                 Sbar_DrawPic (519, 0, sb_ammo[2]);
1430                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1431                                                 Sbar_DrawPic (519, 0, sb_ammo[3]);
1432
1433                                         if(cl.stats[STAT_AMMO] <= 10)
1434                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1435                                         else
1436                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1437
1438                                 }
1439
1440                                 if (sb_lines > 24)
1441                                         DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1442
1443                                 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1444                                         Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1445
1446                                 if (sbar_x > 0)
1447                                         Sbar_Score(-16);
1448                                         // Because:
1449                                         //   Mini scoreboard uses 12*4 per other team, that is, 144
1450                                         //   pixels when there are four teams...
1451                                         //   Nexuiz by default sets vid_conwidth to 800... makes
1452                                         //   sbar_x == 80...
1453                                         //   so we need to shift it by 64 pixels to the right to fit
1454                                         //   BUT: then it overlaps with the image that gets drawn
1455                                         //   for viewsize 100! Therefore, just account for 3 teams,
1456                                         //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1457                                         //   to the right!
1458                         }
1459                 }
1460                 else if (gamemode == GAME_ZYMOTIC)
1461                 {
1462 #if 1
1463                         float scale = 64.0f / 256.0f;
1464                         float kickoffset[3];
1465                         VectorClear(kickoffset);
1466                         if (v_dmg_time > 0)
1467                         {
1468                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1469                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1470                         }
1471                         sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1472                         sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1473                         // left1 16, 48 : 126 -66
1474                         // left2 16, 128 : 196 -66
1475                         // right 176, 48 : 196 -136
1476                         Sbar_DrawGauge(sbar_x +  16 * scale, sbar_y +  48 * scale, zymsb_crosshair_left1, 64*scale,  80*scale, 78*scale,  -66*scale, cl.stats[STAT_AMMO]  * (1.0 / 200.0), cl.stats[STAT_SHELLS]  * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1477                         Sbar_DrawGauge(sbar_x +  16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2, 64*scale,  80*scale, 68*scale,  -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1478                         Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y +  48 * scale, zymsb_crosshair_right, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR]  * (1.0 / 300.0), cl.stats[STAT_HEALTH]  * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1479                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1480 #else
1481                         float scale = 128.0f / 256.0f;
1482                         float healthstart, healthheight, healthstarttc, healthendtc;
1483                         float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1484                         float ammostart, ammoheight, ammostarttc, ammoendtc;
1485                         float clipstart, clipheight, clipstarttc, clipendtc;
1486                         float kickoffset[3], offset;
1487                         VectorClear(kickoffset);
1488                         if (v_dmg_time > 0)
1489                         {
1490                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1491                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1492                         }
1493                         sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1494                         sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1495                         offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1496                         DrawQ_SuperPic(sbar_x +  120           * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0);
1497                         DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120            * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0);
1498                         DrawQ_SuperPic(sbar_x +  120           * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0);
1499                         DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120            * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0);
1500                         healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1501                         shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1502                         healthstart = 204 - healthheight;
1503                         shieldstart = healthstart - shieldheight;
1504                         healthstarttc = healthstart * (1.0f / 256.0f);
1505                         healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1506                         shieldstarttc = shieldstart * (1.0f / 256.0f);
1507                         shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1508                         ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1509                         ammostart = 114 - ammoheight;
1510                         ammostarttc = ammostart * (1.0f / 256.0f);
1511                         ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1512                         clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1513                         clipstart = 190 - clipheight;
1514                         clipstarttc = clipstart * (1.0f / 256.0f);
1515                         clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1516                         if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1517                         if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL);
1518                         if (ammoheight > 0)   DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart   * scale, zymsb_crosshair_ammo,   256 * scale, ammoheight   * scale, 0,ammostarttc,   0.8f,0.8f,0.0f,1.0f, 1,ammostarttc,   0.8f,0.8f,0.0f,1.0f, 0,ammoendtc,   0.8f,0.8f,0.0f,1.0f, 1,ammoendtc,   0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL);
1519                         if (clipheight > 0)   DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart   * scale, zymsb_crosshair_clip,   256 * scale, clipheight   * scale, 0,clipstarttc,   1.0f,1.0f,0.0f,1.0f, 1,clipstarttc,   1.0f,1.0f,0.0f,1.0f, 0,clipendtc,   1.0f,1.0f,0.0f,1.0f, 1,clipendtc,   1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1520                         DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1521                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1522 #endif
1523                 }
1524                 else // Quake and others
1525                 {
1526                         sbar_x = (vid_conwidth.integer - 320)/2;
1527                         sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1528                         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1529                         //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1530
1531                         if (sb_lines > 24)
1532                         {
1533                                 if (gamemode != GAME_GOODVSBAD2)
1534                                         Sbar_DrawInventory ();
1535                                 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1536                                         Sbar_DrawFrags ();
1537                         }
1538
1539                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1540                         {
1541                                 if (gamemode != GAME_GOODVSBAD2)
1542                                         Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1543                                 Sbar_DrawScoreboard ();
1544                         }
1545                         else if (sb_lines)
1546                         {
1547                                 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1548
1549                                 // keys (hipnotic only)
1550                                 //MED 01/04/97 moved keys here so they would not be overwritten
1551                                 if (gamemode == GAME_HIPNOTIC)
1552                                 {
1553                                         if (cl.stats[STAT_ITEMS] & IT_KEY1)
1554                                                 Sbar_DrawPic (209, 3, sb_items[0]);
1555                                         if (cl.stats[STAT_ITEMS] & IT_KEY2)
1556                                                 Sbar_DrawPic (209, 12, sb_items[1]);
1557                                 }
1558                                 // armor
1559                                 if (gamemode != GAME_GOODVSBAD2)
1560                                 {
1561                                         if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1562                                         {
1563                                                 Sbar_DrawNum (24, 0, 666, 3, 1);
1564                                                 Sbar_DrawPic (0, 0, sb_disc);
1565                                         }
1566                                         else
1567                                         {
1568                                                 if (gamemode == GAME_ROGUE)
1569                                                 {
1570                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1571                                                         if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1572                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1573                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1574                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1575                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1576                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1577                                                 }
1578                                                 else
1579                                                 {
1580                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1581                                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1582                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1583                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1584                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1585                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1586                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1587                                                 }
1588                                         }
1589                                 }
1590
1591                                 // face
1592                                 Sbar_DrawFace ();
1593
1594                                 // health
1595                                 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1596
1597                                 // ammo icon
1598                                 if (gamemode == GAME_ROGUE)
1599                                 {
1600                                         if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1601                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1602                                         else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1603                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1604                                         else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1605                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1606                                         else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1607                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1608                                         else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1609                                                 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1610                                         else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1611                                                 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1612                                         else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1613                                                 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1614                                 }
1615                                 else
1616                                 {
1617                                         if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1618                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1619                                         else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1620                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1621                                         else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1622                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1623                                         else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1624                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1625                                 }
1626
1627                                 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1628
1629                                 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1630                                 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1631                                 {
1632                                         if (gamemode == GAME_TRANSFUSION)
1633                                                 Sbar_MiniDeathmatchOverlay (0, 0);
1634                                         else
1635                                                 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1636                                         Sbar_Score(24);
1637                                 }
1638                         }
1639                 }
1640         }
1641
1642         Sbar_ShowFPS();
1643
1644         if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1645         {
1646                 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1647                 DrawQ_Pic((vid_conwidth.integer - pic->width * crosshair_size.value) * 0.5f, (vid_conheight.integer - pic->height * crosshair_size.value) * 0.5f, pic, pic->width * crosshair_size.value, pic->height * crosshair_size.value, crosshair_color_red.value, crosshair_color_green.value, crosshair_color_blue.value, crosshair_color_alpha.value, 0);
1648         }
1649
1650         if (cl_prydoncursor.integer)
1651                 DrawQ_Pic((cl.cmd.cursor_screen[0] + 1) * 0.5 * vid_conwidth.integer, (cl.cmd.cursor_screen[1] + 1) * 0.5 * vid_conheight.integer, Draw_CachePic (va("gfx/prydoncursor%03i", cl_prydoncursor.integer)), 0, 0, 1, 1, 1, 1, 0);
1652 }
1653
1654 //=============================================================================
1655
1656 /*
1657 ==================
1658 Sbar_DeathmatchOverlay
1659
1660 ==================
1661 */
1662 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1663 {
1664         int minutes;
1665         qboolean myself = false;
1666         unsigned char *c;
1667         minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1668
1669         if((s - cl.scores) == cl.playerentity - 1)
1670                 myself = true;
1671         if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1672                 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1673                         myself = true;
1674
1675         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1676         {
1677                 if (s->qw_spectator)
1678                 {
1679                         if (s->qw_ping || s->qw_packetloss)
1680                                 DrawQ_String_Font(x, y, va("%4i %3i %4i spectator  %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1681                         else
1682                                 DrawQ_String_Font(x, y, va("         %4i spectator  %c%s", minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1683                 }
1684                 else
1685                 {
1686                         // draw colors behind score
1687                         //
1688                         //
1689                         //
1690                         //
1691                         //
1692                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1693                         DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1694                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1695                         DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1696                         // print the text
1697                         //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1698                         if (s->qw_ping || s->qw_packetloss)
1699                                 DrawQ_String_Font(x, y, va("%4i %3i %4i %5i %-4s %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1700                         else
1701                                 DrawQ_String_Font(x, y, va("         %4i %5i %-4s %c%s", minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1702                 }
1703         }
1704         else
1705         {
1706                 if (s->qw_spectator)
1707                 {
1708                         if (s->qw_ping || s->qw_packetloss)
1709                                 DrawQ_String_Font(x, y, va("%4i %3i spect %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1710                         else
1711                                 DrawQ_String_Font(x, y, va("         spect %c%s", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1712                 }
1713                 else
1714                 {
1715                         // draw colors behind score
1716                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1717                         DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1718                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1719                         DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1720                         // print the text
1721                         //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1722                         if (s->qw_ping || s->qw_packetloss)
1723                                 DrawQ_String_Font(x, y, va("%4i %3i %5i %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1724                         else
1725                                 DrawQ_String_Font(x, y, va("         %5i %c%s", (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1726                 }
1727         }
1728         return 8;
1729 }
1730
1731 void Sbar_DeathmatchOverlay (void)
1732 {
1733         int i, y, xmin, xmax, ymin, ymax;
1734
1735         // request new ping times every two second
1736         if (cl.last_ping_request < realtime - 2 && cls.netcon)
1737         {
1738                 cl.last_ping_request = realtime;
1739                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1740                 {
1741                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1742                         MSG_WriteString(&cls.netcon->message, "pings");
1743                 }
1744                 else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5 || cls.protocol == PROTOCOL_DARKPLACES6/* || cls.protocol == PROTOCOL_DARKPLACES7*/)
1745                 {
1746                         // these servers usually lack the pings command and so a less efficient "ping" command must be sent, which on modern DP servers will also reply with a pingplreport command after the ping listing
1747                         static int ping_anyway_counter = 0;
1748                         if(cl.parsingtextexpectingpingforscores == 1)
1749                         {
1750                                 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1751                                 if(++ping_anyway_counter >= 5)
1752                                         cl.parsingtextexpectingpingforscores = 0;
1753                         }
1754                         if(cl.parsingtextexpectingpingforscores != 1)
1755                         {
1756                                 ping_anyway_counter = 0;
1757                                 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1758                                 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1759                                 MSG_WriteString(&cls.netcon->message, "ping");
1760                         }
1761                 }
1762                 else
1763                 {
1764                         // newer server definitely has pings command, so use it for more efficiency, avoids ping reports spamming the console if they are misparsed, and saves a little bandwidth
1765                         MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1766                         MSG_WriteString(&cls.netcon->message, "pings");
1767                 }
1768         }
1769
1770         // scores
1771         Sbar_SortFrags ();
1772
1773         ymin = 8;
1774         ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1775
1776         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1777                 xmin = (vid_conwidth.integer - (26 + 15) * 8 * FONT_SBAR->width_of[0]) / 2; // 26 characters until name, then we assume 15 character names (they can be longer but usually aren't)
1778         else
1779                 xmin = (vid_conwidth.integer - (16 + 25) * 8 * FONT_SBAR->width_of[0]) / 2; // 16 characters until name, then we assume 25 character names (they can be longer but usually aren't)
1780         xmax = vid_conwidth.integer - xmin;
1781
1782         if(gamemode == GAME_NEXUIZ)
1783                 DrawQ_Pic (xmin - 8, ymin - 8, 0, xmax-xmin+1 + 2*8, ymax-ymin+1 + 2*8, 0, 0, 0, sbar_alpha_bg.value, 0);
1784
1785         DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1786
1787         // draw the text
1788         y = 40;
1789         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1790         {
1791                 DrawQ_String_Font(xmin, y, va("ping pl%% time frags team  name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1792         }
1793         else
1794         {
1795                 DrawQ_String_Font(xmin, y, va("ping pl%% frags  name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1796         }
1797         y += 8;
1798
1799         if (Sbar_IsTeammatch ())
1800         {
1801                 // show team scores first
1802                 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1803                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1804                 y += 5;
1805         }
1806
1807         for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1808                 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1809 }
1810
1811 /*
1812 ==================
1813 Sbar_MiniDeathmatchOverlay
1814
1815 ==================
1816 */
1817 void Sbar_MiniDeathmatchOverlay (int x, int y)
1818 {
1819         int i, j, numlines, range_begin, range_end, myteam, teamsep;
1820
1821         // do not draw this if sbar_miniscoreboard_size is zero
1822         if(sbar_miniscoreboard_size.value == 0)
1823                 return;
1824         // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1825         if(sbar_miniscoreboard_size.value > 0)
1826                 y = vid_conheight.integer - sbar_miniscoreboard_size.value * 8;
1827
1828         // scores
1829         Sbar_SortFrags ();
1830
1831         // decide where to print
1832         if (gamemode == GAME_TRANSFUSION)
1833                 numlines = (vid_conwidth.integer - x + 127) / 128;
1834         else
1835                 numlines = (vid_conheight.integer - y + 7) / 8;
1836
1837         // give up if there isn't room
1838         if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1839                 return;
1840
1841         //find us
1842         for (i = 0; i < scoreboardlines; i++)
1843                 if (fragsort[i] == cl.playerentity - 1)
1844                         break;
1845
1846         range_begin = 0;
1847         range_end = scoreboardlines;
1848         teamsep = 0;
1849
1850         if (gamemode != GAME_TRANSFUSION)
1851                 if (Sbar_IsTeammatch ())
1852                 {
1853                         // reserve space for the team scores
1854                         numlines -= teamlines;
1855
1856                         // find first and last player of my team (only draw the team totals and my own team)
1857                         range_begin = range_end = i;
1858                         myteam = cl.scores[fragsort[i]].colors & 15;
1859                         while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1860                                 --range_begin;
1861                         while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1862                                 ++range_end;
1863
1864                         // looks better than two players
1865                         if(numlines == 2)
1866                         {
1867                                 teamsep = 8;
1868                                 numlines = 1;
1869                         }
1870                 }
1871
1872         // figure out start
1873         i -= numlines/2;
1874         i = min(i, range_end - numlines);
1875         i = max(i, range_begin);
1876
1877         if (gamemode == GAME_TRANSFUSION)
1878         {
1879                 for (;i < range_end && x < vid_conwidth.integer;i++)
1880                         x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1881         }
1882         else
1883         {
1884                 if(range_end - i < numlines) // won't draw to bottom?
1885                         y += 8 * (numlines - (range_end - i)); // bottom align
1886                 // show team scores first
1887                 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1888                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1889                 y += teamsep;
1890                 for (;i < range_end && y < vid_conheight.integer;i++)
1891                         y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1892         }
1893 }
1894
1895 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1896 {
1897         static int const sortorder[16] =
1898         {
1899                 1001,
1900                 1002,
1901                 1003,
1902                 1004,
1903                 1, // red
1904                 1005,
1905                 1006,
1906                 1007,
1907                 1008,
1908                 4, // pink
1909                 1009,
1910                 1010,
1911                 3, // yellow
1912                 2, // blue
1913                 1011,
1914                 1012
1915         };
1916         const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1917         const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1918         int tc1 = sortorder[t1->colors & 15];
1919         int tc2 = sortorder[t2->colors & 15];
1920         return tc1 - tc2;
1921 }
1922
1923 void Sbar_Score (int margin)
1924 {
1925         int i, me, score, otherleader, place, distribution, minutes, seconds;
1926         double timeleft;
1927         int sbar_x_save = sbar_x;
1928         int sbar_y_save = sbar_y;
1929
1930         sbar_y = vid_conheight.value - (32+12);
1931         sbar_x -= margin;
1932
1933         me = cl.playerentity - 1;
1934         if (me >= 0 && me < cl.maxclients)
1935         {
1936                 if(Sbar_IsTeammatch())
1937                 {
1938                         // Layout:
1939                         //
1940                         //   team1 team3 team4
1941                         //
1942                         //         TEAM2
1943
1944                         scoreboard_t *teamcolorsort[16];
1945
1946                         Sbar_SortFrags();
1947                         for(i = 0; i < teamlines; ++i)
1948                                 teamcolorsort[i] = &(teams[i]);
1949
1950                         // Now sort them by color
1951                         qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
1952
1953                         // : margin
1954                         // -12*4: four digits space
1955                         place = (teamlines - 1) * (-12 * 4);
1956
1957                         for(i = 0; i < teamlines; ++i)
1958                         {
1959                                 int cindex = teamcolorsort[i]->colors & 15;
1960                                 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
1961                                 float cm = max(max(c[0], c[1]), c[2]);
1962                                 float cr = c[0] / cm;
1963                                 float cg = c[1] / cm;
1964                                 float cb = c[2] / cm;
1965                                 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
1966                                 {
1967                                         Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
1968                                 }
1969                                 else // other team
1970                                 {
1971                                         Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
1972                                         place += 4 * 12;
1973                                 }
1974                         }
1975                 }
1976                 else
1977                 {
1978                         // Layout:
1979                         //
1980                         //   leading  place
1981                         //
1982                         //        FRAGS
1983                         //
1984                         // find leading score other than ourselves, to calculate distribution
1985                         // find our place in the scoreboard
1986                         score = cl.scores[me].frags;
1987                         for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
1988                         {
1989                                 if (cl.scores[i].name[0] && i != me)
1990                                 {
1991                                         if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
1992                                                 otherleader = i;
1993                                         if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
1994                                                 place++;
1995                                 }
1996                         }
1997                         distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
1998                         if (place == 1)
1999                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2000                         else if (place == 2)
2001                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2002                         else
2003                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2004                         if (otherleader < 0)
2005                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2006                         if (distribution >= 0)
2007                         {
2008                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2009                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2010                         }
2011                         else if (distribution >= -5)
2012                         {
2013                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2014                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 0, 1, 0);
2015                         }
2016                         else
2017                         {
2018                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2019                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 0, 0, 1, 0);
2020                         }
2021                 }
2022         }
2023
2024         if (cl.statsf[STAT_TIMELIMIT])
2025         {
2026                 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2027                 minutes = (int)floor(timeleft / 60);
2028                 seconds = (int)(floor(timeleft) - minutes * 60);
2029                 if (minutes >= 5)
2030                 {
2031                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2032                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2033                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2034                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2035                 }
2036                 else if (minutes >= 1)
2037                 {
2038                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 0, 1, 0);
2039                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2040                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2041                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2042                 }
2043                 else if ((int)(timeleft * 4) & 1)
2044                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2045                 else
2046                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2047         }
2048         else
2049         {
2050                 minutes = (int)floor(cl.time / 60);
2051                 seconds = (int)(floor(cl.time) - minutes * 60);
2052                 Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2053                 if(sb_colon && sb_colon->tex != r_texture_notexture)
2054                         DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2055                 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2056         }
2057
2058         sbar_x = sbar_x_save;
2059         sbar_y = sbar_y_save;
2060 }
2061
2062 /*
2063 ==================
2064 Sbar_IntermissionOverlay
2065
2066 ==================
2067 */
2068 void Sbar_IntermissionOverlay (void)
2069 {
2070         int             dig;
2071         int             num;
2072
2073         if (cl.gametype == GAME_DEATHMATCH)
2074         {
2075                 Sbar_DeathmatchOverlay ();
2076                 return;
2077         }
2078
2079         sbar_x = (vid_conwidth.integer - 320) >> 1;
2080         sbar_y = (vid_conheight.integer - 200) >> 1;
2081
2082         DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2083         DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2084
2085 // time
2086         dig = (int)cl.completed_time / 60;
2087         Sbar_DrawNum (160, 64, dig, 3, 0);
2088         num = (int)cl.completed_time - dig*60;
2089         Sbar_DrawPic (234,64,sb_colon);
2090         Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2091         Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2092
2093         Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2094         if (gamemode != GAME_NEXUIZ)
2095                 Sbar_DrawPic (232, 104, sb_slash);
2096         Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2097
2098         Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2099         if (gamemode != GAME_NEXUIZ)
2100                 Sbar_DrawPic (232, 144, sb_slash);
2101         Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2102
2103 }
2104
2105
2106 /*
2107 ==================
2108 Sbar_FinaleOverlay
2109
2110 ==================
2111 */
2112 void Sbar_FinaleOverlay (void)
2113 {
2114         DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2115 }
2116