From 7b1e92862c43ee3d56c8c606d1cd4844cdb6fe19 Mon Sep 17 00:00:00 2001 From: monica Date: Mon, 9 Jan 2023 11:30:57 -0500 Subject: [PATCH 1/9] fixed version number --- cybersyn/info.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/info.json b/cybersyn/info.json index 4af65bd..57dbd81 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "1.3.0", + "version": "1.2.10", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", From c5763389d635592dac8ab99b0d01e18be8b9f931 Mon Sep 17 00:00:00 2001 From: monica Date: Mon, 9 Jan 2023 11:32:54 -0500 Subject: [PATCH 2/9] fixed changelog typo --- cybersyn/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index a113d57..f4b6ecc 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -5,7 +5,7 @@ Date: 2022-1-9 - Improved performance when fuel threshold is set to 1 Bugfixes: - Fixed a bug where it was possible for a single station to be updated twice per dispatch cycle, which could cause a crash - - Fixed a crash where trains would sometimes think a destoyed depot still exists + - Fixed a crash where trains would sometimes think a destroyed depot still exists - Removed unfinished mod setting with the broken translation key --------------------------------------------------------------------------------------------------- Version: 1.2.9 From ff3ae9c317c1472b30b93963aac0d053e922a03f Mon Sep 17 00:00:00 2001 From: monica Date: Mon, 9 Jan 2023 17:11:20 -0500 Subject: [PATCH 3/9] fixed crash --- TODO | 1 + cybersyn/scripts/central-planning.lua | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index 8eccce9..b3633c0 100644 --- a/TODO +++ b/TODO @@ -35,3 +35,4 @@ compat: railloader picker dollies? cargo ships + editor extensions (test surface separation) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 64a6b01..931e129 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -725,9 +725,8 @@ function tick_init(map_data, mod_settings) map_data.economy.all_r_stations = {} map_data.economy.all_names = {} - local i = 1 - while i <= #map_data.warmup_station_ids do - local id = map_data.warmup_station_ids[i] + while #map_data.warmup_station_ids > 0 do + local id = map_data.warmup_station_ids[1] local station = map_data.stations[id] if station then local cycles = map_data.warmup_station_cycles[id] @@ -736,20 +735,22 @@ function tick_init(map_data, mod_settings) if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps < map_data.total_ticks then station.is_warming_up = nil map_data.active_station_ids[#map_data.active_station_ids + 1] = id - map_data.warmup_station_ids[i] = nil + table_remove(map_data.warmup_station_ids, 1) map_data.warmup_station_cycles[id] = nil if station.entity_comb1.valid then combinator_update(map_data, station.entity_comb1) else on_station_broken(map_data, id, station) end + else + break end else map_data.warmup_station_cycles[id] = cycles + 1 + break end - i = i + 1 else - table_remove(map_data.warmup_station_ids, i) + table_remove(map_data.warmup_station_ids, 1) map_data.warmup_station_cycles[id] = nil end end From 94c015e1216d053b542d7c86c94f98731874225d Mon Sep 17 00:00:00 2001 From: monica Date: Tue, 10 Jan 2023 09:46:10 -0500 Subject: [PATCH 4/9] changed minor issues --- cybersyn/changelog.txt | 2 + cybersyn/data.lua | 15 +- cybersyn/scripts/central-planning.lua | 58 +++--- cybersyn/scripts/factorio-api.lua | 4 +- cybersyn/scripts/gui/main.lua | 93 +++++++++ cybersyn/scripts/gui/manager.lua | 199 ++++++++----------- cybersyn/scripts/gui/trains.lua | 266 +++++++++++++++++--------- cybersyn/scripts/gui/util.lua | 5 +- cybersyn/scripts/layout.lua | 6 +- cybersyn/settings.lua | 9 +- 10 files changed, 413 insertions(+), 244 deletions(-) create mode 100644 cybersyn/scripts/gui/main.lua diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index f4b6ecc..add31db 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -2,10 +2,12 @@ Version: 1.2.10 Date: 2022-1-9 Changes: + - Made the automatic allow list slightly more forgiving to stations where the last wagon would be on a curved rail - Improved performance when fuel threshold is set to 1 Bugfixes: - Fixed a bug where it was possible for a single station to be updated twice per dispatch cycle, which could cause a crash - Fixed a crash where trains would sometimes think a destroyed depot still exists + - Fixed a case where the central planner generated a confusing alert - Removed unfinished mod setting with the broken translation key --------------------------------------------------------------------------------------------------- Version: 1.2.9 diff --git a/cybersyn/data.lua b/cybersyn/data.lua index f46420a..e4877d0 100644 --- a/cybersyn/data.lua +++ b/cybersyn/data.lua @@ -20,5 +20,18 @@ data:extend({ locked_slots_signal, missing_train_icon, lost_train_icon, - nonempty_train_icon + nonempty_train_icon, + + --{ + -- type = "shortcut", + -- name = "ltnm-toggle-gui", + -- icon = data_util.build_sprite(nil, { 0, 0 }, util.paths.shortcut_icons, 32, 2), + -- disabled_icon = data_util.build_sprite(nil, { 48, 0 }, util.paths.shortcut_icons, 32, 2), + -- small_icon = data_util.build_sprite(nil, { 0, 32 }, util.paths.shortcut_icons, 24, 2), + -- disabled_small_icon = data_util.build_sprite(nil, { 36, 32 }, util.paths.shortcut_icons, 24, 2), + -- toggleable = true, + -- action = "lua", + -- associated_control_input = "ltnm-toggle-gui", + -- technology_to_unlock = "logistic-train-network", + --}, }) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 931e129..e68e964 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -363,6 +363,35 @@ local function tick_dispatch(map_data, mod_settings) if netand == 0 then goto p_continue end + + effective_count = p_station.item_p_counts[item_name] + override_threshold = p_station.item_thresholds and p_station.item_thresholds[item_name] + if override_threshold and p_station.is_stack and not is_fluid then + override_threshold = override_threshold*get_stack_size(map_data, item_name) + end + if effective_count < (override_threshold or r_threshold) then + --this p station should have serviced the current r station, lock it so it can't serve any others + --this will lock stations even when the r station manages to find a p station, this not a problem because all stations will be unlocked before it could be an issue + table_remove(p_stations, j) + if band(p_station.display_state, 4) == 0 then + p_station.display_state = p_station.display_state + 4 + update_display(map_data, p_station) + end + goto p_continue_remove + end + + p_prior = p_station.priority + if override_threshold and p_station.item_priority then + p_prior = p_station.item_priority--[[@as int]] + end + if p_prior < best_p_prior then + goto p_continue + end + + best_p_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and (best_t_to_p_dist + get_dist(p_station.entity_stop, r_station.entity_stop)) or INF + if p_prior == best_p_prior and best_p_dist > best_dist then + goto p_continue + end if correctness < 1 then correctness = 1 closest_to_correct_p_station = p_station @@ -442,35 +471,6 @@ local function tick_dispatch(map_data, mod_settings) goto p_continue end - effective_count = p_station.item_p_counts[item_name] - override_threshold = p_station.item_thresholds and p_station.item_thresholds[item_name] - if override_threshold and p_station.is_stack and not is_fluid then - override_threshold = override_threshold*get_stack_size(map_data, item_name) - end - if effective_count < (override_threshold or r_threshold) then - --this p station should have serviced the current r station, lock it so it can't serve any others - --this will lock stations even when the r station manages to find a p station, this not a problem because all stations will be unlocked before it could be an issue - table_remove(p_stations, j) - if band(p_station.display_state, 4) == 0 then - p_station.display_state = p_station.display_state + 4 - update_display(map_data, p_station) - end - goto p_continue_remove - end - - p_prior = p_station.priority - if override_threshold and p_station.item_priority then - p_prior = p_station.item_priority--[[@as int]] - end - if p_prior < best_p_prior then - goto p_continue - end - - best_p_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and (best_t_to_p_dist + get_dist(p_station.entity_stop, r_station.entity_stop)) or INF - if p_prior == best_p_prior and best_p_dist > best_dist then - goto p_continue - end - p_station_i = j best_train_id = best_p_train_id best_p_prior = p_prior diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index d8c0ab0..d5b2c24 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -40,6 +40,7 @@ end ---@param train LuaTrain +---@return LuaEntity? function get_any_train_entity(train) return train.valid and (train.front_stock or train.back_stock or train.carriages[1]) or nil end @@ -47,8 +48,9 @@ end ---@param e Station|Refueler|Train ---@param network_name string +---@return int function get_network_flag(e, network_name) - return e.network_name == NETWORK_EACH and (e.network_flag[network_name] or 0) or e.network_flag + return e.network_name == NETWORK_EACH and (e.network_flag[network_name] or 0) or e.network_flag--[[@as int]] end diff --git a/cybersyn/scripts/gui/main.lua b/cybersyn/scripts/gui/main.lua new file mode 100644 index 0000000..fc72dcd --- /dev/null +++ b/cybersyn/scripts/gui/main.lua @@ -0,0 +1,93 @@ +local gui = require("__flib__.gui-lite") +local mod_gui = require("__core__.lualib.mod-gui") + +local manager = require("scripts.gui.manager") + + +--- @class PlayerData +--- @field refs {[string]: LuaGuiElement}? +--- @field search_query string? +--- @field search_network_name string? +--- @field search_network_mask int +--- @field search_surface_idx uint? +--- @field search_item string? +--- @field trains_orderings uint[] +--- @field trains_orderings_invert boolean[] +--- @field pinning boolean + + + + +local function top_left_button_update(player, player_data) + local button_flow = mod_gui.get_button_flow(player) + local button = button_flow["top_left_button"] + if player_data.disable_top_left_button then + if button then + button.destroy() + end + elseif not button then + gui.add(button_flow, { + type = "sprite-button", + name = "top_left_button", + style = "mis_mod_gui_button_green", + sprite = "mis_configure_white", + tooltip = { "", "\n", { "mis-config-gui.configure-tooltip" } }, + handler = manager.handle.toggle, + }) + end +end + + + +local manager_gui = {} + +function manager_gui.on_lua_shortcut(e) + if e.prototype_name == "ltnm-toggle-gui" then + manager.wrapper(e, manager.handle.toggle) + end +end + + + +function manager_gui.on_player_created(e) + local player = game.get_player(e.player_index) + if not player then return end + local player_data = { + search_network_mask = -1, + trains_orderings = {}, + trains_orderings_invert = {}, + pinning = false, + refs = manager.create(player), + } + global.manager_data.players[e.player_index] = player_data + + manager.update(global, player, player_data) + top_left_button_update(player, player_data) +end + +function manager_gui.on_player_removed(e) + global.manager_data.players[e.player_index] = nil +end + +--script.on_event(defines.events.on_player_joined_game, function(e) +--end) + +--script.on_event(defines.events.on_player_left_game, function(e) +--end) + +function manager_gui.on_runtime_mod_setting_changed(e) + if e.setting == "cybersyn-disable-top-left-button" then + if not e.player_index then return end + local player = game.get_player(e.player_index) + if not player then return end + + local player_data = global.manager_data.players[e.player_index] + player_data.disable_top_left_button = player.mod_settings["cybersyn-disable-top-left-button"].value + top_left_button_update(player, player_data) + end +end + + +--gui.handle_events() + +return manager_gui diff --git a/cybersyn/scripts/gui/manager.lua b/cybersyn/scripts/gui/manager.lua index df8f5d7..bd421cb 100644 --- a/cybersyn/scripts/gui/manager.lua +++ b/cybersyn/scripts/gui/manager.lua @@ -13,61 +13,16 @@ local trains_tab = require("scripts.gui.trains") --local alerts_tab = require("scripts.gui.alerts") ---- @class PlayerData ---- @field refs {[string]: LuaGuiElement}? ---- @field search_query string? ---- @field network_name string ---- @field network_flag int ---- @field pinning boolean - - - -function Index:dispatch(msg, e) - -- "Transform" the action based on criteria - if msg.transform == "handle_refresh_click" then - if e.shift then - msg.action = "toggle_auto_refresh" - else - self.state.ltn_data = global.data - self.do_update = true - end - elseif msg.transform == "handle_titlebar_click" then - if e.button == defines.mouse_button_type.middle then - msg.action = "recenter" - end - end - - -- Dispatch the associated action - if msg.action then - local func = self.actions[msg.action] - if func then - func(self, msg, e) - else - log("Attempted to call action `" .. msg.action .. "` for which there is no handler yet.") - end - end - - -- Update if necessary - if self.do_update then - self:update() - self.do_update = false - end -end - -function Index:schedule_update() - self.do_update = true -end - - local manager = {} -function manager.build(player, player_data) +--- @param player LuaPlayer +function manager.create(player) local widths = constants.gui["en"] ---@type table local refs = {} - local _, window = gui.add(player.gui.screen, { + gui.add(player.gui.screen, { { name = "manager_window", type = "frame", @@ -111,25 +66,25 @@ function manager.build(player, player_data) name = "manager_text_search_field", type = "textfield", clear_and_focus_on_right_click = true, - handler = manager.handle.update_text_search_query, --on_gui_text_changed + handler = manager.handle.update_text_search, --on_gui_text_changed }, { type = "empty-widget", style = "flib_horizontal_pusher" }, { type = "label", style = "caption_label", caption = { "gui.ltnm-network-id-label" } }, { - name = "manager_network_id_field", + name = "manager_network_mask_field", type = "textfield", style_mods = { width = 120 }, numeric = true, allow_negative = true, clear_and_focus_on_right_click = true, text = "-1", - handler = manager.handle.update_network_id_query, --on_gui_text_changed + handler = manager.handle.update_network_mask, --on_gui_text_changed }, { type = "label", style = "caption_label", caption = { "gui.ltnm-surface-label" } }, { name = "manager_surface_dropdown", type = "drop-down", - handler = manager.handle.change_surface, --on_gui_selection_state_changed + handler = manager.handle.update_surface, --on_gui_selection_state_changed }, }, }, @@ -137,9 +92,6 @@ function manager.build(player, player_data) name = "manager_tabbed_pane", type = "tabbed-pane", style = "ltnm_tabbed_pane", - children = { - trains_tab.build(widths, refs), - }, }, }, }, @@ -149,23 +101,49 @@ function manager.build(player, player_data) - refs.manager_titlebar.drag_target = window - window.force_auto_center() + refs.manager_titlebar.drag_target = refs.manager_window + refs.manager_window.force_auto_center() + + return refs end +--- @param map_data MapData --- @param player LuaPlayer ---- @param refs table -function manager.destroy(player, refs) - refs.manager_window.destroy() - - player.set_shortcut_toggled("ltnm-toggle-gui", false) - player.set_shortcut_available("ltnm-toggle-gui", false) +--- @param player_data PlayerData +function manager.update(map_data, player, player_data) + local tab = trains_tab.build(map_data, player_data) + gui.add(_, tab, player_data.refs) end + + +manager.handle = {} + +--- @param e {player_index: uint} +function manager.wrapper(e, handler) + local player = game.get_player(e.player_index) + if not player then return end + local player_data = global.manager_data.players[e.player_index] + handler(player, player_data, player_data.refs, e) +end + + +local function toggle_fab(elem, sprite, state) + if state then + elem.style = "flib_selected_frame_action_button" + elem.sprite = sprite .. "_black" + else + elem.style = "frame_action_button" + elem.sprite = sprite .. "_white" + end +end + + + --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.open(player, player_data, refs) +function manager.handle.open(player, player_data, refs) refs.manager_window.bring_to_front() refs.manager_window.visible = true player_data.visible = true @@ -177,10 +155,11 @@ function manager.open(player, player_data, refs) player.set_shortcut_toggled("ltnm-toggle-gui", true) end + --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.close(player, player_data, refs) +function manager.handle.close(player, player_data, refs) if player_data.pinning then return end @@ -195,86 +174,76 @@ function manager.close(player, player_data, refs) player.set_shortcut_toggled("ltnm-toggle-gui", false) end +--- @param player LuaPlayer +--- @param player_data PlayerData +--- @param refs table +function manager.handle.toggle(player, player_data, refs) -manager.handle = {} - ---- @param e GuiEventData -function manager.wrapper(e, handler) - local player = game.get_player(e.player_index) - if not player then return end - local player_data = global.manager.players[e.player_index] - handler(player, player_data, player_data.refs) end - -local function toggle_fab(elem, sprite, state) - if state then - elem.style = "flib_selected_frame_action_button" - elem.sprite = sprite .. "_black" - else - elem.style = "frame_action_button" - elem.sprite = sprite .. "_white" - end -end - - -manager.handle.close = manager.close - --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table function manager.handle.recenter(player, player_data, refs) - refs.window.force_auto_center() + refs.window.force_auto_center() end --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table function manager.handle.toggle_auto_refresh(player, player_data, refs) - player_data.auto_refresh = not player_data.auto_refresh - toggle_fab(refs.manager_refresh_button, "ltnm_refresh", player_data.auto_refresh) + player_data.auto_refresh = not player_data.auto_refresh + toggle_fab(refs.manager_refresh_button, "ltnm_refresh", player_data.auto_refresh) end --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table function manager.handle.toggle_pinned(player, player_data, refs) - player_data.pinned = not player_data.pinned - toggle_fab(refs.manager_pin_button, "ltnm_pin", player_data.pinned) + player_data.pinned = not player_data.pinned + toggle_fab(refs.manager_pin_button, "ltnm_pin", player_data.pinned) end --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table --- @param e GuiEventData -function manager.handle.update_text_search_query(player, player_data, refs, e) - local query = e.text - -- Input sanitization - for pattern, replacement in pairs(constants.input_sanitizers) do - query = string.gsub(query, pattern, replacement) - end - player_data.search_query = query - - if Gui.state.search_job then - on_tick_n.remove(Gui.state.search_job) - end - - if #query == 0 then - Gui:schedule_update() - else - Gui.state.search_job = on_tick_n.add( - game.tick + 30, - { gui = "main", action = "update", player_index = Gui.player.index } - ) - end +function manager.handle.update_text_search(player, player_data, refs, e) + local query = e.text + -- Input sanitization + for pattern, replacement in pairs(constants.input_sanitizers) do + query = string.gsub(query, pattern, replacement) + end + player_data.search_query = query end + --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.handle.update_network_id_query(player, player_data, refs) - Gui.state.network_id = tonumber(Gui.refs.toolbar.network_id_field.text) or -1 - Gui:schedule_update() +function manager.handle.update_network_name(player, player_data, refs) + local signal = refs.manager_network_name.elem_value + if signal then + player_data.search_network_name = signal.name + else + player_data.search_network_name = nil + end +end +--- @param player LuaPlayer +--- @param player_data PlayerData +--- @param refs table +function manager.handle.update_network_mask(player, player_data, refs) + player_data.search_network_mask = tonumber(refs.manager_network_mask_field.text) or -1 +end +--- @param player LuaPlayer +--- @param player_data PlayerData +--- @param refs table +function manager.handle.update_surface(player, player_data, refs) + local i = refs.manager_surface_dropdown.selected_index + player_data.search_surface_idx = i--TODO: fix this end + +gui.add_handlers(manager.handle, manager.wrapper) + return manager diff --git a/cybersyn/scripts/gui/trains.lua b/cybersyn/scripts/gui/trains.lua index bdbd628..05fd9c8 100644 --- a/cybersyn/scripts/gui/trains.lua +++ b/cybersyn/scripts/gui/trains.lua @@ -1,146 +1,226 @@ +local format = require("__flib__.format") local gui = require("__flib__.gui-lite") local constants = require("constants") local util = require("scripts.gui.util") -local templates = require("templates") +local templates = require("scripts.gui.templates") local trains_tab = {} -function trains_tab.build(map_data, player_id, player_data) +--- @param map_data MapData +--- @param player_data PlayerData +--- @return GuiElemDef +function trains_tab.build(map_data, player_data) local widths = constants.gui["en"] - local search_query = player_data.search_query - local search_network_flag = player_data.network_flag - local search_network = player_data.network + 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 trains_sorted = {} + for id, train in pairs(map_data.trains) do + if search_network_name then + if search_network_name ~= train.network_name then + goto continue + end + local train_flag = get_network_flag(train, search_network_name) + if not bit32.btest(search_network_mask, train_flag) then + goto continue + end + elseif search_network_mask ~= -1 then + if train.network_name == NETWORK_EACH then + local masks = train.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, train.network_flag) then + goto continue + end + end + + if search_surface_idx then + local entity = get_any_train_entity(train.entity) + if not entity then + goto continue + end + if entity.surface.index ~= search_surface_idx then + goto continue + end + end + + if search_item then + if not train.manifest then + goto continue + end + for i, v in ipairs(train.manifest) do + if v.name == search_item then + goto has_match + end + end + goto continue + ::has_match:: + end + + trains_sorted[#trains_sorted + 1] = id + ::continue:: + end + + + table.sort(trains_sorted, function(a, b) + local train1 = map_data.trains[a] + local train2 = map_data.trains[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 train1.layout_id ~= train2.layout_id then + local layout1 = map_data.layouts[train1.layout_id] + local layout2 = map_data.layouts[train2.layout_id] + 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 + end + elseif v == ORDER_DEPOT then + local depot1 = map_data.depots[train1.depot_id] + local depot2 = map_data.depots[train2.depot_id] + local name1 = depot1.entity_stop.valid and depot1.entity_stop.backer_name + local name2 = depot2.entity_stop.valid and depot2.entity_stop.backer_name + if name1 ~= name2 then + return invert ~= (name1 and (name2 and name1 < name2 or true) or false) + end + elseif v == ORDER_STATUS then + if train1.status ~= train2.status then + return invert ~= (train1.status < train2.status) + end + elseif v == ORDER_MANIFEST then + if not train1.manifest then + if train2.manifest then + return invert ~= true + end + elseif not train2.manifest then + return invert ~= false + else + local primary_item1 = train1.manifest[1] + local primary_item2 = train2.manifest[1] + if primary_item1.name ~= primary_item2.name then + return invert ~= (primary_item1.type == primary_item2.type and primary_item1.name < primary_item2.name or primary_item1.type == "item") + elseif primary_item1.count ~= primary_item2.count then + return invert ~= (primary_item1.count < primary_item2.count) + end + end + end + end + return a < b + end) - local trains_sorted = player_data.trains_sorted ---@type GuiElemDef local train_list = {} - --if not sorted_trains then - -- sorted_trains = {} - -- ids = {} - -- for id, train in pairs(map_data) do - -- local i = #ids + 1 - -- ids[i] = id - -- sorted_trains[i] = train - -- end - -- dual_sort(ids, sorted_trains) - --end if #trains_sorted == 0 then train_list[1] = { type = "label", style = "ltnm_semibold_label", caption = { "gui.ltnm-no-trains" }, - ref = { "trains", "warning_label" }, } else - local start, finish, step - if player_data.trains_ascending then - start = #trains_sorted - finish = 1 - step = -1 - else - start = 1 - finish = #trains_sorted - step = 1 - end - - local gui_idx = 1 - for idx = start, finish, step do - local train_id = trains_sorted[idx] + for idx, train_id in ipairs(trains_sorted) do local train = map_data.trains[train_id] + local depot = map_data.depots[train.depot_id] + local depot_name = depot.entity_stop.valid and depot.entity_stop.backer_name or "" - if - true - then - local color = gui_idx % 2 == 0 and "dark" or "light" - train_list[gui_idx] = { - type = "frame", - style = "ltnm_table_row_frame_" .. color, - children = { - { - type = "frame", - style = "ltnm_table_inset_frame_" .. color, - children = { - type = "minimap", - style = "ltnm_train_minimap", - { type = "label", style = "ltnm_minimap_label" }, - { - type = "button", - style = "ltnm_train_minimap_button", - tooltip = { "gui.ltnm-open-train-gui" }, - elem_mods = { entity = get_any_train_entity(train.entity) }, - actions = { - on_click = { gui = "main", action = "open_train_gui", train_id = train_id }, - }, - }, - }, - }, - { - type = "label", - style_mods = { width = widths.trains.composition }, - elem_mods = { caption = train.composition }, - }, - { - type = "label", style_mods = { width = widths.trains.depot }, - elem_mods = { caption = train.depot }, - }, - { - type = "frame", - name = "shipment_frame", - style = "ltnm_small_slot_table_frame_" .. color, - style_mods = { width = widths.trains.shipment }, - children = { - { - type = "table", - name = "shipment_table", - style = "slot_table", - column_count = widths.trains.shipment_columns, - children = util.slot_table_build(train.manifest, "default"), - }, + local color = idx % 2 == 0 and "dark" or "light" + train_list[idx] = { + type = "frame", + style = "ltnm_table_row_frame_" .. color, + children = { + { + type = "frame", + style = "ltnm_table_inset_frame_" .. color, + children = { + type = "minimap", + style = "ltnm_train_minimap", + { type = "label", style = "ltnm_minimap_label" }, + { + type = "button", + style = "ltnm_train_minimap_button", + tooltip = { "gui.ltnm-open-train-gui" }, + elem_mods = { entity = get_any_train_entity(train.entity) }, + handler = trains_tab.handle.open_train_gui, --on_click + tags = { train_id = train_id }, }, }, }, - } - gui_idx = gui_idx + 1 - end + { + type = "label", + style_mods = { width = widths.trains.composition }, + elem_mods = { caption = train.layout_id }, + }, + { + type = "label", + style_mods = { width = widths.trains.depot }, + elem_mods = { caption = depot_name }, + }, + { + type = "frame", + name = "shipment_frame", + style = "ltnm_small_slot_table_frame_" .. color, + style_mods = { width = widths.trains.shipment }, + children = { + { + type = "table", + name = "shipment_table", + style = "slot_table", + column_count = widths.trains.shipment_columns, + children = util.slot_table_build(train.manifest, "default"), + }, + }, + }, + }, + } end end return { tab = { + name = "trains_tab", type = "tab", caption = #trains_sorted == 0 and { "gui.ltnm-trains" } or { "gui.ltnm-trains", #train_list }, - badge_text = misc.delineate_number(#ltn_data.sorted_trains.composition), - ref = { "trains", "tab" }, - actions = { - on_click = { gui = "main", action = "change_tab", tab = "trains" }, - }, + --badge_text = format.number(#ltn_data.sorted_trains.composition), + handler = trains_tab.handle.change_tab, --on_click + tags = { tab = "trains_tab" }, }, content = { + name = "trains_content_frame", type = "frame", style = "ltnm_main_content_frame", direction = "vertical", - ref = { "trains", "content_frame" }, children = { { type = "frame", style = "ltnm_table_toolbar_frame", - templates.sort_checkbox(widths, "trains", "train_id", true), templates.sort_checkbox(widths, "trains", "status", false), - templates.sort_checkbox(widths, "trains", "composition", false, { "gui.ltnm-composition-description" }), + templates.sort_checkbox(widths, "trains", "layout", false, { "gui.ltnm-composition-description" }), templates.sort_checkbox(widths, "trains", "depot", false), templates.sort_checkbox(widths, "trains", "shipment", false), }, - { type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "trains", "scroll_pane" } }, + { name = "trains_scroll_pane", type = "scroll-pane", style = "ltnm_table_scroll_pane" }, { + name = "trains_warning_flow", type = "flow", style = "ltnm_warning_flow", - visible = false, - ref = { "trains", "warning_flow" }, children = train_list, }, }, diff --git a/cybersyn/scripts/gui/util.lua b/cybersyn/scripts/gui/util.lua index c1b01bf..5b3893a 100644 --- a/cybersyn/scripts/gui/util.lua +++ b/cybersyn/scripts/gui/util.lua @@ -38,8 +38,8 @@ end --- @param color string --- @return GuiElemDef[] function util.slot_table_build(manifest, color) + ---@type GuiElemDef[] local children = {} - local i = 1 for _, item in pairs(manifest) do local name = item.name local sprite @@ -49,7 +49,7 @@ function util.slot_table_build(manifest, color) sprite = string.gsub(name, ",", "/") end if game.is_valid_sprite_path(sprite) then - children[i] = { + children[#children + 1] = { type = "sprite-button", enabled = false, style = "ltnm_small_slot_button_" .. color, @@ -61,7 +61,6 @@ function util.slot_table_build(manifest, color) "\n"..format.number(count), }, } - i = i + 1 end end return children diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 5c9497b..4bea069 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -471,7 +471,7 @@ function reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_ent local wagon_number = 0 for i = 1, 112 do local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_stop, rail_connection_direction = defines.rail_connection_direction.straight}) - if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then + if not rail or not rail.valid then is_break = true break end @@ -555,6 +555,10 @@ function reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_ent end search_area = area.move(search_area, area_delta) end + if not rail_connection_direction ~= defines.rail_connection_direction.straight then + is_break = true + break + end end stop.layout_pattern = layout_pattern if is_station_or_refueler then diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index f2888e9..664fcca 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -103,12 +103,19 @@ data:extend({ default_value = false, }, --{ + -- type = "bool-setting", + -- name = "cybersyn-disable-top-left-button", + -- setting_type = "runtime-player", + -- default_value = false, + -- order = "ea", + --}, + --{ -- type = "int-setting", -- name = "cybersyn-history-length", -- setting_type = "runtime-global", -- minimum_value = 10, -- maximum_value = 1000, -- default_value = 50, - -- order = "ea", + -- order = "eb", --}, }) From 368046c3aa12bfc711bc1414c82d3e2e97fdc22f Mon Sep 17 00:00:00 2001 From: monica Date: Tue, 10 Jan 2023 09:48:00 -0500 Subject: [PATCH 5/9] fixed crash --- cybersyn/scripts/central-planning.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index e68e964..1c1d9d6 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -387,11 +387,6 @@ local function tick_dispatch(map_data, mod_settings) if p_prior < best_p_prior then goto p_continue end - - best_p_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and (best_t_to_p_dist + get_dist(p_station.entity_stop, r_station.entity_stop)) or INF - if p_prior == best_p_prior and best_p_dist > best_dist then - goto p_continue - end if correctness < 1 then correctness = 1 closest_to_correct_p_station = p_station @@ -471,6 +466,11 @@ local function tick_dispatch(map_data, mod_settings) goto p_continue end + best_p_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and (best_t_to_p_dist + get_dist(p_station.entity_stop, r_station.entity_stop)) or INF + if p_prior == best_p_prior and best_p_dist > best_dist then + goto p_continue + end + p_station_i = j best_train_id = best_p_train_id best_p_prior = p_prior From 2ecbace0695e84a8c36f06092e2d7a3fad9e557b Mon Sep 17 00:00:00 2001 From: monica Date: Tue, 10 Jan 2023 10:03:34 -0500 Subject: [PATCH 6/9] changed provider priority --- cybersyn/changelog.txt | 1 + cybersyn/scripts/central-planning.lua | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index add31db..e951e34 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -4,6 +4,7 @@ Date: 2022-1-9 Changes: - Made the automatic allow list slightly more forgiving to stations where the last wagon would be on a curved rail - Improved performance when fuel threshold is set to 1 + - Prioritized distance from provider to requester over distance from train to provider Bugfixes: - Fixed a bug where it was possible for a single station to be updated twice per dispatch cycle, which could cause a crash - Fixed a crash where trains would sometimes think a destroyed depot still exists diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 1c1d9d6..0f069b8 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -349,7 +349,7 @@ local function tick_dispatch(map_data, mod_settings) ---@type uint local j = 1 while j <= #p_stations do - local p_flag, r_flag, netand, best_p_train_id, best_t_prior, best_capacity, best_t_to_p_dist, effective_count, override_threshold, p_prior, best_p_dist + local p_flag, r_flag, netand, best_p_train_id, best_t_prior, best_capacity, best_t_to_p_dist, effective_count, override_threshold, p_prior, best_p_to_r_dist local p_station_id = p_stations[j] local p_station = stations[p_station_id] @@ -387,6 +387,11 @@ local function tick_dispatch(map_data, mod_settings) if p_prior < best_p_prior then goto p_continue end + + best_p_to_r_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and get_dist(p_station.entity_stop, r_station.entity_stop) or INF + if p_prior == best_p_prior and best_p_to_r_dist > best_dist then + goto p_continue + end if correctness < 1 then correctness = 1 closest_to_correct_p_station = p_station @@ -466,15 +471,10 @@ local function tick_dispatch(map_data, mod_settings) goto p_continue end - best_p_dist = p_station.entity_stop.valid and r_station.entity_stop.valid and (best_t_to_p_dist + get_dist(p_station.entity_stop, r_station.entity_stop)) or INF - if p_prior == best_p_prior and best_p_dist > best_dist then - goto p_continue - end - p_station_i = j best_train_id = best_p_train_id best_p_prior = p_prior - best_dist = best_p_dist + best_dist = best_t_to_p_dist + best_p_to_r_dist ::p_continue:: j = j + 1 ::p_continue_remove:: From e6c23626c4a815bcdff6a9220f23b6a36cb7395c Mon Sep 17 00:00:00 2001 From: monica Date: Tue, 10 Jan 2023 10:06:37 -0500 Subject: [PATCH 7/9] fixed typo bug --- cybersyn/scripts/central-planning.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 0f069b8..cf7f0fa 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -474,7 +474,7 @@ local function tick_dispatch(map_data, mod_settings) p_station_i = j best_train_id = best_p_train_id best_p_prior = p_prior - best_dist = best_t_to_p_dist + best_p_to_r_dist + best_dist = best_p_to_r_dist ::p_continue:: j = j + 1 ::p_continue_remove:: From de8d7381b370490028cb18eabe8b6736fea7f06a Mon Sep 17 00:00:00 2001 From: monica Date: Tue, 10 Jan 2023 17:37:26 -0500 Subject: [PATCH 8/9] fixed date --- cybersyn/changelog.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index e951e34..24c0448 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -1,6 +1,6 @@ --------------------------------------------------------------------------------------------------- Version: 1.2.10 -Date: 2022-1-9 +Date: 2023-1-9 Changes: - Made the automatic allow list slightly more forgiving to stations where the last wagon would be on a curved rail - Improved performance when fuel threshold is set to 1 @@ -12,7 +12,7 @@ Date: 2022-1-9 - Removed unfinished mod setting with the broken translation key --------------------------------------------------------------------------------------------------- Version: 1.2.9 -Date: 2022-1-7 +Date: 2023-1-7 Bugfixes: - Fixed a bug with deliveries sometimes failing to be removed - Fixed several rare crashes @@ -20,7 +20,7 @@ Date: 2022-1-7 - Updated flib dependency to the correct version --------------------------------------------------------------------------------------------------- Version: 1.2.8 -Date: 2022-1-5 +Date: 2023-1-5 Features: - Improved placeholder cybernetic combinator art - Added a wagon control setting to bar unfiltered slots in adjacent cargo wagons @@ -36,7 +36,7 @@ Date: 2022-1-5 - Migrated to non-deprecated flib modules --------------------------------------------------------------------------------------------------- Version: 1.2.7 -Date: 2022-1-1 +Date: 2023-1-1 Bugfixes: - Fixed a bug with items attempting to be loaded into fluid wagons --------------------------------------------------------------------------------------------------- From 9de913536ad75a2e48a3999093b63b636254c451 Mon Sep 17 00:00:00 2001 From: monica Date: Wed, 11 Jan 2023 09:45:11 -0500 Subject: [PATCH 9/9] save for 1.2.10 --- TODO | 1 + cybersyn/scripts/factorio-api.lua | 8 + cybersyn/scripts/global.lua | 1 + cybersyn/scripts/gui/main.lua | 41 ++++ cybersyn/scripts/gui/stations.lua | 327 +++++++++++++++++++++--------- cybersyn/scripts/lib.lua | 55 ++--- 6 files changed, 303 insertions(+), 130 deletions(-) diff --git a/TODO b/TODO index b3633c0..6cb2eee 100644 --- a/TODO +++ b/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 diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index d5b2c24..3706f11 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -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 +---@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 diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index c2557c0..d0180fa 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -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 diff --git a/cybersyn/scripts/gui/main.lua b/cybersyn/scripts/gui/main.lua index fc72dcd..5a42e5b 100644 --- a/cybersyn/scripts/gui/main.lua +++ b/cybersyn/scripts/gui/main.lua @@ -3,6 +3,8 @@ local mod_gui = require("__core__.lualib.mod-gui") local manager = require("scripts.gui.manager") +--- @class Manager +--- @field item_order table --- @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 diff --git a/cybersyn/scripts/gui/stations.lua b/cybersyn/scripts/gui/stations.lua index dacc567..e7ab746 100644 --- a/cybersyn/scripts/gui/stations.lua +++ b/cybersyn/scripts/gui/stations.lua @@ -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 diff --git a/cybersyn/scripts/lib.lua b/cybersyn/scripts/lib.lua index 95afb2f..e4bdaf4 100644 --- a/cybersyn/scripts/lib.lua +++ b/cybersyn/scripts/lib.lua @@ -50,45 +50,24 @@ function irpairs(a) return irnext, a, 0 end ----@generic V ----@param arr Array ----@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 +--- @param t2 table +--- @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