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