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 (getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != GAME_CTS)
885 else if (spectatee_status == -1)
887 else if (scoreboard_showscores_force)
893 float average_accuracy;
894 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
897 float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
899 if(autocvar_scoreboard_accuracy_doublerows)
904 float fontsize = height * 1/3;
905 float weapon_height = height * 2/3;
906 float weapon_width = sbwidth / weapon_cnt;
908 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
909 pos_y += 1.25 * hud_fontsize_y;
912 tmp_y = height * rows;
915 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
917 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
918 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
920 // column highlighting
921 for(i = 0; i < weapon_cnt/rows; ++i)
924 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);
928 for(i = 0; i < rows; ++i)
930 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);
933 average_accuracy = 0;
934 float weapons_with_stats;
935 weapons_with_stats = 0;
937 pos_x += weapon_width / 2;
939 if(getstati(STAT_SWITCHWEAPON) == WEP_MINSTANEX)
940 g_minstagib = 1; // TODO: real detection for minstagib?
942 float weapon_stats, weapon_number;
946 else if (acc_col_x[0] == -1)
947 for (i = 0; i < acc_levels; ++i)
948 acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
950 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
952 self = get_weaponinfo(i);
955 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
957 weapon_stats = weapon_accuracy[i-WEP_FIRST];
960 if(weapon_stats >= 0)
961 weapon_alpha = scoreboard_alpha_fg;
963 weapon_alpha = 0.2 * scoreboard_alpha_fg;
966 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);
968 if(weapon_stats >= 0) {
969 weapons_with_stats += 1;
970 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
973 s = sprintf(_("%d%%"), weapon_stats*100);
976 padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
980 // find the max level lower than weapon_stats
983 while ( j && weapon_stats < acc_lev[j] )
986 // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
988 factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
990 rgb = rgb + factor * (acc_col[j+1] - rgb);
993 drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
995 pos_x += weapon_width * rows;
996 if(rows == 2 && i == 6) {
1002 if(weapons_with_stats)
1003 average_accuracy = floor(average_accuracy / weapons_with_stats);
1006 pos_x -= weapon_width / 2;
1010 pos_y += 1.25 * hud_fontsize_y;
1014 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1017 RANKINGS_RECEIVED_CNT = 0;
1018 for (i=RANKINGS_CNT-1; i>=0; --i)
1020 ++RANKINGS_RECEIVED_CNT;
1022 if (RANKINGS_RECEIVED_CNT == 0)
1026 is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
1028 hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1029 hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1030 hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1032 pos_y += hud_fontsize_y;
1033 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1034 pos_y += hud_fontsize_y;
1037 tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1040 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1042 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1043 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1046 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1053 n = grecordholder[i];
1054 p = race_PlaceName(i+1);
1055 if(grecordholder[i] == GetPlayerName(player_localentnum - 1))
1056 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1057 else if(!mod(i, 2) && scoreboard_highlight)
1058 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1059 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1060 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);
1061 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1062 pos_y += 1.25 * hud_fontsize_y;
1068 float hud_woulddrawscoreboard_prev;
1069 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1070 void HUD_DrawScoreboard()
1072 float hud_woulddrawscoreboard;
1073 hud_woulddrawscoreboard = scoreboard_active;
1074 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1075 hud_woulddrawscoreboard_change = time;
1076 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1079 if(hud_woulddrawscoreboard) {
1080 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1081 if (scoreboard_fadeinspeed)
1082 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1084 scoreboard_fade_alpha = 1;
1087 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1088 if (scoreboard_fadeoutspeed)
1089 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1091 scoreboard_fade_alpha = 0;
1094 if not(scoreboard_fade_alpha)
1097 HUD_UpdatePlayerTeams();
1099 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1100 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1101 scoreboard_highlight = autocvar_scoreboard_highlight;
1102 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1103 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1104 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1105 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1107 vector rgb, pos, tmp;
1110 xmin = autocvar_scoreboard_offset_left * vid_conwidth;
1111 ymin = autocvar_con_notify * autocvar_con_notifysize;
1113 xmax = (1 - autocvar_scoreboard_offset_right) * vid_conwidth;
1114 ymax = vid_conheight - ymin;
1116 sbwidth = xmax - xmin;
1118 // Initializes position
1124 drawstring(pos, _("Scoreboard"), '24 24 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1126 centerprint_start_x = vid_conwidth - 0.5 * (pos_x + stringwidth(_("Scoreboard"), FALSE, '24 24 0'));
1127 centerprint_start_y = pos_y;
1131 // Draw the scoreboard
1132 vector bg_size = drawgetimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1136 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1138 if(tm.team == COLOR_SPECTATOR)
1141 rgb = GetTeamRGB(tm.team);
1142 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);
1144 if(ts_primary != ts_secondary)
1145 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);
1147 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1152 rgb_x = autocvar_scoreboard_color_bg_r;
1153 rgb_y = autocvar_scoreboard_color_bg_g;
1154 rgb_z = autocvar_scoreboard_color_bg_b;
1156 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1158 if(tm.team == COLOR_SPECTATOR)
1161 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1165 // FIXME i18n until here
1166 if(gametype == GAME_CTS || gametype == GAME_RACE) {
1167 if(race_speedaward) {
1168 drawcolorcodedstring(pos, strcat("Speed award: ", ftos(race_speedaward), " ^7(", race_speedaward_holder, "^7)"), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1169 pos_y += 1.25 * hud_fontsize_y;
1171 if(race_speedaward_alltimebest) {
1172 drawcolorcodedstring(pos, strcat("All-time fastest: ", ftos(race_speedaward_alltimebest), " ^7(", race_speedaward_alltimebest_holder, "^7)"), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1173 pos_y += 1.25 * hud_fontsize_y;
1175 pos = HUD_DrawScoreboardRankings(pos, pl, rgb, bg_size);
1177 else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1179 pos = HUD_DrawScoreboardAccuracyStats(pos, GetTeamRGB(myteam), bg_size);
1181 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1188 for(pl = players.sort_next; pl; pl = pl.sort_next)
1190 if(pl.team != COLOR_SPECTATOR)
1192 pos_y += 1.25 * hud_fontsize_y;
1193 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
1199 drawstring(tmp, "Spectators", hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1200 pos_y += 1.25 * hud_fontsize_y;
1203 // Print info string
1206 str = strcat("playing on ^2", shortmapname, "^7");
1207 tl = getstatf(STAT_TIMELIMIT);
1208 fl = getstatf(STAT_FRAGLIMIT);
1209 ll = getstatf(STAT_LEADLIMIT);
1210 if(gametype == GAME_LMS)
1213 str = strcat(str, " for up to ^1", ftos(tl), " minutes^7");
1218 str = strcat(str, " for ^1", ftos(tl), " minutes^7");
1222 str = strcat(str, " or");
1225 str = strcat(str, " until ^3", ScoreString(teamscores_flags[ts_primary], fl));
1226 if(teamscores_label[ts_primary] == "score")
1227 str = strcat(str, " points^7");
1228 else if(teamscores_label[ts_primary] == "fastest")
1229 str = strcat(str, " is beaten^7");
1231 str = strcat(str, " ", teamscores_label[ts_primary]);
1235 str = strcat(str, " until ^3", ScoreString(scores_flags[ps_primary], fl));
1236 if(scores_label[ps_primary] == "score")
1237 str = strcat(str, " points^7");
1238 else if(scores_label[ps_primary] == "fastest")
1239 str = strcat(str, " is beaten^7");
1241 str = strcat(str, " ", scores_label[ps_primary]);
1246 if(tl > 0 || fl > 0)
1247 str = strcat(str, " or");
1250 str = strcat(str, " until a lead of ^3", ScoreString(teamscores_flags[ts_primary], ll));
1251 if(teamscores_label[ts_primary] == "score")
1252 str = strcat(str, " points^7");
1253 else if(teamscores_label[ts_primary] == "fastest")
1254 str = strcat(str, " is beaten^7");
1256 str = strcat(str, " ", teamscores_label[ts_primary]);
1260 str = strcat(str, " until a lead of ^3", ScoreString(scores_flags[ps_primary], ll));
1261 if(scores_label[ps_primary] == "score")
1262 str = strcat(str, " points^7");
1263 else if(scores_label[ps_primary] == "fastest")
1264 str = strcat(str, " is beaten^7");
1266 str = strcat(str, " ", scores_label[ps_primary]);
1272 pos_y += 1.2 * hud_fontsize_y;
1273 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1275 scoreboard_bottom = pos_y + 2 * hud_fontsize_y;