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