Merge pull request #40 from wdberry/make-gui-work

Fully Functional Stations & Inventory Tabs in Manager
This commit is contained in:
Will Berry
2023-03-13 16:53:34 -04:00
committed by GitHub
23 changed files with 784 additions and 212 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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" },
},
},
{},

View File

@@ -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

View File

@@ -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" },
},
},

View File

@@ -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" },
},
},

View File

@@ -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

View File

@@ -4,7 +4,7 @@ local mod_gui = require("__core__.lualib.mod-gui")
local manager = require("scripts.gui.manager")
--- @class Manager
--- @field players table<int, PlayerData>
--- @field players table<uint, PlayerData>
--- @field item_order table<string, int>
--- @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

View File

@@ -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<string, LuaGuiElement>
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<string, LuaGuiElement>
--- @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<string, LuaGuiElement>
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<string, LuaGuiElement>
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<string, LuaGuiElement>
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

View File

@@ -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

View File

@@ -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" },

View File

@@ -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),
},

View File

@@ -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

View File

@@ -825,6 +825,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()
@@ -903,7 +905,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"]
@@ -933,6 +935,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

View File

@@ -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]]
------------------------------------------------------------------