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