diff --git a/.gitignore b/.gitignore index c4c4ffc..31f6fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.zip +.vscode/launch.json +.vscode/settings.json \ No newline at end of file diff --git a/cybersyn/constants.lua b/cybersyn/constants.lua index 98f9d75..086de2b 100644 --- a/cybersyn/constants.lua +++ b/cybersyn/constants.lua @@ -85,14 +85,14 @@ constants.gui_content_frame_height = 744 constants.gui_inventory_table_height = 40 * 18 constants.gui_translations = { - delivering_to = { "gui.ltnm-delivering-to" }, - fetching_from = { "gui.ltnm-fetching-from" }, - loading_at = { "gui.ltnm-loading-at" }, - not_available = { "gui.ltnm-not-available" }, - parked_at_depot_with_residue = { "gui.ltnm-parked-at-depot-with-residue" }, - parked_at_depot = { "gui.ltnm-parked-at-depot" }, - returning_to_depot = { "gui.ltnm-returning-to-depot" }, - unloading_at = { "gui.ltnm-unloading-at" }, + delivering_to = { "cybersyn-gui.delivering-to" }, + fetching_from = { "cybersyn-gui.fetching-from" }, + loading_at = { "cybersyn-gui.loading-at" }, + not_available = { "cybersyn-gui.not-available" }, + parked_at_depot_with_residue = { "cybersyn-gui.parked-at-depot-with-residue" }, + parked_at_depot = { "cybersyn-gui.parked-at-depot" }, + returning_to_depot = { "cybersyn-gui.returning-to-depot" }, + unloading_at = { "cybersyn-gui.unloading-at" }, } constants.input_sanitizers = { @@ -144,8 +144,8 @@ constants.ltn_event_names = { if script then constants.open_station_gui_tooltip = { "", - { "gui.ltnm-open-station-gui" }, - remote.interfaces["ltn-combinator"] and { "", "\n", { "gui.ltnm-open-ltn-combinator-gui" } } or nil, + { "cybersyn-gui.open-station-gui" }, + remote.interfaces["ltn-combinator"] and { "", "\n", { "cybersyn-gui.open-ltn-combinator-gui" } } or nil, } end diff --git a/cybersyn/data.lua b/cybersyn/data.lua index 7cfd739..3d57f68 100644 --- a/cybersyn/data.lua +++ b/cybersyn/data.lua @@ -26,14 +26,14 @@ data:extend({ --{ -- type = "shortcut", - -- name = "ltnm-toggle-gui", + -- name = "cybersyn-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", + -- associated_control_input = "cybersyn-toggle-gui", -- technology_to_unlock = "logistic-train-network", --}, }) diff --git a/cybersyn/info.json b/cybersyn/info.json index e1a036f..9bb2ecc 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -11,6 +11,7 @@ "? space-exploration >= 0.6.94", "? miniloader", "? nullius", - "? pypostprocessing" + "? pypostprocessing", + "! LtnManager" ] } diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 6671d9d..42d7217 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -12,6 +12,9 @@ cybersyn-warmup-time=Station warmup time (sec) cybersyn-stuck-train-time=Stuck train timeout (sec) cybersyn-allow-cargo-in-depot=Allow cargo in depots cybersyn-invert-sign=Invert combinator output (deprecated) +cybersyn-manager-enabled=Enable the Cybersyn GUI. +cybersyn-manager-update-rate=Manager refresh tick interval +cybersyn-manager-result-limit=Max entities displayed on GUI pages. [mod-setting-description] cybersyn-enable-planner=Enable or disable the central planning algorithm. If disabled no new trains will be dispatched. @@ -27,6 +30,7 @@ cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before c cybersyn-stuck-train-time=After this many seconds from a train's dispatch, an alert will be sent to let you know a train is probably stuck and has not completed its delivery. The player will likely have to debug their network to get the train unstuck. cybersyn-allow-cargo-in-depot=If checked, trains will be allowed to have cargo in depots; no alerts will be generated and the train will not be held. In addition, trains with orders to visit requester stations with "Inactivity condition" checked will wait for inactivity instead of waiting for empty cargo. Useful for creating train systems where depots handle excess cargo. For advanced users only. cybersyn-invert-sign=Flip the sign of the output of cybernetic combinators to be the same as it is in LTN or in earlier versions of Project Cybersyn. +cybersyn-manager-result-limit=Caps the number of matching enitities (e.g. stations, trains) to limit the amount of update time consumed when the list is refreshed.\n-1 means return all results. [item-name] cybersyn-combinator=Cybernetic combinator diff --git a/cybersyn/locale/en/manager.cfg b/cybersyn/locale/en/manager.cfg index fadaa5a..a82667b 100644 --- a/cybersyn/locale/en/manager.cfg +++ b/cybersyn/locale/en/manager.cfg @@ -1,5 +1,8 @@ +[mod-name] +cybersyn=Project Cybersyn + [controls] -cybersyn-toggle-gui=Toggle LTN Manager +cybersyn-toggle-gui=Toggle Cybersyn Manager [cybersyn-gui] alert-delivery-failed=Delivery failed @@ -33,6 +36,7 @@ inventory=Inventory keep-open=Keep open loading-at=Loading at name=Name +network-name-label=Network Filter: network-id-label=Network ID: network-id=Network ID no-alerts=[img=warning-white] No alerts @@ -41,8 +45,7 @@ no-history=[img=warning-white] No history no-stations=[img=warning-white] No stations not-available=Not available no-trains=[img=warning-white] No trains -open-ltn-combinator-gui=[font=default-semibold][color=128,206,240]Control:[/color][/font] Open LTN Combinator GUI -open-station-gui=Open station GUI\n[font=default-semibold][color=128,206,240]Shift:[/color][/font] Open station on map +open-station-gui=Open station GUI\n[font=default-semibold][color=128,206,240]Shift:[/color][/font] Open station on map\n[font=default-semibold][color=128,206,240]Control:[/color][/font] Open Station Cybernetic Combinator\n[font=default-semibold][color=128,206,240]Alt:[/color][/font] Open Station Control Cybernetic Combinator open-train-gui=Open train GUI parked-at-depot=Parked at depot parked-at-depot-with-residue=Parked at depot with residue @@ -54,13 +57,14 @@ requested=Requested returning-to-depot=Returning to depot route=Route runtime=Runtime -search-label=Search: +search-label=Station Name: +search-item-label=Item Filter: shipments-description=Green = Inbound\nBlue = Outbound shipment=Shipment shipments=Shipments stations=Stations status-description=[img=flib_indicator_green]1 = normal status\n[img=flib_indicator_blue]n = LTN Controlled Train parked at stop, n = number of trains\n[img=flib_indicator_yellow]n = Stop is part of a scheduled delivery, n = number of trains\n[img=flib_indicator_white]1 = Error - not initialized\n[img=flib_indicator_red]1 = Error - short circuit\n[img=flib_indicator_red]2 = Error - deactivated stop -status=Status +status=Network Signal surface-label=Surface: time=Time train-id=Train ID @@ -69,9 +73,11 @@ type=Type unloading-at=Unloading at [cybersyn-message] -error-ltn-combinator-not-found=Could not find an LTN Combinator for this station. +error-cybernetic-combinator-not-found=Could not find a cybernetic combinator for this station. +error-station-control-combinator-not-found=Could not find a station control cybernetic combinator for this station. error-station-is-invalid=Station is invalid, please refresh the GUI. error-train-is-invalid=Train is invalid, please refresh the GUI. +error-cross-surface-camera-invalid=Cannot move the camera to an entity on a different surface! [cybersyn-mod-setting-description] iterations-per-tick=Decrease this number if you're having performance issues. @@ -81,4 +87,4 @@ history-length=History length iterations-per-tick=Iterations per tick [img=info] [shortcut-name] -cybersyn-toggle-gui=Toggle LTN Manager +cybersyn-toggle-gui=Toggle Cybersyn Manager diff --git a/cybersyn/prototypes/gui-style.lua b/cybersyn/prototypes/gui-style.lua index b4ce7da..e0657da 100644 --- a/cybersyn/prototypes/gui-style.lua +++ b/cybersyn/prototypes/gui-style.lua @@ -364,7 +364,7 @@ data:extend({ -- custom inputs --{ -- type = "custom-input", - -- name = "ltnm-toggle-gui", + -- name = "cybersyn-toggle-gui", -- key_sequence = "CONTROL + T", --}, --{ @@ -376,14 +376,14 @@ data:extend({ -- shortcuts { type = "shortcut", - name = "ltnm-toggle-gui", + name = "cybersyn-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", + --associated_control_input = "cybersyn-toggle-gui", technology_to_unlock = "cybersyn-train-network", }, }) diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 999edcb..23637e3 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -30,6 +30,8 @@ SETTING_DISABLE_DEPOT_BYPASS = 6 SETTING_ENABLE_SLOT_BARRING = 7 NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"} +NETWORK_SIGNAL_GUI_DEFAULT = {name="signal-each", type="virtual"} +NETWORK_ANYTHING = "signal-anything" NETWORK_EACH = "signal-each" INACTIVITY_TIME = 100 LOCK_TRAIN_TIME = 60*60*60*24*7 diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 9255f7a..0d0c749 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -124,6 +124,8 @@ ---@field public stuck_train_alert_enabled boolean --interface setting ---@field public react_to_train_at_incorrect_station boolean --interface setting ---@field public react_to_train_early_to_depot boolean --interface setting +---@field public enable_manager boolean +---@field public manager_update_rate int --if this is uncommented it means there are migrations to write diff --git a/cybersyn/scripts/gui/alerts.lua b/cybersyn/scripts/gui/alerts.lua index 1e915a5..46840f3 100644 --- a/cybersyn/scripts/gui/alerts.lua +++ b/cybersyn/scripts/gui/alerts.lua @@ -12,7 +12,7 @@ function alerts_tab.build(widths) return { tab = { type = "tab", - caption = { "gui.ltnm-alerts" }, + caption = { "cybersyn-gui.alerts" }, ref = { "alerts", "tab" }, actions = { on_click = { gui = "main", action = "change_tab", tab = "alerts" }, @@ -36,7 +36,7 @@ function alerts_tab.build(widths) type = "sprite-button", style = "tool_button_red", sprite = "utility/trash", - tooltip = { "gui.ltnm-delete-all-alerts" }, + tooltip = { "cybersyn-gui.delete-all-alerts" }, ref = { "alerts", "delete_all_button" }, actions = { on_click = { gui = "main", action = "delete_all_alerts" }, @@ -52,7 +52,7 @@ function alerts_tab.build(widths) { type = "label", style = "ltnm_semibold_label", - caption = { "gui.ltnm-no-alerts" }, + caption = { "cybersyn-gui.no-alerts" }, ref = { "alerts", "warning_label" }, }, }, @@ -121,7 +121,7 @@ function alerts_tab.update(self) type = "label", style = "ltnm_clickable_semibold_label", style_mods = { width = widths.alerts.train_id, horizontal_align = "center" }, - tooltip = { "gui.ltnm-open-train-gui" }, + tooltip = { "cybersyn-gui.open-train-gui" }, }, { type = "flow", @@ -153,7 +153,7 @@ function alerts_tab.update(self) type = "sprite-button", style = "tool_button_red", sprite = "utility/trash", - tooltip = { "gui.ltnm-delete-alert" }, + tooltip = { "cybersyn-gui.delete-alert" }, }, }) end @@ -185,8 +185,8 @@ function alerts_tab.update(self) { elem_mods = { caption = util.signed_int32(alerts_entry.train.network_id) } }, { elem_mods = { - caption = { "gui.ltnm-alert-" .. string.gsub(alerts_entry.type, "_", "-") }, - tooltip = { "gui.ltnm-alert-" .. string.gsub(alerts_entry.type, "_", "-") .. "-description" }, + caption = { "cybersyn-gui.alert-" .. string.gsub(alerts_entry.type, "_", "-") }, + tooltip = { "cybersyn-gui.alert-" .. string.gsub(alerts_entry.type, "_", "-") .. "-description" }, }, }, {}, diff --git a/cybersyn/scripts/gui/constants.lua b/cybersyn/scripts/gui/constants.lua index 98f9d75..6d7305d 100644 --- a/cybersyn/scripts/gui/constants.lua +++ b/cybersyn/scripts/gui/constants.lua @@ -85,14 +85,14 @@ constants.gui_content_frame_height = 744 constants.gui_inventory_table_height = 40 * 18 constants.gui_translations = { - delivering_to = { "gui.ltnm-delivering-to" }, - fetching_from = { "gui.ltnm-fetching-from" }, - loading_at = { "gui.ltnm-loading-at" }, - not_available = { "gui.ltnm-not-available" }, - parked_at_depot_with_residue = { "gui.ltnm-parked-at-depot-with-residue" }, - parked_at_depot = { "gui.ltnm-parked-at-depot" }, - returning_to_depot = { "gui.ltnm-returning-to-depot" }, - unloading_at = { "gui.ltnm-unloading-at" }, + delivering_to = { "cybersyn-gui.delivering-to" }, + fetching_from = { "cybersyn-gui.fetching-from" }, + loading_at = { "cybersyn-gui.loading-at" }, + not_available = { "cybersyn-gui.not-available" }, + parked_at_depot_with_residue = { "cybersyn-gui.parked-at-depot-with-residue" }, + parked_at_depot = { "cybersyn-gui.parked-at-depot" }, + returning_to_depot = { "cybersyn-gui.returning-to-depot" }, + unloading_at = { "cybersyn-gui.unloading-at" }, } constants.input_sanitizers = { @@ -144,8 +144,7 @@ constants.ltn_event_names = { if script then constants.open_station_gui_tooltip = { "", - { "gui.ltnm-open-station-gui" }, - remote.interfaces["ltn-combinator"] and { "", "\n", { "gui.ltnm-open-ltn-combinator-gui" } } or nil, + { "cybersyn-gui.open-station-gui" }, } end diff --git a/cybersyn/scripts/gui/depots.lua b/cybersyn/scripts/gui/depots.lua index 5dfa81a..4520bcc 100644 --- a/cybersyn/scripts/gui/depots.lua +++ b/cybersyn/scripts/gui/depots.lua @@ -8,7 +8,7 @@ function depots_tab.build(widths) return { tab = { type = "tab", - caption = { "gui.ltnm-depots" }, + caption = { "cybersyn-gui.depots" }, ref = { "depots", "tab" }, actions = { on_click = { gui = "main", action = "change_tab", tab = "depots" }, @@ -25,7 +25,7 @@ function depots_tab.build(widths) style_mods = { right_padding = 4 }, templates.sort_checkbox(widths, "depots", "name", true, nil, true), templates.sort_checkbox(widths, "depots", "network_id", false), - templates.sort_checkbox(widths, "depots", "status", false, { "gui.ltnm-status-description" }), + templates.sort_checkbox(widths, "depots", "status", false, { "cybersyn-gui.status-description" }), templates.sort_checkbox(widths, "depots", "trains", false), }, { type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "depots", "scroll_pane" } }, @@ -37,7 +37,7 @@ function depots_tab.build(widths) { type = "label", style = "ltnm_semibold_label", - caption = { "gui.ltnm-no-depots" }, + caption = { "cybersyn-gui.no-depots" }, ref = { "depots", "warning_label" }, }, }, diff --git a/cybersyn/scripts/gui/history.lua b/cybersyn/scripts/gui/history.lua index fa57171..d2277f0 100644 --- a/cybersyn/scripts/gui/history.lua +++ b/cybersyn/scripts/gui/history.lua @@ -12,7 +12,7 @@ function history_tab.build(widths) return { tab = { type = "tab", - caption = { "gui.ltnm-history" }, + caption = { "cybersyn-gui.history" }, ref = { "history", "tab" }, actions = { on_click = { gui = "main", action = "change_tab", tab = "history" }, @@ -38,7 +38,7 @@ function history_tab.build(widths) type = "sprite-button", style = "tool_button_red", sprite = "utility/trash", - tooltip = { "gui.ltnm-clear-history" }, + tooltip = { "cybersyn-gui.clear-history" }, ref = { "history", "clear_button" }, actions = { on_click = { gui = "main", action = "clear_history" }, @@ -54,7 +54,7 @@ function history_tab.build(widths) { type = "label", style = "ltnm_semibold_label", - caption = { "gui.ltnm-no-history" }, + caption = { "cybersyn-gui.no-history" }, ref = { "history", "warning_label" }, }, }, diff --git a/cybersyn/scripts/gui/inventory.lua b/cybersyn/scripts/gui/inventory.lua index f882a03..8540d07 100644 --- a/cybersyn/scripts/gui/inventory.lua +++ b/cybersyn/scripts/gui/inventory.lua @@ -1,20 +1,22 @@ -local misc = require("__flib__.misc") +local gui = require("__flib__.gui-lite") -local templates = require("templates") +local util = require("scripts.gui.util") +local templates = require("scripts.gui.templates") +local format = require("__flib__.format") local inventory_tab = {} -function inventory_tab.build() +function inventory_tab.create() return { tab = { + name = "manager_inventory_tab", type = "tab", - caption = { "gui.ltnm-inventory" }, + caption = { "cybersyn-gui.inventory" }, ref = { "inventory", "tab" }, - actions = { - on_click = { gui = "main", action = "change_tab", tab = "inventory" }, - }, + handler = inventory_tab.handle.on_inventory_tab_selected }, content = { + name = "manager_inventory_content_frame", type = "flow", style_mods = { horizontal_spacing = 12 }, direction = "horizontal", @@ -26,62 +28,245 @@ function inventory_tab.build() } end -local function update_table(self, name, color) - local translations = self.player_table.dictionaries.materials +---@param map_data MapData +---@param player_data PlayerData +function inventory_tab.build(map_data, player_data) - local state = self.state - local refs = self.refs.inventory + local refs = player_data.refs - local search_query = state.search_query - local search_network_id = state.network_id - local search_surface = state.surface + local search_query = player_data.search_query + 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 inventory_provided = {} + local inventory_in_transit = {} + local inventory_requested = {} - local ltn_inventory = state.ltn_data.inventory[name][search_surface] + local stations_sorted = {} - local i = 0 + - local table = refs[name].table - local children = table.children + for id, station in pairs(map_data.stations) do + local entity = station.entity_stop + if not entity.valid then + goto continue + end - for name, count_by_network_id in pairs(ltn_inventory or {}) do - if - bit32.btest(count_by_network_id.combined_id, search_network_id) - and string.find(string.lower(translations[name]), string.lower(search_query)) - then - local running_count = 0 - for network_id, count in pairs(count_by_network_id) do - if network_id ~= "combined_id" and bit32.btest(network_id, search_network_id) then - running_count = running_count + count + if search_query then + if not string.match(entity.backer_name, search_query) then + goto continue + end + end + -- move surface comparison up higher in query to short circuit query earlier if surface doesn't match + if search_surface_idx then + if search_surface_idx == -1 then + goto has_match + elseif entity.surface.index ~= search_surface_idx then + goto continue + end + ::has_match:: + end + if search_network_name then + if search_network_name == (NETWORK_EACH or NETWORK_ANYTHING) then + goto has_match + end + if search_network_name ~= station.network_name then + goto continue + end + ::has_match:: + local train_flag = get_network_flag(station, station.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_item then + if station.deliveries then + for item_name, _ in pairs(station.deliveries) do + if item_name == search_item then + goto has_match + end + end + end + local comb1_signals, _ = get_signals(station) + if comb1_signals then + for _, signal_ID in pairs(comb1_signals) do + local item = signal_ID.signal.name + if item then + if item == search_item then + goto has_match + end + end + end + end + goto continue + ::has_match:: + end + + stations_sorted[#stations_sorted + 1] = id + ::continue:: + end + + for i, station_id in pairs(stations_sorted) do + --- @class Station + local station = map_data.stations[station_id] + + local comb1_signals, _ = get_signals(station) + if comb1_signals then + for _, v in pairs(comb1_signals) do + local item = v.signal + local count = v.count + if item.type ~= "virtual" then + if count > 0 then + if inventory_provided[item.name] == nil then + inventory_provided[item.name] = count + else + inventory_provided[item.name] = inventory_provided[item.name] + count + end + else + if inventory_requested[item.name] == nil then + inventory_requested[item.name] = count + else + inventory_requested[item.name] = inventory_requested[item.name] + count + end + end end end + end - if running_count > 0 then - i = i + 1 - local button = children[i] - if not button then - button = table.add({ type = "sprite-button", style = "flib_slot_button_" .. color, enabled = false }) + local deliveries = station.deliveries + if deliveries then + for item, count in pairs(deliveries) do + if count > 0 then + if inventory_in_transit[item] == nil then + inventory_in_transit[item] = 0 + inventory_in_transit[item] = inventory_in_transit[item] + count + else + inventory_in_transit[item] = inventory_in_transit[item] + count + end end - button.sprite = string.gsub(name, ",", "/") - button.number = running_count - button.tooltip = "[img=" - .. string.gsub(name, ",", "/") - .. "] [font=default-semibold]" - .. translations[name] - .. "[/font]\n" - .. misc.delineate_number(running_count) end end end - for j = i + 1, #children do - children[j].destroy() + local inventory_provided_table = refs.inventory_provided_table + local provided_children = {} + + local i = 0 + for item, count in pairs(inventory_provided) do + i = i + 1 + local sprite, img_path, item_string = util.generate_item_references(item) + if game.is_valid_sprite_path(sprite) then + provided_children[#provided_children+1] = { + type = "sprite-button", + style = "flib_slot_button_green", + enabled = false, + sprite = sprite, + number = count, + tooltip = { "", + img_path, + " [font=default-semibold]", + { item_string }, + "[/font]\n"..format.number(count), + }, + } + end end + + local inventory_requested_table = refs.inventory_requested_table + local requested_children = {} + + local i = 0 + for item, count in pairs(inventory_requested) do + i = i + 1 + local sprite, img_path, item_string = util.generate_item_references(item) + if game.is_valid_sprite_path(sprite) then + requested_children[#requested_children+1] = { + type = "sprite-button", + style = "flib_slot_button_red", + enabled = false, + sprite = sprite, + number = count, + tooltip = { "", + img_path, + " [font=default-semibold]", + { item_string }, + "[/font]\n"..format.number(count), + }, + } + end + end + + local inventory_in_transit_table = refs.inventory_in_transit_table + local in_transit_children = {} + + local i = 0 + for item, count in pairs(inventory_in_transit) do + i = i + 1 + local sprite, img_path, item_string = util.generate_item_references(item) + if game.is_valid_sprite_path(sprite) then + in_transit_children[#in_transit_children+1] = { + type = "sprite-button", + style = "flib_slot_button_blue", + enabled = false, + sprite = sprite, + number = count, + tooltip = { "", + img_path, + " [font=default-semibold]", + { item_string }, + "[/font]\n"..format.number(count), + }, + } + end + end + + if next(inventory_provided_table.children) ~= nil then + refs.inventory_provided_table.clear() + end + if next(inventory_requested_table.children) ~= nil then + refs.inventory_requested_table.clear() + end + if next(inventory_in_transit_table.children) ~= nil then + refs.inventory_in_transit_table.clear() + end + gui.add(refs.inventory_provided_table, provided_children) + gui.add(refs.inventory_requested_table, requested_children) + gui.add(refs.inventory_in_transit_table, in_transit_children) + end -function inventory_tab.update(self) - update_table(self, "provided", "green") - update_table(self, "in_transit", "blue") - update_table(self, "requested", "red") +inventory_tab.handle = {} + +--- @param e {player_index: uint} +function inventory_tab.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, e) end +---@param player LuaPlayer +---@param player_data PlayerData +function inventory_tab.handle.on_inventory_tab_selected(player, player_data) + player_data.selected_tab = "inventory_tab" +end + +gui.add_handlers(inventory_tab.handle, inventory_tab.wrapper) + return inventory_tab diff --git a/cybersyn/scripts/gui/main.lua b/cybersyn/scripts/gui/main.lua index e94adc1..8c8cd4a 100644 --- a/cybersyn/scripts/gui/main.lua +++ b/cybersyn/scripts/gui/main.lua @@ -4,7 +4,7 @@ local mod_gui = require("__core__.lualib.mod-gui") local manager = require("scripts.gui.manager") --- @class Manager ---- @field players table +--- @field players table --- @field item_order table --- @class PlayerData @@ -18,6 +18,7 @@ local manager = require("scripts.gui.manager") --- @field trains_orderings uint[] --- @field trains_orderings_invert boolean[] --- @field pinning boolean +--- @field selected_tab string? @@ -46,7 +47,7 @@ end local manager_gui = {} function manager_gui.on_lua_shortcut(e) - if e.prototype_name == "ltnm-toggle-gui" then + if e.prototype_name == "cybersyn-toggle-gui" then manager.wrapper(e, manager.handle.manager_toggle) end end @@ -65,7 +66,7 @@ function manager_gui.on_player_created(e) } global.manager.players[e.player_index] = player_data - manager.update(global, player, player_data) + --manager.update(global, player, player_data) --top_left_button_update(player, player_data) end @@ -91,6 +92,21 @@ function manager_gui.on_runtime_mod_setting_changed(e) end end +commands.add_command("cybersyn_rebuild_manager_windows", nil, function(command) + local manager_data = global.manager + if manager_data then + + ---@param v PlayerData + for i, v in pairs(manager_data.players) do + local player = game.get_player(i) + if player ~= nil then + v.refs.manager_window.destroy() + v.refs = manager.create(player) + end + end + end +end) + --- @param manager Manager local function init_items(manager) @@ -135,4 +151,17 @@ function manager_gui.on_init() end --gui.handle_events() +---@param global cybersyn.global +function manager_gui.tick(global) + local manager_data = global.manager + if manager_data then + for i, v in pairs(manager_data.players) do + if v.is_manager_open then + local query_limit = settings.get_player_settings(i)["cybersyn-manager-result-limit"].value + manager.update(global, v, query_limit) + end + end + end +end + return manager_gui diff --git a/cybersyn/scripts/gui/manager.lua b/cybersyn/scripts/gui/manager.lua index c801d73..70d3320 100644 --- a/cybersyn/scripts/gui/manager.lua +++ b/cybersyn/scripts/gui/manager.lua @@ -8,9 +8,10 @@ local templates = require("scripts.gui.templates") local stations_tab = require("scripts.gui.stations") --local trains_tab = require("scripts.gui.trains") --local depots_tab = require("scripts.gui.depots") ---local inventory_tab = require("scripts.gui.inventory") +local inventory_tab = require("scripts.gui.inventory") --local history_tab = require("scripts.gui.history") --local alerts_tab = require("scripts.gui.alerts") +local util = require("scripts.gui.util") local manager = {} @@ -36,19 +37,19 @@ function manager.create(player) style = "flib_titlebar_flow", handler = manager.handle.manager_titlebar_click, children = { - { type = "label", style = "frame_title", caption = { "mod-name.LtnManager" }, ignored_by_interaction = true }, + { type = "label", style = "frame_title", caption = { "mod-name.cybersyn" }, ignored_by_interaction = true }, { type = "empty-widget", style = "flib_titlebar_drag_handle", ignored_by_interaction = true }, { name = "manager_dispatcher_status_label", type = "label", style = "bold_label", style_mods = { font_color = constants.colors.red.tbl, left_margin = -4, top_margin = 1 }, - caption = { "gui.ltnm-dispatcher-disabled" }, - tooltip = { "gui.ltnm-dispatcher-disabled-description" }, + caption = { "cybersyn-gui.dispatcher-disabled" }, + tooltip = { "cybersyn-gui.dispatcher-disabled-description" }, visible = not settings.global["cybersyn-enable-planner"].value, }, - templates.frame_action_button("manager_pin_button", "ltnm_pin", { "gui.ltnm-keep-open" }, manager.handle.manager_pin),--on_gui_clicked - templates.frame_action_button("manager_refresh_button", "ltnm_refresh", { "gui.ltnm-refresh-tooltip" }, manager.handle.manager_refresh_click),--on_gui_clicked + templates.frame_action_button("manager_pin_button", "ltnm_pin", { "cybersyn-gui.keep-open" }, manager.handle.manager_pin),--on_gui_clicked + templates.frame_action_button("manager_refresh_button", "ltnm_refresh", { "cybersyn-gui.refresh-tooltip" }, manager.handle.manager_refresh_click),--on_gui_clicked templates.frame_action_button(nil, "utility/close", { "gui.close-instruction" }, manager.handle.manager_close),--on_gui_clicked }, }, @@ -61,15 +62,20 @@ function manager.create(player) type = "frame", style = "ltnm_main_toolbar_frame", children = { - { type = "label", style = "subheader_caption_label", caption = { "gui.ltnm-search-label" } }, + { type = "label", style = "subheader_caption_label", caption = { "cybersyn-gui.search-label" } }, { name = "manager_text_search_field", type = "textfield", clear_and_focus_on_right_click = true, handler = manager.handle.manager_update_text_search, --on_gui_text_changed }, + --item search box commented out. It *works*, but, the filtering logic only checks delivieres, so I'm not sure what Mami intended it for, so I'm leaving it off for now... + { type = "label", style = "subheader_caption_label", caption = { "cybersyn-gui.search-item-label" } }, + { type= "choose-elem-button", name="manager_item_filter", style="slot_button_in_shallow_frame", elem_type="signal", handler=manager.handle.manager_update_item_search, }, { type = "empty-widget", style = "flib_horizontal_pusher" }, - { type = "label", style = "caption_label", caption = { "gui.ltnm-network-id-label" } }, + { type = "label", style = "caption_label", caption = { "cybersyn-gui.network-name-label" } }, + { type= "choose-elem-button", name="network", style="slot_button_in_shallow_frame", elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, handler=manager.handle.manager_update_network_name, }, + { type = "label", style = "caption_label", caption = { "cybersyn-gui.network-id-label" } }, { name = "manager_network_mask_field", type = "textfield", @@ -80,7 +86,7 @@ function manager.create(player) text = "-1", handler = manager.handle.manager_update_network_mask, --on_gui_text_changed }, - { type = "label", style = "caption_label", caption = { "gui.ltnm-surface-label" } }, + { type = "label", style = "caption_label", caption = { "cybersyn-gui.surface-label" } }, { name = "manager_surface_dropdown", type = "drop-down", @@ -92,10 +98,9 @@ function manager.create(player) name = "manager_tabbed_pane", type = "tabbed-pane", style = "ltnm_tabbed_pane", + stations_tab.create(widths), + inventory_tab.create(), selected_tab_index = 1, - tabs = { - stations_tab.create(widths) - } }, }, }, @@ -111,12 +116,53 @@ function manager.create(player) return refs end +function manager.build(player_data) + local refs = player_data.refs + -- Surface dropdown + --- @type LuaGuiElement + local surface_dropdown = refs.manager_surface_dropdown + local surfaces = game.surfaces + local selected_surface_id = player_data.search_surface_idx + local currently_selected_index = surface_dropdown.selected_index + local currently_selected_surface = nil + if currently_selected_index ~= (nil or 0) then + currently_selected_surface = surface_dropdown.get_item(currently_selected_index) + end + surface_dropdown.clear_items() + surface_dropdown.add_item("all", 1) + i = 1 + for name, _ in pairs(surfaces) do + i = i + 1 + surface_dropdown.add_item(name, i) + --reselect same surface + if name == currently_selected_surface then + refs.manager_surface_dropdown.selected_index = i + end + end + -- Validate that the selected index still exist + if selected_surface_id then + local selected_surface = game.get_surface(selected_surface_id) + -- If the surface was invalidated since last update, reset to all + if not selected_surface then + player_data.search_surface_idx = -1 + end + else + player_data.search_surface_idx = -1 + end + +end + --- @param map_data MapData ---- @param player LuaPlayer --- @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) +function manager.update(map_data, player_data, query_limit) + if player_data.selected_tab ~= nil then + manager.build(player_data) + end + if player_data.selected_tab == "stations_tab" then + stations_tab.build(map_data, player_data, query_limit) + elseif player_data.selected_tab == "inventory_tab" then + inventory_tab.build(map_data, player_data) + end end @@ -157,7 +203,7 @@ function manager.handle.manager_open(player, player_data, refs) end player_data.is_manager_open = true - player.set_shortcut_toggled("ltnm-toggle-gui", true) + player.set_shortcut_toggled("cybersyn-toggle-gui", true) end @@ -165,23 +211,7 @@ end --- @param player_data PlayerData --- @param refs table function manager.handle.manager_close(player, player_data, refs) - if player_data.pinning then - return - end - - refs.manager_window.visible = false - player_data.visible = false - - if player.opened == refs.manager_window then - player.opened = nil - end - - player_data.is_manager_open = false - player.set_shortcut_toggled("ltnm-toggle-gui", false) - - - player_data.refs.manager_window.destroy() - player_data.refs = manager.create(player) + util.close_manager_window(player, player_data, refs) end --- @param player LuaPlayer @@ -233,12 +263,27 @@ function manager.handle.manager_update_text_search(player, player_data, refs, e) player_data.search_query = query end +--- @param player LuaPlayer +--- @param player_data PlayerData +--- @param refs table +--- @param e GuiEventData +function manager.handle.manager_update_item_search(player, player_data, refs, e) + local element = e.element + local signal = element.elem_value + if signal then + player_data.search_item = signal.name + else + player_data.search_item = nil + end +end + --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.handle.manager_update_network_name(player, player_data, refs) - local signal = refs.manager_network_name.elem_value +function manager.handle.manager_update_network_name(player, player_data, refs, e) + local element = e.element + local signal = element.elem_value if signal then player_data.search_network_name = signal.name else @@ -248,18 +293,30 @@ end --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.handle.manager_update_network_mask(player, player_data, refs) - player_data.search_network_mask = tonumber(refs.manager_network_mask_field.text) or -1 +function manager.handle.manager_update_network_mask(player, player_data, refs, e) + player_data.search_network_mask = tonumber(e.text) or -1 end --- @param player LuaPlayer --- @param player_data PlayerData --- @param refs table -function manager.handle.manager_update_surface(player, player_data, refs) - local i = refs.manager_surface_dropdown.selected_index - player_data.search_surface_idx = i--TODO: fix this +function manager.handle.manager_update_surface(player, player_data, refs, e) + --- @type LuaGuiElement + local element = e.element + local i = element.selected_index + local refs = player_data.refs + local surface_id = -1 + --all surfaces should always be the first entry with an index of 1 + if i > 1 then + local surface_name = refs.manager_surface_dropdown.get_item(i) + local surface = game.get_surface(surface_name) + surface_id = surface.index + end + + player_data.search_surface_idx = surface_id end + gui.add_handlers(manager.handle, manager.wrapper) return manager diff --git a/cybersyn/scripts/gui/stations.lua b/cybersyn/scripts/gui/stations.lua index ef5341f..c8ee7c7 100644 --- a/cybersyn/scripts/gui/stations.lua +++ b/cybersyn/scripts/gui/stations.lua @@ -9,14 +9,14 @@ local stations_tab = {} function stations_tab.create(widths) return { tab = { + name = "manager_stations_tab", type = "tab", - caption = { "gui.ltnm-stations" }, + caption = { "cybersyn-gui.stations" }, ref = { "stations", "tab" }, - actions = { - on_click = { gui = "main", action = "change_tab", tab = "stations" }, - }, + handler = stations_tab.handle.on_stations_tab_selected }, content = { + name = "manager_stations_content_frame", type = "frame", style = "ltnm_main_content_frame", direction = "vertical", @@ -25,19 +25,19 @@ function stations_tab.create(widths) type = "frame", style = "ltnm_table_toolbar_frame", templates.sort_checkbox(widths, "stations", "name", true), - templates.sort_checkbox(widths, "stations", "status", false, { "gui.ltnm-status-description" }), + templates.sort_checkbox(widths, "stations", "status", false), --repurposed status column, description no longer necessary templates.sort_checkbox(widths, "stations", "network_id", false), templates.sort_checkbox( widths, "stations", "provided_requested", false, - { "gui.ltnm-provided-requested-description" } + { "cybersyn-gui-provided-requested-description" } ), - templates.sort_checkbox(widths, "stations", "shipments", false, { "gui.ltnm-shipments-description" }), + templates.sort_checkbox(widths, "stations", "shipments", false, { "cybersyn-gui-shipments-description" }), templates.sort_checkbox(widths, "stations", "control_signals", false), }, - { type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "stations", "scroll_pane" } }, + { name = "manager_stations_tab_scroll_pane", type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "stations", "scroll_pane" } }, { type = "flow", style = "ltnm_warning_flow", @@ -46,7 +46,7 @@ function stations_tab.create(widths) { type = "label", style = "ltnm_semibold_label", - caption = { "gui.ltnm-no-stations" }, + caption = { "cybersyn-gui-no-stations" }, ref = { "stations", "warning_label" }, }, }, @@ -57,29 +57,52 @@ end --- @param map_data MapData --- @param player_data PlayerData --- @return GuiElemDef -function stations_tab.build(map_data, player_data) +function stations_tab.build(map_data, player_data, query_limit) local widths = constants.gui["en"] + local refs = player_data.refs + local search_query = player_data.search_query 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 = map_data.stations local stations_sorted = {} local to_sorted_manifest = {} - for id, station in pairs(map_data.stations) do + + local i = 0 + for id, station in pairs(stations) do local entity = station.entity_stop if not entity.valid then goto continue end + + + if search_query then + if not string.match(entity.backer_name, search_query) then + goto continue + end + end + + -- move surface comparison up higher in query to short circuit query earlier if surface doesn't match; this can exclude hundreds of stations instantly in SE + if search_surface_idx then + if search_surface_idx == -1 then + goto has_match + elseif entity.surface.index ~= search_surface_idx then + goto continue + end + ::has_match:: + end + 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) + local train_flag = get_network_flag(station, station.network_name) if not bit32.btest(search_network_mask, train_flag) then goto continue end @@ -98,19 +121,24 @@ function stations_tab.build(map_data, player_data) end end - if search_surface_idx then - if entity.surface.index ~= search_surface_idx then - goto continue - end - end if search_item then - if not station.deliveries then - goto continue + if station.deliveries then + for item_name, _ in pairs(station.deliveries) do + if item_name == search_item then + goto has_match + end + end end - for item_name, _ in pairs(station.deliveries) do - if item_name == search_item then - goto has_match + local comb1_signals, _ = get_signals(station) + if comb1_signals then + for _, signal_ID in pairs(comb1_signals) do + local item = signal_ID.signal.name + if item then + if item == search_item then + goto has_match + end + end end end goto continue @@ -118,6 +146,10 @@ function stations_tab.build(map_data, player_data) end stations_sorted[#stations_sorted + 1] = id + i = i + 1 + if query_limit ~= -1 and i >= query_limit then + break + end ::continue:: end @@ -182,72 +214,127 @@ function stations_tab.build(map_data, player_data) return (not player_data.trains_orderings_invert[#player_data.trains_orderings_invert]) == (a < b) end) - local scroll_pane = refs.scroll_pane - + local scroll_pane = refs.manager_stations_tab_scroll_pane + if next(scroll_pane.children) ~= nil then + refs.manager_stations_tab_scroll_pane.clear() + end for i, station_id in pairs(stations_sorted) do - local station = map_data.stations[station_id] - + --- @type Station + local station = stations[station_id] + local network_sprite = "utility/close_black" + local network_name = station.network_name + local network_flag = get_network_flag(station, network_name) + if network_name ~= nil then + network_sprite, _, _ = util.generate_item_references(network_name) + end local color = i % 2 == 0 and "dark" or "light" - local row = gui.add(scroll_pane, { + gui.add(scroll_pane, { type = "frame", - style = "ltnm_table_row_frame_" .. color, { type = "label", style = "ltnm_clickable_semibold_label", - style_mods = { width = widths.name }, + style_mods = { width = widths.stations.name }, tooltip = constants.open_station_gui_tooltip, + caption = station.entity_stop.backer_name, + handler = stations_tab.handle.open_station_gui, + tags = { station_id = station_id } }, - 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"), - }) + --templates.status_indicator(widths.stations.status, true), --repurposing status column for network name + { type = "sprite-button", style = "ltnm_small_slot_button_default", enabled = false, sprite = network_sprite, }, + { type = "label", style_mods = { width = widths.stations.network_id, horizontal_align = "center" }, caption = network_flag }, + templates.small_slot_table(widths.stations, color, "provided_requested"), + templates.small_slot_table(widths.stations, color, "shipments"), + templates.small_slot_table(widths.stations, color, "control_signals"), + }, refs) - gui.add(row, { - { - elem_mods = { caption = station.entity_stop.name }, - handler = stations_tab.hande.open_station_gui, - tags = station_id, - }, - { elem_mods = { caption = station.network_name } }, - { elem_mods = { caption = station.network_flag } }, - }) + gui.add(refs.provided_requested_table, util.slot_table_build_from_station(station)) + gui.add(refs.shipments_table, util.slot_table_build_from_deliveries(station)) + gui.add(refs.control_signals_table, util.slot_table_build_from_control_signals(station)) - util.slot_table_update(row.provided_requested_frame.provided_requested_table, { - { color = "green", entries = station.provided, translations = dictionaries.materials }, - { color = "red", entries = station.requested, translations = dictionaries.materials }, - }) - util.slot_table_update(row.shipments_frame.shipments_table, { - { color = "green", entries = station.inbound, translations = dictionaries.materials }, - { color = "blue", entries = station.outbound, translations = dictionaries.materials }, - }) - util.slot_table_update(row.control_signals_frame.control_signals_table, { - { - color = "default", - entries = station.control_signals, - translations = dictionaries.virtual_signals, - type = "virtual-signal", - }, - }) end if #stations_sorted == 0 then - refs.warning_flow.visible = true + --refs.warning_flow.visible = true scroll_pane.visible = false - refs.content_frame.style = "ltnm_main_warning_frame" + --refs.content_frame.style = "ltnm_main_warning_frame" else - refs.warning_flow.visible = false + --refs.warning_flow.visible = false scroll_pane.visible = true - refs.content_frame.style = "ltnm_main_content_frame" + --refs.content_frame.style = "ltnm_main_content_frame" end end -stations_tab.hande = {} +stations_tab.handle = {} -function stations_tab.hande.open_station_gui() +--- @param e {player_index: uint} +function stations_tab.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, e) end +--- @param e GuiEventData +--- @param player LuaPlayer +--- @param player_data PlayerData +function stations_tab.handle.open_station_gui(player, player_data, refs, e) + local station_id = e.element.tags.station_id + --- @type Station + local station = global.stations[station_id] + local station_entity = station.entity_stop + local station_comb1 = station.entity_comb1 + local station_comb2 = station.entity_comb2 + + if not station_entity or not station_entity.valid then + util.error_flying_text(player, { "message.ltnm-error-station-is-invalid" }) + return + end + + if e.shift then + if station_entity.surface ~= player.surface then + util.error_flying_text(player, { "cybersyn-message.error-cross-surface-camera-invalid" }) + else + player.zoom_to_world(station_entity.position, 1, station_entity) + + rendering.draw_circle({ + color = constants.colors.red.tbl, + target = station_entity.position, + surface = station_entity.surface, + radius = 0.5, + filled = false, + width = 5, + time_to_live = 60 * 3, + players = { player }, + }) + + if not player_data.pinning then util.close_manager_window(player, player_data, refs) end + end + elseif e.control then + if station_comb1 ~= nil and station_comb1.valid then + player.opened = station_comb1 + else + util.error_flying_text(player, { "cybersyn-message.error-cybernetic-combinator-not-found" }) + end + + elseif e.alt then + if station_comb2 ~= nil and station_comb2.valid then + player.opened = station_comb2 + else + util.error_flying_text(player, { "cybersyn-message.error-station-control-combinator-not-found" }) + end + else + player.opened = station_entity + end +end + +---@param player LuaPlayer +---@param player_data PlayerData +function stations_tab.handle.on_stations_tab_selected(player, player_data) + player_data.selected_tab = "stations_tab" +end + +gui.add_handlers(stations_tab.handle, stations_tab.wrapper) + return stations_tab diff --git a/cybersyn/scripts/gui/templates.lua b/cybersyn/scripts/gui/templates.lua index e2553d3..3490c32 100644 --- a/cybersyn/scripts/gui/templates.lua +++ b/cybersyn/scripts/gui/templates.lua @@ -30,7 +30,7 @@ function templates.inventory_slot_table(name, columns) return { type = "flow", direction = "vertical", - { type = "label", style = "bold_label", caption = { "gui.ltnm-" .. string.gsub(name, "_", "-") } }, + { type = "label", style = "bold_label", caption = { "cybersyn-gui." .. string.gsub(name, "_", "-") } }, { type = "frame", style = "deep_frame_in_shallow_frame", @@ -43,7 +43,7 @@ function templates.inventory_slot_table(name, columns) vertical_scroll_policy = "auto-and-reserve-space", -- vertical_scroll_policy = "always", ref = { "inventory", name, "scroll_pane" }, - { type = "table", style = "slot_table", column_count = columns, ref = { "inventory", name, "table" } }, + { type = "table", name = "inventory_" .. name .. "_table", style = "slot_table", column_count = columns, ref = { "inventory", name, "table" } }, }, }, } @@ -77,7 +77,7 @@ function templates.sort_checkbox(widths, tab, column, selected, tooltip, state) type = "checkbox", style = selected and "ltnm_selected_sort_checkbox" or "ltnm_sort_checkbox", style_mods = { width = widths and widths[tab][column] or nil, horizontally_stretchable = not widths }, - caption = { "gui.ltnm-" .. string.gsub(column, "_", "-") }, + caption = { "cybersyn-gui." .. string.gsub(column, "_", "-") }, tooltip = tooltip, state = state, ref = { tab, "toolbar", column .. "_checkbox" }, diff --git a/cybersyn/scripts/gui/trains.lua b/cybersyn/scripts/gui/trains.lua index 05fd9c8..7c3057c 100644 --- a/cybersyn/scripts/gui/trains.lua +++ b/cybersyn/scripts/gui/trains.lua @@ -12,7 +12,7 @@ local trains_tab = {} --- @param map_data MapData --- @param player_data PlayerData --- @return GuiElemDef -function trains_tab.build(map_data, player_data) +function trains_tab.build(map_data, player_data, query_limit) local widths = constants.gui["en"] local search_item = player_data.search_item @@ -22,6 +22,8 @@ function trains_tab.build(map_data, player_data) local trains_sorted = {} + + local i = 0 for id, train in pairs(map_data.trains) do if search_network_name then if search_network_name ~= train.network_name then @@ -70,6 +72,10 @@ function trains_tab.build(map_data, player_data) end trains_sorted[#trains_sorted + 1] = id + i = i + 1 + if query_limit ~= -1 and i >= query_limit then + break + end ::continue:: end @@ -133,7 +139,7 @@ function trains_tab.build(map_data, player_data) train_list[1] = { type = "label", style = "ltnm_semibold_label", - caption = { "gui.ltnm-no-trains" }, + caption = { "cybersyn-gui.no-trains" }, } else for idx, train_id in ipairs(trains_sorted) do @@ -156,7 +162,7 @@ function trains_tab.build(map_data, player_data) { type = "button", style = "ltnm_train_minimap_button", - tooltip = { "gui.ltnm-open-train-gui" }, + tooltip = { "cybersyn-gui.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 }, @@ -197,7 +203,7 @@ function trains_tab.build(map_data, player_data) tab = { name = "trains_tab", type = "tab", - caption = #trains_sorted == 0 and { "gui.ltnm-trains" } or { "gui.ltnm-trains", #train_list }, + caption = #trains_sorted == 0 and { "cybersyn-gui.trains" } or { "cybersyn-gui.trains", #train_list }, --badge_text = format.number(#ltn_data.sorted_trains.composition), handler = trains_tab.handle.change_tab, --on_click tags = { tab = "trains_tab" }, @@ -212,7 +218,7 @@ function trains_tab.build(map_data, player_data) type = "frame", style = "ltnm_table_toolbar_frame", templates.sort_checkbox(widths, "trains", "status", false), - templates.sort_checkbox(widths, "trains", "layout", false, { "gui.ltnm-composition-description" }), + templates.sort_checkbox(widths, "trains", "layout", false, { "cybersyn-gui.composition-description" }), templates.sort_checkbox(widths, "trains", "depot", false), templates.sort_checkbox(widths, "trains", "shipment", false), }, diff --git a/cybersyn/scripts/gui/util.lua b/cybersyn/scripts/gui/util.lua index 5b3893a..c1981f3 100644 --- a/cybersyn/scripts/gui/util.lua +++ b/cybersyn/scripts/gui/util.lua @@ -33,11 +33,35 @@ function util.gui_list(parent, iterator, test, build, update, ...) end end +--- Builds a valid sprite path or returns nil +--- @param item string +--- @return string, string, string +function util.generate_item_references(item) + local sprite = nil + local image_path = "" + local item_name = "" + if game.is_valid_sprite_path("item/" .. item) then + sprite = "item/" .. item + image_path = "[img=item." .. item .. "]" + item_name = "item-name." .. item + elseif game.is_valid_sprite_path("fluid/" .. item) then + sprite = "fluid/" .. item + image_path = "[img=fluid." .. item .. "]" + item_name = "fluid-name." .. item + elseif game.is_valid_sprite_path("virtual-signal/" .. item) then + sprite = "virtual-signal/" .. item + image_path = "[img=virtual-signal." .. item .. "]" + item_name = "virtual-signal." .. item + end + return sprite, image_path, item_name +end + + --- Updates a slot table based on the passed criteria. --- @param manifest Manifest --- @param color string --- @return GuiElemDef[] -function util.slot_table_build(manifest, color) +function util.slot_table_build_from_manifest(manifest, color) ---@type GuiElemDef[] local children = {} for _, item in pairs(manifest) do @@ -66,6 +90,125 @@ function util.slot_table_build(manifest, color) return children end +--- @param station Station +--- @param color string +--- @return GuiElemDef[] +function util.slot_table_build_from_station(station) + ---@type GuiElemDef[] + local children = {} + local comb1_signals, comb2_signals = get_signals(station) + if comb1_signals then + for _, v in pairs(comb1_signals) do + local item = v.signal + if item.type == "virtual" then + goto continue + end + local count = v.count + local name = item.name + local sprite, img_path, item_string = util.generate_item_references(name) + if sprite ~= nil then + local color + if count > 0 then + color = "green" + else + color = "red" + end + if game.is_valid_sprite_path(sprite) then + children[#children + 1] = { + type = "sprite-button", + enabled = false, + style = "ltnm_small_slot_button_" .. color, + sprite = sprite, + tooltip = { + "", + img_path, + { item_string }, + "\n"..format.number(count), + }, + number = count + } + end + end + ::continue:: + end + end + return children +end + +function util.slot_table_build_from_deliveries(station) + ---@type GuiElemDef[] + local children = {} + local deliveries = station.deliveries + + for item, count in pairs(deliveries) do + + local sprite, img_path, item_string = util.generate_item_references(item) + if sprite ~= nil then + local color + if count > 0 then + color = "green" + else + color = "blue" + end + if game.is_valid_sprite_path(sprite) then + children[#children + 1] = { + type = "sprite-button", + enabled = false, + style = "ltnm_small_slot_button_" .. color, + sprite = sprite, + tooltip = { + "", + img_path, + { item_string }, + "\n"..format.number(count), + }, + number = count + } + end + end + end + return children +end + +--- @param station Station +--- @return GuiElemDef[] +function util.slot_table_build_from_control_signals(station) + ---@type GuiElemDef[] + local children = {} + local comb1_signals, comb2_signals = get_signals(station) + if comb1_signals then + for _, v in pairs(comb1_signals) do + local item = v.signal + local count = v.count + local name = item.name + local sprite = "" + local color = "default" + if item.type ~= "virtual" then + goto continue + else + sprite = "virtual-signal" .. "/" .. name + end + if game.is_valid_sprite_path(sprite) then + children[#children + 1] = { + type = "sprite-button", + enabled = false, + style = "ltnm_small_slot_button_" .. color, + sprite = sprite, + tooltip = { + "", + "[img=virtual-signal." .. name .. "]", + { "virtual-signal-name." .. name }, + "\n"..format.number(count), + }, + number = count + } + end + ::continue:: + end + end + return children +end + function util.sorted_iterator(arr, src_tbl, sort_state) local step = sort_state and 1 or -1 local i = sort_state and 1 or #arr @@ -86,4 +229,21 @@ function util.signed_int32(val) return (val >= MAX_INT and val - (2 * MAX_INT)) or val end +function util.close_manager_window(player, player_data, refs) + if player_data.pinning then + return + end + + refs.manager_window.visible = false + player_data.visible = false + + if player.opened == refs.manager_window then + player.opened = nil + end + + player_data.is_manager_open = false + player.set_shortcut_toggled("cybersyn-toggle-gui", false) + +end + return util diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 5b5b0a4..eea7286 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -835,6 +835,8 @@ local function grab_all_settings() mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as double]] mod_settings.allow_cargo_in_depot = settings.global["cybersyn-allow-cargo-in-depot"].value--[[@as boolean]] mod_settings.invert_sign = settings.global["cybersyn-invert-sign"].value--[[@as boolean]] + mod_settings.manager_enabled = settings.startup["cybersyn-manager-enabled"].value--[[@as boolean]] + mod_settings.manager_update_rate = settings.startup["cybersyn-manager-update-rate"].value--[[@as int]] end local function on_settings_changed(event) grab_all_settings() @@ -913,7 +915,7 @@ local function main() end - local MANAGER_ENABLED = true + local MANAGER_ENABLED = mod_settings.manager_enabled script.on_init(function() local setting = settings.global["cybersyn-invert-sign"] @@ -943,6 +945,10 @@ local function main() script.on_event(defines.events.on_player_removed, manager.on_player_removed) script.on_event(defines.events.on_player_created, manager.on_player_created) script.on_event(defines.events.on_lua_shortcut, manager.on_lua_shortcut) + -- TODO: rework this to work as a per-player runtime setting + script.on_nth_tick(mod_settings.manager_update_rate, function() + manager.tick(global) + end) end end diff --git a/cybersyn/scripts/remote-interface.lua b/cybersyn/scripts/remote-interface.lua index 169889f..efaa420 100644 --- a/cybersyn/scripts/remote-interface.lua +++ b/cybersyn/scripts/remote-interface.lua @@ -243,6 +243,7 @@ function interface.update_stop_from_rail(rail, forbidden_entity, force_update) update_stop_from_rail(global, rail, forbidden_entity, force_update) end + ------------------------------------------------------------------ --[[unsafe API]] ------------------------------------------------------------------ diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index 664fcca..56522c0 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -102,6 +102,31 @@ data:extend({ setting_type = "runtime-global", default_value = false, }, + { + type = "int-setting", + name = "cybersyn-manager-update-rate", + order = "ad", + setting_type = "startup", + default_value = 60, + minimum_value = 1, + maximum_value = 2147483647, + }, + { + type = "bool-setting", + name = "cybersyn-manager-enabled", + order = "aa", + setting_type = "startup", + default_value = true, + }, + { + type = "int-setting", + name = "cybersyn-manager-result-limit", + order = "aa", + setting_type = "runtime-per-user", + default_value = -1, + minimum_value = -1, + maximum_value = 2147483647, + } --{ -- type = "bool-setting", -- name = "cybersyn-disable-top-left-button",