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 sbar_alpha_bg = {CVAR_CLIENT | CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
100 cvar_t sbar_alpha_fg = {CVAR_CLIENT | CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
101 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)"};
102 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"};
103 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)"};
104 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"};
105 cvar_t sbar_flagstatus_right = {CVAR_CLIENT | CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
106 cvar_t sbar_flagstatus_pos = {CVAR_CLIENT | CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
107 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"};
109 cvar_t cl_deathscoreboard = {CVAR_CLIENT, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
111 cvar_t crosshair_color_red = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
112 cvar_t crosshair_color_green = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
113 cvar_t crosshair_color_blue = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
114 cvar_t crosshair_color_alpha = {CVAR_CLIENT | CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
115 cvar_t crosshair_size = {CVAR_CLIENT | CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
117 static void Sbar_MiniDeathmatchOverlay (int x, int y);
118 static void Sbar_DeathmatchOverlay (void);
119 static void Sbar_IntermissionOverlay (void);
120 static void Sbar_FinaleOverlay (void);
131 static void Sbar_ShowScores_f(cmd_state_t *cmd)
135 sb_showscores = true;
136 CL_VM_UpdateShowingScoresState(sb_showscores);
146 static void Sbar_DontShowScores_f(cmd_state_t *cmd)
148 sb_showscores = false;
149 CL_VM_UpdateShowingScoresState(sb_showscores);
152 static void sbar_start(void)
157 if (gamemode == GAME_DELUXEQUAKE || gamemode == GAME_BLOODOMNICIDE)
160 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
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);
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);
172 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor", CACHEPICFLAG_QUIET);
176 sb_health = Draw_CachePic_Flags ("gfx/sb_health", CACHEPICFLAG_QUIET);
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);
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);
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);
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);
198 else if (gamemode == GAME_ZYMOTIC)
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);
212 sb_disc = Draw_CachePic_Flags ("gfx/disc", CACHEPICFLAG_QUIET);
214 for (i = 0;i < 10;i++)
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);
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);
223 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET | CACHEPICFLAG_FAILONMISSING);
224 sb_slash = Draw_CachePic_Flags ("gfx/num_slash", CACHEPICFLAG_QUIET);
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);
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);
242 for (i = 0;i < 5;i++)
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);
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);
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);
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);
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);
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);
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);
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);
294 //MED 01/04/97 added new hipnotic weapons
295 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
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);
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);
309 for (i = 0;i < 5;i++)
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);
318 hsb_items[0] = Draw_CachePic_Flags ("gfx/sb_wsuit", CACHEPICFLAG_QUIET);
319 hsb_items[1] = Draw_CachePic_Flags ("gfx/sb_eshld", CACHEPICFLAG_QUIET);
321 else if (gamemode == GAME_ROGUE)
323 rsb_invbar[0] = Draw_CachePic_Flags ("gfx/r_invbar1", CACHEPICFLAG_QUIET);
324 rsb_invbar[1] = Draw_CachePic_Flags ("gfx/r_invbar2", CACHEPICFLAG_QUIET);
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);
332 rsb_items[0] = Draw_CachePic_Flags ("gfx/r_shield1", CACHEPICFLAG_QUIET);
333 rsb_items[1] = Draw_CachePic_Flags ("gfx/r_agrav1", CACHEPICFLAG_QUIET);
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
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);
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);
351 static void sbar_shutdown(void)
355 static void sbar_newmap(void)
359 void Sbar_Init (void)
361 if(gamemode == GAME_NORMAL) // Workaround so Quake doesn't trample on Xonotic.
363 Cmd_AddCommand(CMD_CLIENT, "+showscores", Sbar_ShowScores_f, "show scoreboard");
364 Cmd_AddCommand(CMD_CLIENT, "-showscores", Sbar_DontShowScores_f, "hide scoreboard");
366 Cvar_RegisterVariable(&cl_showfps);
367 Cvar_RegisterVariable(&cl_showsound);
368 Cvar_RegisterVariable(&cl_showblur);
369 Cvar_RegisterVariable(&cl_showspeed);
370 Cvar_RegisterVariable(&cl_showtopspeed);
371 Cvar_RegisterVariable(&cl_showtime);
372 Cvar_RegisterVariable(&cl_showtime_format);
373 Cvar_RegisterVariable(&cl_showdate);
374 Cvar_RegisterVariable(&cl_showdate_format);
375 Cvar_RegisterVariable(&cl_showtex);
377 Cvar_RegisterAlias(&cl_showfps, "showfps");
378 Cvar_RegisterAlias(&cl_showsound, "showsound");
379 Cvar_RegisterAlias(&cl_showblur, "showblur");
380 Cvar_RegisterAlias(&cl_showspeed, "showspeed");
381 Cvar_RegisterAlias(&cl_showtopspeed, "showtopspeed");
382 Cvar_RegisterAlias(&cl_showtime, "showtime");
383 Cvar_RegisterAlias(&cl_showtime_format, "showtime_format");
384 Cvar_RegisterAlias(&cl_showdate, "showdate");
385 Cvar_RegisterAlias(&cl_showdate_format, "showdate_format");
386 Cvar_RegisterAlias(&cl_showtex, "showtex");
388 Cvar_RegisterVariable(&sbar_alpha_bg);
389 Cvar_RegisterVariable(&sbar_alpha_fg);
390 Cvar_RegisterVariable(&sbar_hudselector);
391 Cvar_RegisterVariable(&sbar_scorerank);
392 Cvar_RegisterVariable(&sbar_gametime);
393 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
394 Cvar_RegisterVariable(&sbar_info_pos);
395 Cvar_RegisterVariable(&cl_deathscoreboard);
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);
403 Cvar_RegisterVariable(&sbar_flagstatus_right); // (GAME_NEXUZI ONLY)
404 Cvar_RegisterVariable(&sbar_flagstatus_pos); // (GAME_NEXUIZ ONLY)
406 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap, NULL, NULL);
410 //=============================================================================
412 // drawing routines are relative to the status bar location
421 static void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
423 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
426 static void Sbar_DrawPic (int x, int y, cachepic_t *pic)
428 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
431 static void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
433 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
440 Draws one solid graphics character
443 static void Sbar_DrawCharacter (int x, int y, int num)
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);
454 static void Sbar_DrawString (int x, int y, char *str)
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);
464 static void Sbar_DrawNum (int x, int y, int num, int digits, int color)
469 l = dpsnprintf(str, sizeof(str), "%i", num);
483 Sbar_DrawPic (x, y, sb_nums[color][frame]);
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)
504 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
507 l = dpsnprintf(str, sizeof(str), "%i", num);
512 x += (digits-l) * lettersize;
521 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
528 //=============================================================================
531 static int Sbar_IsTeammatch(void)
533 // currently only nexuiz uses the team score board
534 return (IS_OLDNEXUIZ_DERIVED(gamemode)
535 && (teamplay.integer > 0));
543 static int fragsort[MAX_SCOREBOARD];
544 static int scoreboardlines;
546 int Sbar_GetSortedPlayerIndex (int index)
548 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
551 static scoreboard_t teams[MAX_SCOREBOARD];
552 static int teamsort[MAX_SCOREBOARD];
553 static int teamlines;
554 void Sbar_SortFrags (void)
560 for (i=0 ; i<cl.maxclients ; i++)
562 if (cl.scores[i].name[0])
564 fragsort[scoreboardlines] = i;
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)
574 fragsort[j] = fragsort[j+1];
579 if (Sbar_IsTeammatch ())
581 // now sort players by teams.
582 for (i=0 ; i<scoreboardlines ; i++)
584 for (j=0 ; j<scoreboardlines-1-i ; j++)
586 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
589 fragsort[j] = fragsort[j+1];
595 // calculate team scores
597 for (i=0 ; i<scoreboardlines ; i++)
599 if (color != (cl.scores[fragsort[i]].colors & 15))
601 const char* teamname;
603 color = cl.scores[fragsort[i]].colors & 15;
609 teamname = "^1Red Team";
612 teamname = "^4Blue Team";
615 teamname = "^6Pink Team";
618 teamname = "^3Yellow Team";
621 teamname = "Total Team Score";
624 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
626 teams[teamlines-1].frags = 0;
627 teams[teamlines-1].colors = color + 16 * color;
630 if (cl.scores[fragsort[i]].frags != -666)
632 // do not add spedcators
633 // (ugly hack for nexuiz)
634 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
638 // now sort teams by scores.
639 for (i=0 ; i<teamlines ; i++)
641 for (i=0 ; i<teamlines ; i++)
643 for (j=0 ; j<teamlines-1-i ; j++)
645 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
648 teamsort[j] = teamsort[j+1];
661 static void Sbar_SoloScoreboard (void)
664 char str[80], timestr[40];
666 int minutes, seconds;
670 t = (cl.intermission ? cl.completed_time : cl.time);
671 minutes = (int)(t / 60);
672 seconds = (int)(t - 60*floor(t/60));
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]));
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]));
685 // format is like this: e1m1:The Sligpate Complex
686 dpsnprintf(str, sizeof(str), "%s:%s", cl.worldbasename, cl.worldmessage);
688 // if there's a newline character, terminate the string there
689 if (strchr(str, '\n'))
690 *(strchr(str, '\n')) = 0;
692 // make the time string
693 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
695 // truncate the level name if necessary to make room for time
697 if ((int)strlen(str) > max)
700 // print the filename and message
701 Sbar_DrawString(8, 12, str);
704 Sbar_DrawString(8 + max*8, 12, timestr);
708 int minutes, seconds, tens, units;
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);
715 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
716 Sbar_DrawString (8, 12, str);
720 minutes = (int)(cl.time / 60);
721 seconds = (int)(cl.time - 60*minutes);
723 units = seconds - 10*tens;
724 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
725 Sbar_DrawString (184, 4, str);
728 if (IS_OLDNEXUIZ_DERIVED(gamemode)) {
729 l = (int) strlen (cl.worldname);
730 Sbar_DrawString (232 - l*4, 12, cl.worldname);
732 l = (int) strlen (cl.worldmessage);
733 Sbar_DrawString (232 - l*4, 12, cl.worldmessage);
743 static void Sbar_DrawScoreboard (void)
745 Sbar_SoloScoreboard ();
746 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
747 //if (cl.gametype == GAME_DEATHMATCH)
749 Sbar_DeathmatchOverlay ();
752 //=============================================================================
754 // AK to make DrawInventory smaller
755 static void Sbar_DrawWeapon(int nr, float fade, int active)
758 if (sbar_hudselector.integer == 1)
760 // width = 300, height = 100
761 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
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);
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);
769 // width = 300, height = 100
770 const int w_width = 300, w_height = 100, w_space = 10;
771 const float w_scale = 0.4;
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);
783 static void Sbar_DrawInventory (void)
790 if (gamemode == GAME_ROGUE)
792 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
793 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
795 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
798 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
801 for (i=0 ; i<7 ; i++)
803 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
805 time = cl.item_gettime[i];
806 flashon = (int)(max(0, cl.time - time)*10);
809 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
815 flashon = (flashon%5) + 2;
817 Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
823 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
825 int grenadeflashing=0;
826 for (i=0 ; i<4 ; i++)
828 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
830 time = max(0, cl.item_gettime[hipweapons[i]]);
831 flashon = (int)((cl.time - time)*10);
834 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
840 flashon = (flashon%5) + 2;
842 // check grenade launcher
845 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
850 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
856 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
858 if (!grenadeflashing)
859 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
862 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
865 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
870 if (gamemode == GAME_ROGUE)
872 // check for powered up weapon.
873 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
875 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
876 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
880 for (i=0 ; i<4 ; i++)
882 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
884 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
886 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
888 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
890 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
894 for (i=0 ; i<6 ; i++)
895 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
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]);
902 //MED 01/04/97 added hipnotic items
904 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
906 for (i=0 ; i<2 ; i++)
907 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
908 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
911 if (gamemode == GAME_ROGUE)
914 for (i=0 ; i<2 ; i++)
915 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
916 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
921 for (i=0 ; i<4 ; i++)
922 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
923 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
927 //=============================================================================
934 static void Sbar_DrawFrags (void)
944 l = min(scoreboardlines, 4);
948 for (i = 0;i < l;i++)
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);
961 dpsnprintf (num, sizeof(num), "%3i",f);
963 if (k == cl.viewentity - 1)
965 Sbar_DrawCharacter ( x + 2, -24, 16);
966 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
968 Sbar_DrawCharacter (x + 8, -24, num[0]);
969 Sbar_DrawCharacter (x + 16, -24, num[1]);
970 Sbar_DrawCharacter (x + 24, -24, num[2]);
975 //=============================================================================
983 static void Sbar_DrawFace (void)
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))
995 s = &cl.scores[cl.viewentity - 1];
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);
1005 dpsnprintf (num, sizeof(num), "%3i",f);
1007 if ((s->colors & 0xf0)==0)
1010 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1012 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1014 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1018 Sbar_DrawCharacter ( 109, 3, num[0]);
1019 Sbar_DrawCharacter ( 116, 3, num[1]);
1020 Sbar_DrawCharacter ( 123, 3, num[2]);
1025 // PGM 01/19/97 - team color drawing
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);
1037 f = cl.stats[STAT_HEALTH] / 20;
1039 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
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;
1048 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1051 unitnumber = cl_showspeed.integer;
1056 if(IS_NEXUIZ_DERIVED(gamemode))
1060 *conversion_factor = 1.0;
1064 *conversion_factor = 0.0254;
1065 if(!IS_NEXUIZ_DERIVED(gamemode))
1066 *conversion_factor *= 1.5;
1067 // 1qu=1.5in is for non-Nexuiz/Xonotic only - Nexuiz/Xonotic players are overly large, but 1qu=1in fixes that
1071 *conversion_factor = 0.0254 * 3.6;
1072 if(!IS_NEXUIZ_DERIVED(gamemode))
1073 *conversion_factor *= 1.5;
1077 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1078 if(!IS_NEXUIZ_DERIVED(gamemode))
1079 *conversion_factor *= 1.5;
1083 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1084 if(!IS_NEXUIZ_DERIVED(gamemode))
1085 *conversion_factor *= 1.5;
1090 static double showfps_nexttime = 0, showfps_lasttime = -1;
1091 static double showfps_framerate = 0;
1092 static int showfps_framecount = 0;
1094 void Sbar_ShowFPS_Update(void)
1096 double interval = 1;
1098 newtime = host.realtime;
1099 if (newtime >= showfps_nexttime)
1101 showfps_framerate = showfps_framecount / (newtime - showfps_lasttime);
1102 if (showfps_nexttime < newtime - interval * 1.5)
1103 showfps_nexttime = newtime;
1104 showfps_lasttime = newtime;
1105 showfps_nexttime += interval;
1106 showfps_framecount = 0;
1108 showfps_framecount++;
1111 void Sbar_ShowFPS(void)
1113 float fps_x, fps_y, fps_scalex, fps_scaley, fps_strings = 0;
1114 char soundstring[32];
1116 char timestring[32];
1117 char datestring[32];
1118 char timedemostring1[32];
1119 char timedemostring2[32];
1120 char speedstring[32];
1121 char blurstring[32];
1122 char topspeedstring[48];
1123 char texstring[MAX_QPATH];
1125 qboolean red = false;
1128 timedemostring1[0] = 0;
1129 timedemostring2[0] = 0;
1134 topspeedstring[0] = 0;
1137 if (cl_showfps.integer)
1139 red = (showfps_framerate < 1.0f);
1140 if(cl_showfps.integer == 2)
1141 dpsnprintf(fpsstring, sizeof(fpsstring), "%7.3f mspf", (1000.0 / showfps_framerate));
1143 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0 / showfps_framerate + 0.5));
1145 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(showfps_framerate + 0.5));
1149 dpsnprintf(timedemostring1, sizeof(timedemostring1), "frame%4i %f", cls.td_frames, host.realtime - cls.td_starttime);
1150 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);
1155 if (cl_showtime.integer)
1157 strlcpy(timestring, Sys_TimeString(cl_showtime_format.string), sizeof(timestring));
1160 if (cl_showdate.integer)
1162 strlcpy(datestring, Sys_TimeString(cl_showdate_format.string), sizeof(datestring));
1165 if (cl_showblur.integer)
1167 dpsnprintf(blurstring, sizeof(blurstring), "%3i%% blur", (int)(cl.motionbluralpha * 100));
1170 if (cl_showsound.integer)
1172 dpsnprintf(soundstring, sizeof(soundstring), "%4i/4%i at %3ims", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1175 if (cl_showspeed.integer || cl_showtopspeed.integer)
1177 double speed, speedxy, f;
1179 speed = VectorLength(cl.movement_velocity);
1180 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1181 if (cl_showspeed.integer)
1183 get_showspeed_unit(cl_showspeed.integer, &f, &unit);
1184 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1187 if (cl_showtopspeed.integer)
1189 qboolean topspeed_latched = false, topspeedxy_latched = false;
1190 get_showspeed_unit(cl_showtopspeed.integer, &f, &unit);
1191 if (speed >= topspeed || current_time - top_time > 3)
1197 topspeed_latched = true;
1198 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1200 topspeedxy = speedxy;
1204 topspeedxy_latched = true;
1205 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1206 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1207 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1209 time(¤t_time);
1213 if (cl_showtex.integer)
1218 trace_t svtrace, cltrace;
1219 int hitnetentity = -1;
1221 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, org);
1222 VectorSet(temp, 65536, 0, 0);
1223 Matrix4x4_Transform(&r_refdef.view.matrix, temp, dest);
1224 // 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
1225 memset(&svtrace, 0, sizeof(svtrace));
1226 memset(&cltrace, 0, sizeof(cltrace));
1227 svtrace.fraction = 2.0;
1228 cltrace.fraction = 2.0;
1229 // ray hits models (even animated ones) and ignores translucent materials
1230 if (SVVM_prog != NULL)
1231 svtrace = SV_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value);
1232 cltrace = CL_TraceLine(org, dest, MOVE_HITMODEL, NULL, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, &hitnetentity, true, true);
1233 if (cltrace.hittexture)
1234 strlcpy(texstring, cltrace.hittexture->name, sizeof(texstring));
1236 strlcpy(texstring, "(no texture hit)", sizeof(texstring));
1238 if (svtrace.fraction < cltrace.fraction)
1240 if (svtrace.ent != NULL)
1242 prvm_prog_t *prog = SVVM_prog;
1243 dpsnprintf(entstring, sizeof(entstring), "server entity %i", (int)PRVM_EDICT_TO_PROG(svtrace.ent));
1246 strlcpy(entstring, "(no entity hit)", sizeof(entstring));
1250 if (CLVM_prog != NULL && cltrace.ent != NULL)
1252 prvm_prog_t *prog = CLVM_prog;
1253 dpsnprintf(entstring, sizeof(entstring), "client entity %i", (int)PRVM_EDICT_TO_PROG(cltrace.ent));
1255 else if (hitnetentity > 0)
1256 dpsnprintf(entstring, sizeof(entstring), "network entity %i", hitnetentity);
1257 else if (hitnetentity == 0)
1258 strlcpy(entstring, "world entity", sizeof(entstring));
1260 strlcpy(entstring, "(no entity hit)", sizeof(entstring));
1268 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1269 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_strings*fps_scaley);
1270 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_strings*fps_scaley;
1273 fps_x = vid_conwidth.integer - DrawQ_TextWidth(soundstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1274 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1275 DrawQ_String(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1276 fps_y += fps_scaley;
1280 r_draw2d_force = true;
1281 fps_x = vid_conwidth.integer - DrawQ_TextWidth(fpsstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1282 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1284 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1286 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1287 fps_y += fps_scaley;
1288 r_draw2d_force = false;
1290 if (timedemostring1[0])
1292 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring1, 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);
1294 DrawQ_String(fps_x, fps_y, timedemostring1, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1295 fps_y += fps_scaley;
1297 if (timedemostring2[0])
1299 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring2, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1300 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1301 DrawQ_String(fps_x, fps_y, timedemostring2, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1302 fps_y += fps_scaley;
1306 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1307 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1308 DrawQ_String(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1309 fps_y += fps_scaley;
1313 fps_x = vid_conwidth.integer - DrawQ_TextWidth(datestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1314 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1315 DrawQ_String(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1316 fps_y += fps_scaley;
1320 fps_x = vid_conwidth.integer - DrawQ_TextWidth(speedstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1321 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1322 DrawQ_String(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1323 fps_y += fps_scaley;
1325 if (topspeedstring[0])
1327 fps_x = vid_conwidth.integer - DrawQ_TextWidth(topspeedstring, 0, fps_scalex, fps_scaley, false, FONT_INFOBAR);
1328 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1329 DrawQ_String(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1330 fps_y += fps_scaley;
1334 fps_x = vid_conwidth.integer - DrawQ_TextWidth(blurstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1335 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1336 DrawQ_String(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1337 fps_y += fps_scaley;
1341 fps_x = vid_conwidth.integer - DrawQ_TextWidth(texstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1342 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1343 DrawQ_String(fps_x, fps_y, texstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1344 fps_y += fps_scaley;
1348 fps_x = vid_conwidth.integer - DrawQ_TextWidth(entstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1349 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1350 DrawQ_String(fps_x, fps_y, entstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1351 fps_y += fps_scaley;
1356 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)
1359 c2 = bound(0, c2, 1);
1360 c1 = bound(0, c1, 1 - c2);
1362 r[1] = rangey + rangeheight * (c2 + c1);
1363 r[2] = rangey + rangeheight * (c2);
1367 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);
1369 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);
1371 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);
1373 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);
1381 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1382 extern cvar_t v_kicktime;
1383 void Sbar_Score (int margin);
1384 void Sbar_Draw (void)
1389 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1392 Sbar_DrawScoreboard ();
1393 else if (cl.intermission == 1)
1395 if(IS_OLDNEXUIZ_DERIVED(gamemode)) // display full scoreboard (that is, show scores + map name)
1397 Sbar_DrawScoreboard();
1400 Sbar_IntermissionOverlay();
1402 else if (cl.intermission == 2)
1403 Sbar_FinaleOverlay();
1404 else if (gamemode == GAME_DELUXEQUAKE)
1407 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
1409 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1411 sbar_x = (vid_conwidth.integer - 640)/2;
1412 sbar_y = vid_conheight.integer - 47;
1413 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1414 Sbar_DrawScoreboard ();
1416 else if (sb_lines && sbar_hudselector.integer == 1)
1420 int redflag, blueflag;
1423 sbar_x = (vid_conwidth.integer - 320)/2;
1424 sbar_y = vid_conheight.integer - 24 - 16;
1426 // calculate intensity to draw weapons bar at
1427 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1428 fade = bound(0.7, fade, 1);
1429 for (i = 0; i < 8;i++)
1430 if (cl.stats[STAT_ITEMS] & (1 << i))
1431 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1432 if((cl.stats[STAT_ITEMS] & (1<<12)))
1433 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1436 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1437 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1438 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1439 if (redflag == 3 && blueflag == 3)
1441 // The Impossible Combination[tm]
1442 // Can only happen in Key Hunt mode...
1443 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1448 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1450 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1454 if (cl.stats[STAT_ARMOR] > 0)
1456 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1457 if(cl.stats[STAT_ARMOR] > 200)
1458 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1459 else if(cl.stats[STAT_ARMOR] > 100)
1460 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1461 else if(cl.stats[STAT_ARMOR] > 50)
1462 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1463 else if(cl.stats[STAT_ARMOR] > 25)
1464 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1466 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1470 if (cl.stats[STAT_HEALTH] != 0)
1472 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1473 if(cl.stats[STAT_HEALTH] > 200)
1474 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1475 else if(cl.stats[STAT_HEALTH] > 100)
1476 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1477 else if(cl.stats[STAT_HEALTH] > 50)
1478 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1479 else if(cl.stats[STAT_HEALTH] > 25)
1480 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1482 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1486 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1488 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1489 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1490 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1491 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1492 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1493 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1494 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1495 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1496 if(cl.stats[STAT_AMMO] > 10)
1497 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1499 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1502 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1503 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1506 // The margin can be at most 8 to support 640x480 console size:
1507 // 320 + 2 * (144 + 16) = 640
1513 int redflag, blueflag;
1516 sbar_x = (vid_conwidth.integer - 640)/2;
1517 sbar_y = vid_conheight.integer - 47;
1519 // calculate intensity to draw weapons bar at
1520 fade = 3 - 2 * (cl.time - cl.weapontime);
1523 fade = min(fade, 1);
1524 for (i = 0; i < 8;i++)
1525 if (cl.stats[STAT_ITEMS] & (1 << i))
1526 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1528 if((cl.stats[STAT_ITEMS] & (1<<12)))
1529 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1532 //if (!cl.islocalgame)
1533 // Sbar_DrawFrags ();
1536 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1538 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1541 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1542 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1543 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1544 if (redflag == 3 && blueflag == 3)
1546 // The Impossible Combination[tm]
1547 // Can only happen in Key Hunt mode...
1548 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1553 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1555 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1559 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1562 if(cl.stats[STAT_HEALTH] > 100)
1563 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1564 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1565 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1567 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1569 // AK dont draw ammo for the laser
1570 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1572 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1573 Sbar_DrawPic (519, 0, sb_ammo[0]);
1574 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1575 Sbar_DrawPic (519, 0, sb_ammo[1]);
1576 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1577 Sbar_DrawPic (519, 0, sb_ammo[2]);
1578 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1579 Sbar_DrawPic (519, 0, sb_ammo[3]);
1581 if(cl.stats[STAT_AMMO] <= 10)
1582 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1584 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1589 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1591 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1592 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1597 // Mini scoreboard uses 12*4 per other team, that is, 144
1598 // pixels when there are four teams...
1599 // Nexuiz by default sets vid_conwidth to 800... makes
1601 // so we need to shift it by 64 pixels to the right to fit
1602 // BUT: then it overlaps with the image that gets drawn
1603 // for viewsize 100! Therefore, just account for 3 teams,
1604 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1608 else if (gamemode == GAME_ZYMOTIC)
1611 float scale = 64.0f / 256.0f;
1612 float kickoffset[3];
1613 VectorClear(kickoffset);
1616 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1617 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1619 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1620 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1621 // left1 16, 48 : 126 -66
1622 // left2 16, 128 : 196 -66
1623 // right 176, 48 : 196 -136
1624 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);
1625 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);
1626 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);
1627 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1629 float scale = 128.0f / 256.0f;
1630 float healthstart, healthheight, healthstarttc, healthendtc;
1631 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1632 float ammostart, ammoheight, ammostarttc, ammoendtc;
1633 float clipstart, clipheight, clipstarttc, clipendtc;
1634 float kickoffset[3], offset;
1635 VectorClear(kickoffset);
1638 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1639 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1641 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1642 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1643 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1644 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);
1645 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);
1646 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);
1647 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);
1648 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1649 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1650 healthstart = 204 - healthheight;
1651 shieldstart = healthstart - shieldheight;
1652 healthstarttc = healthstart * (1.0f / 256.0f);
1653 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1654 shieldstarttc = shieldstart * (1.0f / 256.0f);
1655 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1656 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1657 ammostart = 114 - ammoheight;
1658 ammostarttc = ammostart * (1.0f / 256.0f);
1659 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1660 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1661 clipstart = 190 - clipheight;
1662 clipstarttc = clipstart * (1.0f / 256.0f);
1663 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1664 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);
1665 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);
1666 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);
1667 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);
1668 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1669 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1672 else // Quake and others
1674 sbar_x = (vid_conwidth.integer - 320)/2;
1675 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1676 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1677 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1681 if (gamemode != GAME_GOODVSBAD2)
1682 Sbar_DrawInventory ();
1683 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1687 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1689 if (gamemode != GAME_GOODVSBAD2)
1690 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1691 Sbar_DrawScoreboard ();
1695 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1697 // keys (hipnotic only)
1698 //MED 01/04/97 moved keys here so they would not be overwritten
1699 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
1701 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1702 Sbar_DrawPic (209, 3, sb_items[0]);
1703 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1704 Sbar_DrawPic (209, 12, sb_items[1]);
1707 if (gamemode != GAME_GOODVSBAD2)
1709 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1711 Sbar_DrawNum (24, 0, 666, 3, 1);
1712 Sbar_DrawPic (0, 0, sb_disc);
1716 if (gamemode == GAME_ROGUE)
1718 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1719 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1720 Sbar_DrawPic (0, 0, sb_armor[2]);
1721 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1722 Sbar_DrawPic (0, 0, sb_armor[1]);
1723 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1724 Sbar_DrawPic (0, 0, sb_armor[0]);
1728 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1729 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1730 Sbar_DrawPic (0, 0, sb_armor[2]);
1731 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1732 Sbar_DrawPic (0, 0, sb_armor[1]);
1733 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1734 Sbar_DrawPic (0, 0, sb_armor[0]);
1743 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1746 if (gamemode == GAME_ROGUE)
1748 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1749 Sbar_DrawPic (224, 0, sb_ammo[0]);
1750 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1751 Sbar_DrawPic (224, 0, sb_ammo[1]);
1752 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1753 Sbar_DrawPic (224, 0, sb_ammo[2]);
1754 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1755 Sbar_DrawPic (224, 0, sb_ammo[3]);
1756 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1757 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1758 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1759 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1760 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1761 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1765 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1766 Sbar_DrawPic (224, 0, sb_ammo[0]);
1767 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1768 Sbar_DrawPic (224, 0, sb_ammo[1]);
1769 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1770 Sbar_DrawPic (224, 0, sb_ammo[2]);
1771 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1772 Sbar_DrawPic (224, 0, sb_ammo[3]);
1775 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1777 // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1778 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1780 if (gamemode == GAME_TRANSFUSION)
1781 Sbar_MiniDeathmatchOverlay (0, 0);
1783 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1790 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1792 pic = Draw_CachePic (va(vabuf, sizeof(vabuf), "gfx/crosshair%i", crosshair.integer));
1793 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);
1796 if (cl_prydoncursor.integer > 0)
1797 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);
1800 //=============================================================================
1804 Sbar_DeathmatchOverlay
1808 static float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1811 qboolean myself = false;
1814 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1816 if((s - cl.scores) == cl.playerentity - 1)
1818 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1819 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1822 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1824 if (s->qw_spectator)
1826 if (s->qw_ping || s->qw_packetloss)
1827 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 );
1829 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 );
1833 // draw colors behind score
1839 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1840 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);
1841 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1842 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);
1844 //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);
1845 if (s->qw_ping || s->qw_packetloss)
1846 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 );
1848 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 );
1853 if (s->qw_spectator)
1855 if (s->qw_ping || s->qw_packetloss)
1856 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 );
1858 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 );
1862 // draw colors behind score
1863 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1864 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);
1865 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1866 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);
1868 //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);
1869 if (s->qw_ping || s->qw_packetloss)
1870 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 );
1872 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 );
1878 void Sbar_DeathmatchOverlay (void)
1880 int i, y, xmin, xmax, ymin, ymax;
1883 // request new ping times every two second
1884 if (cl.last_ping_request < host.realtime - 2 && cls.netcon)
1886 cl.last_ping_request = host.realtime;
1887 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1889 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1890 MSG_WriteString(&cls.netcon->message, "pings");
1892 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*/)
1894 // 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
1895 static int ping_anyway_counter = 0;
1896 if(cl.parsingtextexpectingpingforscores == 1)
1898 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1899 if(++ping_anyway_counter >= 5)
1900 cl.parsingtextexpectingpingforscores = 0;
1902 if(cl.parsingtextexpectingpingforscores != 1)
1904 ping_anyway_counter = 0;
1905 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1906 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1907 MSG_WriteString(&cls.netcon->message, "ping");
1912 // 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
1913 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1914 MSG_WriteString(&cls.netcon->message, "pings");
1922 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1924 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1925 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)
1927 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)
1928 xmax = vid_conwidth.integer - xmin;
1930 if(IS_OLDNEXUIZ_DERIVED(gamemode))
1931 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);
1933 DrawQ_Pic ((vid_conwidth.integer - Draw_GetPicWidth(sb_ranking))/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1937 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1939 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 );
1943 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 );
1947 if (Sbar_IsTeammatch ())
1949 // show team scores first
1950 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1951 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1955 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1956 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1961 Sbar_MiniDeathmatchOverlay
1965 void Sbar_MiniDeathmatchOverlay (int x, int y)
1967 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1969 // do not draw this if sbar_miniscoreboard_size is zero
1970 if(sbar_miniscoreboard_size.value == 0)
1972 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1973 if(sbar_miniscoreboard_size.value > 0)
1974 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1979 // decide where to print
1980 if (gamemode == GAME_TRANSFUSION)
1981 numlines = (vid_conwidth.integer - x + 127) / 128;
1983 numlines = (vid_conheight.integer - y + 7) / 8;
1985 // give up if there isn't room
1986 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1990 for (i = 0; i < scoreboardlines; i++)
1991 if (fragsort[i] == cl.playerentity - 1)
1995 range_end = scoreboardlines;
1998 if (gamemode != GAME_TRANSFUSION)
1999 if (Sbar_IsTeammatch ())
2001 // reserve space for the team scores
2002 numlines -= teamlines;
2004 // find first and last player of my team (only draw the team totals and my own team)
2005 range_begin = range_end = i;
2006 myteam = cl.scores[fragsort[i]].colors & 15;
2007 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
2009 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
2012 // looks better than two players
2022 i = min(i, range_end - numlines);
2023 i = max(i, range_begin);
2025 if (gamemode == GAME_TRANSFUSION)
2027 for (;i < range_end && x < vid_conwidth.integer;i++)
2028 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2032 if(range_end - i < numlines) // won't draw to bottom?
2033 y += 8 * (numlines - (range_end - i)); // bottom align
2034 // show team scores first
2035 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
2036 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
2038 for (;i < range_end && y < vid_conheight.integer;i++)
2039 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2043 static int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
2045 static int const sortorder[16] =
2064 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
2065 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
2066 int tc1 = sortorder[t1->colors & 15];
2067 int tc2 = sortorder[t2->colors & 15];
2071 void Sbar_Score (int margin)
2073 int i, me, score, otherleader, place, distribution, minutes, seconds;
2075 int sbar_x_save = sbar_x;
2076 int sbar_y_save = sbar_y;
2079 sbar_y = (int) (vid_conheight.value - (32+12));
2082 me = cl.playerentity - 1;
2083 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2085 if(Sbar_IsTeammatch())
2089 // team1 team3 team4
2093 scoreboard_t *teamcolorsort[16];
2096 for(i = 0; i < teamlines; ++i)
2097 teamcolorsort[i] = &(teams[i]);
2099 // Now sort them by color
2100 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2103 // -12*4: four digits space
2104 place = (teamlines - 1) * (-12 * 4);
2106 for(i = 0; i < teamlines; ++i)
2108 int cindex = teamcolorsort[i]->colors & 15;
2109 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2110 float cm = max(max(c[0], c[1]), c[2]);
2111 float cr = c[0] / cm;
2112 float cg = c[1] / cm;
2113 float cb = c[2] / cm;
2114 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2116 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2120 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2133 // find leading score other than ourselves, to calculate distribution
2134 // find our place in the scoreboard
2135 score = cl.scores[me].frags;
2136 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2138 if (cl.scores[i].name[0] && i != me)
2140 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2142 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2146 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2148 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2149 else if (place == 2)
2150 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2152 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2153 if (otherleader < 0)
2154 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2155 if (distribution >= 0)
2157 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2158 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2160 else if (distribution >= -5)
2162 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2163 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2167 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2168 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2173 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2175 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2176 minutes = (int)floor(timeleft / 60);
2177 seconds = (int)(floor(timeleft) - minutes * 60);
2180 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2181 if (Draw_IsPicLoaded(sb_colon))
2182 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2183 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2185 else if (minutes >= 1)
2187 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2188 if (Draw_IsPicLoaded(sb_colon))
2189 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2190 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2192 else if ((int)(timeleft * 4) & 1)
2193 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2195 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2197 else if (sbar_gametime.integer)
2199 minutes = (int)floor(cl.time / 60);
2200 seconds = (int)(floor(cl.time) - minutes * 60);
2201 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2202 if (Draw_IsPicLoaded(sb_colon))
2203 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2204 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2207 sbar_x = sbar_x_save;
2208 sbar_y = sbar_y_save;
2213 Sbar_IntermissionOverlay
2217 void Sbar_IntermissionOverlay (void)
2222 if (cl.gametype == GAME_DEATHMATCH)
2224 Sbar_DeathmatchOverlay ();
2228 sbar_x = (vid_conwidth.integer - 320) >> 1;
2229 sbar_y = (vid_conheight.integer - 200) >> 1;
2231 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2232 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2235 dig = (int)cl.completed_time / 60;
2236 Sbar_DrawNum (160, 64, dig, 3, 0);
2237 num = (int)cl.completed_time - dig*60;
2238 Sbar_DrawPic (234,64,sb_colon);
2239 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2240 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2242 // LA: Display as "a" instead of "a/b" if b is 0
2243 if(cl.stats[STAT_TOTALSECRETS])
2245 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2246 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2247 Sbar_DrawPic (232, 104, sb_slash);
2248 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2252 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2255 if(cl.stats[STAT_TOTALMONSTERS])
2257 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2258 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2259 Sbar_DrawPic (232, 144, sb_slash);
2260 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2264 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2275 void Sbar_FinaleOverlay (void)
2277 DrawQ_Pic((vid_conwidth.integer - Draw_GetPicWidth(sb_finale))/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);