Files

215 lines
8.4 KiB
Lua

--[[-- Gui - Player Data
Displays the player data for a player
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_gui")
local ElementsExtra = require("modules/exp_scenario/gui/elements")
local Roles = require("modules/exp_roles")
local ExpPlayerStatistics = require("modules/exp_player_statistics")
--- @class ExpGui_PlayerStats.elements
local Elements = {}
local short_time_format = ExpUtil.format_time_factory_locale{ format = "short", coefficient = 3600, hours = true, minutes = true }
local format_number = require("util").format_number
local function format_number_2dp(n)
return format_number(math.floor(n), false) .. string.format("%.2f", n % 1):sub(2)
end
local short_time_zero, format_number_zero = short_time_format(0), format_number_2dp(0)
--- Get a statistic of a player, or the default when it is zero
--- @param player LuaPlayer
--- @param statistic string
--- @param default number
--- @return number
local function get_stat(player, statistic, default)
local value = ExpPlayerStatistics.get(player, statistic)
return value == 0 and default or value
end
--- @type table<string, { default: LocalisedString, calculate: fun(player: LuaPlayer): LocalisedString }>
local computed_stats = {
DamageDeathRatio = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "DamageDealt", 0) / get_stat(player, "Deaths", 1))
end,
},
KillDeathRatio = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "Kills", 0) / get_stat(player, "Deaths", 1))
end,
},
SessionTime = {
default = short_time_zero,
calculate = function(player)
return short_time_format((get_stat(player, "Playtime", 0) - get_stat(player, "AfkTime", 0)) / get_stat(player, "JoinCount", 1))
end,
},
BuildRatio = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "MachinesBuilt", 0) / get_stat(player, "MachinesRemoved", 1))
end,
},
RocketPerHour = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "RocketsLaunched", 0) * 60 / get_stat(player, "Playtime", 1))
end,
},
TreeKillPerMinute = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "TreesDestroyed", 0) / get_stat(player, "Playtime", 1))
end,
},
NetPlayTime = {
default = short_time_zero,
calculate = function(player)
return short_time_format((get_stat(player, "Playtime", 0) - get_stat(player, "AfkTime", 0)))
end,
},
AFKTimeRatio = {
default = format_number_zero,
calculate = function(player)
return format_number_2dp(get_stat(player, "AfkTime", 0) * 100 / get_stat(player, "Playtime", 1))
end,
},
}
--- Label used for all data in the data table
--- @class ExpGui_PlayerStats.elements.table_label: ExpElement
--- @overload fun(parent: LuaGuiElement, opts: { caption: LocalisedString, tooltip: LocalisedString, width: number })
Elements.table_label = Gui.define("player_stats/table_label")
:draw{
type = "label",
caption = Gui.from_argument("caption"),
tooltip = Gui.from_argument("tooltip"),
style = "heading_2_label",
}
:style{
width = Gui.from_argument("width"),
} --[[@as any]]
--- Data table that shows all data for a player
--- @class ExpGui_PlayerStats.elements.player_stats_table: ExpElement
--- @field data table<LuaGuiElement, { [string]: LuaGuiElement }>
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.player_stats_table = Gui.define("player_stats/data_table")
:draw(function(def, parent)
--- @cast def ExpGui_PlayerStats.elements.player_stats_table
local data_table = Gui.elements.scroll_table(parent, 240, 4)
local labels = {}
-- Add all standalone stats
for _, stat_name in ipairs(ExpPlayerStatistics.display_order) do
Elements.table_label(data_table, {
caption = { "exp-statistics." .. stat_name },
tooltip = { "exp-statistics." .. stat_name .. "-tooltip" },
width = 135,
})
labels[stat_name] = Elements.table_label(data_table, {
caption = { "exp-gui_readme.data-format", ExpPlayerStatistics.format(stat_name, 0, true), ExpPlayerStatistics.units[stat_name] or "" },
tooltip = { "exp-statistics." .. stat_name .. "-tooltip" },
width = 105,
})
end
-- Add all computed stats
for stat_name, data in pairs(computed_stats) do
Elements.table_label(data_table, {
caption = { "exp-statistics." .. stat_name },
tooltip = { "exp-statistics." .. stat_name .. "-tooltip" },
width = 135,
})
labels[stat_name] = Elements.table_label(data_table, {
caption = { "exp-gui_readme.data-format", data.default, "" },
tooltip = { "exp-statistics." .. stat_name .. "-tooltip" },
width = 105,
})
end
def.data[data_table] = labels
return data_table
end) --[[@as any]]
--- Refresh a data table with the most recent stats for a player
--- @param data_table LuaGuiElement
--- @param player LuaPlayer
function Elements.player_stats_table.refresh(data_table, player)
local labels = Elements.player_stats_table.data[data_table]
-- Update all standalone stats
for _, stat_name in ipairs(ExpPlayerStatistics.display_order) do
local value = ExpPlayerStatistics.format(stat_name, ExpPlayerStatistics.get(player, stat_name), true)
labels[stat_name].caption = { "exp-gui_readme.data-format", value, ExpPlayerStatistics.units[stat_name] or "" }
end
-- Update all computed stats
for stat_name, data in pairs(computed_stats) do
labels[stat_name].caption = { "exp-gui_readme.data-format", data.calculate(player), "" }
end
end
--- Dropdown which sets the target player
--- @class ExpGui_PlayerStats.elements.player_dropdown: ExpElement
--- @field data table<LuaGuiElement, LuaGuiElement>
--- @overload fun(parent: LuaGuiElement, camera: LuaGuiElement): LuaGuiElement
Elements.player_dropdown = Gui.define("player_stats/player_dropdown")
:track_all_elements()
:draw(function(def, parent)
return ElementsExtra.online_player_dropdown(parent)
end)
:element_data(
Gui.from_argument(1)
)
:on_selection_state_changed(function(def, player, element, event)
--- @cast def ExpGui_PlayerStats.elements.player_dropdown
local data_table = def.data[element]
local target_player = ElementsExtra.online_player_dropdown.get_selected(element)
Elements.player_stats_table.refresh(data_table, target_player)
end) --[[@as any]]
--- Refresh all stats tables associated with a player dropdown
function Elements.player_dropdown.refresh_online()
for _, player_dropdown in Elements.player_dropdown:online_elements() do
local target_player = ElementsExtra.online_player_dropdown.get_selected(player_dropdown)
local data_table = Elements.player_dropdown.data[player_dropdown]
Elements.player_stats_table.refresh(data_table, target_player)
end
end
--- Container added to the left gui flow
Elements.container = Gui.define("player_stats/container")
:draw(function(def, parent)
local container = Gui.elements.container(parent)
local header = Gui.elements.header(container, { caption = { "exp-gui_player-stats.caption-main" } })
local data_table = Elements.player_stats_table(container)
Elements.player_dropdown(header, data_table)
return Gui.elements.container.get_root_element(container)
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(Elements.container, false)
Gui.toolbar.create_button{
name = "toggle_player_stats",
sprite = "item/power-armor-mk2",
tooltip = { "exp-gui_player-stats.tooltip-main" },
left_element = Elements.container,
visible = function(player, element)
return Roles.player_has_permission(player, "exp_scenario.gui.playerdata")
end
}
return {
elements = Elements,
on_nth_tick = {
[300] = Elements.player_dropdown.refresh_online
}
}