mirror of
https://github.com/Xevion/project-cybersyn.git
synced 2025-12-10 06:08:11 -06:00
save for 1.2.10
This commit is contained in:
1
TODO
1
TODO
@@ -19,6 +19,7 @@ major:
|
||||
dramatically improve the mods handling of invalid entities
|
||||
|
||||
minor:
|
||||
break the central planner into a separate library
|
||||
check if necessary entities can be destroyed without raising events
|
||||
improve the behavior of trains if players intervene during deliveries
|
||||
handle if signals are removed from the game during migration
|
||||
|
||||
@@ -15,6 +15,14 @@ function get_stack_size(map_data, item_name)
|
||||
return game.item_prototypes[item_name].stack_size
|
||||
end
|
||||
|
||||
---@param item_order table<string, int>
|
||||
---@param item1_name string
|
||||
---@param item2_name string
|
||||
function item_lt(item_order, item1_name, item2_name)
|
||||
return item_order[item1_name] < item_order[item2_name]
|
||||
end
|
||||
|
||||
|
||||
---NOTE: does not check .valid
|
||||
---@param entity0 LuaEntity
|
||||
---@param entity1 LuaEntity
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
---@field public economy Economy
|
||||
---@field public each_refuelers {[uint]: true}
|
||||
---@field public active_alerts {[uint]: {[1]: LuaTrain, [2]: int}}?
|
||||
---@field public manager Manager
|
||||
|
||||
---@class Station
|
||||
---@field public entity_stop LuaEntity
|
||||
|
||||
@@ -3,6 +3,8 @@ local mod_gui = require("__core__.lualib.mod-gui")
|
||||
|
||||
local manager = require("scripts.gui.manager")
|
||||
|
||||
--- @class Manager
|
||||
--- @field item_order table<string, int>
|
||||
|
||||
--- @class PlayerData
|
||||
--- @field refs {[string]: LuaGuiElement}?
|
||||
@@ -88,6 +90,45 @@ function manager_gui.on_runtime_mod_setting_changed(e)
|
||||
end
|
||||
|
||||
|
||||
--- @param manager Manager
|
||||
local function init_items(manager)
|
||||
local item_order = {}
|
||||
manager.item_order = item_order
|
||||
local i = 1
|
||||
|
||||
for _, protos in pairs{game.item_prototypes, game.fluid_prototypes} do
|
||||
--- @type (LuaItemPrototype|LuaFluidPrototype)[]
|
||||
local all_items = {}
|
||||
for _, proto in pairs(protos) do
|
||||
all_items[#all_items + 1] = proto
|
||||
end
|
||||
table.sort(all_items, function(a, b)
|
||||
if a.group.order == b.group.order then
|
||||
if a.subgroup.order == b.subgroup.order then
|
||||
return a.order < b.order
|
||||
else
|
||||
return a.subgroup.order < b.subgroup.order
|
||||
end
|
||||
else
|
||||
return a.group.order < b.group.order
|
||||
end
|
||||
end)
|
||||
for _, v in ipairs(all_items) do
|
||||
item_order[v.name] = i
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function manager.on_migration()
|
||||
init_items(global.manager)
|
||||
end
|
||||
|
||||
function manager.on_init()
|
||||
global.manager = {}
|
||||
init_items(global.manager)
|
||||
end
|
||||
--gui.handle_events()
|
||||
|
||||
return manager_gui
|
||||
|
||||
@@ -30,36 +30,179 @@ function stations_tab.build(widths)
|
||||
templates.sort_checkbox(widths, "stations", "status", false, { "gui.ltnm-status-description" }),
|
||||
templates.sort_checkbox(widths, "stations", "network_id", false),
|
||||
templates.sort_checkbox(
|
||||
widths,
|
||||
"stations",
|
||||
"provided_requested",
|
||||
false,
|
||||
{ "gui.ltnm-provided-requested-description" }
|
||||
),
|
||||
templates.sort_checkbox(widths, "stations", "shipments", false, { "gui.ltnm-shipments-description" }),
|
||||
templates.sort_checkbox(widths, "stations", "control_signals", false),
|
||||
},
|
||||
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "stations", "scroll_pane" } },
|
||||
widths,
|
||||
"stations",
|
||||
"provided_requested",
|
||||
false,
|
||||
{ "gui.ltnm-provided-requested-description" }
|
||||
),
|
||||
templates.sort_checkbox(widths, "stations", "shipments", false, { "gui.ltnm-shipments-description" }),
|
||||
templates.sort_checkbox(widths, "stations", "control_signals", false),
|
||||
},
|
||||
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "stations", "scroll_pane" } },
|
||||
{
|
||||
type = "flow",
|
||||
style = "ltnm_warning_flow",
|
||||
visible = false,
|
||||
ref = { "stations", "warning_flow" },
|
||||
{
|
||||
type = "flow",
|
||||
style = "ltnm_warning_flow",
|
||||
visible = false,
|
||||
ref = { "stations", "warning_flow" },
|
||||
{
|
||||
type = "label",
|
||||
style = "ltnm_semibold_label",
|
||||
caption = { "gui.ltnm-no-stations" },
|
||||
ref = { "stations", "warning_label" },
|
||||
},
|
||||
type = "label",
|
||||
style = "ltnm_semibold_label",
|
||||
caption = { "gui.ltnm-no-stations" },
|
||||
ref = { "stations", "warning_label" },
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
--- @param map_data MapData
|
||||
--- @param player_data PlayerData
|
||||
--- @return GuiElemDef
|
||||
function stations_tab.build(map_data, player_data)
|
||||
|
||||
local widths = constants.gui["en"]
|
||||
|
||||
local search_item = player_data.search_item
|
||||
local search_network_name = player_data.search_network_name
|
||||
local search_network_mask = player_data.search_network_mask
|
||||
local search_surface_idx = player_data.search_surface_idx
|
||||
|
||||
|
||||
local stations_sorted = {}
|
||||
local to_sorted_manifest = {}
|
||||
for id, station in pairs(map_data.stations) do
|
||||
if search_network_name then
|
||||
if search_network_name ~= station.network_name then
|
||||
goto continue
|
||||
end
|
||||
local train_flag = get_network_flag(station, search_network_name)
|
||||
if not bit32.btest(search_network_mask, train_flag) then
|
||||
goto continue
|
||||
end
|
||||
elseif search_network_mask ~= -1 then
|
||||
if station.network_name == NETWORK_EACH then
|
||||
local masks = station.network_flag--[[@as {}]]
|
||||
for _, network_flag in pairs(masks) do
|
||||
if bit32.btest(search_network_mask, network_flag) then
|
||||
goto has_match
|
||||
end
|
||||
end
|
||||
goto continue
|
||||
::has_match::
|
||||
elseif not bit32.btest(search_network_mask, station.network_flag) then
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
|
||||
if search_surface_idx then
|
||||
local entity = station.entity_stop
|
||||
if not entity.valid then
|
||||
goto continue
|
||||
end
|
||||
if entity.surface.index ~= search_surface_idx then
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
|
||||
if search_item then
|
||||
if not station.deliveries then
|
||||
goto continue
|
||||
end
|
||||
for item_name, _ in pairs(station.deliveries) do
|
||||
if item_name == search_item then
|
||||
goto has_match
|
||||
end
|
||||
end
|
||||
goto continue
|
||||
::has_match::
|
||||
end
|
||||
|
||||
stations_sorted[#stations_sorted + 1] = id
|
||||
--insertion sort
|
||||
local manifest = {}
|
||||
local manifest_type = {}
|
||||
for name, _ in pairs(station.deliveries) do
|
||||
local is_fluid = get_is_fluid(name)
|
||||
local i = 1
|
||||
while i <= #manifest do
|
||||
if (not is_fluid and manifest_type[i]) or (is_fluid == manifest_type[i] and name < manifest[i]) then
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
table.insert(manifest, i, name)
|
||||
table.insert(manifest_type, i, is_fluid)
|
||||
end
|
||||
to_sorted_manifest[id] = manifest
|
||||
::continue::
|
||||
end
|
||||
|
||||
|
||||
table.sort(stations_sorted, function(a, b)
|
||||
local station1 = map_data.stations[a]
|
||||
local station2 = map_data.stations[b]
|
||||
for i, v in ipairs(player_data.trains_orderings) do
|
||||
local invert = player_data.trains_orderings_invert[i]
|
||||
if v == ORDER_LAYOUT then
|
||||
if not station1.allows_all_trains and not station2.allows_all_trains then
|
||||
local layout1 = station1.layout_pattern--[[@as uint[] ]]
|
||||
local layout2 = station2.layout_pattern--[[@as uint[] ]]
|
||||
for j, c1 in ipairs(layout1) do
|
||||
local c2 = layout2[j]
|
||||
if c1 ~= c2 then
|
||||
return invert ~= (c2 and c1 < c2)
|
||||
end
|
||||
end
|
||||
if layout2[#layout1 + 1] then
|
||||
return invert ~= true
|
||||
end
|
||||
elseif station1.allows_all_trains ~= station2.allows_all_trains then
|
||||
return invert ~= station2.allows_all_trains
|
||||
end
|
||||
elseif v == ORDER_NAME then
|
||||
local name1 = station1.entity_stop.valid and station1.entity_stop.backer_name
|
||||
local name2 = station2.entity_stop.valid and station2.entity_stop.backer_name
|
||||
if name1 ~= name2 then
|
||||
return invert ~= (name1 and (name2 and name1 < name2 or true) or false)
|
||||
end
|
||||
elseif v == ORDER_TOTAL_TRAINS then
|
||||
if station1.deliveries_total ~= station2.deliveries_total then
|
||||
return invert ~= (station1.deliveries_total < station2.deliveries_total)
|
||||
end
|
||||
elseif v == ORDER_MANIFEST then
|
||||
if not next(station1.deliveries) then
|
||||
if next(station2.deliveries) then
|
||||
return invert ~= true
|
||||
end
|
||||
elseif not next(station2.deliveries) then
|
||||
return invert ~= false
|
||||
else
|
||||
local first_item = nil
|
||||
local first_direction = nil
|
||||
for item_name in dual_pairs(station1.deliveries, station2.deliveries) do
|
||||
if not first_item or item_lt(map_data.manager, item_name, first_item) then
|
||||
local count1 = station1.deliveries[item_name] or 0
|
||||
local count2 = station2.deliveries[item_name] or 0
|
||||
if count1 ~= count2 then
|
||||
first_item = item_name
|
||||
first_direction = count1 < count2
|
||||
end
|
||||
end
|
||||
end
|
||||
if first_direction ~= nil then
|
||||
return invert ~= first_direction
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return (not player_data.trains_orderings_invert[#player_data.trains_orderings_invert]) == (a < b)
|
||||
end)
|
||||
|
||||
|
||||
end
|
||||
|
||||
function stations_tab.update(self)
|
||||
local dictionaries = self.player_table.dictionaries
|
||||
|
||||
local state = self.state
|
||||
local refs = self.refs.stations
|
||||
local widths = self.widths.stations
|
||||
|
||||
@@ -95,80 +238,80 @@ function stations_tab.update(self)
|
||||
|
||||
if station_data.entity.valid then
|
||||
if
|
||||
(search_surface == -1 or station_data.entity.surface.index == search_surface)
|
||||
and bit32.btest(station_data.network_id, search_network_id)
|
||||
and (
|
||||
#search_query == 0 or string.find(station_data.search_strings[self.player.index], string.lower(search_query))
|
||||
)
|
||||
then
|
||||
table_index = table_index + 1
|
||||
local row = children[table_index]
|
||||
local color = table_index % 2 == 0 and "dark" or "light"
|
||||
if not row then
|
||||
row = gui.add(scroll_pane, {
|
||||
type = "frame",
|
||||
style = "ltnm_table_row_frame_" .. color,
|
||||
{
|
||||
type = "label",
|
||||
style = "ltnm_clickable_semibold_label",
|
||||
style_mods = { width = widths.name },
|
||||
tooltip = constants.open_station_gui_tooltip,
|
||||
},
|
||||
templates.status_indicator(widths.status, true),
|
||||
{ type = "label", style_mods = { width = widths.network_id, horizontal_align = "center" } },
|
||||
templates.small_slot_table(widths, color, "provided_requested"),
|
||||
templates.small_slot_table(widths, color, "shipments"),
|
||||
templates.small_slot_table(widths, color, "control_signals"),
|
||||
})
|
||||
end
|
||||
|
||||
gui.update(row, {
|
||||
(search_surface == -1 or station_data.entity.surface.index == search_surface)
|
||||
and bit32.btest(station_data.network_id, search_network_id)
|
||||
and (
|
||||
#search_query == 0 or string.find(station_data.search_strings[self.player.index], string.lower(search_query))
|
||||
)
|
||||
then
|
||||
table_index = table_index + 1
|
||||
local row = children[table_index]
|
||||
local color = table_index % 2 == 0 and "dark" or "light"
|
||||
if not row then
|
||||
row = gui.add(scroll_pane, {
|
||||
type = "frame",
|
||||
style = "ltnm_table_row_frame_" .. color,
|
||||
{
|
||||
elem_mods = { caption = station_data.name },
|
||||
actions = {
|
||||
on_click = { gui = "main", action = "open_station_gui", station_id = station_id },
|
||||
},
|
||||
},
|
||||
{
|
||||
{ elem_mods = { sprite = "flib_indicator_" .. station_data.status.color } },
|
||||
{ elem_mods = { caption = station_data.status.count } },
|
||||
},
|
||||
{ elem_mods = { caption = station_data.network_id } },
|
||||
})
|
||||
|
||||
util.slot_table_update(row.provided_requested_frame.provided_requested_table, {
|
||||
{ color = "green", entries = station_data.provided, translations = dictionaries.materials },
|
||||
{ color = "red", entries = station_data.requested, translations = dictionaries.materials },
|
||||
})
|
||||
util.slot_table_update(row.shipments_frame.shipments_table, {
|
||||
{ color = "green", entries = station_data.inbound, translations = dictionaries.materials },
|
||||
{ color = "blue", entries = station_data.outbound, translations = dictionaries.materials },
|
||||
})
|
||||
util.slot_table_update(row.control_signals_frame.control_signals_table, {
|
||||
{
|
||||
color = "default",
|
||||
entries = station_data.control_signals,
|
||||
translations = dictionaries.virtual_signals,
|
||||
type = "virtual-signal",
|
||||
type = "label",
|
||||
style = "ltnm_clickable_semibold_label",
|
||||
style_mods = { width = widths.name },
|
||||
tooltip = constants.open_station_gui_tooltip,
|
||||
},
|
||||
templates.status_indicator(widths.status, true),
|
||||
{ type = "label", style_mods = { width = widths.network_id, horizontal_align = "center" } },
|
||||
templates.small_slot_table(widths, color, "provided_requested"),
|
||||
templates.small_slot_table(widths, color, "shipments"),
|
||||
templates.small_slot_table(widths, color, "control_signals"),
|
||||
})
|
||||
end
|
||||
|
||||
gui.update(row, {
|
||||
{
|
||||
elem_mods = { caption = station_data.name },
|
||||
actions = {
|
||||
on_click = { gui = "main", action = "open_station_gui", station_id = station_id },
|
||||
},
|
||||
},
|
||||
{
|
||||
{ elem_mods = { sprite = "flib_indicator_" .. station_data.status.color } },
|
||||
{ elem_mods = { caption = station_data.status.count } },
|
||||
},
|
||||
{ elem_mods = { caption = station_data.network_id } },
|
||||
})
|
||||
|
||||
util.slot_table_update(row.provided_requested_frame.provided_requested_table, {
|
||||
{ color = "green", entries = station_data.provided, translations = dictionaries.materials },
|
||||
{ color = "red", entries = station_data.requested, translations = dictionaries.materials },
|
||||
})
|
||||
util.slot_table_update(row.shipments_frame.shipments_table, {
|
||||
{ color = "green", entries = station_data.inbound, translations = dictionaries.materials },
|
||||
{ color = "blue", entries = station_data.outbound, translations = dictionaries.materials },
|
||||
})
|
||||
util.slot_table_update(row.control_signals_frame.control_signals_table, {
|
||||
{
|
||||
color = "default",
|
||||
entries = station_data.control_signals,
|
||||
translations = dictionaries.virtual_signals,
|
||||
type = "virtual-signal",
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for child_index = table_index + 1, #children do
|
||||
children[child_index].destroy()
|
||||
end
|
||||
|
||||
if table_index == 0 then
|
||||
refs.warning_flow.visible = true
|
||||
scroll_pane.visible = false
|
||||
refs.content_frame.style = "ltnm_main_warning_frame"
|
||||
else
|
||||
refs.warning_flow.visible = false
|
||||
scroll_pane.visible = true
|
||||
refs.content_frame.style = "ltnm_main_content_frame"
|
||||
end
|
||||
end
|
||||
|
||||
for child_index = table_index + 1, #children do
|
||||
children[child_index].destroy()
|
||||
end
|
||||
|
||||
if table_index == 0 then
|
||||
refs.warning_flow.visible = true
|
||||
scroll_pane.visible = false
|
||||
refs.content_frame.style = "ltnm_main_warning_frame"
|
||||
else
|
||||
refs.warning_flow.visible = false
|
||||
scroll_pane.visible = true
|
||||
refs.content_frame.style = "ltnm_main_content_frame"
|
||||
end
|
||||
end
|
||||
|
||||
return stations_tab
|
||||
|
||||
@@ -50,45 +50,24 @@ function irpairs(a)
|
||||
return irnext, a, 0
|
||||
end
|
||||
|
||||
---@generic V
|
||||
---@param arr Array<V>
|
||||
---@param comp fun(a: V, b: V) A comparison function for sorting. Must return truthy if `a < b`.
|
||||
function stable_sort(arr, comp)
|
||||
local size = #arr
|
||||
for i = 2, size do
|
||||
local a = arr[i]
|
||||
local j = i
|
||||
while j > 1 do
|
||||
local b = arr[j - 1]
|
||||
if comp(a, b) then
|
||||
arr[j] = b
|
||||
j = j - 1
|
||||
else
|
||||
break
|
||||
--- @generic K
|
||||
--- @param t1 table<K, any>
|
||||
--- @param t2 table<K, any>
|
||||
--- @return fun(): K?
|
||||
function dual_pairs(t1, t2)
|
||||
local state = true
|
||||
local key = nil
|
||||
return function()
|
||||
if state then
|
||||
key = next(t1, key)
|
||||
if key then
|
||||
return key
|
||||
end
|
||||
state = false
|
||||
end
|
||||
arr[j] = a
|
||||
end
|
||||
end
|
||||
|
||||
---@param values number[]
|
||||
---@param keys any[]
|
||||
function dual_sort(values, keys)
|
||||
local size = #values
|
||||
for i = 2, size do
|
||||
local a = values[i]
|
||||
local j = i
|
||||
while j > 1 do
|
||||
local b = values[j - 1]
|
||||
if a < b then
|
||||
values[j] = b
|
||||
keys[j] = keys[j - 1]
|
||||
j = j - 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
values[j] = a
|
||||
keys[j] = keys[i]
|
||||
repeat
|
||||
key = next(t2, key)
|
||||
until t1[key] == nil
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user