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)
1217 VectorMA(r_refdef.view.origin, 65536, r_refdef.view.forward, dest);
1218 trace.hittexture = NULL; // to make sure
1219 // TODO change this trace to be stopped by anything "visible" (i.e. with a drawsurface), but not stuff like weapclip
1220 // probably needs adding a new SUPERCONTENTS type
1221 trace = CL_TraceLine(r_refdef.view.origin, dest, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID, true, false, NULL, true, true);
1222 if(trace.hittexture)
1223 strlcpy(texstring, trace.hittexture->name, sizeof(texstring));
1225 strlcpy(texstring, "(no texture hit)", sizeof(texstring));
1232 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1233 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_strings*fps_scaley);
1234 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_strings*fps_scaley;
1237 fps_x = vid_conwidth.integer - DrawQ_TextWidth(soundstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1238 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1239 DrawQ_String(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1240 fps_y += fps_scaley;
1244 r_draw2d_force = true;
1245 fps_x = vid_conwidth.integer - DrawQ_TextWidth(fpsstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1246 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1248 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1250 DrawQ_String(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1251 fps_y += fps_scaley;
1252 r_draw2d_force = false;
1254 if (timedemostring1[0])
1256 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring1, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1257 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1258 DrawQ_String(fps_x, fps_y, timedemostring1, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1259 fps_y += fps_scaley;
1261 if (timedemostring2[0])
1263 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timedemostring2, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1264 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1265 DrawQ_String(fps_x, fps_y, timedemostring2, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1266 fps_y += fps_scaley;
1270 fps_x = vid_conwidth.integer - DrawQ_TextWidth(timestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1271 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1272 DrawQ_String(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1273 fps_y += fps_scaley;
1277 fps_x = vid_conwidth.integer - DrawQ_TextWidth(datestring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1278 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1279 DrawQ_String(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1280 fps_y += fps_scaley;
1284 fps_x = vid_conwidth.integer - DrawQ_TextWidth(speedstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1285 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1286 DrawQ_String(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1287 fps_y += fps_scaley;
1289 if (topspeedstring[0])
1291 fps_x = vid_conwidth.integer - DrawQ_TextWidth(topspeedstring, 0, fps_scalex, fps_scaley, false, FONT_INFOBAR);
1292 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1293 DrawQ_String(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1294 fps_y += fps_scaley;
1298 fps_x = vid_conwidth.integer - DrawQ_TextWidth(blurstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1299 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1300 DrawQ_String(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1301 fps_y += fps_scaley;
1305 fps_x = vid_conwidth.integer - DrawQ_TextWidth(texstring, 0, fps_scalex, fps_scaley, true, FONT_INFOBAR);
1306 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1307 DrawQ_String(fps_x, fps_y, texstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1308 fps_y += fps_scaley;
1313 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)
1316 c2 = bound(0, c2, 1);
1317 c1 = bound(0, c1, 1 - c2);
1319 r[1] = rangey + rangeheight * (c2 + c1);
1320 r[2] = rangey + rangeheight * (c2);
1324 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);
1326 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);
1328 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);
1330 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);
1338 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1339 extern cvar_t v_kicktime;
1340 void Sbar_Score (int margin);
1341 void Sbar_Draw (void)
1346 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1349 Sbar_DrawScoreboard ();
1350 else if (cl.intermission == 1)
1352 if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1354 Sbar_DrawScoreboard();
1357 Sbar_IntermissionOverlay();
1359 else if (cl.intermission == 2)
1360 Sbar_FinaleOverlay();
1361 else if (gamemode == GAME_DELUXEQUAKE)
1364 else if (gamemode == GAME_SOM)
1366 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1367 Sbar_DrawScoreboard ();
1370 // this is the top left of the sbar area
1372 sbar_y = vid_conheight.integer - 24*3;
1375 if (cl.stats[STAT_ARMOR])
1377 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1378 Sbar_DrawPic(0, 0, somsb_armor[2]);
1379 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1380 Sbar_DrawPic(0, 0, somsb_armor[1]);
1381 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1382 Sbar_DrawPic(0, 0, somsb_armor[0]);
1383 Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1387 Sbar_DrawPic(0, 24, somsb_health);
1388 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1391 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1392 Sbar_DrawPic(0, 48, somsb_ammo[0]);
1393 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1394 Sbar_DrawPic(0, 48, somsb_ammo[1]);
1395 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1396 Sbar_DrawPic(0, 48, somsb_ammo[2]);
1397 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1398 Sbar_DrawPic(0, 48, somsb_ammo[3]);
1399 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1400 if (cl.stats[STAT_SHELLS])
1401 Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1404 else if (gamemode == GAME_NEXUIZ)
1406 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1408 sbar_x = (vid_conwidth.integer - 640)/2;
1409 sbar_y = vid_conheight.integer - 47;
1410 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1411 Sbar_DrawScoreboard ();
1413 else if (sb_lines && sbar_hudselector.integer == 1)
1417 int redflag, blueflag;
1420 sbar_x = (vid_conwidth.integer - 320)/2;
1421 sbar_y = vid_conheight.integer - 24 - 16;
1423 // calculate intensity to draw weapons bar at
1424 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1425 fade = bound(0.7, fade, 1);
1426 for (i = 0; i < 8;i++)
1427 if (cl.stats[STAT_ITEMS] & (1 << i))
1428 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1429 if((cl.stats[STAT_ITEMS] & (1<<12)))
1430 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1433 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1434 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1435 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1436 if (redflag == 3 && blueflag == 3)
1438 // The Impossible Combination[tm]
1439 // Can only happen in Key Hunt mode...
1440 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1445 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1447 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1451 if (cl.stats[STAT_ARMOR] > 0)
1453 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1454 if(cl.stats[STAT_ARMOR] > 200)
1455 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1456 else if(cl.stats[STAT_ARMOR] > 100)
1457 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1458 else if(cl.stats[STAT_ARMOR] > 50)
1459 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1460 else if(cl.stats[STAT_ARMOR] > 25)
1461 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1463 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1467 if (cl.stats[STAT_HEALTH] != 0)
1469 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1470 if(cl.stats[STAT_HEALTH] > 200)
1471 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1472 else if(cl.stats[STAT_HEALTH] > 100)
1473 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1474 else if(cl.stats[STAT_HEALTH] > 50)
1475 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1476 else if(cl.stats[STAT_HEALTH] > 25)
1477 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1479 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1483 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1485 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1486 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1487 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1488 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1489 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1490 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1491 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1492 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1493 if(cl.stats[STAT_AMMO] > 10)
1494 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1496 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1499 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1500 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1503 // The margin can be at most 8 to support 640x480 console size:
1504 // 320 + 2 * (144 + 16) = 640
1510 int redflag, blueflag;
1513 sbar_x = (vid_conwidth.integer - 640)/2;
1514 sbar_y = vid_conheight.integer - 47;
1516 // calculate intensity to draw weapons bar at
1517 fade = 3 - 2 * (cl.time - cl.weapontime);
1520 fade = min(fade, 1);
1521 for (i = 0; i < 8;i++)
1522 if (cl.stats[STAT_ITEMS] & (1 << i))
1523 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1525 if((cl.stats[STAT_ITEMS] & (1<<12)))
1526 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1529 //if (!cl.islocalgame)
1530 // Sbar_DrawFrags ();
1533 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1535 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1538 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1539 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1540 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1541 if (redflag == 3 && blueflag == 3)
1543 // The Impossible Combination[tm]
1544 // Can only happen in Key Hunt mode...
1545 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1550 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1552 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1556 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1559 if(cl.stats[STAT_HEALTH] > 100)
1560 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1561 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1562 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1564 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1566 // AK dont draw ammo for the laser
1567 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1569 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1570 Sbar_DrawPic (519, 0, sb_ammo[0]);
1571 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1572 Sbar_DrawPic (519, 0, sb_ammo[1]);
1573 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1574 Sbar_DrawPic (519, 0, sb_ammo[2]);
1575 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1576 Sbar_DrawPic (519, 0, sb_ammo[3]);
1578 if(cl.stats[STAT_AMMO] <= 10)
1579 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1581 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1586 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1588 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1589 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1594 // Mini scoreboard uses 12*4 per other team, that is, 144
1595 // pixels when there are four teams...
1596 // Nexuiz by default sets vid_conwidth to 800... makes
1598 // so we need to shift it by 64 pixels to the right to fit
1599 // BUT: then it overlaps with the image that gets drawn
1600 // for viewsize 100! Therefore, just account for 3 teams,
1601 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1605 else if (gamemode == GAME_ZYMOTIC)
1608 float scale = 64.0f / 256.0f;
1609 float kickoffset[3];
1610 VectorClear(kickoffset);
1613 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1614 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1616 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1617 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1618 // left1 16, 48 : 126 -66
1619 // left2 16, 128 : 196 -66
1620 // right 176, 48 : 196 -136
1621 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);
1622 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);
1623 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);
1624 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1626 float scale = 128.0f / 256.0f;
1627 float healthstart, healthheight, healthstarttc, healthendtc;
1628 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1629 float ammostart, ammoheight, ammostarttc, ammoendtc;
1630 float clipstart, clipheight, clipstarttc, clipendtc;
1631 float kickoffset[3], offset;
1632 VectorClear(kickoffset);
1635 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1636 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1638 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1639 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1640 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1641 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);
1642 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);
1643 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);
1644 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);
1645 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1646 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1647 healthstart = 204 - healthheight;
1648 shieldstart = healthstart - shieldheight;
1649 healthstarttc = healthstart * (1.0f / 256.0f);
1650 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1651 shieldstarttc = shieldstart * (1.0f / 256.0f);
1652 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1653 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1654 ammostart = 114 - ammoheight;
1655 ammostarttc = ammostart * (1.0f / 256.0f);
1656 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1657 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1658 clipstart = 190 - clipheight;
1659 clipstarttc = clipstart * (1.0f / 256.0f);
1660 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1661 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);
1662 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);
1663 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);
1664 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);
1665 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1666 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1669 else // Quake and others
1671 sbar_x = (vid_conwidth.integer - 320)/2;
1672 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1673 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1674 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1678 if (gamemode != GAME_GOODVSBAD2)
1679 Sbar_DrawInventory ();
1680 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1684 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1686 if (gamemode != GAME_GOODVSBAD2)
1687 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1688 Sbar_DrawScoreboard ();
1692 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1694 // keys (hipnotic only)
1695 //MED 01/04/97 moved keys here so they would not be overwritten
1696 if (gamemode == GAME_HIPNOTIC)
1698 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1699 Sbar_DrawPic (209, 3, sb_items[0]);
1700 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1701 Sbar_DrawPic (209, 12, sb_items[1]);
1704 if (gamemode != GAME_GOODVSBAD2)
1706 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1708 Sbar_DrawNum (24, 0, 666, 3, 1);
1709 Sbar_DrawPic (0, 0, sb_disc);
1713 if (gamemode == GAME_ROGUE)
1715 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1716 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1717 Sbar_DrawPic (0, 0, sb_armor[2]);
1718 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1719 Sbar_DrawPic (0, 0, sb_armor[1]);
1720 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1721 Sbar_DrawPic (0, 0, sb_armor[0]);
1725 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1726 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1727 Sbar_DrawPic (0, 0, sb_armor[2]);
1728 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1729 Sbar_DrawPic (0, 0, sb_armor[1]);
1730 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1731 Sbar_DrawPic (0, 0, sb_armor[0]);
1740 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1743 if (gamemode == GAME_ROGUE)
1745 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1746 Sbar_DrawPic (224, 0, sb_ammo[0]);
1747 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1748 Sbar_DrawPic (224, 0, sb_ammo[1]);
1749 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1750 Sbar_DrawPic (224, 0, sb_ammo[2]);
1751 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1752 Sbar_DrawPic (224, 0, sb_ammo[3]);
1753 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1754 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1755 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1756 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1757 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1758 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1762 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1763 Sbar_DrawPic (224, 0, sb_ammo[0]);
1764 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1765 Sbar_DrawPic (224, 0, sb_ammo[1]);
1766 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1767 Sbar_DrawPic (224, 0, sb_ammo[2]);
1768 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1769 Sbar_DrawPic (224, 0, sb_ammo[3]);
1772 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1774 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1775 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1777 if (gamemode == GAME_TRANSFUSION)
1778 Sbar_MiniDeathmatchOverlay (0, 0);
1780 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1787 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1789 pic = Draw_CachePic (va(vabuf, sizeof(vabuf), "gfx/crosshair%i", crosshair.integer));
1790 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);
1793 if (cl_prydoncursor.integer > 0)
1794 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);
1797 //=============================================================================
1801 Sbar_DeathmatchOverlay
1805 static float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1808 qboolean myself = false;
1811 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1813 if((s - cl.scores) == cl.playerentity - 1)
1815 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1816 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1819 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1821 if (s->qw_spectator)
1823 if (s->qw_ping || s->qw_packetloss)
1824 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 );
1826 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 );
1830 // draw colors behind score
1836 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1837 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);
1838 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1839 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);
1841 //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);
1842 if (s->qw_ping || s->qw_packetloss)
1843 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 );
1845 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 );
1850 if (s->qw_spectator)
1852 if (s->qw_ping || s->qw_packetloss)
1853 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 );
1855 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 );
1859 // draw colors behind score
1860 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1861 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);
1862 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1863 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);
1865 //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);
1866 if (s->qw_ping || s->qw_packetloss)
1867 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 );
1869 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 );
1875 void Sbar_DeathmatchOverlay (void)
1877 int i, y, xmin, xmax, ymin, ymax;
1880 // request new ping times every two second
1881 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1883 cl.last_ping_request = realtime;
1884 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1886 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1887 MSG_WriteString(&cls.netcon->message, "pings");
1889 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*/)
1891 // 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
1892 static int ping_anyway_counter = 0;
1893 if(cl.parsingtextexpectingpingforscores == 1)
1895 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1896 if(++ping_anyway_counter >= 5)
1897 cl.parsingtextexpectingpingforscores = 0;
1899 if(cl.parsingtextexpectingpingforscores != 1)
1901 ping_anyway_counter = 0;
1902 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1903 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1904 MSG_WriteString(&cls.netcon->message, "ping");
1909 // 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
1910 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1911 MSG_WriteString(&cls.netcon->message, "pings");
1919 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1921 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1922 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)
1924 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)
1925 xmax = vid_conwidth.integer - xmin;
1927 if(gamemode == GAME_NEXUIZ)
1928 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);
1930 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1934 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1936 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 );
1940 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 );
1944 if (Sbar_IsTeammatch ())
1946 // show team scores first
1947 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1948 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1952 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1953 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1958 Sbar_MiniDeathmatchOverlay
1962 void Sbar_MiniDeathmatchOverlay (int x, int y)
1964 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1966 // do not draw this if sbar_miniscoreboard_size is zero
1967 if(sbar_miniscoreboard_size.value == 0)
1969 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1970 if(sbar_miniscoreboard_size.value > 0)
1971 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1976 // decide where to print
1977 if (gamemode == GAME_TRANSFUSION)
1978 numlines = (vid_conwidth.integer - x + 127) / 128;
1980 numlines = (vid_conheight.integer - y + 7) / 8;
1982 // give up if there isn't room
1983 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1987 for (i = 0; i < scoreboardlines; i++)
1988 if (fragsort[i] == cl.playerentity - 1)
1992 range_end = scoreboardlines;
1995 if (gamemode != GAME_TRANSFUSION)
1996 if (Sbar_IsTeammatch ())
1998 // reserve space for the team scores
1999 numlines -= teamlines;
2001 // find first and last player of my team (only draw the team totals and my own team)
2002 range_begin = range_end = i;
2003 myteam = cl.scores[fragsort[i]].colors & 15;
2004 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
2006 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
2009 // looks better than two players
2019 i = min(i, range_end - numlines);
2020 i = max(i, range_begin);
2022 if (gamemode == GAME_TRANSFUSION)
2024 for (;i < range_end && x < vid_conwidth.integer;i++)
2025 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2029 if(range_end - i < numlines) // won't draw to bottom?
2030 y += 8 * (numlines - (range_end - i)); // bottom align
2031 // show team scores first
2032 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
2033 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
2035 for (;i < range_end && y < vid_conheight.integer;i++)
2036 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
2040 static int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
2042 static int const sortorder[16] =
2061 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
2062 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
2063 int tc1 = sortorder[t1->colors & 15];
2064 int tc2 = sortorder[t2->colors & 15];
2068 void Sbar_Score (int margin)
2070 int i, me, score, otherleader, place, distribution, minutes, seconds;
2072 int sbar_x_save = sbar_x;
2073 int sbar_y_save = sbar_y;
2076 sbar_y = (int) (vid_conheight.value - (32+12));
2079 me = cl.playerentity - 1;
2080 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2082 if(Sbar_IsTeammatch())
2086 // team1 team3 team4
2090 scoreboard_t *teamcolorsort[16];
2093 for(i = 0; i < teamlines; ++i)
2094 teamcolorsort[i] = &(teams[i]);
2096 // Now sort them by color
2097 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2100 // -12*4: four digits space
2101 place = (teamlines - 1) * (-12 * 4);
2103 for(i = 0; i < teamlines; ++i)
2105 int cindex = teamcolorsort[i]->colors & 15;
2106 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2107 float cm = max(max(c[0], c[1]), c[2]);
2108 float cr = c[0] / cm;
2109 float cg = c[1] / cm;
2110 float cb = c[2] / cm;
2111 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2113 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2117 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2130 // find leading score other than ourselves, to calculate distribution
2131 // find our place in the scoreboard
2132 score = cl.scores[me].frags;
2133 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2135 if (cl.scores[i].name[0] && i != me)
2137 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2139 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2143 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2145 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2146 else if (place == 2)
2147 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2149 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2150 if (otherleader < 0)
2151 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2152 if (distribution >= 0)
2154 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2155 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2157 else if (distribution >= -5)
2159 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2160 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2164 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2165 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2170 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2172 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2173 minutes = (int)floor(timeleft / 60);
2174 seconds = (int)(floor(timeleft) - minutes * 60);
2177 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2178 if(sb_colon && sb_colon->tex != r_texture_notexture)
2179 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2180 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2182 else if (minutes >= 1)
2184 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2185 if(sb_colon && sb_colon->tex != r_texture_notexture)
2186 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2187 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2189 else if ((int)(timeleft * 4) & 1)
2190 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2192 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2194 else if (sbar_gametime.integer)
2196 minutes = (int)floor(cl.time / 60);
2197 seconds = (int)(floor(cl.time) - minutes * 60);
2198 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2199 if(sb_colon && sb_colon->tex != r_texture_notexture)
2200 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2201 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2204 sbar_x = sbar_x_save;
2205 sbar_y = sbar_y_save;
2210 Sbar_IntermissionOverlay
2214 void Sbar_IntermissionOverlay (void)
2219 if (cl.gametype == GAME_DEATHMATCH)
2221 Sbar_DeathmatchOverlay ();
2225 sbar_x = (vid_conwidth.integer - 320) >> 1;
2226 sbar_y = (vid_conheight.integer - 200) >> 1;
2228 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2229 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2232 dig = (int)cl.completed_time / 60;
2233 Sbar_DrawNum (160, 64, dig, 3, 0);
2234 num = (int)cl.completed_time - dig*60;
2235 Sbar_DrawPic (234,64,sb_colon);
2236 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2237 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2239 // LA: Display as "a" instead of "a/b" if b is 0
2240 if(cl.stats[STAT_TOTALSECRETS])
2242 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2243 if (gamemode != GAME_NEXUIZ)
2244 Sbar_DrawPic (232, 104, sb_slash);
2245 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2249 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2252 if(cl.stats[STAT_TOTALMONSTERS])
2254 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2255 if (gamemode != GAME_NEXUIZ)
2256 Sbar_DrawPic (232, 144, sb_slash);
2257 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2261 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2272 void Sbar_FinaleOverlay (void)
2274 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);