Jump to content
Sign in to follow this  
Bonuspunkt

MiniScoreboard

Recommended Posts

NOTE: i did not stop fiddling with the code, and extracted functions to own modules so you need more than one file, and specific file locations etc etc etc.

if you want an up-to-date copy you can download the bundle here

 

Draws current standings on the screen

[YourTeam] [difference] [otherTeam]

require "base/internal/ui/reflexcore"

local function map(tbl, func)
    local newtbl = {}
    for i,v in pairs(tbl) do
        newtbl[i] = func(v)
    end
    return newtbl
end

local function filter(tbl, func)
    local newtbl= {}
    for i,v in pairs(tbl) do
        if func(v) then
            newtbl[i]=v
        end
    end
    return newtbl
 end


local function reduce(tbl, func, initialValue)
    for key,value in pairs(tbl) do
        initialValue = func(initialValue, value)
    end
    return initialValue;
 end

local function forEach(tbl, func)
    for key,value in pairs(tbl) do
        func(value, key, tbl)
    end

end

MiniScores =
{
    draw = function()

        -- Early out if HUD should not be shown.
        if not shouldShowHUD() then return end;

        -- Find player
        local player = getPlayer();

        local textColor = Color(255,255,255,255);

        nvgFontFace("TitilliumWeb-Bold");
        nvgTextAlign(NVG_ALIGN_CENTER, NVG_ALIGN_TOP);

        local activePlayers = filter(players, function(player)
            return player.state == PLAYER_STATE_INGAME and player.connected;
        end)

        local ourScore = 0;
        local theirScore = 0;
        local gameMode = gamemodes[world.gameModeIndex].shortName;

        if gameMode == "tdm" or gameMode == "atdm" then
            ourScore = reduce(map(filter(activePlayers, function(p)
                return player.team == p.team;
            end), function(p)
                return p.score;
            end), function(prev, curr)
                return prev + curr;
            end, 0)

            theirScore = reduce(map(filter(activePlayers, function(p)
                return player.team ~= p.team;
            end), function(p)
                return p.score;
            end), function(prev, curr)
                return prev + curr;
            end, 0)
        end

        if gameMode == "1v1" then
            ourScore = player.score;
            theirScore = reduce(map(filter(activePlayers, function(p)
                return player ~= p;
            end), function(p)
                return p.score;
            end), function(prev, curr)
                return prev + curr;
            end, 0)
        end

        if gameMode == "a1v1" or gameMode == "ffa" or gameMode == "affa" then
            ourScore = player.score;
            theirScore = reduce(map(filter(activePlayers, function(p)
                return player ~= p;
            end), function(p)
                return p.score;
            end), function(prev, curr)
                return math.max(prev, curr);
            end, 0)
        end


        nvgFontSize(64);

        -- our score
        nvgBeginPath();
        nvgRect(-120, 5, 80, 55);
        nvgFillColor(Color(64,64,255,64));
        nvgFill();

        nvgFillColor(textColor);
        nvgText(-80,0, ourScore);

        -- difference
        nvgBeginPath();
        nvgRect(-40, 5, 80, 55);
        nvgFillColor(Color(255,255,255,64));
        nvgFill();

        nvgFillColor(textColor);
        nvgText(0,0, ourScore - theirScore);

        -- their score
        nvgBeginPath();
        nvgRect(40, 5, 80, 55);
        nvgFillColor(Color(255,64,64,64));
        nvgFill();

        nvgFillColor(textColor);
        nvgText(80,0, theirScore);
    end
};
registerWidget("MiniScores");

Share this post


Link to post
Share on other sites

i have placed the file at

<steam>steamappscommonReflexbaseinternaluibonusMiniScores.lua

than you need to tell Reflex to show the widget

ui_show_widget MiniScores

add the line to your game.cfg or type it in the console

 

and for positioning

Share this post


Link to post
Share on other sites

I'd suggest formatting the code:

        if gameMode == "1v1" then
            ourScore = player.score;
            theirScore = reduce(map(filter(activePlayers, function(p)
                return player ~= p;
            end), function(p)
                return p.score;
            end), function(prev, curr)
                return prev + curr;
            end, 0)
        end

and the other sections differently. The above on first glance made no sense to me at all, until I reformatted it to:

    if gameMode == "1v1" then
        ourScore = player.score;
        theirScore =
            reduce(
                map(
                    filter(activePlayers, function(p) return player ~= p; end),
                    function(p) return p.score; end
                ),
                function(prev, curr) return prev + curr; end,
                0
            )
    end

even then... passing unnamed functions as arguments is pretty wild. Will need to do more reading up on LUA it seems.

Share this post


Link to post
Share on other sites

usually i have those methods on Enumerables

you could use this wrapper as syntactic sugar to get the same results

so you'd write

funcArray(activePlayers)
  .filter(function(p) return player ~= p; end)
  .map(function(p) return p.score; end)
  .reduce(function(prev, curr) return prev + curr; end, 0)

so its

  • check
  • get relevant values
  • sum it up

Share this post


Link to post
Share on other sites

I'll read up on your wrapper next... just to be clear I understand the code, I numbered the order of the comments, how you get to theirScore for the 1on1 case:

    if gameMode == "1v1" then
        ourScore = player.score;
        theirScore =
            -- 3. "reduce": Then go through the score tbl of other players and add the current
            --     scores to the initial score (always 0)... and return the sum as theirScore.
            reduce(
                -- 2. "map": Go through the tbl with "other" players, and extract "their"
                ---   score values into another table.
                map(
                    -- 1. "filter": Search the list of activePlayers and create a new
                    --    table of players that excludes the current "player".
                    --    (in 1on1, just the other player).
                    filter(activePlayers, function(p) return player ~= p; end),
                    function(p) return p.score; end
                ),
                function(prev, curr) return prev + curr; end,
                0   -- = initial score
            )
    end

Is my understanding correct?

 

If so, the reduce() function, since it always uses the current .score and then always adds the initialValue = 0, to it... should be redundant, i.e. the following should do the same?

    if gameMode == "1v1" then
        ourScore = player.score;
        theirScore =
                -- 2. Go through the tbl "other" players, and extract "their"
                ---   score values into another table.
                map(
                    -- 1. Search the list of activePlayers and create a new
                    --    table of players that excludes the current "player".
                    --    (in 1on1 the other player).
                    filter(activePlayers, function(p) return player ~= p; end),
                    function(p) return p.score; end
                )
            )
    end

And if I read that correctly none of the other gameModes would require a reduce() function either.

 

Thanks for bearing with me, passing "template functions" is pretty new to me. So to properly understand how it works I am going through them step by step. It is a really elegant way to go about things.

 

 

In the first post source local function forEach(tbl, func) is not used, AFAICT.

Share this post


Link to post
Share on other sites
theirScore = funcArray(activePlayers)
  .filter(function(p) return player ~= p; end)
  .map(function(p) return p.score; end)
  .reduce(function(prev, curr) return prev + curr; end, 0)

Uncanny that something like that is even possible. Very elegant. The "theirScore =" would be placed as I added it above, right?

Share this post


Link to post
Share on other sites

oh yeah "crazy code" the 1v1 :)

i think i had the same code for all modes until i realized that in a 1v1 the opponent is not on the other team. then i just lazy-assed and copy pasted it for every mode an adapted it

 

it would have been sufficent to do

map(filter(...)...)[1]

that should be the other players score

 

the forEach may be a carry over from before the refactoring to FuncArray

 

yeah thats the theirScore part for it

Share this post


Link to post
Share on other sites

Interesting to see non-trivial code, one only learns new things by reading up on practical examples. At least I do :). From my old-school ANSI C background, LUA is doing a lot of evil things... fascinating.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×