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