1 float scoreboard_alpha_bg;
2 float scoreboard_alpha_fg;
3 float scoreboard_highlight;
4 float scoreboard_highlight_alpha;
5 float scoreboard_highlight_alpha_self;
6 float scoreboard_alpha_name;
7 float scoreboard_alpha_name_self;
9 void drawstringright(vector, string, vector, vector, float, float);
10 void drawstringcenter(vector, string, vector, vector, float, float);
12 float SCOREBOARD_OFFSET = 50;
15 void HUD_FinaleOverlay()
18 pos_x = (vid_conwidth - 1)/2;
22 //drawpic(pos, "gfx/finale", '0 0 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
24 //drawstring(pos, "END", hud_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
28 void Cmd_HUD_SetFields(float argc);
33 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
34 for(i = 0; i < MAX_SCORE; ++i)
36 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
37 if(f == SFL_SORT_PRIO_PRIMARY)
39 if(f == SFL_SORT_PRIO_SECONDARY)
42 if(ps_secondary == -1)
43 ps_secondary = ps_primary;
45 for(i = 0; i < MAX_TEAMSCORE; ++i)
47 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
48 if(f == SFL_SORT_PRIO_PRIMARY)
50 if(f == SFL_SORT_PRIO_SECONDARY)
53 if(ts_secondary == -1)
54 ts_secondary = ts_primary;
59 void HUD_UpdatePlayerPos(entity pl);
60 float SetTeam(entity pl, float Team);
62 void HUD_UpdatePlayerTeams()
69 for(pl = players.sort_next; pl; pl = pl.sort_next)
72 Team = GetPlayerColor(pl.sv_entnum);
76 HUD_UpdatePlayerPos(pl);
80 pl = players.sort_next;
85 print(strcat("PNUM: ", ftos(num), "\n"));
90 float HUD_ComparePlayerScores(entity left, entity right)
93 vl = GetPlayerColor(left.sv_entnum);
94 vr = GetPlayerColor(right.sv_entnum);
106 if(vl == COLOR_SPECTATOR)
108 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
110 if(!left.gotscores && right.gotscores)
115 vl = left.scores[ps_primary];
116 vr = right.scores[ps_primary];
117 if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
119 if(vl == 0 && vr != 0)
121 if(vl != 0 && vr == 0)
125 return IS_INCREASING(scores_flags[ps_primary]);
127 return IS_DECREASING(scores_flags[ps_primary]);
129 vl = left.scores[ps_secondary];
130 vr = right.scores[ps_secondary];
131 if(scores_flags[ps_secondary] & SFL_ZERO_IS_WORST)
133 if(vl == 0 && vr != 0)
135 if(vl != 0 && vr == 0)
139 return IS_INCREASING(scores_flags[ps_secondary]);
141 return IS_DECREASING(scores_flags[ps_secondary]);
146 void HUD_UpdatePlayerPos(entity player)
148 for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
150 SORT_SWAP(player, other);
152 for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
154 SORT_SWAP(other, player);
158 float HUD_CompareTeamScores(entity left, entity right)
162 if(left.team == COLOR_SPECTATOR)
164 if(right.team == COLOR_SPECTATOR)
167 vl = left.teamscores[ts_primary];
168 vr = right.teamscores[ts_primary];
170 return IS_INCREASING(teamscores_flags[ts_primary]);
172 return IS_DECREASING(teamscores_flags[ts_primary]);
174 vl = left.teamscores[ts_secondary];
175 vr = right.teamscores[ts_secondary];
177 return IS_INCREASING(teamscores_flags[ts_secondary]);
179 return IS_DECREASING(teamscores_flags[ts_secondary]);
184 void HUD_UpdateTeamPos(entity Team)
186 for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
188 SORT_SWAP(Team, other);
190 for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
192 SORT_SWAP(other, Team);
196 void Cmd_HUD_Help(float argc)
198 print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
199 print(_("^3|---------------------------------------------------------------|\n"));
200 print(_("Usage:\n"));
201 print(_("^2scoreboard_columns_set default\n"));
202 print(_("^2scoreboard_columns_set ^7filed1 field2 ...\n"));
203 print(_("The following field names are recognized (case insensitive):\n"));
204 print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
206 print(_("^3name^7 or ^3nick^7 Name of a player\n"));
207 print(_("^3ping^7 Ping time\n"));
208 print(_("^3pl^7 Packet loss\n"));
209 print(_("^3kills^7 Number of kills\n"));
210 print(_("^3deaths^7 Number of deaths\n"));
211 print(_("^3suicides^7 Number of suicides\n"));
212 print(_("^3frags^7 kills - suicides\n"));
213 print(_("^3kd^7 The kill-death ratio\n"));
214 print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
215 print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) was picked up\n"));
216 print(_("^3fckills^7 Number of flag carrier kills\n"));
217 print(_("^3returns^7 Number of flag returns\n"));
218 print(_("^3drops^7 Number of flag drops\n"));
219 print(_("^3lives^7 Number of lives (LMS)\n"));
220 print(_("^3rank^7 Player rank\n"));
221 print(_("^3pushes^7 Number of players pushed into void\n"));
222 print(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
223 print(_("^3kckills^7 Number of keys carrier kills\n"));
224 print(_("^3losses^7 Number of times a key was lost\n"));
225 print(_("^3laps^7 Number of laps finished (race/cts)\n"));
226 print(_("^3time^7 Total time raced (race/cts)\n"));
227 print(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
228 print(_("^3ticks^7 Number of ticks (DOM)\n"));
229 print(_("^3takes^7 Number of domination points taken (DOM)\n"));
230 print(_("^3score^7 Total score\n\n"));
232 print(_("Before a field you can put a + or - sign, then a comma separated list\n"
233 "of game types, then a slash, to make the field show up only in these\n"
234 "or in all but these game types. You can also specify 'all' as a\n"
235 "field to show all fields available for the current game mode.\n\n"));
237 print(_("The special game type names 'teams' and 'noteams' can be used to\n"
238 "include/exclude ALL teams/noteams game modes.\n\n"));
240 print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
241 print(_("will display name, ping and pl aligned to the left, and the fields\n"
242 "right of the vertical bar aligned to the right.\n"));
243 print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
244 "other gamemodes except DM.\n"));
247 string HUD_DefaultColumnLayout()
249 return strcat( // fteqcc sucks
251 "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score"
252 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
253 "+lms/lives +lms/rank ",
254 "+kh/caps +kh/pushes +kh/destroyed ",
255 "?+race/laps ?+race/time ?+race/fastest ",
256 "+as/objectives +nexball/faults +nexball/goals +ka/drops +ka/pickups +ka/bckills +ka/time +freezetag/revivals ",
257 "-lms,race,nexball/score");
260 void Cmd_HUD_SetFields(float argc)
264 float have_name, have_primary, have_secondary, have_separator;
267 // TODO: re enable with gametype dependant cvars?
268 if(argc < 2) // no arguments provided
269 argc = tokenizebyseparator(strcat("x ", autocvar_scoreboard_columns), " ");
272 argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
276 if(argv(1) == "default")
277 argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
278 else if(argv(1) == "all")
281 s = "ping pl color name |";
282 for(i = 0; i < MAX_SCORE; ++i)
285 if(i != ps_secondary)
286 if(scores_label[i] != "")
287 s = strcat(s, " ", scores_label[i]);
289 if(ps_secondary != ps_primary)
290 s = strcat(s, " ", scores_label[ps_secondary]);
291 s = strcat(s, " ", scores_label[ps_primary]);
292 argc = tokenizebyseparator(strcat("x ", s), " ");
299 hud_fontsize = HUD_GetFontsize("hud_fontsize");
301 for(i = 0; i < argc - 1; ++i)
307 if(substring(str, 0, 1) == "?")
310 str = substring(str, 1, strlen(str) - 1);
313 slash = strstrofs(str, "/", 0);
316 pattern = substring(str, 0, slash);
317 str = substring(str, slash + 1, strlen(str) - (slash + 1));
319 if not(isGametypeInFilter(gametype, teamplay, pattern))
323 strunzone(hud_title[hud_num_fields]);
324 hud_title[hud_num_fields] = strzone(str);
325 hud_size[hud_num_fields] = stringwidth(str, FALSE, hud_fontsize);
326 str = strtolower(str);
329 hud_field[hud_num_fields] = SP_PING;
330 } else if(str == "pl") {
331 hud_field[hud_num_fields] = SP_PL;
332 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
333 hud_field[hud_num_fields] = SP_KDRATIO;
334 } else if(str == "name" || str == "nick") {
335 hud_field[hud_num_fields] = SP_NAME;
337 } else if(str == "|") {
338 hud_field[hud_num_fields] = SP_SEPARATOR;
341 for(j = 0; j < MAX_SCORE; ++j)
342 if(str == strtolower(scores_label[j]))
343 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
352 print(sprintf("^1Error:^7 Unknown score field: '%s'\n", str));
356 hud_field[hud_num_fields] = j;
359 if(j == ps_secondary)
363 if(hud_num_fields >= MAX_HUD_FIELDS)
367 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
369 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
371 if(ps_primary == ps_secondary)
373 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
375 if(hud_num_fields+missing < MAX_HUD_FIELDS)
379 strunzone(hud_title[hud_num_fields]);
380 for(i = hud_num_fields; i > 0; --i)
382 hud_title[i] = hud_title[i-1];
383 hud_size[i] = hud_size[i-1];
384 hud_field[i] = hud_field[i-1];
386 hud_title[0] = strzone("name");
387 hud_field[0] = SP_NAME;
389 print(sprintf(_("fixed missing field '%s'\n"), "name"));
393 strunzone(hud_title[hud_num_fields]);
394 for(i = hud_num_fields; i > 1; --i)
396 hud_title[i] = hud_title[i-1];
397 hud_size[i] = hud_size[i-1];
398 hud_field[i] = hud_field[i-1];
400 hud_title[1] = strzone("|");
401 hud_field[1] = SP_SEPARATOR;
402 hud_size[1] = stringwidth("|", FALSE, hud_fontsize);
404 print(sprintf(_("fixed missing field '%s'\n"), "|"));
407 else if(!have_separator)
409 strunzone(hud_title[hud_num_fields]);
410 hud_title[hud_num_fields] = strzone("|");
411 hud_size[hud_num_fields] = stringwidth("|", FALSE, hud_fontsize);
412 hud_field[hud_num_fields] = SP_SEPARATOR;
414 print(sprintf(_("fixed missing field '%s'\n"), "|"));
418 strunzone(hud_title[hud_num_fields]);
419 hud_title[hud_num_fields] = strzone(scores_label[ps_secondary]);
420 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
421 hud_field[hud_num_fields] = ps_secondary;
423 print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_secondary]));
427 strunzone(hud_title[hud_num_fields]);
428 hud_title[hud_num_fields] = strzone(scores_label[ps_primary]);
429 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
430 hud_field[hud_num_fields] = ps_primary;
432 print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_primary]));
436 hud_field[hud_num_fields] = SP_END;
440 vector hud_field_rgb;
441 string hud_field_icon0;
442 string hud_field_icon1;
443 string hud_field_icon2;
444 vector hud_field_icon0_rgb;
445 vector hud_field_icon1_rgb;
446 vector hud_field_icon2_rgb;
447 float hud_field_icon0_alpha;
448 float hud_field_icon1_alpha;
449 float hud_field_icon2_alpha;
450 string HUD_GetField(entity pl, float field)
452 float tmp, num, denom, f;
454 hud_field_rgb = '1 1 1';
455 hud_field_icon0 = "";
456 hud_field_icon1 = "";
457 hud_field_icon2 = "";
458 hud_field_icon0_rgb = '1 1 1';
459 hud_field_icon1_rgb = '1 1 1';
460 hud_field_icon2_rgb = '1 1 1';
461 hud_field_icon0_alpha = 1;
462 hud_field_icon1_alpha = 1;
463 hud_field_icon2_alpha = 1;
468 return "\xEE\x82\x8D\xEE\x82\x8D\xEE\x82\x8D"; // >>> sign
469 //str = getplayerkey(pl.sv_entnum, "ping");
473 tmp = max(0, min(220, f-80)) / 220;
474 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
480 f = pl.ping_packetloss;
481 tmp = pl.ping_movementloss;
482 if(f == 0 && tmp == 0)
484 str = ftos(ceil(f * 100));
486 str = strcat(str, "~", ftos(ceil(tmp * 100)));
487 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
488 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
492 if(ready_waiting && pl.ready)
494 hud_field_icon0 = "gfx/scoreboard/player_ready";
498 f = stof(getplayerkey(pl.sv_entnum, "colors"));
500 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
501 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
502 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
503 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
504 hud_field_icon2_rgb = colormapPaletteColor(mod(f, 16), 1);
507 return GetPlayerName(pl.sv_entnum);
510 f = pl.(scores[SP_KILLS]);
511 f -= pl.(scores[SP_SUICIDES]);
515 num = pl.(scores[SP_KILLS]);
516 denom = pl.(scores[SP_DEATHS]);
519 hud_field_rgb = '0 1 0';
520 str = sprintf("%d", num);
521 } else if(num <= 0) {
522 hud_field_rgb = '1 0 0';
523 str = sprintf("%.1f", num/denom);
525 str = sprintf("%.1f", num/denom);
529 tmp = pl.(scores[field]);
530 f = scores_flags[field];
531 if(field == ps_primary)
532 hud_field_rgb = '1 1 0';
533 else if(field == ps_secondary)
534 hud_field_rgb = '0 1 1';
536 hud_field_rgb = '1 1 1';
537 return ScoreString(f, tmp);
542 float xmin, xmax, ymin, ymax, sbwidth;
543 float hud_fixscoreboardcolumnwidth_len;
544 float hud_fixscoreboardcolumnwidth_iconlen;
545 float hud_fixscoreboardcolumnwidth_marginlen;
547 string HUD_FixScoreboardColumnWidth(float i, string str)
551 field = hud_field[i];
553 hud_fixscoreboardcolumnwidth_iconlen = 0;
555 if(hud_field_icon0 != "")
557 sz = drawgetimagesize(hud_field_icon0);
559 if(hud_fixscoreboardcolumnwidth_iconlen < f)
560 hud_fixscoreboardcolumnwidth_iconlen = f;
563 if(hud_field_icon1 != "")
565 sz = drawgetimagesize(hud_field_icon1);
567 if(hud_fixscoreboardcolumnwidth_iconlen < f)
568 hud_fixscoreboardcolumnwidth_iconlen = f;
571 if(hud_field_icon2 != "")
573 sz = drawgetimagesize(hud_field_icon2);
575 if(hud_fixscoreboardcolumnwidth_iconlen < f)
576 hud_fixscoreboardcolumnwidth_iconlen = f;
579 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize_y / hud_fontsize_x; // fix icon aspect
581 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
582 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", FALSE, hud_fontsize);
584 hud_fixscoreboardcolumnwidth_marginlen = 0;
586 if(field == SP_NAME) // name gets all remaining space
589 namesize = sbwidth;// / hud_fontsize_x;
590 for(j = 0; j < hud_num_fields; ++j)
592 if (hud_field[i] != SP_SEPARATOR)
593 namesize -= hud_size[j] + hud_fontsize_x;
594 namesize += hud_fontsize_x;
595 hud_size[i] = namesize;
597 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
598 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
599 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
600 hud_fixscoreboardcolumnwidth_len = stringwidth(str, TRUE, hud_fontsize);
603 hud_fixscoreboardcolumnwidth_len = stringwidth(str, FALSE, hud_fontsize);
605 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
612 void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
615 rgb = GetTeamRGB(pl.team);
619 is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
621 if((rgb == '1 1 1') && (!is_spec)) {
622 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
623 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
624 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
628 tmp_y = hud_fontsize_y * 1.25;
631 // alternated rows highlighting
633 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
634 else if((scoreboard_highlight) && (!mod(pl_number,2)))
635 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
639 for(i = 0; i < hud_num_fields; ++i)
641 field = hud_field[i];
642 if(field == SP_SEPARATOR)
645 if(is_spec && field != SP_NAME && field != SP_PING) {
646 pos_x += hud_size[i] + hud_fontsize_x;
649 str = HUD_GetField(pl, field);
650 str = HUD_FixScoreboardColumnWidth(i, str);
652 pos_x += hud_size[i] + hud_fontsize_x;
654 if(field == SP_NAME) {
655 tmp_x = hud_size[i] - hud_fontsize_x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize_x;
657 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
659 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
661 tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize_x;
663 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
665 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
668 tmp_x = hud_size[i] + hud_fontsize_x;
669 if(hud_field_icon0 != "")
671 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
673 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
674 if(hud_field_icon1 != "")
676 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
678 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
679 if(hud_field_icon2 != "")
681 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
683 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
686 if(hud_field[i] == SP_SEPARATOR)
689 for(i = hud_num_fields-1; i > 0; --i)
691 field = hud_field[i];
692 if(field == SP_SEPARATOR)
695 if(is_spec && field != SP_NAME && field != SP_PING) {
696 pos_x -= hud_size[i] + hud_fontsize_x;
700 str = HUD_GetField(pl, field);
701 str = HUD_FixScoreboardColumnWidth(i, str);
703 if(field == SP_NAME) {
704 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
706 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
708 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
710 tmp_x = hud_fixscoreboardcolumnwidth_len;
712 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
714 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
718 if(hud_field_icon0 != "")
720 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
722 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
723 if(hud_field_icon1 != "")
725 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
727 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
728 if(hud_field_icon2 != "")
730 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
732 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
733 pos_x -= hud_size[i] + hud_fontsize_x;
739 * HUD_Scoreboard_MakeTable
741 * Makes a table for a team (for all playing players in DM) and fills it
744 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
746 float body_table_height, i;
747 vector tmp, column_dim;
750 body_table_height = 1.25 * hud_fontsize_y * max(1, tm.team_size); // no player? show 1 empty line
755 tmp_y = 1.25 * hud_fontsize_y;
759 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
761 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
764 tmp_y += autocvar_scoreboard_border_thickness;
765 tmp_y += body_table_height;
766 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
768 // separator header/table
769 pos_y += 1.25 * hud_fontsize_y;
770 tmp_y = autocvar_scoreboard_border_thickness;
771 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
773 pos_y += autocvar_scoreboard_border_thickness;
776 tmp_y = body_table_height;
778 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
780 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
782 // anyway, apply some color
783 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
785 // go back to the top to make alternated columns highlighting and to print the strings
786 pos_y -= 1.25 * hud_fontsize_y;
787 pos_y -= autocvar_scoreboard_border_thickness;
791 if (scoreboard_highlight)
793 column_dim_y = 1.25 * hud_fontsize_y; // header
794 column_dim_y += autocvar_scoreboard_border_thickness;
795 column_dim_y += body_table_height;
798 // print the strings of the columns headers and draw the columns
799 for(i = 0; i < hud_num_fields; ++i)
801 if(hud_field[i] == SP_SEPARATOR)
803 column_dim_x = hud_size[i] + hud_fontsize_x;
804 if (scoreboard_highlight)
807 drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
809 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
810 pos_x += column_dim_x;
812 if(hud_field[i] == SP_SEPARATOR)
816 for(i = hud_num_fields-1; i > 0; --i)
818 if(hud_field[i] == SP_SEPARATOR)
821 pos_x -= hud_size[i];
823 if (scoreboard_highlight)
827 if (i == hud_num_fields-1)
828 column_dim_x = hud_size[i] + hud_fontsize_x / 2 + 1;
830 column_dim_x = hud_size[i] + hud_fontsize_x;
831 drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
835 tmp_x = stringwidth(hud_title[i], FALSE, hud_fontsize);
836 tmp_x = (hud_size[i] - tmp_x);
837 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
838 pos_x -= hud_fontsize_x;
843 pos_y += 1.25 * hud_fontsize_y; // skip the header
844 pos_y += autocvar_scoreboard_border_thickness;
846 // fill the table and draw the rows
849 for(pl = players.sort_next; pl; pl = pl.sort_next)
851 if(pl.team != tm.team)
853 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
854 pos_y += 1.25 * hud_fontsize_y;
858 for(pl = players.sort_next; pl; pl = pl.sort_next)
860 if(pl.team == COLOR_SPECTATOR)
862 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
863 pos_y += 1.25 * hud_fontsize_y;
868 pos_y += 1.25 * hud_fontsize_y; // move to the end of the table
869 pos_y += 1.25 * hud_fontsize_y; // move empty row (out of the table)
874 float HUD_WouldDrawScoreboard() {
875 if (autocvar__hud_configure)
877 else if (scoreboard_showscores)
879 else if (intermission == 1)
881 else if (intermission == 2)
883 else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != GAME_CTS)
885 else if (scoreboard_showscores_force)
891 float average_accuracy;
892 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
895 float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
897 if(autocvar_scoreboard_accuracy_doublerows)
902 float fontsize = height * 1/3;
903 float weapon_height = height * 2/3;
904 float weapon_width = sbwidth / weapon_cnt;
906 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
907 pos_y += 1.25 * hud_fontsize_y;
910 tmp_y = height * rows;
913 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
915 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
916 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
918 // column highlighting
919 for(i = 0; i < weapon_cnt/rows; ++i)
922 drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
926 for(i = 0; i < rows; ++i)
928 drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
931 average_accuracy = 0;
932 float weapons_with_stats;
933 weapons_with_stats = 0;
935 pos_x += weapon_width / 2;
937 if(getstati(STAT_SWITCHWEAPON) == WEP_MINSTANEX)
938 g_minstagib = 1; // TODO: real detection for minstagib?
940 float weapon_stats, weapon_number;
944 else if (acc_col_x[0] == -1)
945 for (i = 0; i < acc_levels; ++i)
946 acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
948 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
950 self = get_weaponinfo(i);
953 if ((i == WEP_NEX && g_minstagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_minstagib) || i == WEP_TUBA) // skip port-o-launch, nex || minstanex and tuba
955 weapon_stats = weapon_accuracy[i-WEP_FIRST];
958 if(weapon_stats >= 0)
959 weapon_alpha = scoreboard_alpha_fg;
961 weapon_alpha = 0.2 * scoreboard_alpha_fg;
964 drawpic_aspect_skin(pos, strcat("weapon", self.netname), '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
966 if(weapon_stats >= 0) {
967 weapons_with_stats += 1;
968 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
971 s = sprintf(_("%d%%"), weapon_stats*100);
974 padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
978 // find the max level lower than weapon_stats
981 while ( j && weapon_stats < acc_lev[j] )
984 // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
986 factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
988 rgb = rgb + factor * (acc_col[j+1] - rgb);
991 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
993 pos_x += weapon_width * rows;
994 if(rows == 2 && i == 6) {
1000 if(weapons_with_stats)
1001 average_accuracy = floor(average_accuracy / weapons_with_stats);
1004 pos_x -= weapon_width / 2;
1008 pos_y += 1.25 * hud_fontsize_y;
1012 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1015 RANKINGS_RECEIVED_CNT = 0;
1016 for (i=RANKINGS_CNT-1; i>=0; --i)
1018 ++RANKINGS_RECEIVED_CNT;
1020 if (RANKINGS_RECEIVED_CNT == 0)
1024 is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
1026 hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1027 hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1028 hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1030 pos_y += hud_fontsize_y;
1031 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1032 pos_y += hud_fontsize_y;
1035 tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1038 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1040 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1041 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1044 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1051 n = grecordholder[i];
1052 p = race_PlaceName(i+1);
1053 if(grecordholder[i] == GetPlayerName(player_localentnum - 1))
1054 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1055 else if(!mod(i, 2) && scoreboard_highlight)
1056 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1057 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1058 drawstring(pos + '3 0 0' * hud_fontsize_y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1059 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1060 pos_y += 1.25 * hud_fontsize_y;
1066 float hud_woulddrawscoreboard_prev;
1067 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1068 void HUD_DrawScoreboard()
1070 float hud_woulddrawscoreboard;
1071 hud_woulddrawscoreboard = scoreboard_active;
1072 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1073 hud_woulddrawscoreboard_change = time;
1074 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1077 if(hud_woulddrawscoreboard) {
1078 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1079 if (scoreboard_fadeinspeed)
1080 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1082 scoreboard_fade_alpha = 1;
1085 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1086 if (scoreboard_fadeoutspeed)
1087 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1089 scoreboard_fade_alpha = 0;
1092 if not(scoreboard_fade_alpha)
1095 HUD_UpdatePlayerTeams();
1097 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1098 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1099 scoreboard_highlight = autocvar_scoreboard_highlight;
1100 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1101 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1102 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1103 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1105 vector rgb, pos, tmp;
1108 xmin = autocvar_scoreboard_offset_left * vid_conwidth;
1109 ymin = autocvar_con_notify * autocvar_con_notifysize;
1111 xmax = (1 - autocvar_scoreboard_offset_right) * vid_conwidth;
1112 ymax = vid_conheight - ymin;
1114 sbwidth = xmax - xmin;
1116 // Initializes position
1122 drawstring(pos, _("Scoreboard"), '24 24 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1124 centerprint_start_x = vid_conwidth - 0.5 * (pos_x + stringwidth(_("Scoreboard"), FALSE, '24 24 0'));
1125 centerprint_start_y = pos_y;
1129 // Draw the scoreboard
1130 vector bg_size = drawgetimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1134 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1136 if(tm.team == COLOR_SPECTATOR)
1139 rgb = GetTeamRGB(tm.team);
1140 drawstring(pos - '2 0 0' * hud_fontsize_x + '0 1 0' * hud_fontsize_y, ftos(tm.(teamscores[ts_primary])), '1 1 0' * hud_fontsize_y * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1142 if(ts_primary != ts_secondary)
1143 drawstring(pos - '2 0 0' * hud_fontsize_x + '0 2.5 0' * hud_fontsize_y, ftos(tm.(teamscores[ts_secondary])), '1 1 0' * hud_fontsize_y * 1, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1145 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1150 rgb_x = autocvar_scoreboard_color_bg_r;
1151 rgb_y = autocvar_scoreboard_color_bg_g;
1152 rgb_z = autocvar_scoreboard_color_bg_b;
1154 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1156 if(tm.team == COLOR_SPECTATOR)
1159 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1163 if(gametype == GAME_CTS || gametype == GAME_RACE) {
1164 if(race_speedaward) {
1165 drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1166 pos_y += 1.25 * hud_fontsize_y;
1168 if(race_speedaward_alltimebest) {
1169 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1170 pos_y += 1.25 * hud_fontsize_y;
1172 pos = HUD_DrawScoreboardRankings(pos, pl, rgb, bg_size);
1174 else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1176 pos = HUD_DrawScoreboardAccuracyStats(pos, GetTeamRGB(myteam), bg_size);
1178 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1185 for(pl = players.sort_next; pl; pl = pl.sort_next)
1187 if(pl.team != COLOR_SPECTATOR)
1189 pos_y += 1.25 * hud_fontsize_y;
1190 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
1196 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1197 pos_y += 1.25 * hud_fontsize_y;
1200 // Print info string
1203 str = sprintf(_("playing on ^2%s^7"), shortmapname);
1204 tl = getstatf(STAT_TIMELIMIT);
1205 fl = getstatf(STAT_FRAGLIMIT);
1206 ll = getstatf(STAT_LEADLIMIT);
1207 if(gametype == GAME_LMS)
1210 str = strcat(str, sprintf(_(" for up to ^1%.1g minutes^7"), tl));
1215 str = strcat(str, sprintf(_(" for up to ^1%.1g minutes^7"), tl));
1219 str = strcat(str, _(" or"));
1222 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1223 (teamscores_label[ts_primary] == "score") ? _("points") :
1224 (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1225 teamscores_label[ts_primary]));
1229 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1230 (teamscores_label[ts_primary] == "score") ? _("points") :
1231 (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1232 teamscores_label[ts_primary]));
1237 if(tl > 0 || fl > 0)
1238 str = strcat(str, _(" or"));
1241 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1242 (teamscores_label[ts_primary] == "score") ? _("points") :
1243 (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1244 teamscores_label[ts_primary]));
1248 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1249 (teamscores_label[ts_primary] == "score") ? _("points") :
1250 (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1251 teamscores_label[ts_primary]));
1257 pos_y += 1.2 * hud_fontsize_y;
1258 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1260 scoreboard_bottom = pos_y + 2 * hud_fontsize_y;