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];
74 cachepic_t *somsb_health;
75 cachepic_t *somsb_ammo[4];
76 cachepic_t *somsb_armor[3];
78 cachepic_t *zymsb_crosshair_center;
79 cachepic_t *zymsb_crosshair_line;
80 cachepic_t *zymsb_crosshair_health;
81 cachepic_t *zymsb_crosshair_ammo;
82 cachepic_t *zymsb_crosshair_clip;
83 cachepic_t *zymsb_crosshair_background;
84 cachepic_t *zymsb_crosshair_left1;
85 cachepic_t *zymsb_crosshair_left2;
86 cachepic_t *zymsb_crosshair_right;
88 cachepic_t *sb_ranking;
89 cachepic_t *sb_complete;
91 cachepic_t *sb_finale;
93 cvar_t showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
94 cvar_t showsound = {CVAR_SAVE, "showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
95 cvar_t showblur = {CVAR_SAVE, "showblur", "0", "shows the current alpha level of motionblur"};
96 cvar_t showspeed = {CVAR_SAVE, "showspeed", "0", "shows your current speed (qu per second); number selects unit: 1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots"};
97 cvar_t showtopspeed = {CVAR_SAVE, "showtopspeed", "0", "shows your top speed (kept on screen for max 3 seconds); value -1 takes over the unit from showspeed, otherwise it's an unit number just like in showspeed"};
98 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
99 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
100 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
101 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
102 cvar_t showtex = {0, "showtex", "0", "shows the name of the texture on the crosshair (for map debugging)"};
103 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
104 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
105 cvar_t sbar_hudselector = {CVAR_SAVE, "sbar_hudselector", "0", "selects which of the builtin hud layouts to use (meaning is somewhat dependent on gamemode, so nexuiz has a very different set of hud layouts than quake for example)"};
106 cvar_t sbar_scorerank = {CVAR_SAVE, "sbar_scorerank", "1", "shows an overlay for your score (or team score) and rank in the scoreboard"};
107 cvar_t sbar_gametime = {CVAR_SAVE, "sbar_gametime", "1", "shows an overlay for the time left in the current match/level (or current game time if there is no timelimit set)"};
108 cvar_t sbar_miniscoreboard_size = {CVAR_SAVE, "sbar_miniscoreboard_size", "-1", "sets the size of the mini deathmatch overlay in items, or disables it when set to 0, or sets it to a sane default when set to -1"};
109 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
110 cvar_t sbar_flagstatus_pos = {CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
111 cvar_t sbar_info_pos = {CVAR_SAVE, "sbar_info_pos", "0", "pixel position of the info strings (such as showfps), from the bottom"};
113 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
115 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
116 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
117 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
118 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
119 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
121 static void Sbar_MiniDeathmatchOverlay (int x, int y);
122 static void Sbar_DeathmatchOverlay (void);
123 static void Sbar_IntermissionOverlay (void);
124 static void Sbar_FinaleOverlay (void);
135 static void Sbar_ShowScores (void)
139 sb_showscores = true;
140 CL_VM_UpdateShowingScoresState(sb_showscores);
150 static void Sbar_DontShowScores (void)
152 sb_showscores = false;
153 CL_VM_UpdateShowingScoresState(sb_showscores);
156 static void sbar_start(void)
161 if (gamemode == GAME_DELUXEQUAKE || gamemode == GAME_BLOODOMNICIDE)
164 else if (gamemode == GAME_SOM)
166 sb_disc = Draw_CachePic_Flags ("gfx/disc", CACHEPICFLAG_QUIET);
168 for (i = 0;i < 10;i++)
169 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
171 somsb_health = Draw_CachePic_Flags ("gfx/hud_health", CACHEPICFLAG_QUIET);
172 somsb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
173 somsb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_nails", CACHEPICFLAG_QUIET);
174 somsb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
175 somsb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
176 somsb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor1", CACHEPICFLAG_QUIET);
177 somsb_armor[1] = Draw_CachePic_Flags ("gfx/sb_armor2", CACHEPICFLAG_QUIET);
178 somsb_armor[2] = Draw_CachePic_Flags ("gfx/sb_armor3", CACHEPICFLAG_QUIET);
180 else if (gamemode == GAME_NEXUIZ)
182 for (i = 0;i < 10;i++)
183 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
184 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
185 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET);
187 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
188 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_bullets", CACHEPICFLAG_QUIET);
189 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
190 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
192 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor", CACHEPICFLAG_QUIET);
196 sb_health = Draw_CachePic_Flags ("gfx/sb_health", CACHEPICFLAG_QUIET);
198 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_slowmo", CACHEPICFLAG_QUIET);
199 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invinc", CACHEPICFLAG_QUIET);
200 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_energy", CACHEPICFLAG_QUIET);
201 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_str", CACHEPICFLAG_QUIET);
203 sb_items[11] = Draw_CachePic_Flags ("gfx/sb_flag_red_taken", CACHEPICFLAG_QUIET);
204 sb_items[12] = Draw_CachePic_Flags ("gfx/sb_flag_red_lost", CACHEPICFLAG_QUIET);
205 sb_items[13] = Draw_CachePic_Flags ("gfx/sb_flag_red_carrying", CACHEPICFLAG_QUIET);
206 sb_items[14] = Draw_CachePic_Flags ("gfx/sb_key_carrying", CACHEPICFLAG_QUIET);
207 sb_items[15] = Draw_CachePic_Flags ("gfx/sb_flag_blue_taken", CACHEPICFLAG_QUIET);
208 sb_items[16] = Draw_CachePic_Flags ("gfx/sb_flag_blue_lost", CACHEPICFLAG_QUIET);
209 sb_items[17] = Draw_CachePic_Flags ("gfx/sb_flag_blue_carrying", CACHEPICFLAG_QUIET);
211 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
212 sb_sbar_minimal = Draw_CachePic_Flags ("gfx/sbar_minimal", CACHEPICFLAG_QUIET);
213 sb_sbar_overlay = Draw_CachePic_Flags ("gfx/sbar_overlay", CACHEPICFLAG_QUIET);
215 for(i = 0; i < 9;i++)
216 sb_weapons[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inv_weapon%i",i), CACHEPICFLAG_QUIET);
218 else if (gamemode == GAME_ZYMOTIC)
220 zymsb_crosshair_center = Draw_CachePic_Flags ("gfx/hud/crosshair_center", CACHEPICFLAG_QUIET);
221 zymsb_crosshair_line = Draw_CachePic_Flags ("gfx/hud/crosshair_line", CACHEPICFLAG_QUIET);
222 zymsb_crosshair_health = Draw_CachePic_Flags ("gfx/hud/crosshair_health", CACHEPICFLAG_QUIET);
223 zymsb_crosshair_clip = Draw_CachePic_Flags ("gfx/hud/crosshair_clip", CACHEPICFLAG_QUIET);
224 zymsb_crosshair_ammo = Draw_CachePic_Flags ("gfx/hud/crosshair_ammo", CACHEPICFLAG_QUIET);
225 zymsb_crosshair_background = Draw_CachePic_Flags ("gfx/hud/crosshair_background", CACHEPICFLAG_QUIET);
226 zymsb_crosshair_left1 = Draw_CachePic_Flags ("gfx/hud/crosshair_left1", CACHEPICFLAG_QUIET);
227 zymsb_crosshair_left2 = Draw_CachePic_Flags ("gfx/hud/crosshair_left2", CACHEPICFLAG_QUIET);
228 zymsb_crosshair_right = Draw_CachePic_Flags ("gfx/hud/crosshair_right", CACHEPICFLAG_QUIET);
232 sb_disc = Draw_CachePic_Flags ("gfx/disc", CACHEPICFLAG_QUIET);
234 for (i = 0;i < 10;i++)
236 sb_nums[0][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/num_%i",i), CACHEPICFLAG_QUIET);
237 sb_nums[1][i] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/anum_%i",i), CACHEPICFLAG_QUIET);
240 sb_nums[0][10] = Draw_CachePic_Flags ("gfx/num_minus", CACHEPICFLAG_QUIET);
241 sb_nums[1][10] = Draw_CachePic_Flags ("gfx/anum_minus", CACHEPICFLAG_QUIET);
243 sb_colon = Draw_CachePic_Flags ("gfx/num_colon", CACHEPICFLAG_QUIET);
244 sb_slash = Draw_CachePic_Flags ("gfx/num_slash", CACHEPICFLAG_QUIET);
246 sb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_shotgun", CACHEPICFLAG_QUIET);
247 sb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_sshotgun", CACHEPICFLAG_QUIET);
248 sb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_nailgun", CACHEPICFLAG_QUIET);
249 sb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_snailgun", CACHEPICFLAG_QUIET);
250 sb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_rlaunch", CACHEPICFLAG_QUIET);
251 sb_weapons[0][5] = Draw_CachePic_Flags ("gfx/inv_srlaunch", CACHEPICFLAG_QUIET);
252 sb_weapons[0][6] = Draw_CachePic_Flags ("gfx/inv_lightng", CACHEPICFLAG_QUIET);
254 sb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_shotgun", CACHEPICFLAG_QUIET);
255 sb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_sshotgun", CACHEPICFLAG_QUIET);
256 sb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_nailgun", CACHEPICFLAG_QUIET);
257 sb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_snailgun", CACHEPICFLAG_QUIET);
258 sb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_rlaunch", CACHEPICFLAG_QUIET);
259 sb_weapons[1][5] = Draw_CachePic_Flags ("gfx/inv2_srlaunch", CACHEPICFLAG_QUIET);
260 sb_weapons[1][6] = Draw_CachePic_Flags ("gfx/inv2_lightng", CACHEPICFLAG_QUIET);
262 for (i = 0;i < 5;i++)
264 sb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_shotgun",i+1), CACHEPICFLAG_QUIET);
265 sb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_sshotgun",i+1), CACHEPICFLAG_QUIET);
266 sb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_nailgun",i+1), CACHEPICFLAG_QUIET);
267 sb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_snailgun",i+1), CACHEPICFLAG_QUIET);
268 sb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_rlaunch",i+1), CACHEPICFLAG_QUIET);
269 sb_weapons[2+i][5] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_srlaunch",i+1), CACHEPICFLAG_QUIET);
270 sb_weapons[2+i][6] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_lightng",i+1), CACHEPICFLAG_QUIET);
273 sb_ammo[0] = Draw_CachePic_Flags ("gfx/sb_shells", CACHEPICFLAG_QUIET);
274 sb_ammo[1] = Draw_CachePic_Flags ("gfx/sb_nails", CACHEPICFLAG_QUIET);
275 sb_ammo[2] = Draw_CachePic_Flags ("gfx/sb_rocket", CACHEPICFLAG_QUIET);
276 sb_ammo[3] = Draw_CachePic_Flags ("gfx/sb_cells", CACHEPICFLAG_QUIET);
278 sb_armor[0] = Draw_CachePic_Flags ("gfx/sb_armor1", CACHEPICFLAG_QUIET);
279 sb_armor[1] = Draw_CachePic_Flags ("gfx/sb_armor2", CACHEPICFLAG_QUIET);
280 sb_armor[2] = Draw_CachePic_Flags ("gfx/sb_armor3", CACHEPICFLAG_QUIET);
282 sb_items[0] = Draw_CachePic_Flags ("gfx/sb_key1", CACHEPICFLAG_QUIET);
283 sb_items[1] = Draw_CachePic_Flags ("gfx/sb_key2", CACHEPICFLAG_QUIET);
284 sb_items[2] = Draw_CachePic_Flags ("gfx/sb_invis", CACHEPICFLAG_QUIET);
285 sb_items[3] = Draw_CachePic_Flags ("gfx/sb_invuln", CACHEPICFLAG_QUIET);
286 sb_items[4] = Draw_CachePic_Flags ("gfx/sb_suit", CACHEPICFLAG_QUIET);
287 sb_items[5] = Draw_CachePic_Flags ("gfx/sb_quad", CACHEPICFLAG_QUIET);
289 sb_sigil[0] = Draw_CachePic_Flags ("gfx/sb_sigil1", CACHEPICFLAG_QUIET);
290 sb_sigil[1] = Draw_CachePic_Flags ("gfx/sb_sigil2", CACHEPICFLAG_QUIET);
291 sb_sigil[2] = Draw_CachePic_Flags ("gfx/sb_sigil3", CACHEPICFLAG_QUIET);
292 sb_sigil[3] = Draw_CachePic_Flags ("gfx/sb_sigil4", CACHEPICFLAG_QUIET);
294 sb_faces[4][0] = Draw_CachePic_Flags ("gfx/face1", CACHEPICFLAG_QUIET);
295 sb_faces[4][1] = Draw_CachePic_Flags ("gfx/face_p1", CACHEPICFLAG_QUIET);
296 sb_faces[3][0] = Draw_CachePic_Flags ("gfx/face2", CACHEPICFLAG_QUIET);
297 sb_faces[3][1] = Draw_CachePic_Flags ("gfx/face_p2", CACHEPICFLAG_QUIET);
298 sb_faces[2][0] = Draw_CachePic_Flags ("gfx/face3", CACHEPICFLAG_QUIET);
299 sb_faces[2][1] = Draw_CachePic_Flags ("gfx/face_p3", CACHEPICFLAG_QUIET);
300 sb_faces[1][0] = Draw_CachePic_Flags ("gfx/face4", CACHEPICFLAG_QUIET);
301 sb_faces[1][1] = Draw_CachePic_Flags ("gfx/face_p4", CACHEPICFLAG_QUIET);
302 sb_faces[0][0] = Draw_CachePic_Flags ("gfx/face5", CACHEPICFLAG_QUIET);
303 sb_faces[0][1] = Draw_CachePic_Flags ("gfx/face_p5", CACHEPICFLAG_QUIET);
305 sb_face_invis = Draw_CachePic_Flags ("gfx/face_invis", CACHEPICFLAG_QUIET);
306 sb_face_invuln = Draw_CachePic_Flags ("gfx/face_invul2", CACHEPICFLAG_QUIET);
307 sb_face_invis_invuln = Draw_CachePic_Flags ("gfx/face_inv2", CACHEPICFLAG_QUIET);
308 sb_face_quad = Draw_CachePic_Flags ("gfx/face_quad", CACHEPICFLAG_QUIET);
310 sb_sbar = Draw_CachePic_Flags ("gfx/sbar", CACHEPICFLAG_QUIET);
311 sb_ibar = Draw_CachePic_Flags ("gfx/ibar", CACHEPICFLAG_QUIET);
312 sb_scorebar = Draw_CachePic_Flags ("gfx/scorebar", CACHEPICFLAG_QUIET);
314 //MED 01/04/97 added new hipnotic weapons
315 if (gamemode == GAME_HIPNOTIC)
317 hsb_weapons[0][0] = Draw_CachePic_Flags ("gfx/inv_laser", CACHEPICFLAG_QUIET);
318 hsb_weapons[0][1] = Draw_CachePic_Flags ("gfx/inv_mjolnir", CACHEPICFLAG_QUIET);
319 hsb_weapons[0][2] = Draw_CachePic_Flags ("gfx/inv_gren_prox", CACHEPICFLAG_QUIET);
320 hsb_weapons[0][3] = Draw_CachePic_Flags ("gfx/inv_prox_gren", CACHEPICFLAG_QUIET);
321 hsb_weapons[0][4] = Draw_CachePic_Flags ("gfx/inv_prox", CACHEPICFLAG_QUIET);
323 hsb_weapons[1][0] = Draw_CachePic_Flags ("gfx/inv2_laser", CACHEPICFLAG_QUIET);
324 hsb_weapons[1][1] = Draw_CachePic_Flags ("gfx/inv2_mjolnir", CACHEPICFLAG_QUIET);
325 hsb_weapons[1][2] = Draw_CachePic_Flags ("gfx/inv2_gren_prox", CACHEPICFLAG_QUIET);
326 hsb_weapons[1][3] = Draw_CachePic_Flags ("gfx/inv2_prox_gren", CACHEPICFLAG_QUIET);
327 hsb_weapons[1][4] = Draw_CachePic_Flags ("gfx/inv2_prox", CACHEPICFLAG_QUIET);
329 for (i = 0;i < 5;i++)
331 hsb_weapons[2+i][0] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_laser",i+1), CACHEPICFLAG_QUIET);
332 hsb_weapons[2+i][1] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_mjolnir",i+1), CACHEPICFLAG_QUIET);
333 hsb_weapons[2+i][2] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_gren_prox",i+1), CACHEPICFLAG_QUIET);
334 hsb_weapons[2+i][3] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox_gren",i+1), CACHEPICFLAG_QUIET);
335 hsb_weapons[2+i][4] = Draw_CachePic_Flags (va(vabuf, sizeof(vabuf), "gfx/inva%i_prox",i+1), CACHEPICFLAG_QUIET);
338 hsb_items[0] = Draw_CachePic_Flags ("gfx/sb_wsuit", CACHEPICFLAG_QUIET);
339 hsb_items[1] = Draw_CachePic_Flags ("gfx/sb_eshld", CACHEPICFLAG_QUIET);
341 else if (gamemode == GAME_ROGUE)
343 rsb_invbar[0] = Draw_CachePic_Flags ("gfx/r_invbar1", CACHEPICFLAG_QUIET);
344 rsb_invbar[1] = Draw_CachePic_Flags ("gfx/r_invbar2", CACHEPICFLAG_QUIET);
346 rsb_weapons[0] = Draw_CachePic_Flags ("gfx/r_lava", CACHEPICFLAG_QUIET);
347 rsb_weapons[1] = Draw_CachePic_Flags ("gfx/r_superlava", CACHEPICFLAG_QUIET);
348 rsb_weapons[2] = Draw_CachePic_Flags ("gfx/r_gren", CACHEPICFLAG_QUIET);
349 rsb_weapons[3] = Draw_CachePic_Flags ("gfx/r_multirock", CACHEPICFLAG_QUIET);
350 rsb_weapons[4] = Draw_CachePic_Flags ("gfx/r_plasma", CACHEPICFLAG_QUIET);
352 rsb_items[0] = Draw_CachePic_Flags ("gfx/r_shield1", CACHEPICFLAG_QUIET);
353 rsb_items[1] = Draw_CachePic_Flags ("gfx/r_agrav1", CACHEPICFLAG_QUIET);
355 // PGM 01/19/97 - team color border
356 rsb_teambord = Draw_CachePic_Flags ("gfx/r_teambord", CACHEPICFLAG_QUIET);
357 // PGM 01/19/97 - team color border
359 rsb_ammo[0] = Draw_CachePic_Flags ("gfx/r_ammolava", CACHEPICFLAG_QUIET);
360 rsb_ammo[1] = Draw_CachePic_Flags ("gfx/r_ammomulti", CACHEPICFLAG_QUIET);
361 rsb_ammo[2] = Draw_CachePic_Flags ("gfx/r_ammoplasma", CACHEPICFLAG_QUIET);
365 sb_ranking = Draw_CachePic_Flags ("gfx/ranking", CACHEPICFLAG_QUIET);
366 sb_complete = Draw_CachePic_Flags ("gfx/complete", CACHEPICFLAG_QUIET);
367 sb_inter = Draw_CachePic_Flags ("gfx/inter", CACHEPICFLAG_QUIET);
368 sb_finale = Draw_CachePic_Flags ("gfx/finale", CACHEPICFLAG_QUIET);
371 static void sbar_shutdown(void)
375 static void sbar_newmap(void)
379 void Sbar_Init (void)
381 Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
382 Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
383 Cvar_RegisterVariable(&showfps);
384 Cvar_RegisterVariable(&showsound);
385 Cvar_RegisterVariable(&showblur);
386 Cvar_RegisterVariable(&showspeed);
387 Cvar_RegisterVariable(&showtopspeed);
388 Cvar_RegisterVariable(&showtime);
389 Cvar_RegisterVariable(&showtime_format);
390 Cvar_RegisterVariable(&showdate);
391 Cvar_RegisterVariable(&showdate_format);
392 Cvar_RegisterVariable(&showtex);
393 Cvar_RegisterVariable(&sbar_alpha_bg);
394 Cvar_RegisterVariable(&sbar_alpha_fg);
395 Cvar_RegisterVariable(&sbar_hudselector);
396 Cvar_RegisterVariable(&sbar_scorerank);
397 Cvar_RegisterVariable(&sbar_gametime);
398 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
399 Cvar_RegisterVariable(&sbar_info_pos);
400 Cvar_RegisterVariable(&cl_deathscoreboard);
402 Cvar_RegisterVariable(&crosshair_color_red);
403 Cvar_RegisterVariable(&crosshair_color_green);
404 Cvar_RegisterVariable(&crosshair_color_blue);
405 Cvar_RegisterVariable(&crosshair_color_alpha);
406 Cvar_RegisterVariable(&crosshair_size);
408 Cvar_RegisterVariable(&sbar_flagstatus_right); // (GAME_NEXUZI ONLY)
409 Cvar_RegisterVariable(&sbar_flagstatus_pos); // (GAME_NEXUIZ ONLY)
411 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap, NULL, NULL);
415 //=============================================================================
417 // drawing routines are relative to the status bar location
426 static void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
428 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
431 static void Sbar_DrawPic (int x, int y, cachepic_t *pic)
433 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
436 static void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
438 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
445 Draws one solid graphics character
448 static void Sbar_DrawCharacter (int x, int y, int num)
451 DrawQ_String (sbar_x + x + 4 , sbar_y + y, va(vabuf, sizeof(vabuf), "%c", num), 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, true, FONT_SBAR);
459 static void Sbar_DrawString (int x, int y, char *str)
461 DrawQ_String (sbar_x + x, sbar_y + y, str, 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR);
469 static void Sbar_DrawNum (int x, int y, int num, int digits, int color)
474 l = dpsnprintf(str, sizeof(str), "%i", num);
488 Sbar_DrawPic (x, y, sb_nums[color][frame]);
501 static void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
509 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
512 l = dpsnprintf(str, sizeof(str), "%i", num);
517 x += (digits-l) * lettersize;
526 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
533 //=============================================================================
536 static int Sbar_IsTeammatch(void)
538 // currently only nexuiz uses the team score board
539 return ((gamemode == GAME_NEXUIZ)
540 && (teamplay.integer > 0));
548 static int fragsort[MAX_SCOREBOARD];
549 static int scoreboardlines;
551 int Sbar_GetSortedPlayerIndex (int index)
553 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
556 static scoreboard_t teams[MAX_SCOREBOARD];
557 static int teamsort[MAX_SCOREBOARD];
558 static int teamlines;
559 void Sbar_SortFrags (void)
565 for (i=0 ; i<cl.maxclients ; i++)
567 if (cl.scores[i].name[0])
569 fragsort[scoreboardlines] = i;
574 for (i=0 ; i<scoreboardlines ; i++)
575 for (j=0 ; j<scoreboardlines-1-i ; j++)
576 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
579 fragsort[j] = fragsort[j+1];
584 if (Sbar_IsTeammatch ())
586 // now sort players by teams.
587 for (i=0 ; i<scoreboardlines ; i++)
589 for (j=0 ; j<scoreboardlines-1-i ; j++)
591 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
594 fragsort[j] = fragsort[j+1];
600 // calculate team scores
602 for (i=0 ; i<scoreboardlines ; i++)
604 if (color != (cl.scores[fragsort[i]].colors & 15))
606 const char* teamname;
608 color = cl.scores[fragsort[i]].colors & 15;
614 teamname = "^1Red Team";
617 teamname = "^4Blue Team";
620 teamname = "^6Pink Team";
623 teamname = "^3Yellow Team";
626 teamname = "Total Team Score";
629 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
631 teams[teamlines-1].frags = 0;
632 teams[teamlines-1].colors = color + 16 * color;
635 if (cl.scores[fragsort[i]].frags != -666)
637 // do not add spedcators
638 // (ugly hack for nexuiz)
639 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
643 // now sort teams by scores.
644 for (i=0 ; i<teamlines ; i++)
646 for (i=0 ; i<teamlines ; i++)
648 for (j=0 ; j<teamlines-1-i ; j++)
650 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
653 teamsort[j] = teamsort[j+1];
666 static void Sbar_SoloScoreboard (void)
669 char str[80], timestr[40];
671 int minutes, seconds;
675 t = (cl.intermission ? cl.completed_time : cl.time);
676 minutes = (int)(t / 60);
677 seconds = (int)(t - 60*floor(t/60));
679 // monsters and secrets are now both on the top row
680 if (cl.stats[STAT_TOTALMONSTERS])
681 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
682 else if (cl.stats[STAT_MONSTERS]) // LA: Display something if monsters_killed is non-zero, but total_monsters is zero
683 Sbar_DrawString(8, 4, va(vabuf, sizeof(vabuf), "Monsters:%3i", cl.stats[STAT_MONSTERS]));
685 if (cl.stats[STAT_TOTALSECRETS])
686 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
687 else if (cl.stats[STAT_SECRETS]) // LA: And similarly for secrets
688 Sbar_DrawString(8+22*8, 4, va(vabuf, sizeof(vabuf), "Secrets:%3i", cl.stats[STAT_SECRETS]));
690 // format is like this: e1m1:The Sligpate Complex
691 dpsnprintf(str, sizeof(str), "%s:%s", cl.worldbasename, cl.worldmessage);
693 // if there's a newline character, terminate the string there
694 if (strchr(str, '\n'))
695 *(strchr(str, '\n')) = 0;
697 // make the time string
698 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
700 // truncate the level name if necessary to make room for time
702 if ((int)strlen(str) > max)
705 // print the filename and message
706 Sbar_DrawString(8, 12, str);
709 Sbar_DrawString(8 + max*8, 12, timestr);
713 int minutes, seconds, tens, units;
716 if (gamemode != GAME_NEXUIZ) {
717 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
718 Sbar_DrawString (8, 4, str);
720 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
721 Sbar_DrawString (8, 12, str);
725 minutes = (int)(cl.time / 60);
726 seconds = (int)(cl.time - 60*minutes);
728 units = seconds - 10*tens;
729 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
730 Sbar_DrawString (184, 4, str);
733 if (gamemode == GAME_NEXUIZ) {
734 l = (int) strlen (cl.worldname);
735 Sbar_DrawString (232 - l*4, 12, cl.worldname);
737 l = (int) strlen (cl.worldmessage);
738 Sbar_DrawString (232 - l*4, 12, cl.worldmessage);
748 static void Sbar_DrawScoreboard (void)
750 Sbar_SoloScoreboard ();
751 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
752 //if (cl.gametype == GAME_DEATHMATCH)
754 Sbar_DeathmatchOverlay ();
757 //=============================================================================
759 // AK to make DrawInventory smaller
760 static void Sbar_DrawWeapon(int nr, float fade, int active)
763 if (sbar_hudselector.integer == 1)
765 // width = 300, height = 100
766 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
768 DrawQ_Pic((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr, vid_conheight.integer - w_height, sb_weapons[0][nr], w_width, w_height, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 0.6, (active ? 1 : 0.6) * fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
770 DrawQ_String((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr + w_space, vid_conheight.integer - w_height + w_space, va(vabuf, sizeof(vabuf), "%i",nr+1), 0, font_size, font_size, 1, 1, 0, sbar_alpha_fg.value, 0, NULL, true, FONT_DEFAULT);
774 // width = 300, height = 100
775 const int w_width = 300, w_height = 100, w_space = 10;
776 const float w_scale = 0.4;
778 DrawQ_Pic(vid_conwidth.integer - (w_width + w_space) * w_scale, (w_height + w_space) * w_scale * nr + w_space, sb_weapons[0][nr], w_width * w_scale, w_height * w_scale, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 1, fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
779 //DrawQ_String(vid_conwidth.integer - (w_space + font_size ), (w_height + w_space) * w_scale * nr + w_space, va(vabuf, sizeof(vabuf), "%i",nr+1), 0, font_size, font_size, 1, 0, 0, fade, 0, NULL, true, FONT_DEFAULT);
788 static void Sbar_DrawInventory (void)
795 if (gamemode == GAME_ROGUE)
797 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
798 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
800 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
803 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
806 for (i=0 ; i<7 ; i++)
808 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
810 time = cl.item_gettime[i];
811 flashon = (int)(max(0, cl.time - time)*10);
814 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
820 flashon = (flashon%5) + 2;
822 Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
828 if (gamemode == GAME_HIPNOTIC)
830 int grenadeflashing=0;
831 for (i=0 ; i<4 ; i++)
833 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
835 time = max(0, cl.item_gettime[hipweapons[i]]);
836 flashon = (int)((cl.time - time)*10);
839 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
845 flashon = (flashon%5) + 2;
847 // check grenade launcher
850 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
855 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
861 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
863 if (!grenadeflashing)
864 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
867 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
870 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
875 if (gamemode == GAME_ROGUE)
877 // check for powered up weapon.
878 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
880 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
881 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
885 for (i=0 ; i<4 ; i++)
887 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
889 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
891 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
893 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
895 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
899 for (i=0 ; i<6 ; i++)
900 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
902 //MED 01/04/97 changed keys
903 if (gamemode != GAME_HIPNOTIC || (i>1))
904 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
907 //MED 01/04/97 added hipnotic items
909 if (gamemode == GAME_HIPNOTIC)
911 for (i=0 ; i<2 ; i++)
912 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
913 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
916 if (gamemode == GAME_ROGUE)
919 for (i=0 ; i<2 ; i++)
920 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
921 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
926 for (i=0 ; i<4 ; i++)
927 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
928 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
932 //=============================================================================
939 static void Sbar_DrawFrags (void)
949 l = min(scoreboardlines, 4);
953 for (i = 0;i < l;i++)
959 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
960 DrawQ_Fill (sbar_x + x + 10, sbar_y - 23, 28, 4, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
961 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
962 DrawQ_Fill (sbar_x + x + 10, sbar_y + 4 - 23, 28, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
966 dpsnprintf (num, sizeof(num), "%3i",f);
968 if (k == cl.viewentity - 1)
970 Sbar_DrawCharacter ( x + 2, -24, 16);
971 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
973 Sbar_DrawCharacter (x + 8, -24, num[0]);
974 Sbar_DrawCharacter (x + 16, -24, num[1]);
975 Sbar_DrawCharacter (x + 24, -24, num[2]);
980 //=============================================================================
988 static void Sbar_DrawFace (void)
992 // PGM 01/19/97 - team color drawing
993 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
994 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
1000 s = &cl.scores[cl.viewentity - 1];
1002 Sbar_DrawPic (112, 0, rsb_teambord);
1003 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1004 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+3, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1005 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1006 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+12, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1010 dpsnprintf (num, sizeof(num), "%3i",f);
1012 if ((s->colors & 0xf0)==0)
1015 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1017 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1019 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1023 Sbar_DrawCharacter ( 109, 3, num[0]);
1024 Sbar_DrawCharacter ( 116, 3, num[1]);
1025 Sbar_DrawCharacter ( 123, 3, num[2]);
1030 // PGM 01/19/97 - team color drawing
1032 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1033 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1034 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1035 Sbar_DrawPic (112, 0, sb_face_quad );
1036 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1037 Sbar_DrawPic (112, 0, sb_face_invis );
1038 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1039 Sbar_DrawPic (112, 0, sb_face_invuln);
1042 f = cl.stats[STAT_HEALTH] / 20;
1044 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1047 double topspeed = 0;
1048 double topspeedxy = 0;
1049 time_t current_time = 3;
1050 time_t top_time = 0;
1051 time_t topxy_time = 0;
1053 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1056 unitnumber = showspeed.integer;
1061 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
1065 *conversion_factor = 1.0;
1069 *conversion_factor = 0.0254;
1070 if(gamemode != GAME_NEXUIZ && gamemode != GAME_XONOTIC) *conversion_factor *= 1.5;
1071 // 1qu=1.5in is for non-Nexuiz/Xonotic only - Nexuiz/Xonotic players are overly large, but 1qu=1in fixes that
1075 *conversion_factor = 0.0254 * 3.6;
1076 if(gamemode != GAME_NEXUIZ && gamemode != GAME_XONOTIC) *conversion_factor *= 1.5;
1080 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1081 if(gamemode != GAME_NEXUIZ && gamemode != GAME_XONOTIC) *conversion_factor *= 1.5;
1085 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1086 if(gamemode != GAME_NEXUIZ && gamemode != GAME_XONOTIC) *conversion_factor *= 1.5;
1091 static double showfps_nexttime = 0, showfps_lasttime = -1;
1092 static double showfps_framerate = 0;
1093 static int showfps_framecount = 0;
1095 void Sbar_ShowFPS_Update(void)
1097 double interval = 1;
1100 if (newtime >= showfps_nexttime)
1102 showfps_framerate = showfps_framecount / (newtime - showfps_lasttime);
1103 if (showfps_nexttime < newtime - interval * 1.5)
1104 showfps_nexttime = newtime;
1105 showfps_lasttime = newtime;
1106 showfps_nexttime += interval;
1107 showfps_framecount = 0;
1109 showfps_framecount++;
1112 void Sbar_ShowFPS(void)
1114 float fps_x, fps_y, fps_scalex, fps_scaley, fps_strings = 0;
1115 char soundstring[32];
1117 char timestring[32];
1118 char datestring[32];
1119 char timedemostring1[32];
1120 char timedemostring2[32];
1121 char speedstring[32];
1122 char blurstring[32];
1123 char topspeedstring[48];
1124 char texstring[MAX_QPATH];
1125 qboolean red = false;
1128 timedemostring1[0] = 0;
1129 timedemostring2[0] = 0;
1135 topspeedstring[0] = 0;
1136 if (showfps.integer)
1138 red = (showfps_framerate < 1.0f);
1139 if(showfps.integer == 2)
1140 dpsnprintf(fpsstring, sizeof(fpsstring), "%7.3f mspf", (1000.0 / showfps_framerate));
1142 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0 / showfps_framerate + 0.5));
1144 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(showfps_framerate + 0.5));
1148 dpsnprintf(timedemostring1, sizeof(timedemostring1), "frame%4i %f", cls.td_frames, realtime - cls.td_starttime);
1149 dpsnprintf(timedemostring2, sizeof(timedemostring2), "%i seconds %3.0f/%3.0f/%3.0f fps", cls.td_onesecondavgcount, cls.td_onesecondminfps, cls.td_onesecondavgfps / max(1, cls.td_onesecondavgcount), cls.td_onesecondmaxfps);
1154 if (showtime.integer)
1156 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1159 if (showdate.integer)
1161 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1164 if (showblur.integer)
1166 dpsnprintf(blurstring, sizeof(blurstring), "%3i%% blur", (int)(cl.motionbluralpha * 100));
1169 if (showsound.integer)
1171 dpsnprintf(soundstring, sizeof(soundstring), "%4i/4%i at %3ims", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1174 if (showspeed.integer || showtopspeed.integer)
1176 double speed, speedxy, f;
1178 speed = VectorLength(cl.movement_velocity);
1179 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1180 if (showspeed.integer)
1182 get_showspeed_unit(showspeed.integer, &f, &unit);
1183 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1186 if (showtopspeed.integer)
1188 qboolean topspeed_latched = false, topspeedxy_latched = false;
1189 get_showspeed_unit(showtopspeed.integer, &f, &unit);
1190 if (speed >= topspeed || current_time - top_time > 3)
1196 topspeed_latched = true;
1197 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1199 topspeedxy = speedxy;
1203 topspeedxy_latched = true;
1204 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1205 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1206 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1208 time(¤t_time);
1212 if (showtex.integer)
1219 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, org);
1220 VectorSet(temp, 65536, 0, 0);
1221 Matrix4x4_Transform(&r_refdef.view.matrix, temp, dest);
1222 trace.hittexture = NULL; // to make sure
1223 // TODO change this trace to be stopped by anything "visible" (i.e. with a drawsurface), but not stuff like weapclip
1224 // probably needs adding a new SUPERCONTENTS type
1225 trace = CL_TraceLine(org, dest, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID, true, false, NULL, true, true);
1226 if(trace.hittexture)
1227 strlcpy(texstring, trace.hittexture->name, sizeof(texstring));
1229 strlcpy(texstring, "(no texture hit)", sizeof(texstring));
1236 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1237 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_strings*fps_scaley);
1238 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_strings*fps_scaley;
1241 fps_x = vid_conwidth.integer - DrawQ_TextWidth(soundstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1242 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1243 DrawQ_String(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1244 fps_y += fps_scaley;
1248 r_draw2d_force = true;
1249 fps_x = vid_conwidth.integer - DrawQ_TextWidth(fpsstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1250 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, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1254 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1255 fps_y += fps_scaley;
1256 r_draw2d_force = false;
1258 if (timedemostring1[0])
1260 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring1, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1261 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1262 DrawQ_String(fps_x, fps_y, timedemostring1, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1263 fps_y += fps_scaley;
1265 if (timedemostring2[0])
1267 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring2, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1268 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1269 DrawQ_String(fps_x, fps_y, timedemostring2, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1270 fps_y += fps_scaley;
1274 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1275 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1276 DrawQ_String(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1277 fps_y += fps_scaley;
1281 fps_x = vid_conwidth.integer - DrawQ_TextWidth(datestring, 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);
1283 DrawQ_String(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1284 fps_y += fps_scaley;
1288 fps_x = vid_conwidth.integer - DrawQ_TextWidth(speedstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1289 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1290 DrawQ_String(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1291 fps_y += fps_scaley;
1293 if (topspeedstring[0])
1295 fps_x = vid_conwidth.integer - DrawQ_TextWidth(topspeedstring, 0, fps_scalex, fps_scaley, false, FONT_INFOBAR);
1296 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1297 DrawQ_String(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1298 fps_y += fps_scaley;
1302 fps_x = vid_conwidth.integer - DrawQ_TextWidth(blurstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1303 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1304 DrawQ_String(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1305 fps_y += fps_scaley;
1309 fps_x = vid_conwidth.integer - DrawQ_TextWidth(texstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1310 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1311 DrawQ_String(fps_x, fps_y, texstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1312 fps_y += fps_scaley;
1317 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)
1320 c2 = bound(0, c2, 1);
1321 c1 = bound(0, c1, 1 - c2);
1323 r[1] = rangey + rangeheight * (c2 + c1);
1324 r[2] = rangey + rangeheight * (c2);
1328 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);
1330 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);
1332 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);
1334 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);
1342 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1343 extern cvar_t v_kicktime;
1344 void Sbar_Score (int margin);
1345 void Sbar_Draw (void)
1350 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1353 Sbar_DrawScoreboard ();
1354 else if (cl.intermission == 1)
1356 if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1358 Sbar_DrawScoreboard();
1361 Sbar_IntermissionOverlay();
1363 else if (cl.intermission == 2)
1364 Sbar_FinaleOverlay();
1365 else if (gamemode == GAME_DELUXEQUAKE)
1368 else if (gamemode == GAME_SOM)
1370 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1371 Sbar_DrawScoreboard ();
1374 // this is the top left of the sbar area
1376 sbar_y = vid_conheight.integer - 24*3;
1379 if (cl.stats[STAT_ARMOR])
1381 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1382 Sbar_DrawPic(0, 0, somsb_armor[2]);
1383 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1384 Sbar_DrawPic(0, 0, somsb_armor[1]);
1385 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1386 Sbar_DrawPic(0, 0, somsb_armor[0]);
1387 Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1391 Sbar_DrawPic(0, 24, somsb_health);
1392 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1395 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1396 Sbar_DrawPic(0, 48, somsb_ammo[0]);
1397 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1398 Sbar_DrawPic(0, 48, somsb_ammo[1]);
1399 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1400 Sbar_DrawPic(0, 48, somsb_ammo[2]);
1401 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1402 Sbar_DrawPic(0, 48, somsb_ammo[3]);
1403 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1404 if (cl.stats[STAT_SHELLS])
1405 Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1408 else if (gamemode == GAME_NEXUIZ)
1410 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1412 sbar_x = (vid_conwidth.integer - 640)/2;
1413 sbar_y = vid_conheight.integer - 47;
1414 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1415 Sbar_DrawScoreboard ();
1417 else if (sb_lines && sbar_hudselector.integer == 1)
1421 int redflag, blueflag;
1424 sbar_x = (vid_conwidth.integer - 320)/2;
1425 sbar_y = vid_conheight.integer - 24 - 16;
1427 // calculate intensity to draw weapons bar at
1428 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1429 fade = bound(0.7, fade, 1);
1430 for (i = 0; i < 8;i++)
1431 if (cl.stats[STAT_ITEMS] & (1 << i))
1432 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1433 if((cl.stats[STAT_ITEMS] & (1<<12)))
1434 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1437 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1438 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1439 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1440 if (redflag == 3 && blueflag == 3)
1442 // The Impossible Combination[tm]
1443 // Can only happen in Key Hunt mode...
1444 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1449 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1451 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1455 if (cl.stats[STAT_ARMOR] > 0)
1457 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1458 if(cl.stats[STAT_ARMOR] > 200)
1459 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1460 else if(cl.stats[STAT_ARMOR] > 100)
1461 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1462 else if(cl.stats[STAT_ARMOR] > 50)
1463 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1464 else if(cl.stats[STAT_ARMOR] > 25)
1465 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1467 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1471 if (cl.stats[STAT_HEALTH] != 0)
1473 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1474 if(cl.stats[STAT_HEALTH] > 200)
1475 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1476 else if(cl.stats[STAT_HEALTH] > 100)
1477 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1478 else if(cl.stats[STAT_HEALTH] > 50)
1479 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1480 else if(cl.stats[STAT_HEALTH] > 25)
1481 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1483 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1487 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1489 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1490 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1491 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1492 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1493 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1494 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1495 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1496 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1497 if(cl.stats[STAT_AMMO] > 10)
1498 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1500 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1503 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1504 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1507 // The margin can be at most 8 to support 640x480 console size:
1508 // 320 + 2 * (144 + 16) = 640
1514 int redflag, blueflag;
1517 sbar_x = (vid_conwidth.integer - 640)/2;
1518 sbar_y = vid_conheight.integer - 47;
1520 // calculate intensity to draw weapons bar at
1521 fade = 3 - 2 * (cl.time - cl.weapontime);
1524 fade = min(fade, 1);
1525 for (i = 0; i < 8;i++)
1526 if (cl.stats[STAT_ITEMS] & (1 << i))
1527 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1529 if((cl.stats[STAT_ITEMS] & (1<<12)))
1530 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1533 //if (!cl.islocalgame)
1534 // Sbar_DrawFrags ();
1537 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1539 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1542 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1543 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1544 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1545 if (redflag == 3 && blueflag == 3)
1547 // The Impossible Combination[tm]
1548 // Can only happen in Key Hunt mode...
1549 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1554 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1556 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1560 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1563 if(cl.stats[STAT_HEALTH] > 100)
1564 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1565 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1566 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1568 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1570 // AK dont draw ammo for the laser
1571 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1573 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1574 Sbar_DrawPic (519, 0, sb_ammo[0]);
1575 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1576 Sbar_DrawPic (519, 0, sb_ammo[1]);
1577 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1578 Sbar_DrawPic (519, 0, sb_ammo[2]);
1579 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1580 Sbar_DrawPic (519, 0, sb_ammo[3]);
1582 if(cl.stats[STAT_AMMO] <= 10)
1583 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1585 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1590 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1592 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1593 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1598 // Mini scoreboard uses 12*4 per other team, that is, 144
1599 // pixels when there are four teams...
1600 // Nexuiz by default sets vid_conwidth to 800... makes
1602 // so we need to shift it by 64 pixels to the right to fit
1603 // BUT: then it overlaps with the image that gets drawn
1604 // for viewsize 100! Therefore, just account for 3 teams,
1605 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1609 else if (gamemode == GAME_ZYMOTIC)
1612 float scale = 64.0f / 256.0f;
1613 float kickoffset[3];
1614 VectorClear(kickoffset);
1617 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1618 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1620 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1621 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1622 // left1 16, 48 : 126 -66
1623 // left2 16, 128 : 196 -66
1624 // right 176, 48 : 196 -136
1625 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);
1626 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);
1627 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);
1628 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1630 float scale = 128.0f / 256.0f;
1631 float healthstart, healthheight, healthstarttc, healthendtc;
1632 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1633 float ammostart, ammoheight, ammostarttc, ammoendtc;
1634 float clipstart, clipheight, clipstarttc, clipendtc;
1635 float kickoffset[3], offset;
1636 VectorClear(kickoffset);
1639 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1640 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1642 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1643 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1644 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1645 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);
1646 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);
1647 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);
1648 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);
1649 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1650 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1651 healthstart = 204 - healthheight;
1652 shieldstart = healthstart - shieldheight;
1653 healthstarttc = healthstart * (1.0f / 256.0f);
1654 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1655 shieldstarttc = shieldstart * (1.0f / 256.0f);
1656 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1657 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1658 ammostart = 114 - ammoheight;
1659 ammostarttc = ammostart * (1.0f / 256.0f);
1660 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1661 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1662 clipstart = 190 - clipheight;
1663 clipstarttc = clipstart * (1.0f / 256.0f);
1664 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1665 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);
1666 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);
1667 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);
1668 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);
1669 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1670 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1673 else // Quake and others
1675 sbar_x = (vid_conwidth.integer - 320)/2;
1676 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1677 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1678 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1682 if (gamemode != GAME_GOODVSBAD2)
1683 Sbar_DrawInventory ();
1684 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1688 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1690 if (gamemode != GAME_GOODVSBAD2)
1691 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1692 Sbar_DrawScoreboard ();
1696 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1698 // keys (hipnotic only)
1699 //MED 01/04/97 moved keys here so they would not be overwritten
1700 if (gamemode == GAME_HIPNOTIC)
1702 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1703 Sbar_DrawPic (209, 3, sb_items[0]);
1704 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1705 Sbar_DrawPic (209, 12, sb_items[1]);
1708 if (gamemode != GAME_GOODVSBAD2)
1710 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1712 Sbar_DrawNum (24, 0, 666, 3, 1);
1713 Sbar_DrawPic (0, 0, sb_disc);
1717 if (gamemode == GAME_ROGUE)
1719 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1720 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1721 Sbar_DrawPic (0, 0, sb_armor[2]);
1722 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1723 Sbar_DrawPic (0, 0, sb_armor[1]);
1724 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1725 Sbar_DrawPic (0, 0, sb_armor[0]);
1729 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1730 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1731 Sbar_DrawPic (0, 0, sb_armor[2]);
1732 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1733 Sbar_DrawPic (0, 0, sb_armor[1]);
1734 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1735 Sbar_DrawPic (0, 0, sb_armor[0]);
1744 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1747 if (gamemode == GAME_ROGUE)
1749 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1750 Sbar_DrawPic (224, 0, sb_ammo[0]);
1751 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1752 Sbar_DrawPic (224, 0, sb_ammo[1]);
1753 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1754 Sbar_DrawPic (224, 0, sb_ammo[2]);
1755 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1756 Sbar_DrawPic (224, 0, sb_ammo[3]);
1757 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1758 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1759 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1760 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1761 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1762 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1766 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1767 Sbar_DrawPic (224, 0, sb_ammo[0]);
1768 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1769 Sbar_DrawPic (224, 0, sb_ammo[1]);
1770 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1771 Sbar_DrawPic (224, 0, sb_ammo[2]);
1772 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1773 Sbar_DrawPic (224, 0, sb_ammo[3]);
1776 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1778 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1779 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1781 if (gamemode == GAME_TRANSFUSION)
1782 Sbar_MiniDeathmatchOverlay (0, 0);
1784 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1791 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1793 pic = Draw_CachePic (va(vabuf, sizeof(vabuf), "gfx/crosshair%i", crosshair.integer));
1794 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);
1797 if (cl_prydoncursor.integer > 0)
1798 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);
1801 //=============================================================================
1805 Sbar_DeathmatchOverlay
1809 static float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1812 qboolean myself = false;
1815 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1817 if((s - cl.scores) == cl.playerentity - 1)
1819 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1820 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1823 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1825 if (s->qw_spectator)
1827 if (s->qw_ping || s->qw_packetloss)
1828 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 );
1830 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 );
1834 // draw colors behind score
1840 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1841 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);
1842 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1843 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);
1845 //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);
1846 if (s->qw_ping || s->qw_packetloss)
1847 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 );
1849 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 );
1854 if (s->qw_spectator)
1856 if (s->qw_ping || s->qw_packetloss)
1857 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 );
1859 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 );
1863 // draw colors behind score
1864 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1865 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);
1866 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1867 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);
1869 //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);
1870 if (s->qw_ping || s->qw_packetloss)
1871 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 );
1873 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 );
1879 void Sbar_DeathmatchOverlay (void)
1881 int i, y, xmin, xmax, ymin, ymax;
1884 // request new ping times every two second
1885 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1887 cl.last_ping_request = realtime;
1888 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1890 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1891 MSG_WriteString(&cls.netcon->message, "pings");
1893 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*/)
1895 // 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
1896 static int ping_anyway_counter = 0;
1897 if(cl.parsingtextexpectingpingforscores == 1)
1899 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1900 if(++ping_anyway_counter >= 5)
1901 cl.parsingtextexpectingpingforscores = 0;
1903 if(cl.parsingtextexpectingpingforscores != 1)
1905 ping_anyway_counter = 0;
1906 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1907 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1908 MSG_WriteString(&cls.netcon->message, "ping");
1913 // 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
1914 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1915 MSG_WriteString(&cls.netcon->message, "pings");
1923 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1925 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1926 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)
1928 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)
1929 xmax = vid_conwidth.integer - xmin;
1931 if(gamemode == GAME_NEXUIZ)
1932 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);
1934 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1938 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1940 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 );
1944 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 );
1948 if (Sbar_IsTeammatch ())
1950 // show team scores first
1951 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1952 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1956 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1957 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1962 Sbar_MiniDeathmatchOverlay
1966 void Sbar_MiniDeathmatchOverlay (int x, int y)
1968 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1970 // do not draw this if sbar_miniscoreboard_size is zero
1971 if(sbar_miniscoreboard_size.value == 0)
1973 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1974 if(sbar_miniscoreboard_size.value > 0)
1975 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1980 // decide where to print
1981 if (gamemode == GAME_TRANSFUSION)
1982 numlines = (vid_conwidth.integer - x + 127) / 128;
1984 numlines = (vid_conheight.integer - y + 7) / 8;
1986 // give up if there isn't room
1987 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1991 for (i = 0; i < scoreboardlines; i++)
1992 if (fragsort[i] == cl.playerentity - 1)
1996 range_end = scoreboardlines;
1999 if (gamemode != GAME_TRANSFUSION)
2000 if (Sbar_IsTeammatch ())
2002 // reserve space for the team scores
2003 numlines -= teamlines;
2005 // find first and last player of my team (only draw the team totals and my own team)
2006 range_begin = range_end = i;
2007 myteam = cl.scores[fragsort[i]].colors & 15;
2008 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
2010 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
2013 // looks better than two players
2023 i = min(i, range_end - numlines);
2024 i = max(i, range_begin);
2026 if (gamemode == GAME_TRANSFUSION)
2028 for (;i < range_end && x < vid_conwidth.integer;i++)
2029 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2033 if(range_end - i < numlines) // won't draw to bottom?
2034 y += 8 * (numlines - (range_end - i)); // bottom align
2035 // show team scores first
2036 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
2037 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
2039 for (;i < range_end && y < vid_conheight.integer;i++)
2040 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2044 static int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
2046 static int const sortorder[16] =
2065 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
2066 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
2067 int tc1 = sortorder[t1->colors & 15];
2068 int tc2 = sortorder[t2->colors & 15];
2072 void Sbar_Score (int margin)
2074 int i, me, score, otherleader, place, distribution, minutes, seconds;
2076 int sbar_x_save = sbar_x;
2077 int sbar_y_save = sbar_y;
2080 sbar_y = (int) (vid_conheight.value - (32+12));
2083 me = cl.playerentity - 1;
2084 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2086 if(Sbar_IsTeammatch())
2090 // team1 team3 team4
2094 scoreboard_t *teamcolorsort[16];
2097 for(i = 0; i < teamlines; ++i)
2098 teamcolorsort[i] = &(teams[i]);
2100 // Now sort them by color
2101 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2104 // -12*4: four digits space
2105 place = (teamlines - 1) * (-12 * 4);
2107 for(i = 0; i < teamlines; ++i)
2109 int cindex = teamcolorsort[i]->colors & 15;
2110 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2111 float cm = max(max(c[0], c[1]), c[2]);
2112 float cr = c[0] / cm;
2113 float cg = c[1] / cm;
2114 float cb = c[2] / cm;
2115 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2117 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2121 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2134 // find leading score other than ourselves, to calculate distribution
2135 // find our place in the scoreboard
2136 score = cl.scores[me].frags;
2137 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2139 if (cl.scores[i].name[0] && i != me)
2141 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2143 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2147 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2149 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2150 else if (place == 2)
2151 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2153 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2154 if (otherleader < 0)
2155 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2156 if (distribution >= 0)
2158 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2159 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2161 else if (distribution >= -5)
2163 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2164 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2168 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2169 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2174 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2176 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2177 minutes = (int)floor(timeleft / 60);
2178 seconds = (int)(floor(timeleft) - minutes * 60);
2181 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2182 if(sb_colon && sb_colon->tex != r_texture_notexture)
2183 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2184 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2186 else if (minutes >= 1)
2188 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2189 if(sb_colon && sb_colon->tex != r_texture_notexture)
2190 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2191 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2193 else if ((int)(timeleft * 4) & 1)
2194 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2196 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2198 else if (sbar_gametime.integer)
2200 minutes = (int)floor(cl.time / 60);
2201 seconds = (int)(floor(cl.time) - minutes * 60);
2202 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2203 if(sb_colon && sb_colon->tex != r_texture_notexture)
2204 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2205 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2208 sbar_x = sbar_x_save;
2209 sbar_y = sbar_y_save;
2214 Sbar_IntermissionOverlay
2218 void Sbar_IntermissionOverlay (void)
2223 if (cl.gametype == GAME_DEATHMATCH)
2225 Sbar_DeathmatchOverlay ();
2229 sbar_x = (vid_conwidth.integer - 320) >> 1;
2230 sbar_y = (vid_conheight.integer - 200) >> 1;
2232 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2233 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2236 dig = (int)cl.completed_time / 60;
2237 Sbar_DrawNum (160, 64, dig, 3, 0);
2238 num = (int)cl.completed_time - dig*60;
2239 Sbar_DrawPic (234,64,sb_colon);
2240 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2241 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2243 // LA: Display as "a" instead of "a/b" if b is 0
2244 if(cl.stats[STAT_TOTALSECRETS])
2246 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2247 if (gamemode != GAME_NEXUIZ)
2248 Sbar_DrawPic (232, 104, sb_slash);
2249 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2253 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2256 if(cl.stats[STAT_TOTALMONSTERS])
2258 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2259 if (gamemode != GAME_NEXUIZ)
2260 Sbar_DrawPic (232, 144, sb_slash);
2261 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2265 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2276 void Sbar_FinaleOverlay (void)
2278 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);