2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // sbar.c -- status bar code
24 #include "cl_collision.h"
29 #define STAT_MINUS 10 // num frame for '-' stats digit
30 cachepic_t *sb_nums[2][11];
31 cachepic_t *sb_colon, *sb_slash;
34 cachepic_t *sb_scorebar;
35 // AK only used by NEX
36 cachepic_t *sb_sbar_minimal;
37 cachepic_t *sb_sbar_overlay;
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];
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
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;
56 qboolean sb_showscores;
58 int sb_lines; // scan lines to draw
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
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];
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;
83 cachepic_t *sb_ranking;
84 cachepic_t *sb_complete;
86 cachepic_t *sb_finale;
88 cvar_t cl_showfps = {CVAR_CLIENT | CVAR_SAVE, "cl_showfps", "0", "shows your rendered fps (frames per second)"};
89 cvar_t cl_showsound = {CVAR_CLIENT | CVAR_SAVE, "cl_showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
90 cvar_t cl_showblur = {CVAR_CLIENT | CVAR_SAVE, "cl_showblur", "0", "shows the current alpha level of motionblur"};
91 cvar_t cl_showspeed = {CVAR_CLIENT | CVAR_SAVE, "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_showtopspeed = {CVAR_CLIENT | CVAR_SAVE, "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"};
93 cvar_t cl_showtime = {CVAR_CLIENT | CVAR_SAVE, "cl_showtime", "0", "shows current time of day (useful on screenshots)"};
94 cvar_t cl_showtime_format = {CVAR_CLIENT | CVAR_SAVE, "cl_showtime_format", "%H:%M:%S", "format string for time of day"};
95 cvar_t cl_showdate = {CVAR_CLIENT | CVAR_SAVE, "cl_showdate", "0", "shows current date (useful on screenshots)"};
96 cvar_t cl_showdate_format = {CVAR_CLIENT | CVAR_SAVE, "cl_showdate_format", "%Y-%m-%d", "format string for date"};
97 cvar_t cl_showtex = {CVAR_CLIENT, "cl_showtex", "0", "shows the name of the texture on the crosshair (for map debugging)"};
99 cvar_t showfps = {CVAR_ALIAS, "showfps"};
100 cvar_t showsound = {CVAR_ALIAS, "showsound"};
101 cvar_t showblur = {CVAR_ALIAS, "showblur"};
102 cvar_t showspeed = {CVAR_ALIAS, "showspeed"};
103 cvar_t showtopspeed = {CVAR_ALIAS, "showtopspeed"};
104 cvar_t showtime = {CVAR_ALIAS, "showtime"};
105 cvar_t showtime_format = {CVAR_ALIAS, "showtime_format"};
106 cvar_t showdate = {CVAR_ALIAS, "showdate"};
107 cvar_t showdate_format = {CVAR_ALIAS, "showdate_format"};
108 cvar_t showtex = {CVAR_ALIAS, "showtex"};
110 cvar_t sbar_alpha_bg = {CVAR_CLIENT | CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
111 cvar_t sbar_alpha_fg = {CVAR_CLIENT | CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
112 cvar_t sbar_hudselector = {CVAR_CLIENT | CVAR_SAVE, "sbar_hudselector", "0", "selects which of the builtin hud layouts to use (meaning is somewhat dependent on gamemode, so nexuiz has a very different set of hud layouts than quake for example)"};
113 cvar_t sbar_scorerank = {CVAR_CLIENT | CVAR_SAVE, "sbar_scorerank", "1", "shows an overlay for your score (or team score) and rank in the scoreboard"};
114 cvar_t sbar_gametime = {CVAR_CLIENT | CVAR_SAVE, "sbar_gametime", "1", "shows an overlay for the time left in the current match/level (or current game time if there is no timelimit set)"};
115 cvar_t sbar_miniscoreboard_size = {CVAR_CLIENT | CVAR_SAVE, "sbar_miniscoreboard_size", "-1", "sets the size of the mini deathmatch overlay in items, or disables it when set to 0, or sets it to a sane default when set to -1"};
116 cvar_t sbar_flagstatus_right = {CVAR_CLIENT | CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
117 cvar_t sbar_flagstatus_pos = {CVAR_CLIENT | CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
118 cvar_t sbar_info_pos = {CVAR_CLIENT | CVAR_SAVE, "sbar_info_pos", "0", "pixel position of the info strings (such as showfps), from the bottom"};
120 cvar_t cl_deathscoreboard = {CVAR_CLIENT, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
122 cvar_t crosshair_color_red = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
123 cvar_t crosshair_color_green = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
124 cvar_t crosshair_color_blue = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
125 cvar_t crosshair_color_alpha = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
126 cvar_t crosshair_size = {CVAR_CLIENT | CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
128 static void Sbar_MiniDeathmatchOverlay (int x, int y);
129 static void Sbar_DeathmatchOverlay (void);
130 static void Sbar_IntermissionOverlay (void);
131 static void Sbar_FinaleOverlay (void);
142 static void Sbar_ShowScores_f(cmd_state_t *cmd)
146 sb_showscores = true;
147 CL_VM_UpdateShowingScoresState(sb_showscores);
157 static void Sbar_DontShowScores_f(cmd_state_t *cmd)
159 sb_showscores = false;
160 CL_VM_UpdateShowingScoresState(sb_showscores);
163 static void sbar_start(void)
168 if (gamemode == GAME_DELUXEQUAKE || gamemode == GAME_BLOODOMNICIDE)
171 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
173 for (i = 0;i < 10;i++)
174 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
175 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
176 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET | CACHEPICFLAG_FAILONMISSING);
178 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
179 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_bullets", CACHEPICFLAG_QUIET);
180 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
181 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
183 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor", CACHEPICFLAG_QUIET);
187 sb_health = Draw_CachePic_Flags ("gfx/sb_health", CACHEPICFLAG_QUIET);
189 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_slowmo", CACHEPICFLAG_QUIET);
190 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invinc", CACHEPICFLAG_QUIET);
191 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_energy", CACHEPICFLAG_QUIET);
192 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_str", CACHEPICFLAG_QUIET);
194 sb_items[11] = Draw_CachePic_Flags ("gfx/sb_flag_red_taken", CACHEPICFLAG_QUIET);
195 sb_items[12] = Draw_CachePic_Flags ("gfx/sb_flag_red_lost", CACHEPICFLAG_QUIET);
196 sb_items[13] = Draw_CachePic_Flags ("gfx/sb_flag_red_carrying", CACHEPICFLAG_QUIET);
197 sb_items[14] = Draw_CachePic_Flags ("gfx/sb_key_carrying", CACHEPICFLAG_QUIET);
198 sb_items[15] = Draw_CachePic_Flags ("gfx/sb_flag_blue_taken", CACHEPICFLAG_QUIET);
199 sb_items[16] = Draw_CachePic_Flags ("gfx/sb_flag_blue_lost", CACHEPICFLAG_QUIET);
200 sb_items[17] = Draw_CachePic_Flags ("gfx/sb_flag_blue_carrying", CACHEPICFLAG_QUIET);
202 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
203 sb_sbar_minimal = Draw_CachePic_Flags ("gfx/sbar_minimal", CACHEPICFLAG_QUIET);
204 sb_sbar_overlay = Draw_CachePic_Flags ("gfx/sbar_overlay", CACHEPICFLAG_QUIET);
206 for(i = 0; i < 9;i++)
207 sb_weapons[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inv_weapon%i",i), CACHEPICFLAG_QUIET);
209 else if (gamemode == GAME_ZYMOTIC)
211 zymsb_crosshair_center = Draw_CachePic_Flags ("gfx/hud/crosshair_center", CACHEPICFLAG_QUIET);
212 zymsb_crosshair_line = Draw_CachePic_Flags ("gfx/hud/crosshair_line", CACHEPICFLAG_QUIET);
213 zymsb_crosshair_health = Draw_CachePic_Flags ("gfx/hud/crosshair_health", CACHEPICFLAG_QUIET);
214 zymsb_crosshair_clip = Draw_CachePic_Flags ("gfx/hud/crosshair_clip", CACHEPICFLAG_QUIET);
215 zymsb_crosshair_ammo = Draw_CachePic_Flags ("gfx/hud/crosshair_ammo", CACHEPICFLAG_QUIET);
216 zymsb_crosshair_background = Draw_CachePic_Flags ("gfx/hud/crosshair_background", CACHEPICFLAG_QUIET);
217 zymsb_crosshair_left1 = Draw_CachePic_Flags ("gfx/hud/crosshair_left1", CACHEPICFLAG_QUIET);
218 zymsb_crosshair_left2 = Draw_CachePic_Flags ("gfx/hud/crosshair_left2", CACHEPICFLAG_QUIET);
219 zymsb_crosshair_right = Draw_CachePic_Flags ("gfx/hud/crosshair_right", CACHEPICFLAG_QUIET);
223 sb_disc = Draw_CachePic_Flags ("gfx/disc", CACHEPICFLAG_QUIET);
225 for (i = 0;i < 10;i++)
227 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
228 sb_nums[1][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/anum_%i",i), CACHEPICFLAG_QUIET);
231 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
232 sb_nums[1][10] = Draw_CachePic_Flags ("gfx/anum_minus", CACHEPICFLAG_QUIET);
234 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET | CACHEPICFLAG_FAILONMISSING);
235 sb_slash = Draw_CachePic_Flags ("gfx/num_slash", CACHEPICFLAG_QUIET);
237 sb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_shotgun", CACHEPICFLAG_QUIET);
238 sb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_sshotgun", CACHEPICFLAG_QUIET);
239 sb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_nailgun", CACHEPICFLAG_QUIET);
240 sb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_snailgun", CACHEPICFLAG_QUIET);
241 sb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_rlaunch", CACHEPICFLAG_QUIET);
242 sb_weapons[0][5] = Draw_CachePic_Flags ("gfx/inv_srlaunch", CACHEPICFLAG_QUIET);
243 sb_weapons[0][6] = Draw_CachePic_Flags ("gfx/inv_lightng", CACHEPICFLAG_QUIET);
245 sb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_shotgun", CACHEPICFLAG_QUIET);
246 sb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_sshotgun", CACHEPICFLAG_QUIET);
247 sb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_nailgun", CACHEPICFLAG_QUIET);
248 sb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_snailgun", CACHEPICFLAG_QUIET);
249 sb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_rlaunch", CACHEPICFLAG_QUIET);
250 sb_weapons[1][5] = Draw_CachePic_Flags ("gfx/inv2_srlaunch", CACHEPICFLAG_QUIET);
251 sb_weapons[1][6] = Draw_CachePic_Flags ("gfx/inv2_lightng", CACHEPICFLAG_QUIET);
253 for (i = 0;i < 5;i++)
255 sb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_shotgun",i+1), CACHEPICFLAG_QUIET);
256 sb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_sshotgun",i+1), CACHEPICFLAG_QUIET);
257 sb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_nailgun",i+1), CACHEPICFLAG_QUIET);
258 sb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_snailgun",i+1), CACHEPICFLAG_QUIET);
259 sb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_rlaunch",i+1), CACHEPICFLAG_QUIET);
260 sb_weapons[2+i][5] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_srlaunch",i+1), CACHEPICFLAG_QUIET);
261 sb_weapons[2+i][6] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_lightng",i+1), CACHEPICFLAG_QUIET);
264 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
265 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_nails", CACHEPICFLAG_QUIET);
266 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
267 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
269 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor1", CACHEPICFLAG_QUIET);
270 sb_armor[1] = Draw_CachePic_Flags ("gfx/sb_armor2", CACHEPICFLAG_QUIET);
271 sb_armor[2] = Draw_CachePic_Flags ("gfx/sb_armor3", CACHEPICFLAG_QUIET);
273 sb_items[0] = Draw_CachePic_Flags ("gfx/sb_key1", CACHEPICFLAG_QUIET);
274 sb_items[1] = Draw_CachePic_Flags ("gfx/sb_key2", CACHEPICFLAG_QUIET);
275 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_invis", CACHEPICFLAG_QUIET);
276 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invuln", CACHEPICFLAG_QUIET);
277 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_suit", CACHEPICFLAG_QUIET);
278 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_quad", CACHEPICFLAG_QUIET);
280 sb_sigil[0] = Draw_CachePic_Flags ("gfx/sb_sigil1", CACHEPICFLAG_QUIET);
281 sb_sigil[1] = Draw_CachePic_Flags ("gfx/sb_sigil2", CACHEPICFLAG_QUIET);
282 sb_sigil[2] = Draw_CachePic_Flags ("gfx/sb_sigil3", CACHEPICFLAG_QUIET);
283 sb_sigil[3] = Draw_CachePic_Flags ("gfx/sb_sigil4", CACHEPICFLAG_QUIET);
285 sb_faces[4][0] = Draw_CachePic_Flags ("gfx/face1", CACHEPICFLAG_QUIET);
286 sb_faces[4][1] = Draw_CachePic_Flags ("gfx/face_p1", CACHEPICFLAG_QUIET);
287 sb_faces[3][0] = Draw_CachePic_Flags ("gfx/face2", CACHEPICFLAG_QUIET);
288 sb_faces[3][1] = Draw_CachePic_Flags ("gfx/face_p2", CACHEPICFLAG_QUIET);
289 sb_faces[2][0] = Draw_CachePic_Flags ("gfx/face3", CACHEPICFLAG_QUIET);
290 sb_faces[2][1] = Draw_CachePic_Flags ("gfx/face_p3", CACHEPICFLAG_QUIET);
291 sb_faces[1][0] = Draw_CachePic_Flags ("gfx/face4", CACHEPICFLAG_QUIET);
292 sb_faces[1][1] = Draw_CachePic_Flags ("gfx/face_p4", CACHEPICFLAG_QUIET);
293 sb_faces[0][0] = Draw_CachePic_Flags ("gfx/face5", CACHEPICFLAG_QUIET);
294 sb_faces[0][1] = Draw_CachePic_Flags ("gfx/face_p5", CACHEPICFLAG_QUIET);
296 sb_face_invis = Draw_CachePic_Flags ("gfx/face_invis", CACHEPICFLAG_QUIET);
297 sb_face_invuln = Draw_CachePic_Flags ("gfx/face_invul2", CACHEPICFLAG_QUIET);
298 sb_face_invis_invuln = Draw_CachePic_Flags ("gfx/face_inv2", CACHEPICFLAG_QUIET);
299 sb_face_quad = Draw_CachePic_Flags ("gfx/face_quad", CACHEPICFLAG_QUIET);
301 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
302 sb_ibar = Draw_CachePic_Flags ("gfx/ibar", CACHEPICFLAG_QUIET);
303 sb_scorebar = Draw_CachePic_Flags ("gfx/scorebar", CACHEPICFLAG_QUIET);
305 //MED 01/04/97 added new hipnotic weapons
306 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
308 hsb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_laser", CACHEPICFLAG_QUIET);
309 hsb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_mjolnir", CACHEPICFLAG_QUIET);
310 hsb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_gren_prox", CACHEPICFLAG_QUIET);
311 hsb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_prox_gren", CACHEPICFLAG_QUIET);
312 hsb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_prox", CACHEPICFLAG_QUIET);
314 hsb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_laser", CACHEPICFLAG_QUIET);
315 hsb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_mjolnir", CACHEPICFLAG_QUIET);
316 hsb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_gren_prox", CACHEPICFLAG_QUIET);
317 hsb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_prox_gren", CACHEPICFLAG_QUIET);
318 hsb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_prox", CACHEPICFLAG_QUIET);
320 for (i = 0;i < 5;i++)
322 hsb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_laser",i+1), CACHEPICFLAG_QUIET);
323 hsb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_mjolnir",i+1), CACHEPICFLAG_QUIET);
324 hsb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_gren_prox",i+1), CACHEPICFLAG_QUIET);
325 hsb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox_gren",i+1), CACHEPICFLAG_QUIET);
326 hsb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox",i+1), CACHEPICFLAG_QUIET);
329 hsb_items[0] = Draw_CachePic_Flags ("gfx/sb_wsuit", CACHEPICFLAG_QUIET);
330 hsb_items[1] = Draw_CachePic_Flags ("gfx/sb_eshld", CACHEPICFLAG_QUIET);
332 else if (gamemode == GAME_ROGUE)
334 rsb_invbar[0] = Draw_CachePic_Flags ("gfx/r_invbar1", CACHEPICFLAG_QUIET);
335 rsb_invbar[1] = Draw_CachePic_Flags ("gfx/r_invbar2", CACHEPICFLAG_QUIET);
337 rsb_weapons[0] = Draw_CachePic_Flags ("gfx/r_lava", CACHEPICFLAG_QUIET);
338 rsb_weapons[1] = Draw_CachePic_Flags ("gfx/r_superlava", CACHEPICFLAG_QUIET);
339 rsb_weapons[2] = Draw_CachePic_Flags ("gfx/r_gren", CACHEPICFLAG_QUIET);
340 rsb_weapons[3] = Draw_CachePic_Flags ("gfx/r_multirock", CACHEPICFLAG_QUIET);
341 rsb_weapons[4] = Draw_CachePic_Flags ("gfx/r_plasma", CACHEPICFLAG_QUIET);
343 rsb_items[0] = Draw_CachePic_Flags ("gfx/r_shield1", CACHEPICFLAG_QUIET);
344 rsb_items[1] = Draw_CachePic_Flags ("gfx/r_agrav1", CACHEPICFLAG_QUIET);
346 // PGM 01/19/97 - team color border
347 rsb_teambord = Draw_CachePic_Flags ("gfx/r_teambord", CACHEPICFLAG_QUIET);
348 // PGM 01/19/97 - team color border
350 rsb_ammo[0] = Draw_CachePic_Flags ("gfx/r_ammolava", CACHEPICFLAG_QUIET);
351 rsb_ammo[1] = Draw_CachePic_Flags ("gfx/r_ammomulti", CACHEPICFLAG_QUIET);
352 rsb_ammo[2] = Draw_CachePic_Flags ("gfx/r_ammoplasma", CACHEPICFLAG_QUIET);
356 sb_ranking = Draw_CachePic_Flags ("gfx/ranking", CACHEPICFLAG_QUIET);
357 sb_complete = Draw_CachePic_Flags ("gfx/complete", CACHEPICFLAG_QUIET);
358 sb_inter = Draw_CachePic_Flags ("gfx/inter", CACHEPICFLAG_QUIET);
359 sb_finale = Draw_CachePic_Flags ("gfx/finale", CACHEPICFLAG_QUIET);
362 static void sbar_shutdown(void)
366 static void sbar_newmap(void)
370 void Sbar_Init (void)
372 if(gamemode == GAME_NORMAL) // Workaround so Quake doesn't trample on Xonotic.
374 Cmd_AddCommand(&cmd_client, "+showscores", Sbar_ShowScores_f, "show scoreboard");
375 Cmd_AddCommand(&cmd_client, "-showscores", Sbar_DontShowScores_f, "hide scoreboard");
377 Cvar_RegisterVariable(&cl_showfps);
378 Cvar_RegisterVariable(&cl_showsound);
379 Cvar_RegisterVariable(&cl_showblur);
380 Cvar_RegisterVariable(&cl_showspeed);
381 Cvar_RegisterVariable(&cl_showtopspeed);
382 Cvar_RegisterVariable(&cl_showtime);
383 Cvar_RegisterVariable(&cl_showtime_format);
384 Cvar_RegisterVariable(&cl_showdate);
385 Cvar_RegisterVariable(&cl_showdate_format);
386 Cvar_RegisterVariable(&cl_showtex);
388 Cvar_RegisterAlias(&showfps, &cl_showfps);
389 Cvar_RegisterAlias(&showsound, &cl_showsound);
390 Cvar_RegisterAlias(&showblur, &cl_showblur);
391 Cvar_RegisterAlias(&showspeed, &cl_showspeed);
392 Cvar_RegisterAlias(&showtopspeed, &cl_showtopspeed);
393 Cvar_RegisterAlias(&showtime, &cl_showtime);
394 Cvar_RegisterAlias(&showtime_format, &cl_showtime_format);
395 Cvar_RegisterAlias(&showdate, &cl_showdate);
396 Cvar_RegisterAlias(&showdate_format, &cl_showdate_format);
397 Cvar_RegisterAlias(&showtex, &cl_showtex);
399 Cvar_RegisterVariable(&sbar_alpha_bg);
400 Cvar_RegisterVariable(&sbar_alpha_fg);
401 Cvar_RegisterVariable(&sbar_hudselector);
402 Cvar_RegisterVariable(&sbar_scorerank);
403 Cvar_RegisterVariable(&sbar_gametime);
404 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
405 Cvar_RegisterVariable(&sbar_info_pos);
406 Cvar_RegisterVariable(&cl_deathscoreboard);
408 Cvar_RegisterVariable(&crosshair_color_red);
409 Cvar_RegisterVariable(&crosshair_color_green);
410 Cvar_RegisterVariable(&crosshair_color_blue);
411 Cvar_RegisterVariable(&crosshair_color_alpha);
412 Cvar_RegisterVariable(&crosshair_size);
414 Cvar_RegisterVariable(&sbar_flagstatus_right); // (GAME_NEXUZI ONLY)
415 Cvar_RegisterVariable(&sbar_flagstatus_pos); // (GAME_NEXUIZ ONLY)
417 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap, NULL, NULL);
421 //=============================================================================
423 // drawing routines are relative to the status bar location
432 static void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
434 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
437 static void Sbar_DrawPic (int x, int y, cachepic_t *pic)
439 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
442 static void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
444 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
451 Draws one solid graphics character
454 static void Sbar_DrawCharacter (int x, int y, int num)
457 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);
465 static void Sbar_DrawString (int x, int y, char *str)
467 DrawQ_String (sbar_x + x, sbar_y + y, str, 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR);
475 static void Sbar_DrawNum (int x, int y, int num, int digits, int color)
480 l = dpsnprintf(str, sizeof(str), "%i", num);
494 Sbar_DrawPic (x, y, sb_nums[color][frame]);
507 static void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
515 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
518 l = dpsnprintf(str, sizeof(str), "%i", num);
523 x += (digits-l) * lettersize;
532 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
539 //=============================================================================
542 static int Sbar_IsTeammatch(void)
544 // currently only nexuiz uses the team score board
545 return (IS_OLDNEXUIZ_DERIVED(gamemode)
546 && (teamplay.integer > 0));
554 static int fragsort[MAX_SCOREBOARD];
555 static int scoreboardlines;
557 int Sbar_GetSortedPlayerIndex (int index)
559 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
562 static scoreboard_t teams[MAX_SCOREBOARD];
563 static int teamsort[MAX_SCOREBOARD];
564 static int teamlines;
565 void Sbar_SortFrags (void)
571 for (i=0 ; i<cl.maxclients ; i++)
573 if (cl.scores[i].name[0])
575 fragsort[scoreboardlines] = i;
580 for (i=0 ; i<scoreboardlines ; i++)
581 for (j=0 ; j<scoreboardlines-1-i ; j++)
582 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
585 fragsort[j] = fragsort[j+1];
590 if (Sbar_IsTeammatch ())
592 // now sort players by teams.
593 for (i=0 ; i<scoreboardlines ; i++)
595 for (j=0 ; j<scoreboardlines-1-i ; j++)
597 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
600 fragsort[j] = fragsort[j+1];
606 // calculate team scores
608 for (i=0 ; i<scoreboardlines ; i++)
610 if (color != (cl.scores[fragsort[i]].colors & 15))
612 const char* teamname;
614 color = cl.scores[fragsort[i]].colors & 15;
620 teamname = "^1Red Team";
623 teamname = "^4Blue Team";
626 teamname = "^6Pink Team";
629 teamname = "^3Yellow Team";
632 teamname = "Total Team Score";
635 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
637 teams[teamlines-1].frags = 0;
638 teams[teamlines-1].colors = color + 16 * color;
641 if (cl.scores[fragsort[i]].frags != -666)
643 // do not add spedcators
644 // (ugly hack for nexuiz)
645 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
649 // now sort teams by scores.
650 for (i=0 ; i<teamlines ; i++)
652 for (i=0 ; i<teamlines ; i++)
654 for (j=0 ; j<teamlines-1-i ; j++)
656 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
659 teamsort[j] = teamsort[j+1];
672 static void Sbar_SoloScoreboard (void)
675 char str[80], timestr[40];
677 int minutes, seconds;
681 t = (cl.intermission ? cl.completed_time : cl.time);
682 minutes = (int)(t / 60);
683 seconds = (int)(t - 60*floor(t/60));
685 // monsters and secrets are now both on the top row
686 if (cl.stats[STAT_TOTALMONSTERS])
687 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
688 else if (cl.stats[STAT_MONSTERS]) // LA: Display something if monsters_killed is non-zero, but total_monsters is zero
689 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i", cl.stats[STAT_MONSTERS]));
691 if (cl.stats[STAT_TOTALSECRETS])
692 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
693 else if (cl.stats[STAT_SECRETS]) // LA: And similarly for secrets
694 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i", cl.stats[STAT_SECRETS]));
696 // format is like this: e1m1:The Sligpate Complex
697 dpsnprintf(str, sizeof(str), "%s:%s", cl.worldbasename, cl.worldmessage);
699 // if there's a newline character, terminate the string there
700 if (strchr(str, '\n'))
701 *(strchr(str, '\n')) = 0;
703 // make the time string
704 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
706 // truncate the level name if necessary to make room for time
708 if ((int)strlen(str) > max)
711 // print the filename and message
712 Sbar_DrawString(8, 12, str);
715 Sbar_DrawString(8 + max*8, 12, timestr);
719 int minutes, seconds, tens, units;
722 if (IS_OLDNEXUIZ_DERIVED(gamemode)) {
723 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
724 Sbar_DrawString (8, 4, str);
726 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
727 Sbar_DrawString (8, 12, str);
731 minutes = (int)(cl.time / 60);
732 seconds = (int)(cl.time - 60*minutes);
734 units = seconds - 10*tens;
735 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
736 Sbar_DrawString (184, 4, str);
739 if (IS_OLDNEXUIZ_DERIVED(gamemode)) {
740 l = (int) strlen (cl.worldname);
741 Sbar_DrawString (232 - l*4, 12, cl.worldname);
743 l = (int) strlen (cl.worldmessage);
744 Sbar_DrawString (232 - l*4, 12, cl.worldmessage);
754 static void Sbar_DrawScoreboard (void)
756 Sbar_SoloScoreboard ();
757 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
758 //if (cl.gametype == GAME_DEATHMATCH)
760 Sbar_DeathmatchOverlay ();
763 //=============================================================================
765 // AK to make DrawInventory smaller
766 static void Sbar_DrawWeapon(int nr, float fade, int active)
769 if (sbar_hudselector.integer == 1)
771 // width = 300, height = 100
772 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
774 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);
776 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);
780 // width = 300, height = 100
781 const int w_width = 300, w_height = 100, w_space = 10;
782 const float w_scale = 0.4;
784 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);
785 //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);
794 static void Sbar_DrawInventory (void)
801 if (gamemode == GAME_ROGUE)
803 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
804 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
806 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
809 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
812 for (i=0 ; i<7 ; i++)
814 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
816 time = cl.item_gettime[i];
817 flashon = (int)(max(0, cl.time - time)*10);
820 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
826 flashon = (flashon%5) + 2;
828 Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
834 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
836 int grenadeflashing=0;
837 for (i=0 ; i<4 ; i++)
839 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
841 time = max(0, cl.item_gettime[hipweapons[i]]);
842 flashon = (int)((cl.time - time)*10);
845 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
851 flashon = (flashon%5) + 2;
853 // check grenade launcher
856 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
861 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
867 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
869 if (!grenadeflashing)
870 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
873 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
876 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
881 if (gamemode == GAME_ROGUE)
883 // check for powered up weapon.
884 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
886 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
887 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
891 for (i=0 ; i<4 ; i++)
893 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
895 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
897 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
899 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
901 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
905 for (i=0 ; i<6 ; i++)
906 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
908 //MED 01/04/97 changed keys
909 if (!(gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH) || (i>1))
910 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
913 //MED 01/04/97 added hipnotic items
915 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
917 for (i=0 ; i<2 ; i++)
918 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
919 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
922 if (gamemode == GAME_ROGUE)
925 for (i=0 ; i<2 ; i++)
926 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
927 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
932 for (i=0 ; i<4 ; i++)
933 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
934 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
938 //=============================================================================
945 static void Sbar_DrawFrags (void)
955 l = min(scoreboardlines, 4);
959 for (i = 0;i < l;i++)
965 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
966 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);
967 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
968 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);
972 dpsnprintf (num, sizeof(num), "%3i",f);
974 if (k == cl.viewentity - 1)
976 Sbar_DrawCharacter ( x + 2, -24, 16);
977 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
979 Sbar_DrawCharacter (x + 8, -24, num[0]);
980 Sbar_DrawCharacter (x + 16, -24, num[1]);
981 Sbar_DrawCharacter (x + 24, -24, num[2]);
986 //=============================================================================
994 static void Sbar_DrawFace (void)
998 // PGM 01/19/97 - team color drawing
999 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
1000 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
1006 s = &cl.scores[cl.viewentity - 1];
1008 Sbar_DrawPic (112, 0, rsb_teambord);
1009 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1010 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);
1011 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1012 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);
1016 dpsnprintf (num, sizeof(num), "%3i",f);
1018 if ((s->colors & 0xf0)==0)
1021 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1023 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1025 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1029 Sbar_DrawCharacter ( 109, 3, num[0]);
1030 Sbar_DrawCharacter ( 116, 3, num[1]);
1031 Sbar_DrawCharacter ( 123, 3, num[2]);
1036 // PGM 01/19/97 - team color drawing
1038 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1039 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1040 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1041 Sbar_DrawPic (112, 0, sb_face_quad );
1042 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1043 Sbar_DrawPic (112, 0, sb_face_invis );
1044 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1045 Sbar_DrawPic (112, 0, sb_face_invuln);
1048 f = cl.stats[STAT_HEALTH] / 20;
1050 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1053 double topspeed = 0;
1054 double topspeedxy = 0;
1055 time_t current_time = 3;
1056 time_t top_time = 0;
1057 time_t topxy_time = 0;
1059 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1062 unitnumber = cl_showspeed.integer;
1067 if(IS_NEXUIZ_DERIVED(gamemode))
1071 *conversion_factor = 1.0;
1075 *conversion_factor = 0.0254;
1076 if(!IS_NEXUIZ_DERIVED(gamemode))
1077 *conversion_factor *= 1.5;
1078 // 1qu=1.5in is for non-Nexuiz/Xonotic only - Nexuiz/Xonotic players are overly large, but 1qu=1in fixes that
1082 *conversion_factor = 0.0254 * 3.6;
1083 if(!IS_NEXUIZ_DERIVED(gamemode))
1084 *conversion_factor *= 1.5;
1088 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1089 if(!IS_NEXUIZ_DERIVED(gamemode))
1090 *conversion_factor *= 1.5;
1094 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1095 if(!IS_NEXUIZ_DERIVED(gamemode))
1096 *conversion_factor *= 1.5;
1101 static double showfps_nexttime = 0, showfps_lasttime = -1;
1102 static double showfps_framerate = 0;
1103 static int showfps_framecount = 0;
1105 void Sbar_ShowFPS_Update(void)
1107 double interval = 1;
1110 if (newtime >= showfps_nexttime)
1112 showfps_framerate = showfps_framecount / (newtime - showfps_lasttime);
1113 if (showfps_nexttime < newtime - interval * 1.5)
1114 showfps_nexttime = newtime;
1115 showfps_lasttime = newtime;
1116 showfps_nexttime += interval;
1117 showfps_framecount = 0;
1119 showfps_framecount++;
1122 void Sbar_ShowFPS(void)
1124 float fps_x, fps_y, fps_scalex, fps_scaley, fps_strings = 0;
1125 char soundstring[32];
1127 char timestring[32];
1128 char datestring[32];
1129 char timedemostring1[32];
1130 char timedemostring2[32];
1131 char speedstring[32];
1132 char blurstring[32];
1133 char topspeedstring[48];
1134 char texstring[MAX_QPATH];
1136 qboolean red = false;
1139 timedemostring1[0] = 0;
1140 timedemostring2[0] = 0;
1145 topspeedstring[0] = 0;
1148 if (cl_showfps.integer)
1150 red = (showfps_framerate < 1.0f);
1151 if(cl_showfps.integer == 2)
1152 dpsnprintf(fpsstring, sizeof(fpsstring), "%7.3f mspf", (1000.0 / showfps_framerate));
1154 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0 / showfps_framerate + 0.5));
1156 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(showfps_framerate + 0.5));
1160 dpsnprintf(timedemostring1, sizeof(timedemostring1), "frame%4i %f", cls.td_frames, realtime - cls.td_starttime);
1161 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);
1166 if (cl_showtime.integer)
1168 strlcpy(timestring, Sys_TimeString(cl_showtime_format.string), sizeof(timestring));
1171 if (cl_showdate.integer)
1173 strlcpy(datestring, Sys_TimeString(cl_showdate_format.string), sizeof(datestring));
1176 if (cl_showblur.integer)
1178 dpsnprintf(blurstring, sizeof(blurstring), "%3i%% blur", (int)(cl.motionbluralpha * 100));
1181 if (cl_showsound.integer)
1183 dpsnprintf(soundstring, sizeof(soundstring), "%4i/4%i at %3ims", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1186 if (cl_showspeed.integer || cl_showtopspeed.integer)
1188 double speed, speedxy, f;
1190 speed = VectorLength(cl.movement_velocity);
1191 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1192 if (cl_showspeed.integer)
1194 get_showspeed_unit(cl_showspeed.integer, &f, &unit);
1195 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1198 if (cl_showtopspeed.integer)
1200 qboolean topspeed_latched = false, topspeedxy_latched = false;
1201 get_showspeed_unit(cl_showtopspeed.integer, &f, &unit);
1202 if (speed >= topspeed || current_time - top_time > 3)
1208 topspeed_latched = true;
1209 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1211 topspeedxy = speedxy;
1215 topspeedxy_latched = true;
1216 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1217 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1218 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1220 time(¤t_time);
1224 if (cl_showtex.integer)
1229 trace_t svtrace, cltrace;
1230 int hitnetentity = -1;
1232 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, org);
1233 VectorSet(temp, 65536, 0, 0);
1234 Matrix4x4_Transform(&r_refdef.view.matrix, temp, dest);
1235 // 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
1236 memset(&svtrace, 0, sizeof(svtrace));
1237 memset(&cltrace, 0, sizeof(cltrace));
1238 svtrace.fraction = 2.0;
1239 cltrace.fraction = 2.0;
1240 // ray hits models (even animated ones) and ignores translucent materials
1241 if (SVVM_prog != NULL)
1242 svtrace = SV_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value);
1243 cltrace = CL_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, &hitnetentity, true, true);
1244 if (cltrace.hittexture)
1245 strlcpy(texstring, cltrace.hittexture->name, sizeof(texstring));
1247 strlcpy(texstring, "(no texture hit)", sizeof(texstring));
1249 if (svtrace.fraction < cltrace.fraction)
1251 if (svtrace.ent != NULL)
1253 prvm_prog_t *prog = SVVM_prog;
1254 dpsnprintf(entstring, sizeof(entstring), "server entity %i", (int)PRVM_EDICT_TO_PROG(svtrace.ent));
1257 strlcpy(entstring, "(no entity hit)", sizeof(entstring));
1261 if (CLVM_prog != NULL && cltrace.ent != NULL)
1263 prvm_prog_t *prog = CLVM_prog;
1264 dpsnprintf(entstring, sizeof(entstring), "client entity %i", (int)PRVM_EDICT_TO_PROG(cltrace.ent));
1266 else if (hitnetentity > 0)
1267 dpsnprintf(entstring, sizeof(entstring), "network entity %i", hitnetentity);
1268 else if (hitnetentity == 0)
1269 strlcpy(entstring, "world entity", sizeof(entstring));
1271 strlcpy(entstring, "(no entity hit)", sizeof(entstring));
1279 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1280 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_strings*fps_scaley);
1281 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_strings*fps_scaley;
1284 fps_x = vid_conwidth.integer - DrawQ_TextWidth(soundstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1285 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1286 DrawQ_String(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1287 fps_y += fps_scaley;
1291 r_draw2d_force = true;
1292 fps_x = vid_conwidth.integer - DrawQ_TextWidth(fpsstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1293 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1295 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1297 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1298 fps_y += fps_scaley;
1299 r_draw2d_force = false;
1301 if (timedemostring1[0])
1303 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring1, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1304 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1305 DrawQ_String(fps_x, fps_y, timedemostring1, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1306 fps_y += fps_scaley;
1308 if (timedemostring2[0])
1310 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring2, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1311 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1312 DrawQ_String(fps_x, fps_y, timedemostring2, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1313 fps_y += fps_scaley;
1317 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1318 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1319 DrawQ_String(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1320 fps_y += fps_scaley;
1324 fps_x = vid_conwidth.integer - DrawQ_TextWidth(datestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1325 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1326 DrawQ_String(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1327 fps_y += fps_scaley;
1331 fps_x = vid_conwidth.integer - DrawQ_TextWidth(speedstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1332 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1333 DrawQ_String(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1334 fps_y += fps_scaley;
1336 if (topspeedstring[0])
1338 fps_x = vid_conwidth.integer - DrawQ_TextWidth(topspeedstring, 0, fps_scalex, fps_scaley, false, FONT_INFOBAR);
1339 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1340 DrawQ_String(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1341 fps_y += fps_scaley;
1345 fps_x = vid_conwidth.integer - DrawQ_TextWidth(blurstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1346 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1347 DrawQ_String(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1348 fps_y += fps_scaley;
1352 fps_x = vid_conwidth.integer - DrawQ_TextWidth(texstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1353 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1354 DrawQ_String(fps_x, fps_y, texstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1355 fps_y += fps_scaley;
1359 fps_x = vid_conwidth.integer - DrawQ_TextWidth(entstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1360 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1361 DrawQ_String(fps_x, fps_y, entstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1362 fps_y += fps_scaley;
1367 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)
1370 c2 = bound(0, c2, 1);
1371 c1 = bound(0, c1, 1 - c2);
1373 r[1] = rangey + rangeheight * (c2 + c1);
1374 r[2] = rangey + rangeheight * (c2);
1378 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);
1380 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);
1382 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);
1384 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);
1392 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1393 extern cvar_t v_kicktime;
1394 void Sbar_Score (int margin);
1395 void Sbar_Draw (void)
1400 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1403 Sbar_DrawScoreboard ();
1404 else if (cl.intermission == 1)
1406 if(IS_OLDNEXUIZ_DERIVED(gamemode)) // display full scoreboard (that is, show scores + map name)
1408 Sbar_DrawScoreboard();
1411 Sbar_IntermissionOverlay();
1413 else if (cl.intermission == 2)
1414 Sbar_FinaleOverlay();
1415 else if (gamemode == GAME_DELUXEQUAKE)
1418 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
1420 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1422 sbar_x = (vid_conwidth.integer - 640)/2;
1423 sbar_y = vid_conheight.integer - 47;
1424 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1425 Sbar_DrawScoreboard ();
1427 else if (sb_lines && sbar_hudselector.integer == 1)
1431 int redflag, blueflag;
1434 sbar_x = (vid_conwidth.integer - 320)/2;
1435 sbar_y = vid_conheight.integer - 24 - 16;
1437 // calculate intensity to draw weapons bar at
1438 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1439 fade = bound(0.7, fade, 1);
1440 for (i = 0; i < 8;i++)
1441 if (cl.stats[STAT_ITEMS] & (1 << i))
1442 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1443 if((cl.stats[STAT_ITEMS] & (1<<12)))
1444 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1447 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1448 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1449 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1450 if (redflag == 3 && blueflag == 3)
1452 // The Impossible Combination[tm]
1453 // Can only happen in Key Hunt mode...
1454 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1459 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1461 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1465 if (cl.stats[STAT_ARMOR] > 0)
1467 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1468 if(cl.stats[STAT_ARMOR] > 200)
1469 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1470 else if(cl.stats[STAT_ARMOR] > 100)
1471 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1472 else if(cl.stats[STAT_ARMOR] > 50)
1473 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1474 else if(cl.stats[STAT_ARMOR] > 25)
1475 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1477 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1481 if (cl.stats[STAT_HEALTH] != 0)
1483 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1484 if(cl.stats[STAT_HEALTH] > 200)
1485 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1486 else if(cl.stats[STAT_HEALTH] > 100)
1487 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1488 else if(cl.stats[STAT_HEALTH] > 50)
1489 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1490 else if(cl.stats[STAT_HEALTH] > 25)
1491 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1493 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1497 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1499 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1500 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1501 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1502 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1503 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1504 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1505 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1506 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1507 if(cl.stats[STAT_AMMO] > 10)
1508 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1510 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1513 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1514 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1517 // The margin can be at most 8 to support 640x480 console size:
1518 // 320 + 2 * (144 + 16) = 640
1524 int redflag, blueflag;
1527 sbar_x = (vid_conwidth.integer - 640)/2;
1528 sbar_y = vid_conheight.integer - 47;
1530 // calculate intensity to draw weapons bar at
1531 fade = 3 - 2 * (cl.time - cl.weapontime);
1534 fade = min(fade, 1);
1535 for (i = 0; i < 8;i++)
1536 if (cl.stats[STAT_ITEMS] & (1 << i))
1537 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1539 if((cl.stats[STAT_ITEMS] & (1<<12)))
1540 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1543 //if (!cl.islocalgame)
1544 // Sbar_DrawFrags ();
1547 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1549 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1552 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1553 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1554 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1555 if (redflag == 3 && blueflag == 3)
1557 // The Impossible Combination[tm]
1558 // Can only happen in Key Hunt mode...
1559 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1564 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1566 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1570 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1573 if(cl.stats[STAT_HEALTH] > 100)
1574 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1575 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1576 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1578 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1580 // AK dont draw ammo for the laser
1581 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1583 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1584 Sbar_DrawPic (519, 0, sb_ammo[0]);
1585 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1586 Sbar_DrawPic (519, 0, sb_ammo[1]);
1587 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1588 Sbar_DrawPic (519, 0, sb_ammo[2]);
1589 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1590 Sbar_DrawPic (519, 0, sb_ammo[3]);
1592 if(cl.stats[STAT_AMMO] <= 10)
1593 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1595 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1600 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1602 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1603 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1608 // Mini scoreboard uses 12*4 per other team, that is, 144
1609 // pixels when there are four teams...
1610 // Nexuiz by default sets vid_conwidth to 800... makes
1612 // so we need to shift it by 64 pixels to the right to fit
1613 // BUT: then it overlaps with the image that gets drawn
1614 // for viewsize 100! Therefore, just account for 3 teams,
1615 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1619 else if (gamemode == GAME_ZYMOTIC)
1622 float scale = 64.0f / 256.0f;
1623 float kickoffset[3];
1624 VectorClear(kickoffset);
1627 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1628 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1630 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1631 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1632 // left1 16, 48 : 126 -66
1633 // left2 16, 128 : 196 -66
1634 // right 176, 48 : 196 -136
1635 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);
1636 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);
1637 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);
1638 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1640 float scale = 128.0f / 256.0f;
1641 float healthstart, healthheight, healthstarttc, healthendtc;
1642 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1643 float ammostart, ammoheight, ammostarttc, ammoendtc;
1644 float clipstart, clipheight, clipstarttc, clipendtc;
1645 float kickoffset[3], offset;
1646 VectorClear(kickoffset);
1649 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1650 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1652 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1653 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1654 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1655 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);
1656 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);
1657 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);
1658 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);
1659 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1660 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1661 healthstart = 204 - healthheight;
1662 shieldstart = healthstart - shieldheight;
1663 healthstarttc = healthstart * (1.0f / 256.0f);
1664 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1665 shieldstarttc = shieldstart * (1.0f / 256.0f);
1666 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1667 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1668 ammostart = 114 - ammoheight;
1669 ammostarttc = ammostart * (1.0f / 256.0f);
1670 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1671 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1672 clipstart = 190 - clipheight;
1673 clipstarttc = clipstart * (1.0f / 256.0f);
1674 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1675 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);
1676 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);
1677 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);
1678 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);
1679 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1680 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1683 else // Quake and others
1685 sbar_x = (vid_conwidth.integer - 320)/2;
1686 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1687 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1688 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1692 if (gamemode != GAME_GOODVSBAD2)
1693 Sbar_DrawInventory ();
1694 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1698 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1700 if (gamemode != GAME_GOODVSBAD2)
1701 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1702 Sbar_DrawScoreboard ();
1706 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1708 // keys (hipnotic only)
1709 //MED 01/04/97 moved keys here so they would not be overwritten
1710 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
1712 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1713 Sbar_DrawPic (209, 3, sb_items[0]);
1714 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1715 Sbar_DrawPic (209, 12, sb_items[1]);
1718 if (gamemode != GAME_GOODVSBAD2)
1720 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1722 Sbar_DrawNum (24, 0, 666, 3, 1);
1723 Sbar_DrawPic (0, 0, sb_disc);
1727 if (gamemode == GAME_ROGUE)
1729 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1730 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1731 Sbar_DrawPic (0, 0, sb_armor[2]);
1732 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1733 Sbar_DrawPic (0, 0, sb_armor[1]);
1734 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1735 Sbar_DrawPic (0, 0, sb_armor[0]);
1739 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1740 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1741 Sbar_DrawPic (0, 0, sb_armor[2]);
1742 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1743 Sbar_DrawPic (0, 0, sb_armor[1]);
1744 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1745 Sbar_DrawPic (0, 0, sb_armor[0]);
1754 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1757 if (gamemode == GAME_ROGUE)
1759 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1760 Sbar_DrawPic (224, 0, sb_ammo[0]);
1761 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1762 Sbar_DrawPic (224, 0, sb_ammo[1]);
1763 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1764 Sbar_DrawPic (224, 0, sb_ammo[2]);
1765 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1766 Sbar_DrawPic (224, 0, sb_ammo[3]);
1767 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1768 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1769 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1770 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1771 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1772 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1776 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1777 Sbar_DrawPic (224, 0, sb_ammo[0]);
1778 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1779 Sbar_DrawPic (224, 0, sb_ammo[1]);
1780 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1781 Sbar_DrawPic (224, 0, sb_ammo[2]);
1782 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1783 Sbar_DrawPic (224, 0, sb_ammo[3]);
1786 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1788 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1789 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1791 if (gamemode == GAME_TRANSFUSION)
1792 Sbar_MiniDeathmatchOverlay (0, 0);
1794 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1801 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1803 pic = Draw_CachePic (va(vabuf, sizeof(vabuf), "gfx/crosshair%i", crosshair.integer));
1804 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);
1807 if (cl_prydoncursor.integer > 0)
1808 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);
1811 //=============================================================================
1815 Sbar_DeathmatchOverlay
1819 static float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1822 qboolean myself = false;
1825 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1827 if((s - cl.scores) == cl.playerentity - 1)
1829 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1830 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1833 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1835 if (s->qw_spectator)
1837 if (s->qw_ping || s->qw_packetloss)
1838 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 );
1840 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 );
1844 // draw colors behind score
1850 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1851 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);
1852 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1853 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);
1855 //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);
1856 if (s->qw_ping || s->qw_packetloss)
1857 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 );
1859 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 );
1864 if (s->qw_spectator)
1866 if (s->qw_ping || s->qw_packetloss)
1867 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 );
1869 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 );
1873 // draw colors behind score
1874 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1875 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);
1876 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1877 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);
1879 //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);
1880 if (s->qw_ping || s->qw_packetloss)
1881 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 );
1883 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 );
1889 void Sbar_DeathmatchOverlay (void)
1891 int i, y, xmin, xmax, ymin, ymax;
1894 // request new ping times every two second
1895 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1897 cl.last_ping_request = realtime;
1898 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1900 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1901 MSG_WriteString(&cls.netcon->message, "pings");
1903 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*/)
1905 // 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
1906 static int ping_anyway_counter = 0;
1907 if(cl.parsingtextexpectingpingforscores == 1)
1909 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1910 if(++ping_anyway_counter >= 5)
1911 cl.parsingtextexpectingpingforscores = 0;
1913 if(cl.parsingtextexpectingpingforscores != 1)
1915 ping_anyway_counter = 0;
1916 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1917 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1918 MSG_WriteString(&cls.netcon->message, "ping");
1923 // 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
1924 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1925 MSG_WriteString(&cls.netcon->message, "pings");
1933 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1935 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1936 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)
1938 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)
1939 xmax = vid_conwidth.integer - xmin;
1941 if(IS_OLDNEXUIZ_DERIVED(gamemode))
1942 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);
1944 DrawQ_Pic ((vid_conwidth.integer - Draw_GetPicWidth(sb_ranking))/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1948 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1950 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 );
1954 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 );
1958 if (Sbar_IsTeammatch ())
1960 // show team scores first
1961 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1962 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1966 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1967 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1972 Sbar_MiniDeathmatchOverlay
1976 void Sbar_MiniDeathmatchOverlay (int x, int y)
1978 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1980 // do not draw this if sbar_miniscoreboard_size is zero
1981 if(sbar_miniscoreboard_size.value == 0)
1983 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1984 if(sbar_miniscoreboard_size.value > 0)
1985 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1990 // decide where to print
1991 if (gamemode == GAME_TRANSFUSION)
1992 numlines = (vid_conwidth.integer - x + 127) / 128;
1994 numlines = (vid_conheight.integer - y + 7) / 8;
1996 // give up if there isn't room
1997 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
2001 for (i = 0; i < scoreboardlines; i++)
2002 if (fragsort[i] == cl.playerentity - 1)
2006 range_end = scoreboardlines;
2009 if (gamemode != GAME_TRANSFUSION)
2010 if (Sbar_IsTeammatch ())
2012 // reserve space for the team scores
2013 numlines -= teamlines;
2015 // find first and last player of my team (only draw the team totals and my own team)
2016 range_begin = range_end = i;
2017 myteam = cl.scores[fragsort[i]].colors & 15;
2018 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
2020 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
2023 // looks better than two players
2033 i = min(i, range_end - numlines);
2034 i = max(i, range_begin);
2036 if (gamemode == GAME_TRANSFUSION)
2038 for (;i < range_end && x < vid_conwidth.integer;i++)
2039 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2043 if(range_end - i < numlines) // won't draw to bottom?
2044 y += 8 * (numlines - (range_end - i)); // bottom align
2045 // show team scores first
2046 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
2047 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
2049 for (;i < range_end && y < vid_conheight.integer;i++)
2050 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2054 static int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
2056 static int const sortorder[16] =
2075 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
2076 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
2077 int tc1 = sortorder[t1->colors & 15];
2078 int tc2 = sortorder[t2->colors & 15];
2082 void Sbar_Score (int margin)
2084 int i, me, score, otherleader, place, distribution, minutes, seconds;
2086 int sbar_x_save = sbar_x;
2087 int sbar_y_save = sbar_y;
2090 sbar_y = (int) (vid_conheight.value - (32+12));
2093 me = cl.playerentity - 1;
2094 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2096 if(Sbar_IsTeammatch())
2100 // team1 team3 team4
2104 scoreboard_t *teamcolorsort[16];
2107 for(i = 0; i < teamlines; ++i)
2108 teamcolorsort[i] = &(teams[i]);
2110 // Now sort them by color
2111 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2114 // -12*4: four digits space
2115 place = (teamlines - 1) * (-12 * 4);
2117 for(i = 0; i < teamlines; ++i)
2119 int cindex = teamcolorsort[i]->colors & 15;
2120 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2121 float cm = max(max(c[0], c[1]), c[2]);
2122 float cr = c[0] / cm;
2123 float cg = c[1] / cm;
2124 float cb = c[2] / cm;
2125 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2127 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2131 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2144 // find leading score other than ourselves, to calculate distribution
2145 // find our place in the scoreboard
2146 score = cl.scores[me].frags;
2147 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2149 if (cl.scores[i].name[0] && i != me)
2151 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2153 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2157 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2159 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2160 else if (place == 2)
2161 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2163 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2164 if (otherleader < 0)
2165 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2166 if (distribution >= 0)
2168 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2169 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2171 else if (distribution >= -5)
2173 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2174 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2178 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2179 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2184 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2186 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2187 minutes = (int)floor(timeleft / 60);
2188 seconds = (int)(floor(timeleft) - minutes * 60);
2191 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2192 if (Draw_IsPicLoaded(sb_colon))
2193 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2194 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2196 else if (minutes >= 1)
2198 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2199 if (Draw_IsPicLoaded(sb_colon))
2200 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2201 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2203 else if ((int)(timeleft * 4) & 1)
2204 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2206 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2208 else if (sbar_gametime.integer)
2210 minutes = (int)floor(cl.time / 60);
2211 seconds = (int)(floor(cl.time) - minutes * 60);
2212 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2213 if (Draw_IsPicLoaded(sb_colon))
2214 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2215 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2218 sbar_x = sbar_x_save;
2219 sbar_y = sbar_y_save;
2224 Sbar_IntermissionOverlay
2228 void Sbar_IntermissionOverlay (void)
2233 if (cl.gametype == GAME_DEATHMATCH)
2235 Sbar_DeathmatchOverlay ();
2239 sbar_x = (vid_conwidth.integer - 320) >> 1;
2240 sbar_y = (vid_conheight.integer - 200) >> 1;
2242 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2243 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2246 dig = (int)cl.completed_time / 60;
2247 Sbar_DrawNum (160, 64, dig, 3, 0);
2248 num = (int)cl.completed_time - dig*60;
2249 Sbar_DrawPic (234,64,sb_colon);
2250 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2251 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2253 // LA: Display as "a" instead of "a/b" if b is 0
2254 if(cl.stats[STAT_TOTALSECRETS])
2256 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2257 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2258 Sbar_DrawPic (232, 104, sb_slash);
2259 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2263 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2266 if(cl.stats[STAT_TOTALMONSTERS])
2268 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2269 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2270 Sbar_DrawPic (232, 144, sb_slash);
2271 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2275 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2286 void Sbar_FinaleOverlay (void)
2288 DrawQ_Pic((vid_conwidth.integer - Draw_GetPicWidth(sb_finale))/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);