Skip to content

Commit 5c25619

Browse files
authored
Merge pull request #24 from Atria1234/sort-by-category-count
Added mod setting to switch between sorting results by distance from player and item/fluid count
2 parents 88e3c55 + 90b1d12 commit 5c25619

6 files changed

Lines changed: 121 additions & 86 deletions

File tree

control.lua

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ require "scripts.remote"
9595
---@field resource_count? number
9696
---@field distance? number
9797

98-
---@alias EntitySurfaceData EntityGroup[]
99-
100-
---@alias CategorisedSurfaceData table<EntityName, EntitySurfaceData>
98+
---@alias CategorisedSurfaceData EntityGroup[]
10199

102100
---@alias SurfaceDataCategoryName "consumers"|"producers"|"storage"|"logistics"|"modules"|"requesters"|"ground_items"|"entities"|"signals"|"map_tags"
103101
---@alias SurfaceData table<SurfaceDataCategoryName, CategorisedSurfaceData>

locale/en/FactorySearch.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fs-chunks-per-tick=Chunks per tick
1414
fs-initial-zoom=Zoom level
1515
fs-clear-highlights-with-gui=Clear highlights when closing interface
1616
fs-highlight-duration=Highlight duration
17+
fs-sort-results-by=Sort results by
1718

1819
[mod-setting-description]
1920
fs-non-blocking-search=Searching takes much longer but doesn't freeze the game.
@@ -26,6 +27,9 @@ fs-highlight-duration=Number of seconds that highlight boxes and arrows exist fo
2627
fs-non-blocking-search-on=On
2728
fs-non-blocking-search-multiplayer=Multiplayer only
2829
fs-non-blocking-search-off=Off
30+
fs-sort-results-by-name=Entity name
31+
fs-sort-results-by-distance=Distance from player
32+
fs-sort-results-by-count=Item/fluid count
2933
3034
[search-gui]
3135
keep-open=Keep open

scripts/search-gui.lua

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -19,78 +19,77 @@ end
1919
---@return GuiElemDef[]
2020
function SearchGui.build_surface_results(surface_name, surface_data)
2121
local gui_elements = {}
22-
for entity_name, entity_surface_data in pairs(surface_data) do
23-
for _, group in pairs(entity_surface_data) do
24-
local distance_info = {""}
25-
if group.distance then
26-
distance_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.distance-tooltip"}, ":[/color][/font] ", util.format_number(math.ceil(group.distance), true), "m"}
22+
for _, group in pairs(surface_data) do
23+
local entity_name = group.entity_name
24+
local distance_info = {""}
25+
if group.distance then
26+
distance_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.distance-tooltip"}, ":[/color][/font] ", util.format_number(math.ceil(group.distance), true), "m"}
27+
end
28+
local extra_info = {""}
29+
if group.recipe_list then
30+
extra_info = {""}
31+
local multiple_recipes = false
32+
local number_of_recipes = 0
33+
for _ in pairs(group.recipe_list) do number_of_recipes = number_of_recipes + 1 end
34+
35+
if number_of_recipes > 1 then
36+
multiple_recipes = true
2737
end
28-
local extra_info = {""}
29-
if group.recipe_list then
30-
extra_info = {""}
31-
local multiple_recipes = false
32-
local number_of_recipes = 0
33-
for _ in pairs(group.recipe_list) do number_of_recipes = number_of_recipes + 1 end
34-
35-
if number_of_recipes > 1 then
36-
multiple_recipes = true
37-
end
38-
if number_of_recipes <= 10 then
39-
-- Localised strings must not have more than 20 parameters
40-
for name, recipe_info in pairs(group.recipe_list) do
41-
local string = "\n"
42-
if multiple_recipes then
43-
string = string .. "[font=default-bold]" .. recipe_info.count .. " × [/font]"
44-
end
45-
string = string .. "[recipe=" .. name .. "] "
46-
table.insert(extra_info, string)
47-
table.insert(extra_info, recipe_info.localised_name)
38+
if number_of_recipes <= 10 then
39+
-- Localised strings must not have more than 20 parameters
40+
for name, recipe_info in pairs(group.recipe_list) do
41+
local string = "\n"
42+
if multiple_recipes then
43+
string = string .. "[font=default-bold]" .. recipe_info.count .. " × [/font]"
4844
end
45+
string = string .. "[recipe=" .. name .. "] "
46+
table.insert(extra_info, string)
47+
table.insert(extra_info, recipe_info.localised_name)
4948
end
5049
end
51-
if group.item_count then
52-
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"gui-train.add-item-count-condition"}, ":[/color][/font] ", util.format_number(math.floor(group.item_count), true)}
53-
end
54-
if group.fluid_count then
55-
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"gui-train.add-fluid-count-condition"}, ":[/color][/font] ", util.format_number(math.floor(group.fluid_count), true)}
56-
end
57-
if group.module_count then
58-
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.module-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.module_count), true)}
59-
end
60-
if group.request_count then
61-
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.request-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.request_count), true)}
62-
end
63-
if group.signal_count then
64-
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.signal-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.signal_count), true)}
65-
end
66-
local sprite = "entity/" .. entity_name
50+
end
51+
if group.item_count then
52+
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"gui-train.add-item-count-condition"}, ":[/color][/font] ", util.format_number(math.floor(group.item_count), true)}
53+
end
54+
if group.fluid_count then
55+
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"gui-train.add-fluid-count-condition"}, ":[/color][/font] ", util.format_number(math.floor(group.fluid_count), true)}
56+
end
57+
if group.module_count then
58+
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.module-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.module_count), true)}
59+
end
60+
if group.request_count then
61+
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.request-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.request_count), true)}
62+
end
63+
if group.signal_count then
64+
extra_info = {"", "\n[font=default-semibold][color=255, 230, 192]", {"search-gui.signal-count-tooltip"}, ":[/color][/font] ", util.format_number(math.floor(group.signal_count), true)}
65+
end
66+
local sprite = "entity/" .. entity_name
67+
if not helpers.is_valid_sprite_path(sprite) then
68+
sprite = "item/" .. entity_name
6769
if not helpers.is_valid_sprite_path(sprite) then
68-
sprite = "item/" .. entity_name
70+
sprite = "fluid/" .. entity_name
6971
if not helpers.is_valid_sprite_path(sprite) then
70-
sprite = "fluid/" .. entity_name
72+
sprite = "recipe/" .. entity_name
7173
if not helpers.is_valid_sprite_path(sprite) then
72-
sprite = "recipe/" .. entity_name
74+
sprite = "virtual-signal/" .. entity_name
7375
if not helpers.is_valid_sprite_path(sprite) then
74-
sprite = "virtual-signal/" .. entity_name
75-
if not helpers.is_valid_sprite_path(sprite) then
76-
sprite = "utility/questionmark"
77-
end
76+
sprite = "utility/questionmark"
7877
end
7978
end
8079
end
8180
end
82-
table.insert(gui_elements,
83-
{
84-
type = "sprite-button",
85-
sprite = sprite,
86-
tooltip = {"", "[font=default-bold]", group.localised_name, "[/font]", distance_info, extra_info, "\n", {"search-gui.result-tooltip"}},
87-
style = "slot_button",
88-
number = group.resource_count or group.count,
89-
tags = {position = group.avg_position, surface = surface_name, selection_boxes = group.selection_boxes},
90-
handler = {[defines.events.on_gui_click] = SearchGui.open_location_on_map}
91-
}
92-
)
9381
end
82+
table.insert(gui_elements,
83+
{
84+
type = "sprite-button",
85+
sprite = sprite,
86+
tooltip = {"", "[font=default-bold]", group.localised_name, "[/font]", distance_info, extra_info, "\n", {"search-gui.result-tooltip"}},
87+
style = "slot_button",
88+
number = group.resource_count or group.count,
89+
tags = {position = group.avg_position, surface = surface_name, selection_boxes = group.selection_boxes},
90+
handler = {[defines.events.on_gui_click] = SearchGui.open_location_on_map}
91+
}
92+
)
9493
end
9594
return gui_elements
9695
end
@@ -223,14 +222,13 @@ function SearchGui.build_results(data, statistics, frame, check_result_found, in
223222
local surface_contains_results = false
224223
for _, category_data in pairs(surface_data) do
225224
-- TODO surface_statistics check here?
226-
surface_contains_results = surface_contains_results or not not next(category_data)
225+
surface_contains_results = surface_contains_results or table_size(category_data) > 0
227226
end
228227
result_found = result_found or surface_contains_results
229228
if not surface_contains_results then
230229
goto continue
231230
end
232231

233-
-- TODO sort somewhere before showing storage, modules, requesters, ...
234232
gui.add(frame, {
235233
SearchGui.build_surface_name(include_surface_name, surface_name),
236234
SearchGui.build_surface_count(statistics[surface_name], include_surface_name),

scripts/search-results.lua

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ function SearchResults.add_entity(entity, surface_data)
1111
local entity_name = entity.name
1212
local entity_position = entity.position
1313
local entity_selection_box = entity.selection_box
14-
local entity_surface_data = surface_data[entity_name] or {}
1514
---@type EntityGroup?
1615
local assigned_group
17-
for _, group in pairs(entity_surface_data) do
16+
for _, group in pairs(surface_data) do
1817
if entity_name == group.entity_name and math2d.bounding_box.collides_with(entity_selection_box, group.selection_box) then
1918
-- Add entity to group
2019
assigned_group = group
@@ -58,9 +57,8 @@ function SearchResults.add_entity(entity, surface_data)
5857
selection_boxes = {[1] = entity.selection_box},
5958
localised_name = entity.localised_name,
6059
}
61-
table.insert(entity_surface_data, assigned_group)
60+
table.insert(surface_data, assigned_group)
6261
end
63-
surface_data[entity_name] = entity_surface_data
6462
return assigned_group
6563
end
6664

@@ -143,10 +141,6 @@ end
143141
---@param tag LuaCustomChartTag
144142
---@param surface_data CategorisedSurfaceData
145143
function SearchResults.add_tag(tag, surface_data)
146-
-- An alternative to add_entity*, for map tags
147-
local icon_name = tag.icon.name ---@cast icon_name -?
148-
local tag_surface_data = surface_data[icon_name] or {}
149-
150144
-- Tag groups always have size 1
151145
local tag_position = tag.position
152146
local tag_box_size = 8
@@ -174,9 +168,7 @@ function SearchResults.add_tag(tag, surface_data)
174168
},
175169
localised_name = localised_name,
176170
}
177-
table.insert(tag_surface_data, group)
178-
179-
surface_data[icon_name] = tag_surface_data
171+
table.insert(surface_data, group)
180172
end
181173

182174
---@param category SurfaceStatisticsCategoryName

scripts/search.lua

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,13 @@ local function add_entity_type(type_list, to_add_list)
114114
end
115115
end
116116

117+
---@param surface_data SurfaceData
118+
---@param player_position MapPosition
117119
local function generate_distance_data(surface_data, player_position)
118120
local distance = math2d.position.distance
119-
for _, entity_groups in pairs(surface_data) do
120-
for _, groups in pairs(entity_groups) do
121-
for _, group in pairs(groups) do
122-
group.distance = distance(group.avg_position, player_position)
123-
end
124-
table.sort(groups, function (k1, k2) return k1.distance < k2.distance end)
121+
for _, groups in pairs(surface_data) do
122+
for _, group in pairs(groups) do
123+
group.distance = distance(group.avg_position, player_position)
125124
end
126125
end
127126
end
@@ -693,6 +692,41 @@ local function get_target_entity_names(target_item)
693692
end
694693
end
695694

695+
--- @type table<SurfaceDataCategoryName, string[]>
696+
local sort_categories_by = {
697+
consumers = {'count'},
698+
producers = {'count'},
699+
storage = {'item_count', 'fluid_count'},
700+
logistics = {'item_count', 'fluid_count'},
701+
modules = {'module_count'},
702+
requesters = {'request_count'},
703+
ground_items = {'count'},
704+
entities = {'resource_count', 'count'},
705+
signals = {'count'}
706+
}
707+
708+
---@param surface_data SurfaceData
709+
---@param player LuaPlayer
710+
local function sort_surface_data(surface_data, player)
711+
for category, groups in pairs(surface_data) do
712+
local sort_by = player.mod_settings["fs-sort-results-by"].value
713+
if sort_by == 'distance' then
714+
table.sort(groups, function (k1, k2) return (k1.distance or math.huge) < (k2.distance or math.huge) end)
715+
elseif sort_by == 'name' then
716+
table.sort(groups, function (k1, k2) return k1.entity_name < k2.entity_name end)
717+
elseif sort_by == 'count' and sort_categories_by[category] then
718+
table.sort(groups, function (k1, k2)
719+
for _, property_name in ipairs(sort_categories_by[category]) do
720+
if k1[property_name] ~= nil and k2[property_name] ~= nil then
721+
return k1[property_name] > k2[property_name]
722+
end
723+
end
724+
return false
725+
end)
726+
end
727+
end
728+
end
729+
696730
---@param force LuaForce
697731
---@param state SearchGuiState
698732
---@param target_item SignalID
@@ -772,6 +806,7 @@ function Search.blocking_search(force, state, target_item, surface_list, type_li
772806
if surface == player.surface then
773807
generate_distance_data(surface_data, player.position)
774808
end
809+
sort_surface_data(surface_data, player)
775810
data[surface.name] = surface_data
776811
statistics[surface.name] = surface_statistics
777812
::continue::
@@ -811,11 +846,6 @@ function on_tick()
811846
next_surface = table.remove(search_data.not_started_surfaces)
812847
if not next_surface then
813848
-- All surfaces are complete
814-
local player = search_data.player
815-
local surface_data = search_data.data[player.surface.name]
816-
if surface_data then
817-
generate_distance_data(surface_data, player.position)
818-
end
819849
search_data.search_complete = true
820850
return
821851
end
@@ -852,6 +882,11 @@ function on_tick()
852882
local chunk = chunk_iterator()
853883
if not chunk then
854884
-- Surface is complete
885+
if current_surface_search_data.surface == search_data.player.surface then
886+
generate_distance_data(current_surface_search_data.surface_data, search_data.player.position)
887+
end
888+
sort_surface_data(current_surface_search_data.surface_data, search_data.player)
889+
855890
search_data.data[current_surface.name] = current_surface_search_data.surface_data
856891
search_data.statistics[current_surface.name] = current_surface_search_data.surface_statistics
857892
search_data.current_surface_search_data = nil

settings.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ data:extend{
4141
maximum_value = 1000,
4242
order = "bb"
4343
},
44+
{
45+
type = "string-setting",
46+
name = "fs-sort-results-by",
47+
setting_type = "runtime-per-user",
48+
default_value = "name",
49+
allowed_values = {"name", "distance", "count"},
50+
order = "bc"
51+
},
4452
}

0 commit comments

Comments
 (0)