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