Files
factorio-scenario-ExpCluster/exp_legacy/module/modules/commands/search.lua
2024-11-19 22:16:34 +00:00

168 lines
6.4 KiB
Lua

--[[-- Commands Module - Inventory Search
- Adds commands that will search all players inventories for an item
@commands InventorySearch
]]
local Commands = require("modules.exp_legacy.expcore.commands") --- @dep expcore.commands
local format_number = require("util").format_number --- @dep util
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
local format_time = _C.format_time
require("modules.exp_legacy.config.expcore.command_general_parse")
--- Input parse for items by name
local function item_parse(input, _, reject)
if input == nil then return end
local lower_input = input:lower():gsub(' ', '-')
-- Simple Case - internal name is given
local item = prototypes.item[lower_input]
if item then return item end
-- Second Case - rich text is given
local item_name = input:match('%[item=([0-9a-z-]+)%]')
item = prototypes.item[item_name]
if item then return item end
-- No item found, we do not attempt to search all prototypes as this will be expensive
return reject{'expcom-inv-search.reject-item', lower_input}
end
--- Search all players for this item
local function search_players(players, item)
local head = 1
local found = {}
-- Check the item count of all players
for _, player in pairs(players) do
local item_count = player.get_item_count(item.name)
if item_count > 0 then
-- Add the player to the array as they have the item
found[head] = { player=player, count=item_count, online_time=player.online_time }
head = head + 1
end
end
return found
end
--- Custom sort function which only retains 5 greatest values
local function sort_players(players, func)
local sorted = {}
local values = {}
local threshold = nil
-- Loop over all provided players
for index, player in ipairs(players) do
local value = func(player)
-- Check if the item will make the top 5 elements
if index <= 5 or value > threshold then
local inserted = false
values[player] = value
-- Find where in the top 5 to insert the element
for next_index, next_player in ipairs(sorted) do
if value > values[next_player] then
table.insert(sorted, next_index, player)
inserted = true
break
end
end
-- Insert the element, this can only be called when index <= 5
if not inserted then
sorted[#sorted+1] = player
end
-- Update the threshold
if sorted[6] then
threshold = values[sorted[5]]
values[sorted[6]] = nil
sorted[6] = nil
else
threshold = values[sorted[#sorted]]
end
end
end
return sorted
end
--- Display to the player the top players which were found
local function display_players(player, players, item)
player.print{'expcom-inv-search.results-heading', item.name}
for index, data in ipairs(players) do
local player_name_color = format_chat_player_name(data.player)
local amount = format_number(data.count)
local time = format_time(data.online_time)
player.print{'expcom-inv-search.results-item', index, player_name_color, amount, time}
end
end
--- Return the amount of an item a player has
local function amount_sort(data)
return data.count
end
--- Get a list of players sorted by the quantity of an item in their inventory
-- @command search-amount
-- @tparam LuaItemPrototype item The item to search for in players inventories
Commands.new_command('search-amount', {'expcom-inv-search.description-ia'}, 'Display players sorted by the quantity of an item held')
:add_alias('ia')
:add_param('item', false, item_parse)
:enable_auto_concat()
:register(function(player, item)
local players = search_players(game.players, item)
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
local top_players = sort_players(players, amount_sort)
display_players(player, top_players, item)
end)
--- Return the index of the player, higher means they joined more recently
local function recent_sort(data)
return data.player.index
end
--- Get a list of players who have the given item, sorted by how recently they joined
-- @command search-recent
-- @tparam LuaItemPrototype item The item to search for in players inventories
Commands.new_command('search-recent', {'expcom-inv-search.description-ir'}, 'Display players who hold an item sorted by join time')
:add_alias('ir')
:add_param('item', false, item_parse)
:enable_auto_concat()
:register(function(player, item)
local players = search_players(game.players, item)
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
local top_players = sort_players(players, recent_sort)
display_players(player, top_players, item)
end)
--- Return the the amount of an item a player has divided by their playtime
local function combined_sort(data)
return data.count/data.online_time
end
--- Get a list of players sorted by quantity held and play time
-- @command search
-- @tparam LuaItemPrototype item The item to search for in players inventories
Commands.new_command('search', {'expcom-inv-search.description-i'}, 'Display players sorted by the quantity of an item held and playtime')
:add_alias('i')
:add_param('item', false, item_parse)
:enable_auto_concat()
:register(function(player, item)
local players = search_players(game.players, item)
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
local top_players = sort_players(players, combined_sort)
display_players(player, top_players, item)
end)
--- Get a list of online players sorted by quantity held and play time
-- @command search-online
-- @tparam LuaItemPrototype item The item to search for in players inventories
Commands.new_command('search-online', {'expcom-inv-search.description-io'}, 'Display online players sorted by the quantity of an item held and playtime')
:add_alias('io')
:add_param('item', false, item_parse)
:enable_auto_concat()
:register(function(player, item)
local players = search_players(game.connected_players, item)
if #players == 0 then return {'expcom-inv-search.results-none', item.name} end
local top_players = sort_players(players, combined_sort)
display_players(player, top_players, item)
end)