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