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