From 34e3190882ce124717632b8f000b7d3783ef0101 Mon Sep 17 00:00:00 2001 From: Lynn Date: Thu, 30 May 2019 20:22:21 +0200 Subject: [PATCH] Use a uniform storage for score storage --- features/gui/player_list.lua | 2 +- features/gui/score.lua | 4 +- features/player_stats.lua | 162 ++++++++++++++--------------------- utils/score_tracker.lua | 152 ++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 100 deletions(-) create mode 100644 utils/score_tracker.lua diff --git a/features/gui/player_list.lua b/features/gui/player_list.lua index 647f902f..8bfca61c 100644 --- a/features/gui/player_list.lua +++ b/features/gui/player_list.lua @@ -331,7 +331,7 @@ local column_builders = { local player_index = player.index return { count = PlayerStats.get_death_count(player_index), - causes = PlayerStats.get_all_death_counts_by_cause(player_index) + causes = PlayerStats.get_all_death_causes_by_player(player_index) } end, sort = function(a, b) diff --git a/features/gui/score.lua b/features/gui/score.lua index 66ed4230..a1d0aeff 100644 --- a/features/gui/score.lua +++ b/features/gui/score.lua @@ -2,8 +2,8 @@ local Event = require 'utils.event' local PlayerStats = require 'features.player_stats' local Gui = require 'utils.gui' local Color = require 'resources.color_presets' -local Server = require('features.server') - +local Server = require 'features.server' +local ScoreTracker = require 'utils.score_tracker' local concat = table.concat local main_frame_name = Gui.uid_name() diff --git a/features/player_stats.lua b/features/player_stats.lua index 0b50b712..0f12260a 100644 --- a/features/player_stats.lua +++ b/features/player_stats.lua @@ -1,24 +1,40 @@ local Event = require 'utils.event' local Global = require 'utils.global' +local ScoreTracker = require 'utils.score_tracker' require 'utils.table' local pairs = pairs local sqrt = math.sqrt +local rocks_smashed_name = 'rocks-smashed' +local trees_cut_name = 'trees-cut' +local player_count_name = 'player-count' +local kills_by_trains_name = 'kills-by-trains' +local built_by_robots_name = 'built-by-robots' +local built_by_players_name = 'built-by-players' +local aliens_killed_name = 'aliens-killed' +local coins_spent_name = 'coins-spent' +local coins_earned_name = 'coins-earned' +local player_deaths_name = 'player-deaths' +local player_console_chats_name = 'player-console-chats' +local player_items_crafted_name = 'player-items-crafted' +local player_distance_walked_name = 'player-distance_walked' + +ScoreTracker.register(rocks_smashed_name, {'Rocks smashed'}, '[img=entity.rock-huge]') +ScoreTracker.register(trees_cut_name, {'Trees cut down'}, '[img=entity.tree-02]') +ScoreTracker.register(player_count_name, {'Total players'}) +ScoreTracker.register(kills_by_trains_name, {'Kills by trains'}, '[img=item.locomotive]') +ScoreTracker.register(built_by_players_name, {'Built by hand'}, '[img=utility.hand]') +ScoreTracker.register(built_by_robots_name, {'Built by robots'}, '[img=item.construction-robot]') +ScoreTracker.register(aliens_killed_name, {'Aliens liberated'}, '[img=entity.medium-biter]') +ScoreTracker.register(coins_earned_name, {'Coins earned'}, '[img=item.coin]') +ScoreTracker.register(coins_spent_name, {'Coins spent'}, '[img=item.coin]') +ScoreTracker.register(player_deaths_name, {'Player deaths'}) +ScoreTracker.register(player_console_chats_name, {'Player console chats'}) +ScoreTracker.register(player_items_crafted_name, {'Player items crafted'}) +ScoreTracker.register(player_distance_walked_name, {'Player distance walked'}) + local player_last_position = {} -local player_walk_distances = {} -local player_coin_earned = {} -local player_coin_spent = {} -local player_crafted_items = {} -local player_console_chats = {} -local player_deaths = {} -local total_players = {0} -local total_train_kills = {0} -local total_player_trees_mined = {0} -local total_player_rocks_mined = {0} -local total_robot_built_entities = {0} -local total_player_built_entities = {0} -local total_biter_kills = {0} -local total_coins_spent = {0} +local player_death_causes = {} local train_kill_causes = { ['locomotive'] = true, @@ -30,37 +46,11 @@ local train_kill_causes = { Global.register( { player_last_position = player_last_position, - player_walk_distances = player_walk_distances, - player_coin_earned = player_coin_earned, - player_coin_spent = player_coin_spent, - player_deaths = player_deaths, - total_players = total_players, - total_train_kills = total_train_kills, - total_player_trees_mined = total_player_trees_mined, - total_player_rocks_mined = total_player_rocks_mined, - player_crafted_items = player_crafted_items, - player_console_chats = player_console_chats, - total_robot_built_entities = total_robot_built_entities, - total_player_built_entities = total_player_built_entities, - total_biter_kills = total_biter_kills, - total_coins_spent = total_coins_spent + player_death_causes = player_death_causes, }, function(tbl) player_last_position = tbl.player_last_position - player_walk_distances = tbl.player_walk_distances - player_coin_earned = tbl.player_coin_earned - player_coin_spent = tbl.player_coin_spent - player_deaths = tbl.player_deaths - total_players = tbl.total_players - total_train_kills = tbl.total_train_kills - total_player_trees_mined = tbl.total_player_trees_mined - total_player_rocks_mined = tbl.total_player_rocks_mined - player_crafted_items = tbl.player_crafted_items - player_console_chats = tbl.player_console_chats - total_robot_built_entities = tbl.total_robot_built_entities - total_player_built_entities = tbl.total_player_built_entities - total_biter_kills = tbl.total_biter_kills - total_coins_spent = tbl.total_coins_spent + player_death_causes = tbl.player_death_causes end ) @@ -69,13 +59,8 @@ local function player_created(event) local index = event.player_index player_last_position[index] = game.get_player(index).position - player_walk_distances[index] = 0 - player_coin_earned[index] = 0 - player_coin_spent[index] = 0 - player_crafted_items[index] = 0 - player_console_chats[index] = 0 - player_deaths[index] = {causes = {}, count = 0} - total_players[1] = total_players[1] + 1 + player_death_causes[index] = {} + ScoreTracker.changeForGlobal(player_count_name, 1) end local function get_cause_name(cause) @@ -97,60 +82,55 @@ local function player_died(event) local player_index = event.player_index local cause = get_cause_name(event.cause) - local data = player_deaths[player_index] - data.count = data.count + 1 - - local causes = data.causes + local causes = player_death_causes[player_index] local cause_count = causes[cause] or 0 causes[cause] = cause_count + 1 + ScoreTracker.changeForPlayer(player_index, player_deaths_name, 1) if train_kill_causes[cause] then - total_train_kills[1] = total_train_kills[1] + 1 + ScoreTracker.changeForGlobal(kills_by_trains_name, 1) end end local function picked_up_item(event) local stack = event.item_stack if stack.name == 'coin' then - local player_index = event.player_index - player_coin_earned[player_index] = player_coin_earned[player_index] + stack.count + ScoreTracker.changeForPlayer(event.player_index, coins_earned_name, stack.count) end end local function player_mined_item(event) if event.entity.type == 'simple-entity' then -- Cheap check for rock, may have other side effects - total_player_rocks_mined[1] = total_player_rocks_mined[1] + 1 + ScoreTracker.changeForGlobal(rocks_smashed_name, 1) return end if event.entity.type == 'tree' then - total_player_trees_mined[1] = total_player_trees_mined[1] + 1 + ScoreTracker.changeForGlobal(trees_cut_name, 1) end end local function player_crafted_item(event) - local stack = event.item_stack - local player_index = event.player_index - player_crafted_items[player_index] = player_crafted_items[player_index] + stack.count + ScoreTracker.changeForPlayer(event.player_index, player_items_crafted_name, event.item_stack.count) end local function player_console_chat(event) local player_index = event.player_index if player_index then - player_console_chats[player_index] = player_console_chats[player_index] + 1 + ScoreTracker.changeForPlayer(player_index, player_console_chats_name, 1) end end local function player_built_entity() - total_player_built_entities[1] = total_player_built_entities[1] + 1 + ScoreTracker.changeForGlobal(built_by_players_name, 1) end local function robot_built_entity() - total_robot_built_entities[1] = total_robot_built_entities[1] + 1 + ScoreTracker.changeForGlobal(built_by_robots_name, 1) end local function biter_kill_counter(event) if event.entity.force.name == 'enemy' then - total_biter_kills[1] = total_biter_kills[1] + 1 + ScoreTracker.changeForGlobal(aliens_killed_name, 1) end end @@ -164,8 +144,8 @@ local function tick() local d_x = last_pos.x - pos.x local d_y = last_pos.y - pos.y - player_walk_distances[index] = player_walk_distances[index] + sqrt(d_x * d_x + d_y * d_y) player_last_position[index] = pos + ScoreTracker.changeForPlayer(index, player_distance_walked_name, sqrt(d_x * d_x + d_y * d_y)) end end end @@ -186,85 +166,73 @@ Event.on_nth_tick(62, tick) local Public = {} function Public.get_walk_distance(player_index) - return player_walk_distances[player_index] + return ScoreTracker.getForPlayer(player_index, player_distance_walked_name) end function Public.get_coin_earned(player_index) - return player_coin_earned[player_index] -end - -function Public.set_coin_earned(player_index, value) - player_coin_earned[player_index] = value + return ScoreTracker.getForPlayer(player_index, coins_earned_name) end function Public.change_coin_earned(player_index, amount) - player_coin_earned[player_index] = player_coin_earned[player_index] + amount + ScoreTracker.changeForPlayer(player_index, coins_earned_name, amount) end function Public.get_coin_spent(player_index) - return player_coin_spent[player_index] -end - -function Public.set_coin_spent(player_index, value) - local old_value = player_coin_spent[player_index] - player_coin_spent[player_index] = value - - local diff = value - old_value - total_coins_spent[1] = total_coins_spent[1] + diff + return ScoreTracker.getForPlayer(player_index, coins_spent_name) end function Public.change_coin_spent(player_index, amount) - player_coin_spent[player_index] = player_coin_spent[player_index] + amount - total_coins_spent[1] = total_coins_spent[1] + amount + ScoreTracker.changeForPlayer(player_index, coins_spent_name, amount) + ScoreTracker.changeForGlobal(coins_spent_name, amount) end function Public.get_death_count(player_index) - return player_deaths[player_index].count + return ScoreTracker.getForPlayer(player_index, player_deaths_name) end function Public.get_crafted_item(player_index) - return player_crafted_items[player_index] + return ScoreTracker.getForPlayer(player_index, player_items_crafted_name) end function Public.get_console_chat(player_index) - return player_console_chats[player_index] + return ScoreTracker.getForPlayer(player_index, player_console_chats_name) end -- Returns a dictionary of cause_name -> count -function Public.get_all_death_counts_by_cause(player_index) - return player_deaths[player_index].causes or {} +function Public.get_all_death_causes_by_player(player_index) + return player_death_causes[player_index] or {} end function Public.get_total_player_count() - return total_players[1] + return ScoreTracker.getForGlobal(player_count_name) end function Public.get_total_train_kills() - return total_train_kills[1] + return ScoreTracker.getForGlobal(kills_by_trains_name) end function Public.get_total_player_trees_mined() - return total_player_trees_mined[1] + return ScoreTracker.getForGlobal(trees_cut_name) end function Public.get_total_player_rocks_mined() - return total_player_rocks_mined[1] + return ScoreTracker.getForGlobal(rocks_smashed_name) end function Public.get_total_robot_built_entities() - return total_robot_built_entities[1] + return ScoreTracker.getForGlobal(built_by_robots_name) end function Public.get_total_player_built_entities() - return total_player_built_entities[1] + return ScoreTracker.getForGlobal(built_by_players_name) end function Public.get_total_biter_kills() - return total_biter_kills[1] + return ScoreTracker.getForGlobal(aliens_killed_name) end function Public.get_total_coins_spent() - return total_coins_spent[1] + return ScoreTracker.getForGlobal(coins_spent_name) end return Public diff --git a/utils/score_tracker.lua b/utils/score_tracker.lua new file mode 100644 index 00000000..62fd7024 --- /dev/null +++ b/utils/score_tracker.lua @@ -0,0 +1,152 @@ +local Global = require 'utils.global' +local Event = require 'utils.event' +local error = error +local format = string.format +local raise_event = script.raise_event + +local score_metadata = {} +local memory = { + players = {}, + global = {} +} + +Global.register(memory, function (tbl) memory = tbl end) + +local Public = {} + +Public.events = { + -- Event { + -- score_name = score_name + -- old_value = old_value + -- new_value = new_value + -- player_index = player_index + -- } + on_player_score_changed = Event.generate_event_name('on_player_score_changed'), + -- Event { + -- score_name = score_name + -- old_value = old_value + -- new_value = new_value + -- } + on_global_score_changed = Event.generate_event_name('on_global_score_changed'), +} + +---Register a specific score. +--- +--- This function must be called in the control stage, i.e. not inside an event. +--- +---@param name string +---@param localisation_string table +---@param icon string +function Public.register(name, localisation_string, icon) + if _LIFECYCLE ~= _STAGE.control then + error(format('You can only register score types in the control stage, i.e. not inside events. Tried setting "%s".', name), 2) + end + + if score_metadata[name] then + error(format('Trying to register score type for "%s" while it has already been registered.', name), 2) + end + + local score = { + name = name, + icon = icon, + localised_string = localisation_string + } + + score_metadata[name] = score + + return score +end + +---Sets a setting to a specific value for a player. +--- +---@param player_index number +---@param score_name string +---@param value number|nil number to subtract or add +function Public.changeForPlayer(player_index, score_name, value) + local setting = score_metadata[score_name] + if not setting then + if _DEBUG then + error(format('Trying to change score "%s" while it has was never registered.', score_name), 2) + end + return + end + + local player_score = memory.players[player_index] + if not player_score then + player_score = {} + memory.players[player_index] = player_score + end + + local old_value = player_score[score_name] or 0 + local new_value = old_value + value + player_score[score_name] = new_value + + raise_event(Public.events.on_player_score_changed, { + score_name = score_name, + old_value = old_value, + new_value = new_value, + player_index = player_index + }) +end + +---Sets a setting to a specific value for a player. +--- +---@param score_name string +---@param value number|nil number to subtract or add +function Public.changeForGlobal(score_name, value) + local setting = score_metadata[score_name] + if not setting then + if _DEBUG then + error(format('Trying to change score "%s" while it has was never registered.', score_name), 2) + end + return + end + + local old_value = memory.global[score_name] or 0 + local new_value = old_value + value + memory.global[score_name] = new_value + + raise_event(Public.events.on_global_score_changed, { + score_name = score_name, + old_value = old_value, + new_value = new_value + }) +end + +---Returns the value for this player of a specific score. +--- +---@param player_index number +---@param score_name string +function Public.getForPlayer(player_index, score_name) + local setting = score_metadata[score_name] + if not setting then + return nil + end + + local player_scores = memory.players[player_index] + if not player_scores then + return 0 + end + + return player_scores[score_name] or 0 +end + +---Returns the value of the game score. +--- +---@param score_name string +function Public.getForGlobal(score_name) + local setting = score_metadata[score_name] + if not setting then + return 0 + end + + return memory.global[score_name] or 0 +end + +---Returns the full score metadata, note that this is a reference, do not modify +---this data unless you know what you're doing! +function Public.get_score_metadata() + return score_metadata +end + +return Public