Learn how to add the new APM counter to your mod
Posted: 05 Jan 2022, 08:09
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
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:
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:

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
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