Learn how to add the new APM counter to your mod

Learn how to add the new APM counter to your mod

Classic game design, maintained to please you...

Moderator: Content Developer

Post Reply
Ares
Balanced Annihilation Developer
Posts: 555
Joined: 19 Mar 2011, 13:43

Learn how to add the new APM counter to your mod

Post by Ares »

APM (actions per minute) are regarded as a good indicator of a player's potential in top RTS games like starcraft. In the next BA update I have added an APM counter so we can measure our APM and monitor how APM changes during battles to understand ourselves better and research possible areas to improve. BA has always taken a scientific approach to balancing and I hope this change will help take us to the next level.

I will explain how it works and how you can add the new APM counter to your mod. The gadget is based on the FPS counter gadget made by Floris, every player's machine broadcasts their APM statistics to each other player in real time, and this make the data publicly available during the match.

line 59 _, mc, kp, _, _ = GetPlayerStatistics(myPlayerID) undocumented engine function, this provides the total sum of all mouseclicks and keypresses at the time it is called

line 60 SendLuaRulesMsg("$" .. (math.ceil((((mc + kp) * 60) / (math.max(GetGameSeconds(), 60))) - 0.5))) calculate the APM statistics and broadcast them so can be receive by a widget

gadget: apm_broadcast.lua, add to luarules\gadgets

Code: Select all

function gadget:GetInfo()
    return {
        name = "APM Broadcast",
        desc = "Broadcasts APM",
        author = "Floris",
        date = "July,2016",
        license = "GNU GPL, v2 or later",
        layer = 0,
        enabled = true
    }
end
local GetMyPlayerID = Spring.GetMyPlayerID
--------------------------------------------------------------------------------
-- config
--------------------------------------------------------------------------------
local sendPacketEvery = 2

--------------------------------------------------------------------------------
-- synced
--------------------------------------------------------------------------------
if gadgetHandler:IsSyncedCode() then
    function gadget:RecvLuaMsg(msg, playerID)
        if msg:sub(1, 1) == "$" then
            SendToUnsynced("apmBroadcast", playerID, tonumber(msg:sub(2)))
            return true
        end
    end
else
    --------------------------------------------------------------------------------
    -- unsynced
    --------------------------------------------------------------------------------
    local GetLastUpdateSeconds = Spring.GetLastUpdateSeconds
    local SendLuaRulesMsg = Spring.SendLuaRulesMsg
    local myPlayerID = GetMyPlayerID()

    local updateTimer = 0

    local GetPlayerStatistics = Spring.GetPlayerStatistics
    local GetGameSeconds = Spring.GetGameSeconds

    function gadget:Initialize()
        gadgetHandler:AddSyncAction("apmBroadcast", handleApmEvent)
    end

    function gadget:Shutdown()
        gadgetHandler:RemoveSyncAction("apmBroadcast")
    end

    function handleApmEvent(_, playerID, apm)
        if Script.LuaUI("ApmEvent") then
            Script.LuaUI.ApmEvent(playerID, apm)
        end
    end

    function gadget:Update()
        if not Spring.GetSpectatingState() then
            updateTimer = updateTimer + GetLastUpdateSeconds()
            if updateTimer > sendPacketEvery then
                _, mc, kp, _, _ = GetPlayerStatistics(myPlayerID)
                SendLuaRulesMsg("$" .. (math.ceil((((mc + kp) * 60) / (math.max(GetGameSeconds(), 60))) - 0.5)))
                updateTimer = 0
            end
        end
    end
end
The gadget will then send the client data so it can be received and displayed by statistics functions in widgets or the ui ingame.

In BA the APM is added to the widget gui_advplayerslist.lua in \luaui\widgets, here are the relevant parts:

Code: Select all

function widget:Initialize()
	widgetHandler:RegisterGlobal('ApmEvent', ApmEvent)
end

function widget:Shutdown()
       widgetHandler:DeregisterGlobal('ApmEvent')
end

local lastApmData = {}

function ApmEvent(playerID, apm)
	lastApmData[player[playerID].team] = apm
end

function DrawID(playerID, posY, dark)
	local apm = 0
	if((lastApmData[playerID] ~= nil))  then
		apm = lastApmData[playerID]
	end
	if(apm>998) then
		apm = 999
	end
	Spring.Echo(apm)
end
Hope you learned something new, you can increase the APM update rate by decreasing local sendPacketEvery = 2 to send more frequent APM update packets. If you added it correctly the data will show ingame as shown below:

Image
Attachments
apm.png
Ares
Balanced Annihilation Developer
Posts: 555
Joined: 19 Mar 2011, 13:43

Re: Learn how to add the new APM counter to your mod

Post by Ares »

Here is an update on the APM counter progress. APM is now counted with a sliding window, showing your APM over a rolling 1 minute window. Previously it was APM over the entire game.

Here is the relevant part of code that does it, incase you want to learn how to add it to your own game.

Code: Select all

    local GetLastUpdateSeconds = Spring.GetLastUpdateSeconds
    local SendLuaRulesMsg = Spring.SendLuaRulesMsg
    local myPlayerID = GetMyPlayerID()
    local updateTimer = 0
    local GetPlayerStatistics = Spring.GetPlayerStatistics
    local prevapm = 0
    local slidercounter = 1
    local slidingwindow = {}
    local finalapm
	local deltaapm

    function gadget:Update()
        if not Spring.GetSpectatingState() then
            updateTimer = updateTimer + GetLastUpdateSeconds()
            if updateTimer > sendPacketEvery then
                _, mc, kp, _, _ = GetPlayerStatistics(myPlayerID)
                deltaapm = (mc + kp) - prevapm
                finalapm = 0
                slidingwindow[slidercounter] = deltaapm
                for i = 1, #slidingwindow do
                    finalapm = (finalapm + slidingwindow[i])
                end
                prevapm = (mc + kp)
                slidercounter = slidercounter + 1
                if (slidercounter == 31) then
                    slidercounter = 1
                end
                SendLuaRulesMsg("$" .. (finalapm))
                updateTimer = 0
            end
        end
    end

Image
Attachments
slider.png
Post Reply

Return to “Balanced Annihilation”