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 showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
89 cvar_t showsound = {CVAR_SAVE, "showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
90 cvar_t showblur = {CVAR_SAVE, "showblur", "0", "shows the current alpha level of motionblur"};
91 cvar_t showspeed = {CVAR_SAVE, "showspeed", "0", "shows your current speed (qu per second); number selects unit: 1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots"};
92 cvar_t showtopspeed = {CVAR_SAVE, "showtopspeed", "0", "shows your top speed (kept on screen for max 3 seconds); value -1 takes over the unit from showspeed, otherwise it's an unit number just like in showspeed"};
93 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
94 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
95 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
96 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
97 cvar_t showtex = {0, "showtex", "0", "shows the name of the texture on the crosshair (for map debugging)"};
98 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
99 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
100 cvar_t sbar_hudselector = {CVAR_SAVE, "sbar_hudselector", "0", "selects which of the builtin hud layouts to use (meaning is somewhat dependent on gamemode, so nexuiz has a very different set of hud layouts than quake for example)"};
101 cvar_t sbar_scorerank = {CVAR_SAVE, "sbar_scorerank", "1", "shows an overlay for your score (or team score) and rank in the scoreboard"};
102 cvar_t sbar_gametime = {CVAR_SAVE, "sbar_gametime", "1", "shows an overlay for the time left in the current match/level (or current game time if there is no timelimit set)"};
103 cvar_t sbar_miniscoreboard_size = {CVAR_SAVE, "sbar_miniscoreboard_size", "-1", "sets the size of the mini deathmatch overlay in items, or disables it when set to 0, or sets it to a sane default when set to -1"};
104 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
105 cvar_t sbar_flagstatus_pos = {CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
106 cvar_t sbar_info_pos = {CVAR_SAVE, "sbar_info_pos", "0", "pixel position of the info strings (such as showfps), from the bottom"};
108 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
110 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
111 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
112 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
113 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
114 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
116 static void Sbar_MiniDeathmatchOverlay (int x, int y);
117 static void Sbar_DeathmatchOverlay (void);
118 static void Sbar_IntermissionOverlay (void);
119 static void Sbar_FinaleOverlay (void);
130 static void Sbar_ShowScores (void)
134 sb_showscores = true;
135 CL_VM_UpdateShowingScoresState(sb_showscores);
145 static void Sbar_DontShowScores (void)
147 sb_showscores = false;
148 CL_VM_UpdateShowingScoresState(sb_showscores);
151 static void sbar_start(void)
156 if (gamemode == GAME_DELUXEQUAKE || gamemode == GAME_BLOODOMNICIDE)
159 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
161 for (i = 0;i < 10;i++)
162 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
163 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
164 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET);
166 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
167 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_bullets", CACHEPICFLAG_QUIET);
168 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
169 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
171 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor", CACHEPICFLAG_QUIET);
175 sb_health = Draw_CachePic_Flags ("gfx/sb_health", CACHEPICFLAG_QUIET);
177 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_slowmo", CACHEPICFLAG_QUIET);
178 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invinc", CACHEPICFLAG_QUIET);
179 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_energy", CACHEPICFLAG_QUIET);
180 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_str", CACHEPICFLAG_QUIET);
182 sb_items[11] = Draw_CachePic_Flags ("gfx/sb_flag_red_taken", CACHEPICFLAG_QUIET);
183 sb_items[12] = Draw_CachePic_Flags ("gfx/sb_flag_red_lost", CACHEPICFLAG_QUIET);
184 sb_items[13] = Draw_CachePic_Flags ("gfx/sb_flag_red_carrying", CACHEPICFLAG_QUIET);
185 sb_items[14] = Draw_CachePic_Flags ("gfx/sb_key_carrying", CACHEPICFLAG_QUIET);
186 sb_items[15] = Draw_CachePic_Flags ("gfx/sb_flag_blue_taken", CACHEPICFLAG_QUIET);
187 sb_items[16] = Draw_CachePic_Flags ("gfx/sb_flag_blue_lost", CACHEPICFLAG_QUIET);
188 sb_items[17] = Draw_CachePic_Flags ("gfx/sb_flag_blue_carrying", CACHEPICFLAG_QUIET);
190 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
191 sb_sbar_minimal = Draw_CachePic_Flags ("gfx/sbar_minimal", CACHEPICFLAG_QUIET);
192 sb_sbar_overlay = Draw_CachePic_Flags ("gfx/sbar_overlay", CACHEPICFLAG_QUIET);
194 for(i = 0; i < 9;i++)
195 sb_weapons[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inv_weapon%i",i), CACHEPICFLAG_QUIET);
197 else if (gamemode == GAME_ZYMOTIC)
199 zymsb_crosshair_center = Draw_CachePic_Flags ("gfx/hud/crosshair_center", CACHEPICFLAG_QUIET);
200 zymsb_crosshair_line = Draw_CachePic_Flags ("gfx/hud/crosshair_line", CACHEPICFLAG_QUIET);
201 zymsb_crosshair_health = Draw_CachePic_Flags ("gfx/hud/crosshair_health", CACHEPICFLAG_QUIET);
202 zymsb_crosshair_clip = Draw_CachePic_Flags ("gfx/hud/crosshair_clip", CACHEPICFLAG_QUIET);
203 zymsb_crosshair_ammo = Draw_CachePic_Flags ("gfx/hud/crosshair_ammo", CACHEPICFLAG_QUIET);
204 zymsb_crosshair_background = Draw_CachePic_Flags ("gfx/hud/crosshair_background", CACHEPICFLAG_QUIET);
205 zymsb_crosshair_left1 = Draw_CachePic_Flags ("gfx/hud/crosshair_left1", CACHEPICFLAG_QUIET);
206 zymsb_crosshair_left2 = Draw_CachePic_Flags ("gfx/hud/crosshair_left2", CACHEPICFLAG_QUIET);
207 zymsb_crosshair_right = Draw_CachePic_Flags ("gfx/hud/crosshair_right", CACHEPICFLAG_QUIET);
211 sb_disc = Draw_CachePic_Flags ("gfx/disc", CACHEPICFLAG_QUIET);
213 for (i = 0;i < 10;i++)
215 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
216 sb_nums[1][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/anum_%i",i), CACHEPICFLAG_QUIET);
219 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
220 sb_nums[1][10] = Draw_CachePic_Flags ("gfx/anum_minus", CACHEPICFLAG_QUIET);
222 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET);
223 sb_slash = Draw_CachePic_Flags ("gfx/num_slash", CACHEPICFLAG_QUIET);
225 sb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_shotgun", CACHEPICFLAG_QUIET);
226 sb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_sshotgun", CACHEPICFLAG_QUIET);
227 sb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_nailgun", CACHEPICFLAG_QUIET);
228 sb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_snailgun", CACHEPICFLAG_QUIET);
229 sb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_rlaunch", CACHEPICFLAG_QUIET);
230 sb_weapons[0][5] = Draw_CachePic_Flags ("gfx/inv_srlaunch", CACHEPICFLAG_QUIET);
231 sb_weapons[0][6] = Draw_CachePic_Flags ("gfx/inv_lightng", CACHEPICFLAG_QUIET);
233 sb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_shotgun", CACHEPICFLAG_QUIET);
234 sb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_sshotgun", CACHEPICFLAG_QUIET);
235 sb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_nailgun", CACHEPICFLAG_QUIET);
236 sb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_snailgun", CACHEPICFLAG_QUIET);
237 sb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_rlaunch", CACHEPICFLAG_QUIET);
238 sb_weapons[1][5] = Draw_CachePic_Flags ("gfx/inv2_srlaunch", CACHEPICFLAG_QUIET);
239 sb_weapons[1][6] = Draw_CachePic_Flags ("gfx/inv2_lightng", CACHEPICFLAG_QUIET);
241 for (i = 0;i < 5;i++)
243 sb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_shotgun",i+1), CACHEPICFLAG_QUIET);
244 sb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_sshotgun",i+1), CACHEPICFLAG_QUIET);
245 sb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_nailgun",i+1), CACHEPICFLAG_QUIET);
246 sb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_snailgun",i+1), CACHEPICFLAG_QUIET);
247 sb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_rlaunch",i+1), CACHEPICFLAG_QUIET);
248 sb_weapons[2+i][5] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_srlaunch",i+1), CACHEPICFLAG_QUIET);
249 sb_weapons[2+i][6] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_lightng",i+1), CACHEPICFLAG_QUIET);
252 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
253 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_nails", CACHEPICFLAG_QUIET);
254 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
255 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
257 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor1", CACHEPICFLAG_QUIET);
258 sb_armor[1] = Draw_CachePic_Flags ("gfx/sb_armor2", CACHEPICFLAG_QUIET);
259 sb_armor[2] = Draw_CachePic_Flags ("gfx/sb_armor3", CACHEPICFLAG_QUIET);
261 sb_items[0] = Draw_CachePic_Flags ("gfx/sb_key1", CACHEPICFLAG_QUIET);
262 sb_items[1] = Draw_CachePic_Flags ("gfx/sb_key2", CACHEPICFLAG_QUIET);
263 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_invis", CACHEPICFLAG_QUIET);
264 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invuln", CACHEPICFLAG_QUIET);
265 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_suit", CACHEPICFLAG_QUIET);
266 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_quad", CACHEPICFLAG_QUIET);
268 sb_sigil[0] = Draw_CachePic_Flags ("gfx/sb_sigil1", CACHEPICFLAG_QUIET);
269 sb_sigil[1] = Draw_CachePic_Flags ("gfx/sb_sigil2", CACHEPICFLAG_QUIET);
270 sb_sigil[2] = Draw_CachePic_Flags ("gfx/sb_sigil3", CACHEPICFLAG_QUIET);
271 sb_sigil[3] = Draw_CachePic_Flags ("gfx/sb_sigil4", CACHEPICFLAG_QUIET);
273 sb_faces[4][0] = Draw_CachePic_Flags ("gfx/face1", CACHEPICFLAG_QUIET);
274 sb_faces[4][1] = Draw_CachePic_Flags ("gfx/face_p1", CACHEPICFLAG_QUIET);
275 sb_faces[3][0] = Draw_CachePic_Flags ("gfx/face2", CACHEPICFLAG_QUIET);
276 sb_faces[3][1] = Draw_CachePic_Flags ("gfx/face_p2", CACHEPICFLAG_QUIET);
277 sb_faces[2][0] = Draw_CachePic_Flags ("gfx/face3", CACHEPICFLAG_QUIET);
278 sb_faces[2][1] = Draw_CachePic_Flags ("gfx/face_p3", CACHEPICFLAG_QUIET);
279 sb_faces[1][0] = Draw_CachePic_Flags ("gfx/face4", CACHEPICFLAG_QUIET);
280 sb_faces[1][1] = Draw_CachePic_Flags ("gfx/face_p4", CACHEPICFLAG_QUIET);
281 sb_faces[0][0] = Draw_CachePic_Flags ("gfx/face5", CACHEPICFLAG_QUIET);
282 sb_faces[0][1] = Draw_CachePic_Flags ("gfx/face_p5", CACHEPICFLAG_QUIET);
284 sb_face_invis = Draw_CachePic_Flags ("gfx/face_invis", CACHEPICFLAG_QUIET);
285 sb_face_invuln = Draw_CachePic_Flags ("gfx/face_invul2", CACHEPICFLAG_QUIET);
286 sb_face_invis_invuln = Draw_CachePic_Flags ("gfx/face_inv2", CACHEPICFLAG_QUIET);
287 sb_face_quad = Draw_CachePic_Flags ("gfx/face_quad", CACHEPICFLAG_QUIET);
289 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
290 sb_ibar = Draw_CachePic_Flags ("gfx/ibar", CACHEPICFLAG_QUIET);
291 sb_scorebar = Draw_CachePic_Flags ("gfx/scorebar", CACHEPICFLAG_QUIET);
293 //MED 01/04/97 added new hipnotic weapons
294 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
296 hsb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_laser", CACHEPICFLAG_QUIET);
297 hsb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_mjolnir", CACHEPICFLAG_QUIET);
298 hsb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_gren_prox", CACHEPICFLAG_QUIET);
299 hsb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_prox_gren", CACHEPICFLAG_QUIET);
300 hsb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_prox", CACHEPICFLAG_QUIET);
302 hsb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_laser", CACHEPICFLAG_QUIET);
303 hsb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_mjolnir", CACHEPICFLAG_QUIET);
304 hsb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_gren_prox", CACHEPICFLAG_QUIET);
305 hsb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_prox_gren", CACHEPICFLAG_QUIET);
306 hsb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_prox", CACHEPICFLAG_QUIET);
308 for (i = 0;i < 5;i++)
310 hsb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_laser",i+1), CACHEPICFLAG_QUIET);
311 hsb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_mjolnir",i+1), CACHEPICFLAG_QUIET);
312 hsb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_gren_prox",i+1), CACHEPICFLAG_QUIET);
313 hsb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox_gren",i+1), CACHEPICFLAG_QUIET);
314 hsb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox",i+1), CACHEPICFLAG_QUIET);
317 hsb_items[0] = Draw_CachePic_Flags ("gfx/sb_wsuit", CACHEPICFLAG_QUIET);
318 hsb_items[1] = Draw_CachePic_Flags ("gfx/sb_eshld", CACHEPICFLAG_QUIET);
320 else if (gamemode == GAME_ROGUE)
322 rsb_invbar[0] = Draw_CachePic_Flags ("gfx/r_invbar1", CACHEPICFLAG_QUIET);
323 rsb_invbar[1] = Draw_CachePic_Flags ("gfx/r_invbar2", CACHEPICFLAG_QUIET);
325 rsb_weapons[0] = Draw_CachePic_Flags ("gfx/r_lava", CACHEPICFLAG_QUIET);
326 rsb_weapons[1] = Draw_CachePic_Flags ("gfx/r_superlava", CACHEPICFLAG_QUIET);
327 rsb_weapons[2] = Draw_CachePic_Flags ("gfx/r_gren", CACHEPICFLAG_QUIET);
328 rsb_weapons[3] = Draw_CachePic_Flags ("gfx/r_multirock", CACHEPICFLAG_QUIET);
329 rsb_weapons[4] = Draw_CachePic_Flags ("gfx/r_plasma", CACHEPICFLAG_QUIET);
331 rsb_items[0] = Draw_CachePic_Flags ("gfx/r_shield1", CACHEPICFLAG_QUIET);
332 rsb_items[1] = Draw_CachePic_Flags ("gfx/r_agrav1", CACHEPICFLAG_QUIET);
334 // PGM 01/19/97 - team color border
335 rsb_teambord = Draw_CachePic_Flags ("gfx/r_teambord", CACHEPICFLAG_QUIET);
336 // PGM 01/19/97 - team color border
338 rsb_ammo[0] = Draw_CachePic_Flags ("gfx/r_ammolava", CACHEPICFLAG_QUIET);
339 rsb_ammo[1] = Draw_CachePic_Flags ("gfx/r_ammomulti", CACHEPICFLAG_QUIET);
340 rsb_ammo[2] = Draw_CachePic_Flags ("gfx/r_ammoplasma", CACHEPICFLAG_QUIET);
344 sb_ranking = Draw_CachePic_Flags ("gfx/ranking", CACHEPICFLAG_QUIET);
345 sb_complete = Draw_CachePic_Flags ("gfx/complete", CACHEPICFLAG_QUIET);
346 sb_inter = Draw_CachePic_Flags ("gfx/inter", CACHEPICFLAG_QUIET);
347 sb_finale = Draw_CachePic_Flags ("gfx/finale", CACHEPICFLAG_QUIET);
350 static void sbar_shutdown(void)
354 static void sbar_newmap(void)
358 void Sbar_Init (void)
360 Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
361 Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
362 Cvar_RegisterVariable(&showfps);
363 Cvar_RegisterVariable(&showsound);
364 Cvar_RegisterVariable(&showblur);
365 Cvar_RegisterVariable(&showspeed);
366 Cvar_RegisterVariable(&showtopspeed);
367 Cvar_RegisterVariable(&showtime);
368 Cvar_RegisterVariable(&showtime_format);
369 Cvar_RegisterVariable(&showdate);
370 Cvar_RegisterVariable(&showdate_format);
371 Cvar_RegisterVariable(&showtex);
372 Cvar_RegisterVariable(&sbar_alpha_bg);
373 Cvar_RegisterVariable(&sbar_alpha_fg);
374 Cvar_RegisterVariable(&sbar_hudselector);
375 Cvar_RegisterVariable(&sbar_scorerank);
376 Cvar_RegisterVariable(&sbar_gametime);
377 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
378 Cvar_RegisterVariable(&sbar_info_pos);
379 Cvar_RegisterVariable(&cl_deathscoreboard);
381 Cvar_RegisterVariable(&crosshair_color_red);
382 Cvar_RegisterVariable(&crosshair_color_green);
383 Cvar_RegisterVariable(&crosshair_color_blue);
384 Cvar_RegisterVariable(&crosshair_color_alpha);
385 Cvar_RegisterVariable(&crosshair_size);
387 Cvar_RegisterVariable(&sbar_flagstatus_right); // (GAME_NEXUZI ONLY)
388 Cvar_RegisterVariable(&sbar_flagstatus_pos); // (GAME_NEXUIZ ONLY)
390 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap, NULL, NULL);
394 //=============================================================================
396 // drawing routines are relative to the status bar location
405 static void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
407 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
410 static void Sbar_DrawPic (int x, int y, cachepic_t *pic)
412 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
415 static void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
417 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
424 Draws one solid graphics character
427 static void Sbar_DrawCharacter (int x, int y, int num)
430 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);
438 static void Sbar_DrawString (int x, int y, char *str)
440 DrawQ_String (sbar_x + x, sbar_y + y, str, 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR);
448 static void Sbar_DrawNum (int x, int y, int num, int digits, int color)
453 l = dpsnprintf(str, sizeof(str), "%i", num);
467 Sbar_DrawPic (x, y, sb_nums[color][frame]);
480 static void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
488 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
491 l = dpsnprintf(str, sizeof(str), "%i", num);
496 x += (digits-l) * lettersize;
505 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
512 //=============================================================================
515 static int Sbar_IsTeammatch(void)
517 // currently only nexuiz uses the team score board
518 return (IS_OLDNEXUIZ_DERIVED(gamemode)
519 && (teamplay.integer > 0));
527 static int fragsort[MAX_SCOREBOARD];
528 static int scoreboardlines;
530 int Sbar_GetSortedPlayerIndex (int index)
532 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
535 static scoreboard_t teams[MAX_SCOREBOARD];
536 static int teamsort[MAX_SCOREBOARD];
537 static int teamlines;
538 void Sbar_SortFrags (void)
544 for (i=0 ; i<cl.maxclients ; i++)
546 if (cl.scores[i].name[0])
548 fragsort[scoreboardlines] = i;
553 for (i=0 ; i<scoreboardlines ; i++)
554 for (j=0 ; j<scoreboardlines-1-i ; j++)
555 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
558 fragsort[j] = fragsort[j+1];
563 if (Sbar_IsTeammatch ())
565 // now sort players by teams.
566 for (i=0 ; i<scoreboardlines ; i++)
568 for (j=0 ; j<scoreboardlines-1-i ; j++)
570 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
573 fragsort[j] = fragsort[j+1];
579 // calculate team scores
581 for (i=0 ; i<scoreboardlines ; i++)
583 if (color != (cl.scores[fragsort[i]].colors & 15))
585 const char* teamname;
587 color = cl.scores[fragsort[i]].colors & 15;
593 teamname = "^1Red Team";
596 teamname = "^4Blue Team";
599 teamname = "^6Pink Team";
602 teamname = "^3Yellow Team";
605 teamname = "Total Team Score";
608 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
610 teams[teamlines-1].frags = 0;
611 teams[teamlines-1].colors = color + 16 * color;
614 if (cl.scores[fragsort[i]].frags != -666)
616 // do not add spedcators
617 // (ugly hack for nexuiz)
618 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
622 // now sort teams by scores.
623 for (i=0 ; i<teamlines ; i++)
625 for (i=0 ; i<teamlines ; i++)
627 for (j=0 ; j<teamlines-1-i ; j++)
629 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
632 teamsort[j] = teamsort[j+1];
645 static void Sbar_SoloScoreboard (void)
648 char str[80], timestr[40];
650 int minutes, seconds;
654 t = (cl.intermission ? cl.completed_time : cl.time);
655 minutes = (int)(t / 60);
656 seconds = (int)(t - 60*floor(t/60));
658 // monsters and secrets are now both on the top row
659 if (cl.stats[STAT_TOTALMONSTERS])
660 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
661 else if (cl.stats[STAT_MONSTERS]) // LA: Display something if monsters_killed is non-zero, but total_monsters is zero
662 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i", cl.stats[STAT_MONSTERS]));
664 if (cl.stats[STAT_TOTALSECRETS])
665 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
666 else if (cl.stats[STAT_SECRETS]) // LA: And similarly for secrets
667 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i", cl.stats[STAT_SECRETS]));
669 // format is like this: e1m1:The Sligpate Complex
670 dpsnprintf(str, sizeof(str), "%s:%s", cl.worldbasename, cl.worldmessage);
672 // if there's a newline character, terminate the string there
673 if (strchr(str, '\n'))
674 *(strchr(str, '\n')) = 0;
676 // make the time string
677 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
679 // truncate the level name if necessary to make room for time
681 if ((int)strlen(str) > max)
684 // print the filename and message
685 Sbar_DrawString(8, 12, str);
688 Sbar_DrawString(8 + max*8, 12, timestr);
692 int minutes, seconds, tens, units;
695 if (IS_OLDNEXUIZ_DERIVED(gamemode)) {
696 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
697 Sbar_DrawString (8, 4, str);
699 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
700 Sbar_DrawString (8, 12, str);
704 minutes = (int)(cl.time / 60);
705 seconds = (int)(cl.time - 60*minutes);
707 units = seconds - 10*tens;
708 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
709 Sbar_DrawString (184, 4, str);
712 if (IS_OLDNEXUIZ_DERIVED(gamemode)) {
713 l = (int) strlen (cl.worldname);
714 Sbar_DrawString (232 - l*4, 12, cl.worldname);
716 l = (int) strlen (cl.worldmessage);
717 Sbar_DrawString (232 - l*4, 12, cl.worldmessage);
727 static void Sbar_DrawScoreboard (void)
729 Sbar_SoloScoreboard ();
730 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
731 //if (cl.gametype == GAME_DEATHMATCH)
733 Sbar_DeathmatchOverlay ();
736 //=============================================================================
738 // AK to make DrawInventory smaller
739 static void Sbar_DrawWeapon(int nr, float fade, int active)
742 if (sbar_hudselector.integer == 1)
744 // width = 300, height = 100
745 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
747 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);
749 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);
753 // width = 300, height = 100
754 const int w_width = 300, w_height = 100, w_space = 10;
755 const float w_scale = 0.4;
757 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);
758 //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);
767 static void Sbar_DrawInventory (void)
774 if (gamemode == GAME_ROGUE)
776 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
777 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
779 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
782 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
785 for (i=0 ; i<7 ; i++)
787 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
789 time = cl.item_gettime[i];
790 flashon = (int)(max(0, cl.time - time)*10);
793 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
799 flashon = (flashon%5) + 2;
801 Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
807 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
809 int grenadeflashing=0;
810 for (i=0 ; i<4 ; i++)
812 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
814 time = max(0, cl.item_gettime[hipweapons[i]]);
815 flashon = (int)((cl.time - time)*10);
818 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
824 flashon = (flashon%5) + 2;
826 // check grenade launcher
829 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
834 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
840 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
842 if (!grenadeflashing)
843 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
846 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
849 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
854 if (gamemode == GAME_ROGUE)
856 // check for powered up weapon.
857 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
859 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
860 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
864 for (i=0 ; i<4 ; i++)
866 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
868 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
870 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
872 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
874 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
878 for (i=0 ; i<6 ; i++)
879 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
881 //MED 01/04/97 changed keys
882 if (!(gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH) || (i>1))
883 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
886 //MED 01/04/97 added hipnotic items
888 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
890 for (i=0 ; i<2 ; i++)
891 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
892 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
895 if (gamemode == GAME_ROGUE)
898 for (i=0 ; i<2 ; i++)
899 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
900 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
905 for (i=0 ; i<4 ; i++)
906 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
907 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
911 //=============================================================================
918 static void Sbar_DrawFrags (void)
928 l = min(scoreboardlines, 4);
932 for (i = 0;i < l;i++)
938 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
939 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);
940 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
941 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);
945 dpsnprintf (num, sizeof(num), "%3i",f);
947 if (k == cl.viewentity - 1)
949 Sbar_DrawCharacter ( x + 2, -24, 16);
950 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
952 Sbar_DrawCharacter (x + 8, -24, num[0]);
953 Sbar_DrawCharacter (x + 16, -24, num[1]);
954 Sbar_DrawCharacter (x + 24, -24, num[2]);
959 //=============================================================================
967 static void Sbar_DrawFace (void)
971 // PGM 01/19/97 - team color drawing
972 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
973 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
979 s = &cl.scores[cl.viewentity - 1];
981 Sbar_DrawPic (112, 0, rsb_teambord);
982 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
983 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);
984 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
985 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);
989 dpsnprintf (num, sizeof(num), "%3i",f);
991 if ((s->colors & 0xf0)==0)
994 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
996 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
998 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1002 Sbar_DrawCharacter ( 109, 3, num[0]);
1003 Sbar_DrawCharacter ( 116, 3, num[1]);
1004 Sbar_DrawCharacter ( 123, 3, num[2]);
1009 // PGM 01/19/97 - team color drawing
1011 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1012 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1013 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1014 Sbar_DrawPic (112, 0, sb_face_quad );
1015 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1016 Sbar_DrawPic (112, 0, sb_face_invis );
1017 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1018 Sbar_DrawPic (112, 0, sb_face_invuln);
1021 f = cl.stats[STAT_HEALTH] / 20;
1023 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1026 double topspeed = 0;
1027 double topspeedxy = 0;
1028 time_t current_time = 3;
1029 time_t top_time = 0;
1030 time_t topxy_time = 0;
1032 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1035 unitnumber = showspeed.integer;
1040 if(IS_NEXUIZ_DERIVED(gamemode))
1044 *conversion_factor = 1.0;
1048 *conversion_factor = 0.0254;
1049 if(!IS_NEXUIZ_DERIVED(gamemode))
1050 *conversion_factor *= 1.5;
1051 // 1qu=1.5in is for non-Nexuiz/Xonotic only - Nexuiz/Xonotic players are overly large, but 1qu=1in fixes that
1055 *conversion_factor = 0.0254 * 3.6;
1056 if(!IS_NEXUIZ_DERIVED(gamemode))
1057 *conversion_factor *= 1.5;
1061 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1062 if(!IS_NEXUIZ_DERIVED(gamemode))
1063 *conversion_factor *= 1.5;
1067 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1068 if(!IS_NEXUIZ_DERIVED(gamemode))
1069 *conversion_factor *= 1.5;
1074 static double showfps_nexttime = 0, showfps_lasttime = -1;
1075 static double showfps_framerate = 0;
1076 static int showfps_framecount = 0;
1078 void Sbar_ShowFPS_Update(void)
1080 double interval = 1;
1083 if (newtime >= showfps_nexttime)
1085 showfps_framerate = showfps_framecount / (newtime - showfps_lasttime);
1086 if (showfps_nexttime < newtime - interval * 1.5)
1087 showfps_nexttime = newtime;
1088 showfps_lasttime = newtime;
1089 showfps_nexttime += interval;
1090 showfps_framecount = 0;
1092 showfps_framecount++;
1095 void Sbar_ShowFPS(void)
1097 float fps_x, fps_y, fps_scalex, fps_scaley, fps_strings = 0;
1098 char soundstring[32];
1100 char timestring[32];
1101 char datestring[32];
1102 char timedemostring1[32];
1103 char timedemostring2[32];
1104 char speedstring[32];
1105 char blurstring[32];
1106 char topspeedstring[48];
1107 char texstring[MAX_QPATH];
1108 qboolean red = false;
1111 timedemostring1[0] = 0;
1112 timedemostring2[0] = 0;
1118 topspeedstring[0] = 0;
1119 if (showfps.integer)
1121 red = (showfps_framerate < 1.0f);
1122 if(showfps.integer == 2)
1123 dpsnprintf(fpsstring, sizeof(fpsstring), "%7.3f mspf", (1000.0 / showfps_framerate));
1125 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0 / showfps_framerate + 0.5));
1127 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(showfps_framerate + 0.5));
1131 dpsnprintf(timedemostring1, sizeof(timedemostring1), "frame%4i %f", cls.td_frames, realtime - cls.td_starttime);
1132 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);
1137 if (showtime.integer)
1139 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1142 if (showdate.integer)
1144 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1147 if (showblur.integer)
1149 dpsnprintf(blurstring, sizeof(blurstring), "%3i%% blur", (int)(cl.motionbluralpha * 100));
1152 if (showsound.integer)
1154 dpsnprintf(soundstring, sizeof(soundstring), "%4i/4%i at %3ims", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1157 if (showspeed.integer || showtopspeed.integer)
1159 double speed, speedxy, f;
1161 speed = VectorLength(cl.movement_velocity);
1162 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1163 if (showspeed.integer)
1165 get_showspeed_unit(showspeed.integer, &f, &unit);
1166 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1169 if (showtopspeed.integer)
1171 qboolean topspeed_latched = false, topspeedxy_latched = false;
1172 get_showspeed_unit(showtopspeed.integer, &f, &unit);
1173 if (speed >= topspeed || current_time - top_time > 3)
1179 topspeed_latched = true;
1180 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1182 topspeedxy = speedxy;
1186 topspeedxy_latched = true;
1187 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1188 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1189 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1191 time(¤t_time);
1195 if (showtex.integer)
1202 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, org);
1203 VectorSet(temp, 65536, 0, 0);
1204 Matrix4x4_Transform(&r_refdef.view.matrix, temp, dest);
1205 trace.hittexture = NULL; // to make sure
1206 // TODO change this trace to be stopped by anything "visible" (i.e. with a drawsurface), but not stuff like weapclip
1207 // probably needs adding a new SUPERCONTENTS type
1208 trace = CL_TraceLine(org, dest, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, true, true);
1209 if(trace.hittexture)
1210 strlcpy(texstring, trace.hittexture->name, sizeof(texstring));
1212 strlcpy(texstring, "(no texture hit)", sizeof(texstring));
1219 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1220 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_strings*fps_scaley);
1221 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_strings*fps_scaley;
1224 fps_x = vid_conwidth.integer - DrawQ_TextWidth(soundstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1225 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1226 DrawQ_String(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1227 fps_y += fps_scaley;
1231 r_draw2d_force = true;
1232 fps_x = vid_conwidth.integer - DrawQ_TextWidth(fpsstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1233 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1235 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1237 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1238 fps_y += fps_scaley;
1239 r_draw2d_force = false;
1241 if (timedemostring1[0])
1243 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring1, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1244 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1245 DrawQ_String(fps_x, fps_y, timedemostring1, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1246 fps_y += fps_scaley;
1248 if (timedemostring2[0])
1250 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring2, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1251 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1252 DrawQ_String(fps_x, fps_y, timedemostring2, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1253 fps_y += fps_scaley;
1257 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1258 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1259 DrawQ_String(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1260 fps_y += fps_scaley;
1264 fps_x = vid_conwidth.integer - DrawQ_TextWidth(datestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1265 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1266 DrawQ_String(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1267 fps_y += fps_scaley;
1271 fps_x = vid_conwidth.integer - DrawQ_TextWidth(speedstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1272 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1273 DrawQ_String(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1274 fps_y += fps_scaley;
1276 if (topspeedstring[0])
1278 fps_x = vid_conwidth.integer - DrawQ_TextWidth(topspeedstring, 0, fps_scalex, fps_scaley, false, FONT_INFOBAR);
1279 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1280 DrawQ_String(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1281 fps_y += fps_scaley;
1285 fps_x = vid_conwidth.integer - DrawQ_TextWidth(blurstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1286 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1287 DrawQ_String(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1288 fps_y += fps_scaley;
1292 fps_x = vid_conwidth.integer - DrawQ_TextWidth(texstring, 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, texstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1295 fps_y += fps_scaley;
1300 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)
1303 c2 = bound(0, c2, 1);
1304 c1 = bound(0, c1, 1 - c2);
1306 r[1] = rangey + rangeheight * (c2 + c1);
1307 r[2] = rangey + rangeheight * (c2);
1311 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);
1313 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);
1315 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);
1317 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);
1325 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1326 extern cvar_t v_kicktime;
1327 void Sbar_Score (int margin);
1328 void Sbar_Draw (void)
1333 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1336 Sbar_DrawScoreboard ();
1337 else if (cl.intermission == 1)
1339 if(IS_OLDNEXUIZ_DERIVED(gamemode)) // display full scoreboard (that is, show scores + map name)
1341 Sbar_DrawScoreboard();
1344 Sbar_IntermissionOverlay();
1346 else if (cl.intermission == 2)
1347 Sbar_FinaleOverlay();
1348 else if (gamemode == GAME_DELUXEQUAKE)
1351 else if (IS_OLDNEXUIZ_DERIVED(gamemode))
1353 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1355 sbar_x = (vid_conwidth.integer - 640)/2;
1356 sbar_y = vid_conheight.integer - 47;
1357 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1358 Sbar_DrawScoreboard ();
1360 else if (sb_lines && sbar_hudselector.integer == 1)
1364 int redflag, blueflag;
1367 sbar_x = (vid_conwidth.integer - 320)/2;
1368 sbar_y = vid_conheight.integer - 24 - 16;
1370 // calculate intensity to draw weapons bar at
1371 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1372 fade = bound(0.7, fade, 1);
1373 for (i = 0; i < 8;i++)
1374 if (cl.stats[STAT_ITEMS] & (1 << i))
1375 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1376 if((cl.stats[STAT_ITEMS] & (1<<12)))
1377 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1380 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1381 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1382 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1383 if (redflag == 3 && blueflag == 3)
1385 // The Impossible Combination[tm]
1386 // Can only happen in Key Hunt mode...
1387 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1392 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1394 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1398 if (cl.stats[STAT_ARMOR] > 0)
1400 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1401 if(cl.stats[STAT_ARMOR] > 200)
1402 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1403 else if(cl.stats[STAT_ARMOR] > 100)
1404 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1405 else if(cl.stats[STAT_ARMOR] > 50)
1406 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1407 else if(cl.stats[STAT_ARMOR] > 25)
1408 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1410 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1414 if (cl.stats[STAT_HEALTH] != 0)
1416 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1417 if(cl.stats[STAT_HEALTH] > 200)
1418 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1419 else if(cl.stats[STAT_HEALTH] > 100)
1420 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1421 else if(cl.stats[STAT_HEALTH] > 50)
1422 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1423 else if(cl.stats[STAT_HEALTH] > 25)
1424 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1426 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1430 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1432 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1433 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1434 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1435 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1436 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1437 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1438 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1439 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1440 if(cl.stats[STAT_AMMO] > 10)
1441 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1443 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1446 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1447 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1450 // The margin can be at most 8 to support 640x480 console size:
1451 // 320 + 2 * (144 + 16) = 640
1457 int redflag, blueflag;
1460 sbar_x = (vid_conwidth.integer - 640)/2;
1461 sbar_y = vid_conheight.integer - 47;
1463 // calculate intensity to draw weapons bar at
1464 fade = 3 - 2 * (cl.time - cl.weapontime);
1467 fade = min(fade, 1);
1468 for (i = 0; i < 8;i++)
1469 if (cl.stats[STAT_ITEMS] & (1 << i))
1470 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1472 if((cl.stats[STAT_ITEMS] & (1<<12)))
1473 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1476 //if (!cl.islocalgame)
1477 // Sbar_DrawFrags ();
1480 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1482 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1485 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1486 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1487 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1488 if (redflag == 3 && blueflag == 3)
1490 // The Impossible Combination[tm]
1491 // Can only happen in Key Hunt mode...
1492 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1497 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1499 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1503 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1506 if(cl.stats[STAT_HEALTH] > 100)
1507 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1508 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1509 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1511 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1513 // AK dont draw ammo for the laser
1514 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1516 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1517 Sbar_DrawPic (519, 0, sb_ammo[0]);
1518 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1519 Sbar_DrawPic (519, 0, sb_ammo[1]);
1520 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1521 Sbar_DrawPic (519, 0, sb_ammo[2]);
1522 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1523 Sbar_DrawPic (519, 0, sb_ammo[3]);
1525 if(cl.stats[STAT_AMMO] <= 10)
1526 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1528 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1533 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1535 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1536 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1541 // Mini scoreboard uses 12*4 per other team, that is, 144
1542 // pixels when there are four teams...
1543 // Nexuiz by default sets vid_conwidth to 800... makes
1545 // so we need to shift it by 64 pixels to the right to fit
1546 // BUT: then it overlaps with the image that gets drawn
1547 // for viewsize 100! Therefore, just account for 3 teams,
1548 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1552 else if (gamemode == GAME_ZYMOTIC)
1555 float scale = 64.0f / 256.0f;
1556 float kickoffset[3];
1557 VectorClear(kickoffset);
1560 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1561 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1563 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1564 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1565 // left1 16, 48 : 126 -66
1566 // left2 16, 128 : 196 -66
1567 // right 176, 48 : 196 -136
1568 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);
1569 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);
1570 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);
1571 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1573 float scale = 128.0f / 256.0f;
1574 float healthstart, healthheight, healthstarttc, healthendtc;
1575 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1576 float ammostart, ammoheight, ammostarttc, ammoendtc;
1577 float clipstart, clipheight, clipstarttc, clipendtc;
1578 float kickoffset[3], offset;
1579 VectorClear(kickoffset);
1582 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1583 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1585 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1586 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1587 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1588 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);
1589 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);
1590 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);
1591 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);
1592 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1593 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1594 healthstart = 204 - healthheight;
1595 shieldstart = healthstart - shieldheight;
1596 healthstarttc = healthstart * (1.0f / 256.0f);
1597 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1598 shieldstarttc = shieldstart * (1.0f / 256.0f);
1599 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1600 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1601 ammostart = 114 - ammoheight;
1602 ammostarttc = ammostart * (1.0f / 256.0f);
1603 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1604 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1605 clipstart = 190 - clipheight;
1606 clipstarttc = clipstart * (1.0f / 256.0f);
1607 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1608 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);
1609 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);
1610 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);
1611 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);
1612 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1613 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1616 else // Quake and others
1618 sbar_x = (vid_conwidth.integer - 320)/2;
1619 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1620 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1621 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1625 if (gamemode != GAME_GOODVSBAD2)
1626 Sbar_DrawInventory ();
1627 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1631 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1633 if (gamemode != GAME_GOODVSBAD2)
1634 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1635 Sbar_DrawScoreboard ();
1639 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1641 // keys (hipnotic only)
1642 //MED 01/04/97 moved keys here so they would not be overwritten
1643 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
1645 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1646 Sbar_DrawPic (209, 3, sb_items[0]);
1647 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1648 Sbar_DrawPic (209, 12, sb_items[1]);
1651 if (gamemode != GAME_GOODVSBAD2)
1653 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1655 Sbar_DrawNum (24, 0, 666, 3, 1);
1656 Sbar_DrawPic (0, 0, sb_disc);
1660 if (gamemode == GAME_ROGUE)
1662 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1663 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1664 Sbar_DrawPic (0, 0, sb_armor[2]);
1665 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1666 Sbar_DrawPic (0, 0, sb_armor[1]);
1667 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1668 Sbar_DrawPic (0, 0, sb_armor[0]);
1672 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1673 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1674 Sbar_DrawPic (0, 0, sb_armor[2]);
1675 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1676 Sbar_DrawPic (0, 0, sb_armor[1]);
1677 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1678 Sbar_DrawPic (0, 0, sb_armor[0]);
1687 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1690 if (gamemode == GAME_ROGUE)
1692 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1693 Sbar_DrawPic (224, 0, sb_ammo[0]);
1694 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1695 Sbar_DrawPic (224, 0, sb_ammo[1]);
1696 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1697 Sbar_DrawPic (224, 0, sb_ammo[2]);
1698 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1699 Sbar_DrawPic (224, 0, sb_ammo[3]);
1700 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1701 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1702 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1703 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1704 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1705 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1709 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1710 Sbar_DrawPic (224, 0, sb_ammo[0]);
1711 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1712 Sbar_DrawPic (224, 0, sb_ammo[1]);
1713 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1714 Sbar_DrawPic (224, 0, sb_ammo[2]);
1715 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1716 Sbar_DrawPic (224, 0, sb_ammo[3]);
1719 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1721 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1722 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1724 if (gamemode == GAME_TRANSFUSION)
1725 Sbar_MiniDeathmatchOverlay (0, 0);
1727 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1734 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1736 pic = Draw_CachePic (va(vabuf, sizeof(vabuf), "gfx/crosshair%i", crosshair.integer));
1737 DrawQ_Pic((vid_conwidth.integer - pic->width * crosshair_size.value) * 0.5f, (vid_conheight.integer - pic->height * crosshair_size.value) * 0.5f, pic, pic->width * crosshair_size.value, pic->height * crosshair_size.value, crosshair_color_red.value, crosshair_color_green.value, crosshair_color_blue.value, crosshair_color_alpha.value, 0);
1740 if (cl_prydoncursor.integer > 0)
1741 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);
1744 //=============================================================================
1748 Sbar_DeathmatchOverlay
1752 static float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1755 qboolean myself = false;
1758 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1760 if((s - cl.scores) == cl.playerentity - 1)
1762 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1763 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1766 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1768 if (s->qw_spectator)
1770 if (s->qw_ping || s->qw_packetloss)
1771 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 );
1773 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 );
1777 // draw colors behind score
1783 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1784 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);
1785 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1786 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);
1788 //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);
1789 if (s->qw_ping || s->qw_packetloss)
1790 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 );
1792 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 );
1797 if (s->qw_spectator)
1799 if (s->qw_ping || s->qw_packetloss)
1800 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 );
1802 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 );
1806 // draw colors behind score
1807 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1808 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);
1809 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1810 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);
1812 //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);
1813 if (s->qw_ping || s->qw_packetloss)
1814 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 );
1816 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 );
1822 void Sbar_DeathmatchOverlay (void)
1824 int i, y, xmin, xmax, ymin, ymax;
1827 // request new ping times every two second
1828 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1830 cl.last_ping_request = realtime;
1831 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1833 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1834 MSG_WriteString(&cls.netcon->message, "pings");
1836 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*/)
1838 // 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
1839 static int ping_anyway_counter = 0;
1840 if(cl.parsingtextexpectingpingforscores == 1)
1842 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1843 if(++ping_anyway_counter >= 5)
1844 cl.parsingtextexpectingpingforscores = 0;
1846 if(cl.parsingtextexpectingpingforscores != 1)
1848 ping_anyway_counter = 0;
1849 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1850 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1851 MSG_WriteString(&cls.netcon->message, "ping");
1856 // 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
1857 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1858 MSG_WriteString(&cls.netcon->message, "pings");
1866 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1868 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1869 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)
1871 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)
1872 xmax = vid_conwidth.integer - xmin;
1874 if(IS_OLDNEXUIZ_DERIVED(gamemode))
1875 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);
1877 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1881 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1883 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 );
1887 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 );
1891 if (Sbar_IsTeammatch ())
1893 // show team scores first
1894 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1895 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1899 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1900 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1905 Sbar_MiniDeathmatchOverlay
1909 void Sbar_MiniDeathmatchOverlay (int x, int y)
1911 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1913 // do not draw this if sbar_miniscoreboard_size is zero
1914 if(sbar_miniscoreboard_size.value == 0)
1916 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1917 if(sbar_miniscoreboard_size.value > 0)
1918 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1923 // decide where to print
1924 if (gamemode == GAME_TRANSFUSION)
1925 numlines = (vid_conwidth.integer - x + 127) / 128;
1927 numlines = (vid_conheight.integer - y + 7) / 8;
1929 // give up if there isn't room
1930 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1934 for (i = 0; i < scoreboardlines; i++)
1935 if (fragsort[i] == cl.playerentity - 1)
1939 range_end = scoreboardlines;
1942 if (gamemode != GAME_TRANSFUSION)
1943 if (Sbar_IsTeammatch ())
1945 // reserve space for the team scores
1946 numlines -= teamlines;
1948 // find first and last player of my team (only draw the team totals and my own team)
1949 range_begin = range_end = i;
1950 myteam = cl.scores[fragsort[i]].colors & 15;
1951 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1953 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1956 // looks better than two players
1966 i = min(i, range_end - numlines);
1967 i = max(i, range_begin);
1969 if (gamemode == GAME_TRANSFUSION)
1971 for (;i < range_end && x < vid_conwidth.integer;i++)
1972 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1976 if(range_end - i < numlines) // won't draw to bottom?
1977 y += 8 * (numlines - (range_end - i)); // bottom align
1978 // show team scores first
1979 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1980 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1982 for (;i < range_end && y < vid_conheight.integer;i++)
1983 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1987 static int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1989 static int const sortorder[16] =
2008 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
2009 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
2010 int tc1 = sortorder[t1->colors & 15];
2011 int tc2 = sortorder[t2->colors & 15];
2015 void Sbar_Score (int margin)
2017 int i, me, score, otherleader, place, distribution, minutes, seconds;
2019 int sbar_x_save = sbar_x;
2020 int sbar_y_save = sbar_y;
2023 sbar_y = (int) (vid_conheight.value - (32+12));
2026 me = cl.playerentity - 1;
2027 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2029 if(Sbar_IsTeammatch())
2033 // team1 team3 team4
2037 scoreboard_t *teamcolorsort[16];
2040 for(i = 0; i < teamlines; ++i)
2041 teamcolorsort[i] = &(teams[i]);
2043 // Now sort them by color
2044 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2047 // -12*4: four digits space
2048 place = (teamlines - 1) * (-12 * 4);
2050 for(i = 0; i < teamlines; ++i)
2052 int cindex = teamcolorsort[i]->colors & 15;
2053 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2054 float cm = max(max(c[0], c[1]), c[2]);
2055 float cr = c[0] / cm;
2056 float cg = c[1] / cm;
2057 float cb = c[2] / cm;
2058 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2060 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2064 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2077 // find leading score other than ourselves, to calculate distribution
2078 // find our place in the scoreboard
2079 score = cl.scores[me].frags;
2080 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2082 if (cl.scores[i].name[0] && i != me)
2084 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2086 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2090 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2092 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2093 else if (place == 2)
2094 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2096 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2097 if (otherleader < 0)
2098 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2099 if (distribution >= 0)
2101 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2102 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2104 else if (distribution >= -5)
2106 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2107 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2111 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2112 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2117 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2119 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2120 minutes = (int)floor(timeleft / 60);
2121 seconds = (int)(floor(timeleft) - minutes * 60);
2124 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2125 if(sb_colon && sb_colon->tex != r_texture_notexture)
2126 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2127 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2129 else if (minutes >= 1)
2131 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2132 if(sb_colon && sb_colon->tex != r_texture_notexture)
2133 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2134 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2136 else if ((int)(timeleft * 4) & 1)
2137 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2139 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2141 else if (sbar_gametime.integer)
2143 minutes = (int)floor(cl.time / 60);
2144 seconds = (int)(floor(cl.time) - minutes * 60);
2145 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2146 if(sb_colon && sb_colon->tex != r_texture_notexture)
2147 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2148 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2151 sbar_x = sbar_x_save;
2152 sbar_y = sbar_y_save;
2157 Sbar_IntermissionOverlay
2161 void Sbar_IntermissionOverlay (void)
2166 if (cl.gametype == GAME_DEATHMATCH)
2168 Sbar_DeathmatchOverlay ();
2172 sbar_x = (vid_conwidth.integer - 320) >> 1;
2173 sbar_y = (vid_conheight.integer - 200) >> 1;
2175 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2176 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2179 dig = (int)cl.completed_time / 60;
2180 Sbar_DrawNum (160, 64, dig, 3, 0);
2181 num = (int)cl.completed_time - dig*60;
2182 Sbar_DrawPic (234,64,sb_colon);
2183 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2184 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2186 // LA: Display as "a" instead of "a/b" if b is 0
2187 if(cl.stats[STAT_TOTALSECRETS])
2189 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2190 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2191 Sbar_DrawPic (232, 104, sb_slash);
2192 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2196 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2199 if(cl.stats[STAT_TOTALMONSTERS])
2201 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2202 if (!IS_OLDNEXUIZ_DERIVED(gamemode))
2203 Sbar_DrawPic (232, 144, sb_slash);
2204 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2208 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2219 void Sbar_FinaleOverlay (void)
2221 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);