mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2024-12-30 23:17:53 +02:00
475 lines
14 KiB
Lua
475 lines
14 KiB
Lua
--scoreboard by mewmew
|
|
|
|
local Event = require 'utils.event'
|
|
local Global = require 'utils.global'
|
|
local Tabs = require 'comfy_panel.main'
|
|
local SpamProtection = require 'utils.spam_protection'
|
|
local Token = require 'utils.token'
|
|
|
|
local Public = {}
|
|
local this = {
|
|
score_table = {},
|
|
sort_by = {}
|
|
}
|
|
|
|
local module_name = 'Scoreboard'
|
|
|
|
Global.register(
|
|
this,
|
|
function(t)
|
|
this = t
|
|
end
|
|
)
|
|
|
|
local sorting_symbol = {ascending = '▲', descending = '▼'}
|
|
local building_and_mining_blacklist = {
|
|
['tile-ghost'] = true,
|
|
['entity-ghost'] = true,
|
|
['item-entity'] = true
|
|
}
|
|
|
|
function Public.get_table()
|
|
return this
|
|
end
|
|
|
|
function Public.reset_tbl()
|
|
this.score_table['player'] = {
|
|
players = {}
|
|
}
|
|
end
|
|
|
|
function Public.init_player_table(player, reset)
|
|
if not player then
|
|
return
|
|
end
|
|
if reset then
|
|
this.score_table[player.force.name].players[player.name] = {
|
|
built_entities = 0,
|
|
deaths = 0,
|
|
killscore = 0,
|
|
mined_entities = 0
|
|
}
|
|
end
|
|
|
|
if not this.score_table[player.force.name] then
|
|
this.score_table[player.force.name] = {}
|
|
end
|
|
|
|
if not this.score_table[player.force.name].players then
|
|
this.score_table[player.force.name].players = {}
|
|
end
|
|
|
|
if not this.score_table[player.force.name].players[player.name] then
|
|
this.score_table[player.force.name].players[player.name] = {
|
|
built_entities = 0,
|
|
deaths = 0,
|
|
killscore = 0,
|
|
mined_entities = 0
|
|
}
|
|
end
|
|
end
|
|
|
|
local function get_score_list(force)
|
|
local score_force = this.score_table[force]
|
|
local score_list = {}
|
|
for _, p in pairs(game.connected_players) do
|
|
if score_force.players[p.name] then
|
|
local score = score_force.players[p.name]
|
|
table.insert(
|
|
score_list,
|
|
{
|
|
name = p.name,
|
|
killscore = score.killscore or 0,
|
|
deaths = score.deaths or 0,
|
|
built_entities = score.built_entities or 0,
|
|
mined_entities = score.mined_entities or 0
|
|
}
|
|
)
|
|
end
|
|
end
|
|
return score_list
|
|
end
|
|
|
|
local function get_sorted_list(method, column_name, score_list)
|
|
local comparators = {
|
|
['ascending'] = function(a, b)
|
|
return a[column_name] < b[column_name]
|
|
end,
|
|
['descending'] = function(a, b)
|
|
return a[column_name] > b[column_name]
|
|
end
|
|
}
|
|
table.sort(score_list, comparators[method])
|
|
return score_list
|
|
end
|
|
|
|
local biters = {
|
|
'small-biter',
|
|
'medium-biter',
|
|
'big-biter',
|
|
'behemoth-biter',
|
|
'small-spitter',
|
|
'medium-spitter',
|
|
'big-spitter',
|
|
'behemoth-spitter'
|
|
}
|
|
local function get_total_biter_killcount(force)
|
|
local count = 0
|
|
for _, biter in pairs(biters) do
|
|
count = count + force.kill_count_statistics.get_input_count(biter)
|
|
end
|
|
return count
|
|
end
|
|
|
|
local function add_global_stats(frame, player)
|
|
local t = frame.add {type = 'table', column_count = 5}
|
|
|
|
local l = t.add {type = 'label', caption = 'Rockets launched: '}
|
|
l.style.font = 'default-game'
|
|
l.style.font_color = {r = 175, g = 75, b = 255}
|
|
l.style.minimal_width = 140
|
|
|
|
local rocketsLaunched_label = t.add {type = 'label', caption = player.force.rockets_launched}
|
|
rocketsLaunched_label.style.font = 'default-listbox'
|
|
rocketsLaunched_label.style.font_color = {r = 0.9, g = 0.9, b = 0.9}
|
|
rocketsLaunched_label.style.minimal_width = 123
|
|
|
|
local bugs_dead_label = t.add {type = 'label', caption = 'Dead bugs: '}
|
|
bugs_dead_label.style.font = 'default-game'
|
|
bugs_dead_label.style.font_color = {r = 0.90, g = 0.3, b = 0.3}
|
|
bugs_dead_label.style.minimal_width = 100
|
|
|
|
local killcount_label = t.add {type = 'label', caption = tostring(get_total_biter_killcount(player.force))}
|
|
killcount_label.style.font = 'default-listbox'
|
|
killcount_label.style.font_color = {r = 0.9, g = 0.9, b = 0.9}
|
|
killcount_label.style.minimal_width = 145
|
|
end
|
|
|
|
local function show_score(data)
|
|
local player = data.player
|
|
local frame = data.frame
|
|
frame.clear()
|
|
|
|
Public.init_player_table(player)
|
|
|
|
-- Global stats : rockets, biters kills
|
|
add_global_stats(frame, player)
|
|
|
|
-- Separator
|
|
local line = frame.add {type = 'line'}
|
|
line.style.top_margin = 8
|
|
line.style.bottom_margin = 8
|
|
|
|
-- Score per player
|
|
local t = frame.add {type = 'table', column_count = 5}
|
|
|
|
-- Score headers
|
|
local headers = {
|
|
{name = 'score_player', caption = 'Player'},
|
|
{column = 'killscore', name = 'score_killscore', caption = 'Killscore'},
|
|
{column = 'deaths', name = 'score_deaths', caption = 'Deaths'},
|
|
{column = 'built_entities', name = 'score_built_entities', caption = 'Built structures'},
|
|
{column = 'mined_entities', name = 'score_mined_entities', caption = 'Mined entities'}
|
|
}
|
|
|
|
local sorting_pref = this.sort_by[player.name]
|
|
for _, header in ipairs(headers) do
|
|
local cap = header.caption
|
|
|
|
-- Add sorting symbol if any
|
|
if header.column and sorting_pref.column == header.column then
|
|
local symbol = sorting_symbol[sorting_pref.method]
|
|
cap = symbol .. cap
|
|
end
|
|
|
|
-- Header
|
|
local label =
|
|
t.add {
|
|
type = 'label',
|
|
caption = cap,
|
|
name = header.name
|
|
}
|
|
label.style.font = 'default-listbox'
|
|
label.style.font_color = {r = 0.98, g = 0.66, b = 0.22} -- yellow
|
|
label.style.minimal_width = 150
|
|
label.style.horizontal_align = 'right'
|
|
end
|
|
|
|
-- Score list
|
|
local score_list = get_score_list(player.force.name)
|
|
|
|
if #game.connected_players > 1 then
|
|
score_list = get_sorted_list(sorting_pref.method, sorting_pref.column, score_list)
|
|
end
|
|
|
|
-- New pane for scores (while keeping headers at same position)
|
|
local scroll_pane =
|
|
frame.add(
|
|
{
|
|
type = 'scroll-pane',
|
|
name = 'score_scroll_pane',
|
|
direction = 'vertical',
|
|
horizontal_scroll_policy = 'never',
|
|
vertical_scroll_policy = 'auto'
|
|
}
|
|
)
|
|
scroll_pane.style.maximal_height = 400
|
|
local column_table = scroll_pane.add {type = 'table', column_count = 5}
|
|
|
|
-- Score entries
|
|
for _, entry in pairs(score_list) do
|
|
local p = game.players[entry.name]
|
|
local special_color = {
|
|
r = p.color.r * 0.6 + 0.4,
|
|
g = p.color.g * 0.6 + 0.4,
|
|
b = p.color.b * 0.6 + 0.4,
|
|
a = 1
|
|
}
|
|
local lines = {
|
|
{caption = entry.name, color = special_color},
|
|
{caption = tostring(entry.killscore)},
|
|
{caption = tostring(entry.deaths)},
|
|
{caption = tostring(entry.built_entities)},
|
|
{caption = tostring(entry.mined_entities)}
|
|
}
|
|
local default_color = {r = 0.9, g = 0.9, b = 0.9}
|
|
|
|
for _, column in ipairs(lines) do
|
|
local label =
|
|
column_table.add {
|
|
type = 'label',
|
|
caption = column.caption,
|
|
color = column.color or default_color
|
|
}
|
|
label.style.font = 'default'
|
|
label.style.minimal_width = 150
|
|
label.style.maximal_width = 150
|
|
label.style.horizontal_align = 'right'
|
|
end -- foreach column
|
|
end -- foreach entry
|
|
end
|
|
|
|
local show_score_token = Token.register(show_score)
|
|
|
|
local function refresh_score_full()
|
|
for _, player in pairs(game.connected_players) do
|
|
local frame = Tabs.comfy_panel_get_active_frame(player)
|
|
if frame then
|
|
if frame.name == module_name then
|
|
show_score({player = player, frame = frame})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function on_player_joined_game(event)
|
|
local player = game.players[event.player_index]
|
|
Public.init_player_table(player)
|
|
if not this.sort_by[player.name] then
|
|
this.sort_by[player.name] = {method = 'descending', column = 'killscore'}
|
|
end
|
|
end
|
|
|
|
local function on_gui_click(event)
|
|
local element = event.element
|
|
if not element or not element.valid then
|
|
return
|
|
end
|
|
local player = game.get_player(event.player_index)
|
|
local name = event.element.name
|
|
|
|
if name == 'tab_Scoreboard' then
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Scoreboard tab_Scoreboard')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
end
|
|
|
|
local frame = Tabs.comfy_panel_get_active_frame(player)
|
|
if not frame then
|
|
return
|
|
end
|
|
if frame.name ~= module_name then
|
|
return
|
|
end
|
|
|
|
local is_spamming = SpamProtection.is_spamming(player, nil, 'Score Gui Click')
|
|
if is_spamming then
|
|
return
|
|
end
|
|
|
|
-- Handles click on a score header
|
|
local element_to_column = {
|
|
['score_killscore'] = 'killscore',
|
|
['score_deaths'] = 'deaths',
|
|
['score_built_entities'] = 'built_entities',
|
|
['score_mined_entities'] = 'mined_entities'
|
|
}
|
|
local column = element_to_column[name]
|
|
if column then
|
|
local sorting_pref = this.sort_by[player.name]
|
|
if sorting_pref.column == column and sorting_pref.method == 'descending' then
|
|
sorting_pref.method = 'ascending'
|
|
else
|
|
sorting_pref.method = 'descending'
|
|
sorting_pref.column = column
|
|
end
|
|
show_score({player = player, frame = frame})
|
|
return
|
|
end
|
|
|
|
-- No more to handle
|
|
end
|
|
|
|
local function on_rocket_launched()
|
|
refresh_score_full()
|
|
end
|
|
|
|
local entity_score_values = {
|
|
['behemoth-biter'] = 100,
|
|
['behemoth-spitter'] = 100,
|
|
['behemoth-worm-turret'] = 300,
|
|
['big-biter'] = 30,
|
|
['big-spitter'] = 30,
|
|
['big-worm-turret'] = 300,
|
|
['biter-spawner'] = 200,
|
|
['medium-biter'] = 15,
|
|
['medium-spitter'] = 15,
|
|
['medium-worm-turret'] = 150,
|
|
['character'] = 1000,
|
|
['small-biter'] = 5,
|
|
['small-spitter'] = 5,
|
|
['small-worm-turret'] = 50,
|
|
['spitter-spawner'] = 200,
|
|
['gun-turret'] = 50,
|
|
['laser-turret'] = 150,
|
|
['flamethrower-turret'] = 300
|
|
}
|
|
|
|
local function train_type_cause(event)
|
|
local players = {}
|
|
if event.cause.train.passengers then
|
|
for _, player in pairs(event.cause.train.passengers) do
|
|
players[#players + 1] = player
|
|
end
|
|
end
|
|
return players
|
|
end
|
|
|
|
local kill_causes = {
|
|
['character'] = function(event)
|
|
if not event.cause.player then
|
|
return
|
|
end
|
|
return {event.cause.player}
|
|
end,
|
|
['combat-robot'] = function(event)
|
|
if not event.cause.last_user then
|
|
return
|
|
end
|
|
if not game.players[event.cause.last_user.index] then
|
|
return
|
|
end
|
|
return {game.players[event.cause.last_user.index]}
|
|
end,
|
|
['car'] = function(event)
|
|
local players = {}
|
|
local driver = event.cause.get_driver()
|
|
if driver then
|
|
if driver.player then
|
|
players[#players + 1] = driver.player
|
|
end
|
|
end
|
|
local passenger = event.cause.get_passenger()
|
|
if passenger then
|
|
if passenger.player then
|
|
players[#players + 1] = passenger.player
|
|
end
|
|
end
|
|
return players
|
|
end,
|
|
['locomotive'] = train_type_cause,
|
|
['cargo-wagon'] = train_type_cause,
|
|
['artillery-wagon'] = train_type_cause,
|
|
['fluid-wagon'] = train_type_cause
|
|
}
|
|
|
|
local function on_entity_died(event)
|
|
if not event.entity.valid then
|
|
return
|
|
end
|
|
if not event.cause then
|
|
return
|
|
end
|
|
if not event.cause.valid then
|
|
return
|
|
end
|
|
if event.entity.force.index == event.cause.force.index then
|
|
return
|
|
end
|
|
if not entity_score_values[event.entity.name] then
|
|
return
|
|
end
|
|
if not kill_causes[event.cause.type] then
|
|
return
|
|
end
|
|
local players_to_reward = kill_causes[event.cause.type](event)
|
|
if not players_to_reward then
|
|
return
|
|
end
|
|
if #players_to_reward == 0 then
|
|
return
|
|
end
|
|
for _, player in pairs(players_to_reward) do
|
|
Public.init_player_table(player)
|
|
local score = this.score_table[player.force.name].players[player.name]
|
|
score.killscore = score.killscore + entity_score_values[event.entity.name]
|
|
end
|
|
end
|
|
|
|
local function on_player_died(event)
|
|
local player = game.players[event.player_index]
|
|
Public.init_player_table(player)
|
|
local score = this.score_table[player.force.name].players[player.name]
|
|
score.deaths = 1 + (score.deaths or 0)
|
|
end
|
|
|
|
local function on_player_mined_entity(event)
|
|
if not event.entity.valid then
|
|
return
|
|
end
|
|
if building_and_mining_blacklist[event.entity.type] then
|
|
return
|
|
end
|
|
|
|
local player = game.players[event.player_index]
|
|
Public.init_player_table(player)
|
|
local score = this.score_table[player.force.name].players[player.name]
|
|
score.mined_entities = 1 + (score.mined_entities or 0)
|
|
end
|
|
|
|
local function on_built_entity(event)
|
|
if not event.created_entity.valid then
|
|
return
|
|
end
|
|
if building_and_mining_blacklist[event.created_entity.type] then
|
|
return
|
|
end
|
|
local player = game.players[event.player_index]
|
|
Public.init_player_table(player)
|
|
local score = this.score_table[player.force.name].players[player.name]
|
|
score.built_entities = 1 + (score.built_entities or 0)
|
|
end
|
|
|
|
Tabs.add_tab_to_gui({name = module_name, id = show_score_token, admin = false})
|
|
|
|
Event.add(defines.events.on_player_mined_entity, on_player_mined_entity)
|
|
Event.add(defines.events.on_player_died, on_player_died)
|
|
Event.add(defines.events.on_built_entity, on_built_entity)
|
|
Event.add(defines.events.on_entity_died, on_entity_died)
|
|
Event.add(defines.events.on_gui_click, on_gui_click)
|
|
Event.add(defines.events.on_player_joined_game, on_player_joined_game)
|
|
Event.add(defines.events.on_rocket_launched, on_rocket_launched)
|
|
|
|
return Public
|