mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 23:54:53 +09:00
The plugin is now exp_player_statistics (@expcluster/player-statistics), with the module, global, permission, route and database directory renamed to match. The exp-statistics locale section keeps its name. Every statistic is tracked unconditionally, so the enable flags are gone from config.lua. Playtime and JoinCount are no longer counted in game: clusterio already keeps online time and joins for each user, so the controller fills them in from the user when a player's totals are requested, and the web page does the same from the user list. They are not stored or adjusted by the plugin. The web table has a column picker, a multiple select with check boxes, Select all and Reset buttons, showing "N statistics selected". The choice is kept in local storage. The user page shows the statistics of the player in a grid like the core player stats. The readme statistics table drops its second title and uses the short time format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
221 lines
8.6 KiB
Lua
221 lines
8.6 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,
|
|
},
|
|
Locale = {
|
|
default = "en",
|
|
calculate = function(player)
|
|
return player.locale
|
|
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
|
|
}
|
|
}
|