]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/scoreboard.qc
Make the scoreboard a panel
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / scoreboard.qc
index 407c087470ab8dfde6f9d21db71fed1169ba8ff8..cc605d630a49b14e39ceb7e5a74ace0a5184fac7 100644 (file)
@@ -1,14 +1,14 @@
 #include "scoreboard.qh"
-#include "_all.qh"
 
-#include "hud.qh"
+#include "hud/panel/quickmenu.qh"
+#include "hud/all.qh"
 
-#include "../common/constants.qh"
-#include "../common/mapinfo.qh"
-#include "../common/minigames/cl_minigames.qh"
-#include "../common/stats.qh"
-#include "../common/teams.qh"
-#include "../common/util.qh"
+#include <common/ent_cs.qh>
+#include <common/constants.qh>
+#include <common/mapinfo.qh>
+#include <common/minigames/cl_minigames.qh>
+#include <common/stats.qh>
+#include <common/teams.qh>
 
 float scoreboard_alpha_bg;
 float scoreboard_alpha_fg;
@@ -34,6 +34,8 @@ string TranslateScoresLabel(string l)
                case "captime": return CTX(_("SCO^captime"));
                case "deaths": return CTX(_("SCO^deaths"));
                case "destroyed": return CTX(_("SCO^destroyed"));
+               case "dmg": return CTX(_("SCO^dmg"));
+               case "dmgtaken": return CTX(_("SCO^dmgtaken"));
                case "drops": return CTX(_("SCO^drops"));
                case "faults": return CTX(_("SCO^faults"));
                case "fckills": return CTX(_("SCO^fckills"));
@@ -108,7 +110,7 @@ void HUD_UpdatePlayerTeams()
        for(pl = players.sort_next; pl; pl = pl.sort_next)
        {
                num += 1;
-               Team = GetPlayerColor(pl.sv_entnum);
+               Team = entcs_GetScoreTeam(pl.sv_entnum);
                if(SetTeam(pl, Team))
                {
                        tmp = pl.sort_prev;
@@ -126,8 +128,9 @@ void HUD_UpdatePlayerTeams()
        */
 }
 
-int HUD_CompareScore(float vl, float vr, int f)
+int HUD_CompareScore(int vl, int vr, int f)
 {
+    TC(int, vl); TC(int, vr); TC(int, f);
        if(f & SFL_ZERO_IS_WORST)
        {
                if(vl == 0 && vr != 0)
@@ -145,8 +148,8 @@ int HUD_CompareScore(float vl, float vr, int f)
 float HUD_ComparePlayerScores(entity left, entity right)
 {
        float vl, vr, r;
-       vl = GetPlayerColor(left.sv_entnum);
-       vr = GetPlayerColor(right.sv_entnum);
+       vl = entcs_GetTeam(left.sv_entnum);
+       vr = entcs_GetTeam(right.sv_entnum);
 
        if(!left.gotscores)
                vl = NUM_SPECTATOR;
@@ -191,13 +194,14 @@ float HUD_ComparePlayerScores(entity left, entity right)
 
 void HUD_UpdatePlayerPos(entity player)
 {
-       for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
+       entity ent;
+       for(ent = player.sort_next; ent && HUD_ComparePlayerScores(player, ent); ent = player.sort_next)
        {
-               SORT_SWAP(player, other);
+               SORT_SWAP(player, ent);
        }
-       for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
+       for(ent = player.sort_prev; ent != players && HUD_ComparePlayerScores(ent, player); ent = player.sort_prev)
        {
-               SORT_SWAP(other, player);
+               SORT_SWAP(ent, player);
        }
 }
 
@@ -233,13 +237,14 @@ float HUD_CompareTeamScores(entity left, entity right)
 
 void HUD_UpdateTeamPos(entity Team)
 {
-       for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
+       entity ent;
+       for(ent = Team.sort_next; ent && HUD_CompareTeamScores(Team, ent); ent = Team.sort_next)
        {
-               SORT_SWAP(Team, other);
+               SORT_SWAP(Team, ent);
        }
-       for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
+       for(ent = Team.sort_prev; ent != teams && HUD_CompareTeamScores(ent, Team); ent = Team.sort_prev)
        {
-               SORT_SWAP(other, Team);
+               SORT_SWAP(ent, Team);
        }
 }
 
@@ -261,6 +266,8 @@ void Cmd_HUD_Help()
        LOG_INFO(_("^3suicides^7                 Number of suicides\n"));
        LOG_INFO(_("^3frags^7                    kills - suicides\n"));
        LOG_INFO(_("^3kd^7                       The kill-death ratio\n"));
+       LOG_INFO(_("^3dmg^7                      The total damage done\n"));
+       LOG_INFO(_("^3dmgtaken^7                 The total damage taken\n"));
        LOG_INFO(_("^3sum^7                      frags - deaths\n"));
        LOG_INFO(_("^3caps^7                     How often a flag (CTF) or a key (KeyHunt) was captured\n"));
        LOG_INFO(_("^3pickups^7                  How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
@@ -298,18 +305,28 @@ void Cmd_HUD_Help()
                "other gamemodes except DM.\n"));
 }
 
-#define HUD_DefaultColumnLayout() \
-"ping pl name | " \
-"-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
-"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
-"+lms/lives +lms/rank " \
-"+kh/caps +kh/pushes +kh/destroyed " \
-"?+rc/laps ?+rc/time ?+rc/fastest " \
-"+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
-"-lms,rc,nb/score"
-
-void Cmd_HUD_SetFields(float argc)
+// NOTE: adding a gametype with ? to not warn for an optional field
+// make sure it's excluded in a previous exclusive rule, if any
+// otherwise the previous exclusive rule warns anyway
+// e.g. -teams,rc,cts,lms/kills ?+rc/kills
+#define SCOREBOARD_DEFAULT_COLUMNS \
+"ping pl name |" \
+" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
+" -teams,lms/deaths +ft,tdm/deaths" \
+" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
+" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
+" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
+" +lms/lives +lms/rank" \
+" +kh/caps +kh/pushes +kh/destroyed" \
+" ?+rc/laps ?+rc/time +rc,cts/fastest" \
+" +as/objectives +nb/faults +nb/goals" \
+" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
+" -lms,rc,cts,inv,nb/score"
+
+void Cmd_HUD_SetFields(int argc)
 {
+    TC(int, argc);
        int i, j, slash;
        string str, pattern;
        float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
@@ -330,12 +347,12 @@ void Cmd_HUD_SetFields(float argc)
                argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
 
        if(argc < 3)
-               argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
+               argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
 
        if(argc == 3)
        {
                if(argv(2) == "default")
-                       argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
+                       argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
                else if(argv(2) == "all")
                {
                        string s;
@@ -390,18 +407,19 @@ void Cmd_HUD_SetFields(float argc)
                {
                        case "ping": hud_field[hud_num_fields] = SP_PING; break;
                        case "pl": hud_field[hud_num_fields] = SP_PL; break;
-                       case "pl": hud_field[hud_num_fields] = SP_PL; break;
                        case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
                        case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
                        case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
                        case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
+                       case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
+                       case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
                        default:
                        {
                                for(j = 0; j < MAX_SCORE; ++j)
                                        if(str == strtolower(scores_label[j]))
                                                goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
 
-:notfound
+LABEL(notfound)
                                if(str == "frags")
                                        j = SP_FRAGS;
                                else
@@ -410,7 +428,7 @@ void Cmd_HUD_SetFields(float argc)
                                                LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
                                        continue;
                                }
-:found
+LABEL(found)
                                hud_field[hud_num_fields] = j;
                                if(j == ps_primary)
                                        have_primary = 1;
@@ -509,6 +527,7 @@ float hud_field_icon1_alpha;
 float hud_field_icon2_alpha;
 string HUD_GetField(entity pl, int field)
 {
+    TC(int, field);
        float tmp, num, denom;
        int f;
        string str;
@@ -565,7 +584,7 @@ string HUD_GetField(entity pl, int field)
                                        hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
                                }
                        }
-                       return GetPlayerName(pl.sv_entnum);
+                       return entcs_GetName(pl.sv_entnum);
 
                case SP_FRAGS:
                        f = pl.(scores[SP_KILLS]);
@@ -599,6 +618,20 @@ string HUD_GetField(entity pl, int field)
                        }
                        return ftos(f);
 
+               case SP_DMG:
+                       num = pl.(scores[SP_DMG]);
+                       denom = 1000;
+
+                       str = sprintf("%.1f k", num/denom);
+                       return str;
+
+               case SP_DMGTAKEN:
+                       num = pl.(scores[SP_DMGTAKEN]);
+                       denom = 1000;
+
+                       str = sprintf("%.1f k", num/denom);
+                       return str;
+
                default:
                        tmp = pl.(scores[field]);
                        f = scores_flags[field];
@@ -619,6 +652,7 @@ float hud_fixscoreboardcolumnwidth_marginlen;
 
 string HUD_FixScoreboardColumnWidth(int i, string str)
 {
+    TC(int, i);
        float field, f;
        vector sz;
        field = hud_field[i];
@@ -683,14 +717,15 @@ string HUD_FixScoreboardColumnWidth(int i, string str)
        return str;
 }
 
-void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, int pl_number)
+void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
 {
+    TC(bool, is_self); TC(int, pl_number);
        vector tmp, rgb;
        rgb = Team_ColorRGB(pl.team);
        string str;
        int field;
        float is_spec;
-       is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
+       is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
 
        if((rgb == '1 1 1') && (!is_spec)) {
                rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
@@ -954,9 +989,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz
 }
 
 float HUD_WouldDrawScoreboard() {
-       if (autocvar__hud_configure)
-               return 0;
-       else if (QuickMenu_IsOpened())
+       if (QuickMenu_IsOpened())
                return 0;
        else if (HUD_Radar_Clickable())
                return 0;
@@ -966,7 +999,7 @@ float HUD_WouldDrawScoreboard() {
                return 1;
        else if (intermission == 2)
                return 0;
-       else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
+       else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
                return 1;
        else if (scoreboard_showscores_force)
                return 1;
@@ -975,35 +1008,25 @@ float HUD_WouldDrawScoreboard() {
 
 float average_accuracy;
 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
-{SELFPARAM();
+{
        WepSet weapons_stat = WepSet_GetFromStat();
        WepSet weapons_inmap = WepSet_GetFromStat_InMap();
        float initial_posx = pos.x;
-       int i;
-       float weapon_stats;
        int disownedcnt = 0;
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-       {
-               setself(get_weaponinfo(i));
-               if(!self.weapon)
-                       continue;
-
-               weapon_stats = weapon_accuracy[i-WEP_FIRST];
+       FOREACH(Weapons, it != WEP_Null, {
+               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
 
-               if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
+               WepSet set = it.m_wepset;
+               if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
                        ++disownedcnt;
-       }
+       });
 
-       int weapon_cnt = WEP_COUNT - disownedcnt;
+       int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
+       if (weapon_cnt <= 0) return pos;
 
-       if(weapon_cnt <= 0)
-               return pos;
-
-       int rows;
-       if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor(WEP_COUNT * 0.5))
+       int rows = 1;
+       if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
                rows = 2;
-       else
-               rows = 1;
        int columnns = ceil(weapon_cnt / rows);
 
        float height = 40;
@@ -1024,24 +1047,24 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
        drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
 
        // column highlighting
-       for(i = 0; i < columnns; ++i)
+       for (int i = 0; i < columnns; ++i)
        {
-               if(!(i % 2))
+               if ((i % 2) == 0)
                        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);
        }
 
        // row highlighting
-       for(i = 0; i < rows; ++i)
+       for (int i = 0; i < rows; ++i)
        {
                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);
        }
 
        average_accuracy = 0;
        int weapons_with_stats = 0;
-       if(rows == 2)
+       if (rows == 2)
                pos.x += weapon_width / 2;
 
-       if(autocvar_scoreboard_accuracy_nocolors)
+       if (autocvar_scoreboard_accuracy_nocolors)
                rgb = '1 1 1';
        else
                Accuracy_LoadColors();
@@ -1049,27 +1072,24 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
        float oldposx = pos.x;
        vector tmpos = pos;
 
-       int column;
-       for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i)
-       {
-               setself(get_weaponinfo(i));
-               if (!self.weapon)
-                       continue;
-               weapon_stats = weapon_accuracy[i-WEP_FIRST];
+       int column = 0;
+       FOREACH(Weapons, it != WEP_Null, {
+               int weapon_stats = weapon_accuracy[i - WEP_FIRST];
 
-               if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i)))
+               WepSet set = it.m_wepset;
+               if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
                        continue;
 
                float weapon_alpha;
-               if(weapon_stats >= 0)
+               if (weapon_stats >= 0)
                        weapon_alpha = scoreboard_alpha_fg;
                else
                        weapon_alpha = 0.2 * scoreboard_alpha_fg;
 
                // weapon icon
-               drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
+               drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
                // the accuracy
-               if(weapon_stats >= 0) {
+               if (weapon_stats >= 0) {
                        weapons_with_stats += 1;
                        average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
 
@@ -1086,15 +1106,15 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
                }
                tmpos.x += weapon_width * rows;
                pos.x += weapon_width * rows;
-               if(rows == 2 && column == columnns - 1) {
+               if (rows == 2 && column == columnns - 1) {
                        tmpos.x = oldposx;
                        tmpos.y += height;
                        pos.y += height;
                }
                ++column;
-       }
+       });
 
-       if(weapons_with_stats)
+       if (weapons_with_stats)
                average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
 
        pos.y += height;
@@ -1122,12 +1142,12 @@ vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
        string val;
 
        // get monster stats
-       stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
-       stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
+       stat_monsters_killed = STAT(MONSTERS_KILLED);
+       stat_monsters_total = STAT(MONSTERS_TOTAL);
 
        // get secrets stats
-       stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
-       stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
+       stat_secrets_found = STAT(SECRETS_FOUND);
+       stat_secrets_total = STAT(SECRETS_TOTAL);
 
        // get number of rows
        if(stat_secrets_total)
@@ -1186,7 +1206,7 @@ vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_
                return pos;
 
        float is_spec;
-       is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
+       is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
        vector hl_rgb;
        hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
        hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
@@ -1215,7 +1235,7 @@ vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_
                        continue;
                n = grecordholder[i];
                p = count_ordinal(i+1);
-               if(grecordholder[i] == GetPlayerName(player_localnum))
+               if(grecordholder[i] == entcs_GetName(player_localnum))
                        drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
                else if(!(i % 2) && scoreboard_highlight)
                        drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
@@ -1233,52 +1253,85 @@ float hud_woulddrawscoreboard_prev;
 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
 void HUD_DrawScoreboard()
 {
-       float hud_woulddrawscoreboard;
-       hud_woulddrawscoreboard = scoreboard_active;
-       if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
-               hud_woulddrawscoreboard_change = time;
-               hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
-       }
+       if(!autocvar__hud_configure)
+       {
+               float hud_woulddrawscoreboard;
+               hud_woulddrawscoreboard = scoreboard_active;
+               if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
+                       hud_woulddrawscoreboard_change = time;
+                       hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
+               }
+       
+               if(hud_woulddrawscoreboard) {
+                       if(menu_enabled == 1)
+                               scoreboard_fade_alpha = 1;
+                       float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
+                       if (scoreboard_fadeinspeed)
+                               scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
+                       else
+                               scoreboard_fade_alpha = 1;
+               }
+               else {
+                       float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
+                       if (scoreboard_fadeoutspeed)
+                               scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
+                       else
+                               scoreboard_fade_alpha = 0;
+               }
 
-       if(hud_woulddrawscoreboard) {
-               float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
-               if (scoreboard_fadeinspeed)
-                       scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
-               else
-                       scoreboard_fade_alpha = 1;
-       }
-       else {
-               float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
-               if (scoreboard_fadeoutspeed)
-                       scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
-               else
-                       scoreboard_fade_alpha = 0;
+               if (!scoreboard_fade_alpha)
+                       return;
        }
+       else
+               scoreboard_fade_alpha = 0;
 
-       if (!scoreboard_fade_alpha)
-               return;
+       if (autocvar_scoreboard_dynamichud)
+               HUD_Scale_Enable();
+       else
+               HUD_Scale_Disable();
 
-       HUD_UpdatePlayerTeams();
+       float hud_fade_alpha_save = hud_fade_alpha;
+       if(menu_enabled == 1)
+               hud_fade_alpha = 1;
+       else
+               hud_fade_alpha = scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
+       HUD_Panel_UpdateCvars();
 
-       scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
-       scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
+       scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * panel_fg_alpha;
+       scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * panel_fg_alpha;
        scoreboard_highlight = autocvar_scoreboard_highlight;
-       scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
-       scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
-       scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
-       scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
+       scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * panel_fg_alpha;
+       scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * panel_fg_alpha;
+       scoreboard_alpha_name = autocvar_scoreboard_alpha_name * panel_fg_alpha;
+       scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * panel_fg_alpha;
+
+       hud_fade_alpha = hud_fade_alpha_save;
+
+       // don't overlap with con_notify
+       if(!autocvar__hud_configure)
+               panel_pos_y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos_y);
+
+       HUD_Panel_DrawBg(scoreboard_fade_alpha);
+
+       if(panel_bg_padding)
+       {
+               panel_pos += '1 1 0' * panel_bg_padding;
+               panel_size -= '2 2 0' * panel_bg_padding;
+       }
+
+       HUD_UpdatePlayerTeams();
 
        vector rgb, pos, tmp;
        entity pl, tm;
        string str;
 
-       xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
-       ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
+       xmin = panel_pos_x;
+       ymin = panel_pos_y;
 
-       xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
-       ymax = (vid_conheight - ymin);
+       xmax = panel_pos_x + panel_size_x;
+       ymax = panel_pos_y + panel_size_y;
 
-       sbwidth = xmax - xmin;
+       sbwidth = panel_size_x;
 
        // Initializes position
        pos.x = xmin;
@@ -1295,7 +1348,7 @@ void HUD_DrawScoreboard()
        pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
 
        // Draw the scoreboard
-       vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
+       vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
 
        if(teamplay)
        {
@@ -1305,6 +1358,8 @@ void HUD_DrawScoreboard()
                {
                        if(tm.team == NUM_SPECTATOR)
                                continue;
+                       if(!tm.team && teamplay)
+                               continue;
 
                        draw_beginBoldFont();
                        rgb = Team_ColorRGB(tm.team);
@@ -1334,6 +1389,8 @@ void HUD_DrawScoreboard()
                {
                        if(tm.team == NUM_SPECTATOR)
                                continue;
+                       if(!tm.team && teamplay)
+                               continue;
 
                        pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
                }
@@ -1341,16 +1398,16 @@ void HUD_DrawScoreboard()
 
        if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
                if(race_speedaward) {
-                       drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
+                       drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
                        pos.y += 1.25 * hud_fontsize.y;
                }
                if(race_speedaward_alltimebest) {
-                       drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
+                       drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
                        pos.y += 1.25 * hud_fontsize.y;
                }
                pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
        }
-       else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
+       else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
                if(teamplay)
                        pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
                else
@@ -1391,9 +1448,9 @@ void HUD_DrawScoreboard()
        // Print info string
        float tl, fl, ll;
        str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
-       tl = getstatf(STAT_TIMELIMIT);
-       fl = getstatf(STAT_FRAGLIMIT);
-       ll = getstatf(STAT_LEADLIMIT);
+       tl = STAT(TIMELIMIT);
+       fl = STAT(FRAGLIMIT);
+       ll = STAT(LEADLIMIT);
        if(gametype == MAPINFO_TYPE_LMS)
        {
                if(tl > 0)
@@ -1447,7 +1504,7 @@ void HUD_DrawScoreboard()
        drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
 
        // print information about respawn status
-       float respawn_time = getstatf(STAT_RESPAWN_TIME);
+       float respawn_time = STAT(RESPAWN_TIME);
        if(!intermission)
        if(respawn_time)
        {